mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-04 14:16:21 +01:00
Compare commits
216 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1e8903128 | ||
|
|
b00ac2aef4 | ||
|
|
8865d15ed9 | ||
|
|
fc3d4b7f33 | ||
|
|
934cc44540 | ||
|
|
106de3dd4c | ||
|
|
9b55cfcbe3 | ||
|
|
8137f1023a | ||
|
|
d670e33b6f | ||
|
|
3d3bb8fc94 | ||
|
|
9c880eae8a | ||
|
|
54a71630f1 | ||
|
|
923a8453cc | ||
|
|
00447ca819 | ||
|
|
ad4ee9d822 | ||
|
|
40e9dbfda2 | ||
|
|
b9da6911e6 | ||
|
|
81f9211098 | ||
|
|
8290081365 | ||
|
|
81af610c11 | ||
|
|
cfa2cf58f3 | ||
|
|
01c17d28f6 | ||
|
|
efd2a5e8c5 | ||
|
|
c437b00727 | ||
|
|
5df0d1ddc6 | ||
|
|
c3521b0d87 | ||
|
|
148916d35e | ||
|
|
06c7da944c | ||
|
|
f2ba741499 | ||
|
|
0f9927686b | ||
|
|
59855a71ef | ||
|
|
fffb21bb4f | ||
|
|
30bb6cdfab | ||
|
|
8771477884 | ||
|
|
55a5070691 | ||
|
|
1182b51e4b | ||
|
|
9976ecc2aa | ||
|
|
3bd8107fcf | ||
|
|
a48c67de5c | ||
|
|
bb0a7c014e | ||
|
|
80729b6b06 | ||
|
|
ff168d1c9e | ||
|
|
331100370c | ||
|
|
47babf2ed7 | ||
|
|
c1388bf23f | ||
|
|
3f4dfe9b0b | ||
|
|
0caac20d77 | ||
|
|
9d0a65a516 | ||
|
|
ab061ba7a6 | ||
|
|
2d24d064d5 | ||
|
|
458685026b | ||
|
|
b0448a4565 | ||
|
|
f902f59b31 | ||
|
|
2b8bfbe544 | ||
|
|
564a25e578 | ||
|
|
f50b08d301 | ||
|
|
26566d9ce0 | ||
|
|
f42d438be2 | ||
|
|
1fbc39b614 | ||
|
|
36af471c3c | ||
|
|
a5e86c9a36 | ||
|
|
4b27dd40b9 | ||
|
|
ff0aac9d8a | ||
|
|
bf24c8253e | ||
|
|
fd92352302 | ||
|
|
fcb66a11a3 | ||
|
|
63d2021a73 | ||
|
|
d110d9c74e | ||
|
|
157bf81dcb | ||
|
|
1beb1b1b4f | ||
|
|
13f523679c | ||
|
|
ed816d9aea | ||
|
|
ebfb51eed5 | ||
|
|
0127b1ea9e | ||
|
|
61f41a1459 | ||
|
|
d3fbc133a2 | ||
|
|
ce7a8e7a3d | ||
|
|
2b45610080 | ||
|
|
5b4dd6731c | ||
|
|
80157a032a | ||
|
|
7b1bf9a4e2 | ||
|
|
337c4d9ce5 | ||
|
|
9afc36a009 | ||
|
|
ebff1387c1 | ||
|
|
801502ec77 | ||
|
|
c07f59201e | ||
|
|
98b799d821 | ||
|
|
02714956d8 | ||
|
|
4d435d58c5 | ||
|
|
1c2fd3be50 | ||
|
|
6222439e21 | ||
|
|
46d46ac90b | ||
|
|
a5fe61eb15 | ||
|
|
0bfec36983 | ||
|
|
11607ecf24 | ||
|
|
9970e5535b | ||
|
|
99492e9c34 | ||
|
|
1d6ecffb3b | ||
|
|
8c33349dcb | ||
|
|
cfb2c55a4b | ||
|
|
e24d29f1d3 | ||
|
|
debd6ef2b9 | ||
|
|
58e9c21e4f | ||
|
|
c7c3083e3e | ||
|
|
63d061e9f2 | ||
|
|
1887bdb90a | ||
|
|
69addb068a | ||
|
|
a62c3ce0f9 | ||
|
|
aecc94bdb3 | ||
|
|
fcb7f2905a | ||
|
|
34c7169ca3 | ||
|
|
6e450061f4 | ||
|
|
fc3d7e9f43 | ||
|
|
514db4f9a2 | ||
|
|
23a8b65ce2 | ||
|
|
f8e6662bc8 | ||
|
|
89be2456fb | ||
|
|
bfd3451717 | ||
|
|
0b759d6a32 | ||
|
|
65e05084e6 | ||
|
|
f25a4ed386 | ||
|
|
b2a1d41829 | ||
|
|
2741a92d31 | ||
|
|
3570ce86d7 | ||
|
|
d809902d1e | ||
|
|
ec4232c00a | ||
|
|
dec793bfac | ||
|
|
fe3fe0527a | ||
|
|
a8855e86ed | ||
|
|
179d1c38e6 | ||
|
|
287da73a4f | ||
|
|
e9ed917888 | ||
|
|
70d842c971 | ||
|
|
bcda228e05 | ||
|
|
000dafc5e6 | ||
|
|
4ea824fe71 | ||
|
|
f72f539516 | ||
|
|
1b192a184f | ||
|
|
0edf896b4c | ||
|
|
7dec4deb70 | ||
|
|
d511221899 | ||
|
|
d2b7de5221 | ||
|
|
79c5be02e2 | ||
|
|
09df37438d | ||
|
|
8561e4c6fd | ||
|
|
976cb4d988 | ||
|
|
20f7d5103c | ||
|
|
d049ec2832 | ||
|
|
00fe2a527e | ||
|
|
f53ec3b43e | ||
|
|
4d0417f114 | ||
|
|
00903b64db | ||
|
|
a3038cad00 | ||
|
|
ef99f1afac | ||
|
|
cc00d4bd04 | ||
|
|
250f26ff6f | ||
|
|
62b786ca4a | ||
|
|
9cfb47e658 | ||
|
|
ccef27b637 | ||
|
|
8dee61fd39 | ||
|
|
ae64bd0e19 | ||
|
|
a75e93dbfc | ||
|
|
0aa66c5729 | ||
|
|
4c2434219d | ||
|
|
8c65fda33b | ||
|
|
14f2751bbb | ||
|
|
d37a543c39 | ||
|
|
4947e0c483 | ||
|
|
f87c630b86 | ||
|
|
73789ea734 | ||
|
|
5a16c9c495 | ||
|
|
9dd8357f67 | ||
|
|
623e77a268 | ||
|
|
73403b98df | ||
|
|
7aa1a2f336 | ||
|
|
08e42d2f56 | ||
|
|
69248fd7bb | ||
|
|
0af188c6ab | ||
|
|
8b6d32d0d1 | ||
|
|
3c11c25d69 | ||
|
|
12e6cc63e8 | ||
|
|
d429c8b59a | ||
|
|
ddd7a3551b | ||
|
|
49ede92e06 | ||
|
|
b813213328 | ||
|
|
14065b3ca9 | ||
|
|
41f9dc490a | ||
|
|
48d25ca73f | ||
|
|
e49e04034c | ||
|
|
c7860f8e5c | ||
|
|
adb982114a | ||
|
|
5b8983deac | ||
|
|
1730bc3b98 | ||
|
|
4374064d98 | ||
|
|
c1d7994045 | ||
|
|
7bda367cc8 | ||
|
|
3de7a2ccd2 | ||
|
|
9cc278df31 | ||
|
|
a0bab07e2f | ||
|
|
149bbd92ca | ||
|
|
1de3c989d7 | ||
|
|
2e547945e2 | ||
|
|
4d4a9a27af | ||
|
|
5b41d0995f | ||
|
|
ab98d72022 | ||
|
|
8a7cd3f74d | ||
|
|
35cb56d6e9 | ||
|
|
a7a2ffd16b | ||
|
|
dbeda324e0 | ||
|
|
bf1fe99d98 | ||
|
|
41429ec7c7 | ||
|
|
ee9ed13a66 | ||
|
|
490ce286ff | ||
|
|
c8ad1dcc7a | ||
|
|
0b7520e3b1 | ||
|
|
48c8ccfe97 |
@@ -8,6 +8,8 @@ omit =
|
|||||||
*/example/*
|
*/example/*
|
||||||
*/project_template/*
|
*/project_template/*
|
||||||
*/bin/*
|
*/bin/*
|
||||||
|
*/tests/*
|
||||||
|
*/tests.py
|
||||||
|
|
||||||
[report]
|
[report]
|
||||||
exclude_lines =
|
exclude_lines =
|
||||||
|
|||||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -62,3 +62,13 @@ celerybeat-schedule
|
|||||||
|
|
||||||
#pycharm
|
#pycharm
|
||||||
.idea/*
|
.idea/*
|
||||||
|
/nbproject/
|
||||||
|
|
||||||
|
#VSCode
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
#gitlab configs
|
||||||
|
.gitlab/
|
||||||
|
|
||||||
|
#transifex
|
||||||
|
.tx/
|
||||||
|
|||||||
61
.gitlab-ci.yml
Normal file
61
.gitlab-ci.yml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
stages:
|
||||||
|
- "test"
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- python -V
|
||||||
|
- pip install wheel tox
|
||||||
|
|
||||||
|
test-3.5-core:
|
||||||
|
image: python:3.5-buster
|
||||||
|
script:
|
||||||
|
- tox -e py35-core
|
||||||
|
|
||||||
|
test-3.6-core:
|
||||||
|
image: python:3.6-buster
|
||||||
|
script:
|
||||||
|
- tox -e py36-core
|
||||||
|
|
||||||
|
test-3.7-core:
|
||||||
|
image: python:3.7-buster
|
||||||
|
script:
|
||||||
|
- tox -e py37-core
|
||||||
|
|
||||||
|
test-3.8-core:
|
||||||
|
image: python:3.8-buster
|
||||||
|
script:
|
||||||
|
- tox -e py38-core
|
||||||
|
|
||||||
|
test-3.5-all:
|
||||||
|
image: python:3.5-buster
|
||||||
|
script:
|
||||||
|
- tox -e py35-all
|
||||||
|
|
||||||
|
test-3.6-all:
|
||||||
|
image: python:3.6-buster
|
||||||
|
script:
|
||||||
|
- tox -e py36-all
|
||||||
|
|
||||||
|
test-3.7-all:
|
||||||
|
image: python:3.7-buster
|
||||||
|
script:
|
||||||
|
- tox -e py37-all
|
||||||
|
|
||||||
|
test-3.8-all:
|
||||||
|
image: python:3.8-buster
|
||||||
|
script:
|
||||||
|
- tox -e py38-all
|
||||||
|
|
||||||
|
deploy_production:
|
||||||
|
stage: deploy
|
||||||
|
image: python:3.6-stretch
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- pip install twine
|
||||||
|
|
||||||
|
script:
|
||||||
|
- python setup.py sdist
|
||||||
|
- twine upload dist/*
|
||||||
|
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
14
.gitlab/issue_templates/Bug.md
Normal file
14
.gitlab/issue_templates/Bug.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Bug
|
||||||
|
|
||||||
|
- I have searched [issues](https://gitlab.com/allianceauth/allianceauth/issues?scope=all&utf8=%E2%9C%93&state=all) (Y/N):
|
||||||
|
- What Version of Alliance Auth:
|
||||||
|
- What Operating System:
|
||||||
|
- Version of other components relevant to issue eg. Service, Database:
|
||||||
|
|
||||||
|
Please include a brief description of your issue here.
|
||||||
|
|
||||||
|
Please include steps to reproduce the issue
|
||||||
|
|
||||||
|
Please include any tracebacks or logs
|
||||||
|
|
||||||
|
Please include the results of the command `pip list`
|
||||||
7
.gitlab/issue_templates/Feature Request.md
Normal file
7
.gitlab/issue_templates/Feature Request.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Feature Request
|
||||||
|
|
||||||
|
- Describe the feature are you requesting.
|
||||||
|
|
||||||
|
- Is this a Service (external integration), a Module (Alliance Auth extension) or an enhancement to an existing service/module.
|
||||||
|
|
||||||
|
- Describe why its useful to you or others.
|
||||||
25
.travis.yml
25
.travis.yml
@@ -1,25 +0,0 @@
|
|||||||
language: python
|
|
||||||
sudo: false
|
|
||||||
cache: pip
|
|
||||||
dist: trusty
|
|
||||||
install:
|
|
||||||
- pip install coveralls>=1.1 tox
|
|
||||||
# command to run tests
|
|
||||||
script:
|
|
||||||
- tox
|
|
||||||
after_success:
|
|
||||||
coveralls
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- env: TOXENV=py35-dj111
|
|
||||||
python: '3.5'
|
|
||||||
- env: TOXENV=py36-dj111
|
|
||||||
python: '3.6'
|
|
||||||
- env: TOXENV=py35-dj20
|
|
||||||
python: '3.5'
|
|
||||||
- env: TOXENV=py36-dj20
|
|
||||||
python: '3.6'
|
|
||||||
- env: TOXENV=py37-dj20
|
|
||||||
python: '3.7-dev'
|
|
||||||
allow_failures:
|
|
||||||
- env: TOXENV=py37-dj20
|
|
||||||
90
README.md
90
README.md
@@ -1,34 +1,84 @@
|
|||||||
Alliance Auth
|
# Alliance Auth
|
||||||
============
|
|
||||||
|
|
||||||
[](https://gitter.im/R4stl1n/allianceauth?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://pypi.org/project/allianceauth/)
|
||||||
|
[](https://pypi.org/project/allianceauth/)
|
||||||
|
[](https://pypi.org/project/allianceauth/)
|
||||||
|
[](https://pypi.org/project/allianceauth/)
|
||||||
|
[](https://gitlab.com/allianceauth/allianceauth/commits/master)
|
||||||
[](http://allianceauth.readthedocs.io/?badge=latest)
|
[](http://allianceauth.readthedocs.io/?badge=latest)
|
||||||
[](https://travis-ci.org/allianceauth/allianceauth)
|
[](https://gitlab.com/allianceauth/allianceauth/commits/master)
|
||||||
[](https://coveralls.io/github/allianceauth/allianceauth?branch=master)
|
[](https://discord.gg/fjnHAmk)
|
||||||
|
|
||||||
|
|
||||||
An auth system for EVE Online to help in-game organizations manage online service access.
|
An auth system for EVE Online to help in-game organizations manage online service access.
|
||||||
|
|
||||||
[Read the docs here.](http://allianceauth.rtfd.io)
|
## Contens
|
||||||
|
|
||||||
[Get help on gitter](https://gitter.im/R4stl1n/allianceauth) or submit an Issue.
|
- [Overview](#overview)
|
||||||
|
- [Documentation](http://allianceauth.rtfd.io)
|
||||||
|
- [Support](#support)
|
||||||
|
- [Release Notes](https://gitlab.com/allianceauth/allianceauth/-/releases)
|
||||||
|
- [Devloper Team](#developer-team)
|
||||||
|
- [Contributing](#contributing)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
Active Developers:
|
Alliance Auth (AA) is a web application that helps Eve Online organizations efficiently manage access to their applications and services.
|
||||||
|
|
||||||
- [Adarnof](https://github.com/adarnof/)
|
Main features:
|
||||||
- [Basraah](https://github.com/basraah/)
|
|
||||||
|
|
||||||
Beta Testers / Bug Fixers:
|
- Automatically grants or revokes user access to external applications / services (e.g. Discord, Mumble) and web apps (e.g. SRP requests) based on the user's current membership to [in-game organizations](https://allianceauth.readthedocs.io/en/latest/features/states/) and [groups](https://allianceauth.readthedocs.io/en/latest/features/groups/)
|
||||||
|
|
||||||
- [ghoti](https://github.com/ghoti/)
|
- Provides a central web site where users can directly access web apps (e.g. SRP requests, Fleet Schedule) and manage their access to external services and groups.
|
||||||
- [mmolitor87](https://github.com/mmolitor87/)
|
|
||||||
- [TargetZ3R0](https://github.com/TargetZ3R0)
|
- Includes a set of connectors (called ["services"](https://allianceauth.readthedocs.io/en/latest/installation/services/)) for integrating access management with many popular external applications / services like Discord, Mumble, Teamspeak 3, SMF and others
|
||||||
- [kaezon](https://github.com/kaezon/)
|
|
||||||
- [orbitroom](https://github.com/orbitroom/)
|
- Includes a set of web apps called ["plug-in apps"](https://allianceauth.readthedocs.io/en/latest/features/) which add many useful functions: fleet schedule, timer board, SRP request management, fleet activity tracker and character application management
|
||||||
- [tehfiend](https://github.com/tehfiend/)
|
|
||||||
|
- Can be easily extended with new services and plugin-apps. Many additional services and plugin-apps are provided by the community and can be found here: [Community Creations](https://gitlab.com/allianceauth/community-creations)
|
||||||
|
|
||||||
|
For further details about AA - including an installation guide and a full list of included services and plugin apps - please see the [offical documentation](http://allianceauth.rtfd.io).
|
||||||
|
|
||||||
|
## Screenshot
|
||||||
|
|
||||||
|
Here is an example of the Alliance Auth web site with some plug-ins apps and services enabled:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
[Get help on Discord](https://discord.gg/fjnHAmk) or submit an [issue](https://gitlab.com/allianceauth/allianceauth/issues).
|
||||||
|
|
||||||
|
## Development Team
|
||||||
|
|
||||||
|
### Active Developers
|
||||||
|
|
||||||
|
- [Aaron Kable](https://gitlab.com/aaronkable/)
|
||||||
|
- [Ariel Rin](https://gitlab.com/soratidus999/)
|
||||||
|
- [Basraah](https://gitlab.com/basraah/)
|
||||||
|
- [Col Crunch](https://gitlab.com/colcrunch/)
|
||||||
|
- [Erik Kalkoken](https://gitlab.com/ErikKalkoken/)
|
||||||
|
|
||||||
|
### Former Developers
|
||||||
|
|
||||||
|
- [Adarnof](https://gitlab.com/adarnof/)
|
||||||
|
|
||||||
|
### Beta Testers / Bug Fixers
|
||||||
|
|
||||||
|
- [ghoti](https://gitlab.com/ChainsawMcGinny/)
|
||||||
|
- [kaezon](https://github.com/kaezon/)
|
||||||
|
- [mmolitor87](https://gitlab.com/mmolitor87/)
|
||||||
|
- [orbitroom](https://github.com/orbitroom/)
|
||||||
|
- [TargetZ3R0](https://github.com/TargetZ3R0)
|
||||||
|
- [tehfiend](https://github.com/tehfiend/)
|
||||||
|
|
||||||
Special thanks to [Nikdoof](https://github.com/nikdoof/), as his [auth](https://github.com/nikdoof/test-auth) was the foundation for the original work on this project.
|
Special thanks to [Nikdoof](https://github.com/nikdoof/), as his [auth](https://github.com/nikdoof/test-auth) was the foundation for the original work on this project.
|
||||||
|
|
||||||
### Contributing
|
## Contributing
|
||||||
Make sure you have signed the [License Agreement](https://developers.eveonline.com/resource/license-agreement) by logging in at [https://developers.eveonline.com](https://developers.eveonline.com) before submitting any pull requests.
|
|
||||||
|
Alliance Auth is maintained and developed by the community and we welcome every contribution!
|
||||||
|
|
||||||
|
To see what needs to be worked on please review our issue list or chat with our active developers on Discord.
|
||||||
|
|
||||||
|
Also, please make sure you have signed the [License Agreement](https://developers.eveonline.com/resource/license-agreement) by logging in at [https://developers.eveonline.com](https://developers.eveonline.com) before submitting any pull requests.
|
||||||
|
|
||||||
|
In addition to the core AA system we also very much welcome contributions to our growing list of 3rd party services and plugin apps. Please see [AA Community Creations](https://gitlab.com/allianceauth/community-creations) for details.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 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.0.3'
|
__version__ = '2.6.2'
|
||||||
NAME = 'Alliance Auth v%s' % __version__
|
NAME = 'Alliance Auth v%s' % __version__
|
||||||
default_app_config = 'allianceauth.apps.AllianceAuthConfig'
|
default_app_config = 'allianceauth.apps.AllianceAuthConfig'
|
||||||
|
|||||||
@@ -1,15 +1,33 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||||
from django.contrib.auth.models import User as BaseUser, Permission as BasePermission
|
from django.contrib.auth.models import User as BaseUser, \
|
||||||
from django.utils.text import slugify
|
Permission as BasePermission, Group
|
||||||
from django.db.models import Q
|
from django.db.models import Q, F
|
||||||
from allianceauth.services.hooks import ServicesHook
|
from allianceauth.services.hooks import ServicesHook
|
||||||
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete, m2m_changed
|
from django.db.models.signals import pre_save, post_save, pre_delete, \
|
||||||
|
post_delete, m2m_changed
|
||||||
|
from django.db.models.functions import Lower
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from allianceauth.authentication.models import State, get_guest_state, CharacterOwnership, UserProfile, OwnershipRecord
|
|
||||||
from allianceauth.hooks import get_hooks
|
|
||||||
from allianceauth.eveonline.models import EveCharacter
|
|
||||||
from django.forms import ModelForm
|
from django.forms import ModelForm
|
||||||
|
from django.utils.html import format_html
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.text import slugify
|
||||||
|
|
||||||
|
from allianceauth.authentication.models import State, get_guest_state,\
|
||||||
|
CharacterOwnership, UserProfile, OwnershipRecord
|
||||||
|
from allianceauth.hooks import get_hooks
|
||||||
|
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo,\
|
||||||
|
EveAllianceInfo
|
||||||
|
from allianceauth.eveonline.tasks import update_character
|
||||||
|
from .app_settings import AUTHENTICATION_ADMIN_USERS_MAX_GROUPS, \
|
||||||
|
AUTHENTICATION_ADMIN_USERS_MAX_CHARS
|
||||||
|
|
||||||
|
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
|
||||||
|
_has_auto_groups = True
|
||||||
|
else:
|
||||||
|
_has_auto_groups = False
|
||||||
|
|
||||||
|
|
||||||
def make_service_hooks_update_groups_action(service):
|
def make_service_hooks_update_groups_action(service):
|
||||||
@@ -83,41 +101,324 @@ class UserProfileInline(admin.StackedInline):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def user_profile_pic(obj):
|
||||||
|
"""profile pic column data for user objects
|
||||||
|
|
||||||
|
works for both User objects and objects with `user` as FK to User
|
||||||
|
To be used for all user based admin lists (requires CSS)
|
||||||
|
"""
|
||||||
|
user_obj = obj.user if hasattr(obj, 'user') else obj
|
||||||
|
if user_obj.profile.main_character:
|
||||||
|
return format_html(
|
||||||
|
'<img src="{}" class="img-circle">',
|
||||||
|
user_obj.profile.main_character.portrait_url(size=32)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
user_profile_pic.short_description = ''
|
||||||
|
|
||||||
|
|
||||||
|
def user_username(obj):
|
||||||
|
"""user column data for user objects
|
||||||
|
|
||||||
|
works for both User objects and objects with `user` as FK to User
|
||||||
|
To be used for all user based admin lists
|
||||||
|
"""
|
||||||
|
link = reverse(
|
||||||
|
'admin:{}_{}_change'.format(
|
||||||
|
obj._meta.app_label,
|
||||||
|
type(obj).__name__.lower()
|
||||||
|
),
|
||||||
|
args=(obj.pk,)
|
||||||
|
)
|
||||||
|
user_obj = obj.user if hasattr(obj, 'user') else obj
|
||||||
|
if user_obj.profile.main_character:
|
||||||
|
return format_html(
|
||||||
|
'<strong><a href="{}">{}</a></strong><br>{}',
|
||||||
|
link,
|
||||||
|
user_obj.username,
|
||||||
|
user_obj.profile.main_character.character_name
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return format_html(
|
||||||
|
'<strong><a href="{}">{}</a></strong>',
|
||||||
|
link,
|
||||||
|
user_obj.username,
|
||||||
|
)
|
||||||
|
|
||||||
|
user_username.short_description = 'user / main'
|
||||||
|
user_username.admin_order_field = 'username'
|
||||||
|
|
||||||
|
|
||||||
|
def user_main_organization(obj):
|
||||||
|
"""main organization column data for user objects
|
||||||
|
|
||||||
|
works for both User objects and objects with `user` as FK to User
|
||||||
|
To be used for all user based admin lists
|
||||||
|
"""
|
||||||
|
user_obj = obj.user if hasattr(obj, 'user') else obj
|
||||||
|
if not user_obj.profile.main_character:
|
||||||
|
result = None
|
||||||
|
else:
|
||||||
|
corporation = user_obj.profile.main_character.corporation_name
|
||||||
|
if user_obj.profile.main_character.alliance_id:
|
||||||
|
result = format_html('{}<br>{}',
|
||||||
|
corporation,
|
||||||
|
user_obj.profile.main_character.alliance_name
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
result = corporation
|
||||||
|
return result
|
||||||
|
|
||||||
|
user_main_organization.short_description = 'Corporation / Alliance (Main)'
|
||||||
|
user_main_organization.admin_order_field = \
|
||||||
|
'profile__main_character__corporation_name'
|
||||||
|
|
||||||
|
|
||||||
|
class MainCorporationsFilter(admin.SimpleListFilter):
|
||||||
|
"""Custom filter to filter on corporations 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 = 'corporation'
|
||||||
|
parameter_name = 'main_corporation_id__exact'
|
||||||
|
|
||||||
|
def lookups(self, request, model_admin):
|
||||||
|
qs = EveCharacter.objects\
|
||||||
|
.exclude(userprofile=None)\
|
||||||
|
.values('corporation_id', 'corporation_name')\
|
||||||
|
.distinct()\
|
||||||
|
.order_by(Lower('corporation_name'))
|
||||||
|
return tuple(
|
||||||
|
[(x['corporation_id'], x['corporation_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__corporation_id=\
|
||||||
|
self.value())
|
||||||
|
else:
|
||||||
|
return qs\
|
||||||
|
.filter(user__profile__main_character__corporation_id=\
|
||||||
|
self.value())
|
||||||
|
|
||||||
|
|
||||||
|
class MainAllianceFilter(admin.SimpleListFilter):
|
||||||
|
"""Custom filter to filter on alliances 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 = 'alliance'
|
||||||
|
parameter_name = 'main_alliance_id__exact'
|
||||||
|
|
||||||
|
def lookups(self, request, model_admin):
|
||||||
|
qs = EveCharacter.objects\
|
||||||
|
.exclude(alliance_id=None)\
|
||||||
|
.exclude(userprofile=None)\
|
||||||
|
.values('alliance_id', 'alliance_name')\
|
||||||
|
.distinct()\
|
||||||
|
.order_by(Lower('alliance_name'))
|
||||||
|
return tuple(
|
||||||
|
[(x['alliance_id'], x['alliance_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__alliance_id=self.value())
|
||||||
|
else:
|
||||||
|
return qs\
|
||||||
|
.filter(user__profile__main_character__alliance_id=\
|
||||||
|
self.value())
|
||||||
|
|
||||||
|
|
||||||
|
def update_main_character_model(modeladmin, request, queryset):
|
||||||
|
tasks_count = 0
|
||||||
|
for obj in queryset:
|
||||||
|
if obj.profile.main_character:
|
||||||
|
update_character.delay(obj.profile.main_character.character_id)
|
||||||
|
tasks_count += 1
|
||||||
|
|
||||||
|
modeladmin.message_user(
|
||||||
|
request,
|
||||||
|
'Update from ESI started for {} characters'.format(tasks_count)
|
||||||
|
)
|
||||||
|
|
||||||
|
update_main_character_model.short_description = \
|
||||||
|
'Update main character model from ESI'
|
||||||
|
|
||||||
|
|
||||||
class UserAdmin(BaseUserAdmin):
|
class UserAdmin(BaseUserAdmin):
|
||||||
|
"""Extending Django's UserAdmin model
|
||||||
|
|
||||||
|
Behavior of groups and characters columns can be configured via settings
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Extending Django's UserAdmin model
|
|
||||||
"""
|
class Media:
|
||||||
|
css = {
|
||||||
|
"all": ("authentication/css/admin.css",)
|
||||||
|
}
|
||||||
|
|
||||||
|
class RealGroupsFilter(admin.SimpleListFilter):
|
||||||
|
"""Custom filter to get groups w/o Autogroups"""
|
||||||
|
title = 'group'
|
||||||
|
parameter_name = 'group_id__exact'
|
||||||
|
|
||||||
|
def lookups(self, request, model_admin):
|
||||||
|
qs = Group.objects.all().order_by(Lower('name'))
|
||||||
|
if _has_auto_groups:
|
||||||
|
qs = qs\
|
||||||
|
.filter(managedalliancegroup__isnull=True)\
|
||||||
|
.filter(managedcorpgroup__isnull=True)
|
||||||
|
return tuple([(x.pk, x.name) for x in qs])
|
||||||
|
|
||||||
|
def queryset(self, request, queryset):
|
||||||
|
if self.value() is None:
|
||||||
|
return queryset.all()
|
||||||
|
else:
|
||||||
|
return queryset.filter(groups__pk=self.value())
|
||||||
|
|
||||||
def get_actions(self, request):
|
def get_actions(self, request):
|
||||||
actions = super(BaseUserAdmin, self).get_actions(request)
|
actions = super(BaseUserAdmin, self).get_actions(request)
|
||||||
|
|
||||||
|
actions[update_main_character_model.__name__] = (
|
||||||
|
update_main_character_model,
|
||||||
|
update_main_character_model.__name__,
|
||||||
|
update_main_character_model.short_description
|
||||||
|
)
|
||||||
|
|
||||||
for hook in get_hooks('services_hook'):
|
for hook in get_hooks('services_hook'):
|
||||||
svc = hook()
|
svc = hook()
|
||||||
# Check update_groups is redefined/overloaded
|
# Check update_groups is redefined/overloaded
|
||||||
if svc.update_groups.__module__ != ServicesHook.update_groups.__module__:
|
if svc.update_groups.__module__ != ServicesHook.update_groups.__module__:
|
||||||
action = make_service_hooks_update_groups_action(svc)
|
action = make_service_hooks_update_groups_action(svc)
|
||||||
actions[action.__name__] = (action,
|
actions[action.__name__] = (
|
||||||
action.__name__,
|
action,
|
||||||
action.short_description)
|
action.__name__,
|
||||||
|
action.short_description
|
||||||
|
)
|
||||||
|
|
||||||
# Create sync nickname action if service implements it
|
# Create sync nickname action if service implements it
|
||||||
if svc.sync_nickname.__module__ != ServicesHook.sync_nickname.__module__:
|
if svc.sync_nickname.__module__ != ServicesHook.sync_nickname.__module__:
|
||||||
action = make_service_hooks_sync_nickname_action(svc)
|
action = make_service_hooks_sync_nickname_action(svc)
|
||||||
actions[action.__name__] = (action,
|
actions[action.__name__] = (
|
||||||
action.__name__,
|
action, action.__name__,
|
||||||
action.short_description)
|
action.short_description
|
||||||
|
)
|
||||||
return actions
|
return actions
|
||||||
list_filter = BaseUserAdmin.list_filter + ('profile__state',)
|
|
||||||
|
def _list_2_html_w_tooltips(self, my_items: list, max_items: int) -> str:
|
||||||
|
"""converts list of strings into HTML with cutoff and tooltip"""
|
||||||
|
items_truncated_str = ', '.join(my_items[:max_items])
|
||||||
|
if not my_items:
|
||||||
|
result = None
|
||||||
|
elif len(my_items) <= max_items:
|
||||||
|
result = items_truncated_str
|
||||||
|
else:
|
||||||
|
items_truncated_str += ', (...)'
|
||||||
|
items_all_str = ', '.join(my_items)
|
||||||
|
result = format_html(
|
||||||
|
'<span data-tooltip="{}" class="tooltip">{}</span>',
|
||||||
|
items_all_str,
|
||||||
|
items_truncated_str
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
inlines = BaseUserAdmin.inlines + [UserProfileInline]
|
inlines = BaseUserAdmin.inlines + [UserProfileInline]
|
||||||
list_display = ('username', 'email', 'get_main_character', 'get_state', 'is_active')
|
|
||||||
|
|
||||||
def get_main_character(self, obj):
|
ordering = ('username', )
|
||||||
return obj.profile.main_character
|
list_select_related = True
|
||||||
get_main_character.short_description = "Main Character"
|
show_full_result_count = True
|
||||||
|
|
||||||
|
list_display = (
|
||||||
|
user_profile_pic,
|
||||||
|
user_username,
|
||||||
|
'_state',
|
||||||
|
'_groups',
|
||||||
|
user_main_organization,
|
||||||
|
'_characters',
|
||||||
|
'is_active',
|
||||||
|
'date_joined',
|
||||||
|
'_role'
|
||||||
|
)
|
||||||
|
list_display_links = None
|
||||||
|
|
||||||
def get_state(self, obj):
|
list_filter = (
|
||||||
return obj.profile.state
|
'profile__state',
|
||||||
get_state.short_description = "State"
|
RealGroupsFilter,
|
||||||
|
MainCorporationsFilter,
|
||||||
|
MainAllianceFilter,
|
||||||
|
'is_active',
|
||||||
|
'date_joined',
|
||||||
|
'is_staff',
|
||||||
|
'is_superuser'
|
||||||
|
)
|
||||||
|
search_fields = (
|
||||||
|
'username',
|
||||||
|
'character_ownerships__character__character_name'
|
||||||
|
)
|
||||||
|
|
||||||
|
def _characters(self, obj):
|
||||||
|
my_characters = [
|
||||||
|
x.character.character_name
|
||||||
|
for x in CharacterOwnership.objects\
|
||||||
|
.filter(user=obj)\
|
||||||
|
.order_by('character__character_name')\
|
||||||
|
.select_related()
|
||||||
|
]
|
||||||
|
return self._list_2_html_w_tooltips(
|
||||||
|
my_characters,
|
||||||
|
AUTHENTICATION_ADMIN_USERS_MAX_CHARS
|
||||||
|
)
|
||||||
|
|
||||||
|
_characters.short_description = 'characters'
|
||||||
|
|
||||||
|
|
||||||
|
def _state(self, obj):
|
||||||
|
return obj.profile.state.name
|
||||||
|
|
||||||
|
_state.short_description = 'state'
|
||||||
|
_state.admin_order_field = 'profile__state'
|
||||||
|
|
||||||
|
def _groups(self, obj):
|
||||||
|
if not _has_auto_groups:
|
||||||
|
my_groups = [x.name for x in obj.groups.order_by('name')]
|
||||||
|
else:
|
||||||
|
my_groups = [
|
||||||
|
x.name for x in obj.groups\
|
||||||
|
.filter(managedalliancegroup__isnull=True)\
|
||||||
|
.filter(managedcorpgroup__isnull=True)\
|
||||||
|
.order_by('name')
|
||||||
|
]
|
||||||
|
|
||||||
|
return self._list_2_html_w_tooltips(
|
||||||
|
my_groups,
|
||||||
|
AUTHENTICATION_ADMIN_USERS_MAX_GROUPS
|
||||||
|
)
|
||||||
|
|
||||||
|
_groups.short_description = 'groups'
|
||||||
|
|
||||||
|
def _role(self, obj):
|
||||||
|
if obj.is_superuser:
|
||||||
|
role = 'Superuser'
|
||||||
|
elif obj.is_staff:
|
||||||
|
role = 'Staff'
|
||||||
|
else:
|
||||||
|
role = 'User'
|
||||||
|
return role
|
||||||
|
|
||||||
|
_role.short_description = 'role'
|
||||||
|
|
||||||
def has_change_permission(self, request, obj=None):
|
def has_change_permission(self, request, obj=None):
|
||||||
return request.user.has_perm('auth.change_user')
|
return request.user.has_perm('auth.change_user')
|
||||||
|
|
||||||
@@ -127,19 +428,54 @@ class UserAdmin(BaseUserAdmin):
|
|||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
return request.user.has_perm('auth.delete_user')
|
return request.user.has_perm('auth.delete_user')
|
||||||
|
|
||||||
|
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||||
|
"""overriding this formfield to have sorted lists in the form"""
|
||||||
|
if db_field.name == "groups":
|
||||||
|
kwargs["queryset"] = Group.objects.all().order_by(Lower('name'))
|
||||||
|
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(State)
|
@admin.register(State)
|
||||||
class StateAdmin(admin.ModelAdmin):
|
class StateAdmin(admin.ModelAdmin):
|
||||||
|
list_select_related = True
|
||||||
|
list_display = ('name', 'priority', '_user_count')
|
||||||
|
|
||||||
|
def _user_count(self, obj):
|
||||||
|
return obj.userprofile_set.all().count()
|
||||||
|
_user_count.short_description = 'Users'
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {
|
(None, {
|
||||||
'fields': ('name', 'permissions', 'priority'),
|
'fields': ('name', 'permissions', 'priority'),
|
||||||
}),
|
}),
|
||||||
('Membership', {
|
('Membership', {
|
||||||
'fields': ('public', 'member_characters', 'member_corporations', 'member_alliances'),
|
'fields': (
|
||||||
|
'public',
|
||||||
|
'member_characters',
|
||||||
|
'member_corporations',
|
||||||
|
'member_alliances'
|
||||||
|
),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
filter_horizontal = ['member_characters', 'member_corporations', 'member_alliances', 'permissions']
|
filter_horizontal = [
|
||||||
list_display = ('name', 'priority', 'user_count')
|
'member_characters',
|
||||||
|
'member_corporations',
|
||||||
|
'member_alliances',
|
||||||
|
'permissions'
|
||||||
|
]
|
||||||
|
|
||||||
|
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||||
|
"""overriding this formfield to have sorted lists in the form"""
|
||||||
|
if db_field.name == "member_characters":
|
||||||
|
kwargs["queryset"] = EveCharacter.objects.all()\
|
||||||
|
.order_by(Lower('character_name'))
|
||||||
|
elif db_field.name == "member_corporations":
|
||||||
|
kwargs["queryset"] = EveCorporationInfo.objects.all()\
|
||||||
|
.order_by(Lower('corporation_name'))
|
||||||
|
elif db_field.name == "member_alliances":
|
||||||
|
kwargs["queryset"] = EveAllianceInfo.objects.all()\
|
||||||
|
.order_by(Lower('alliance_name'))
|
||||||
|
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||||
|
|
||||||
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():
|
||||||
@@ -154,15 +490,31 @@ class StateAdmin(admin.ModelAdmin):
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
return super(StateAdmin, self).get_fieldsets(request, obj=obj)
|
return super(StateAdmin, self).get_fieldsets(request, obj=obj)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def user_count(obj):
|
|
||||||
return obj.userprofile_set.all().count()
|
|
||||||
|
|
||||||
|
|
||||||
class BaseOwnershipAdmin(admin.ModelAdmin):
|
class BaseOwnershipAdmin(admin.ModelAdmin):
|
||||||
list_display = ('user', 'character')
|
class Media:
|
||||||
search_fields = ('user__username', 'character__character_name', 'character__corporation_name', 'character__alliance_name')
|
css = {
|
||||||
|
"all": ("authentication/css/admin.css",)
|
||||||
|
}
|
||||||
|
|
||||||
|
list_select_related = True
|
||||||
|
list_display = (
|
||||||
|
user_profile_pic,
|
||||||
|
user_username,
|
||||||
|
user_main_organization,
|
||||||
|
'character',
|
||||||
|
)
|
||||||
|
search_fields = (
|
||||||
|
'user__user',
|
||||||
|
'character__character_name',
|
||||||
|
'character__corporation_name',
|
||||||
|
'character__alliance_name'
|
||||||
|
)
|
||||||
|
list_filter = (
|
||||||
|
MainCorporationsFilter,
|
||||||
|
MainAllianceFilter,
|
||||||
|
)
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
if obj and obj.pk:
|
if obj and obj.pk:
|
||||||
|
|||||||
46
allianceauth/authentication/app_settings.py
Normal file
46
allianceauth/authentication/app_settings.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
def _clean_setting(
|
||||||
|
name: str,
|
||||||
|
default_value: object,
|
||||||
|
min_value: int = None,
|
||||||
|
max_value: int = None,
|
||||||
|
required_type: type = None
|
||||||
|
):
|
||||||
|
"""cleans the input for a custom setting
|
||||||
|
|
||||||
|
Will use `default_value` if settings does not exit or has the wrong type
|
||||||
|
or is outside define boundaries (for int only)
|
||||||
|
|
||||||
|
Need to define `required_type` if `default_value` is `None`
|
||||||
|
|
||||||
|
Will assume `min_value` of 0 for int (can be overriden)
|
||||||
|
|
||||||
|
Returns cleaned value for setting
|
||||||
|
"""
|
||||||
|
if default_value is None and not required_type:
|
||||||
|
raise ValueError('You must specify a required_type for None defaults')
|
||||||
|
|
||||||
|
if not required_type:
|
||||||
|
required_type = type(default_value)
|
||||||
|
|
||||||
|
if min_value is None and required_type == int:
|
||||||
|
min_value = 0
|
||||||
|
|
||||||
|
if (hasattr(settings, name)
|
||||||
|
and isinstance(getattr(settings, name), required_type)
|
||||||
|
and (min_value is None or getattr(settings, name) >= min_value)
|
||||||
|
and (max_value is None or getattr(settings, name) <= max_value)
|
||||||
|
):
|
||||||
|
return getattr(settings, name)
|
||||||
|
else:
|
||||||
|
return default_value
|
||||||
|
|
||||||
|
|
||||||
|
AUTHENTICATION_ADMIN_USERS_MAX_GROUPS = \
|
||||||
|
_clean_setting('AUTHENTICATION_ADMIN_USERS_MAX_GROUPS', 10)
|
||||||
|
|
||||||
|
AUTHENTICATION_ADMIN_USERS_MAX_CHARS = \
|
||||||
|
_clean_setting('AUTHENTICATION_ADMIN_USERS_MAX_CHARS', 5)
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ class StateBackend(ModelBackend):
|
|||||||
user_obj._perm_cache.update(self.get_state_permissions(user_obj))
|
user_obj._perm_cache.update(self.get_state_permissions(user_obj))
|
||||||
return user_obj._perm_cache
|
return user_obj._perm_cache
|
||||||
|
|
||||||
def authenticate(self, token=None):
|
def authenticate(self, request=None, token=None, **credentials):
|
||||||
if not token:
|
if not token:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from django.conf.urls import include
|
from django.conf.urls import include
|
||||||
|
from django.contrib.auth.decorators import user_passes_test
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
@@ -35,3 +37,32 @@ def main_character_required(view_func):
|
|||||||
messages.error(request, _('A main character is required to perform that action. Add one below.'))
|
messages.error(request, _('A main character is required to perform that action. Add one below.'))
|
||||||
return redirect('authentication:dashboard')
|
return redirect('authentication:dashboard')
|
||||||
return login_required(_wrapped_view)
|
return login_required(_wrapped_view)
|
||||||
|
|
||||||
|
|
||||||
|
def permissions_required(perm, login_url=None, raise_exception=False):
|
||||||
|
"""
|
||||||
|
Decorator for views that checks whether a user has a particular permission
|
||||||
|
enabled, redirecting to the log-in page if necessary.
|
||||||
|
If the raise_exception parameter is given the PermissionDenied exception
|
||||||
|
is raised.
|
||||||
|
|
||||||
|
This decorator is identical to the django permission_required except it
|
||||||
|
allows for passing a tuple/list of perms that will return true if any one
|
||||||
|
of them is present.
|
||||||
|
"""
|
||||||
|
def check_perms(user):
|
||||||
|
if isinstance(perm, str):
|
||||||
|
perms = (perm,)
|
||||||
|
else:
|
||||||
|
perms = perm
|
||||||
|
# First check if the user has the permission (even anon users)
|
||||||
|
for perm_ in perms:
|
||||||
|
perm_ = (perm_,)
|
||||||
|
if user.has_perms(perm_):
|
||||||
|
return True
|
||||||
|
# In case the 403 handler should be called raise the exception
|
||||||
|
if raise_exception:
|
||||||
|
raise PermissionDenied
|
||||||
|
# As the last resort, show the login form
|
||||||
|
return False
|
||||||
|
return user_passes_test(check_perms, login_url=login_url)
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def remove_permission(apps, schema_editor):
|
||||||
|
User = apps.get_model('auth', 'User')
|
||||||
|
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||||
|
Permission = apps.get_model('auth', 'Permission')
|
||||||
|
ct = ContentType.objects.get_for_model(User)
|
||||||
|
Permission.objects.filter(codename="view_fleetup", content_type=ct, name="view_fleetup").delete()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('authentication', '0016_ownershiprecord'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(remove_permission, migrations.RunPython.noop)
|
||||||
|
]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
CSS for allianceauth admin site
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* styling for profile pic */
|
||||||
|
.img-circle {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.column-user_profile_pic {
|
||||||
|
width: 1px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tooltip */
|
||||||
|
.tooltip {
|
||||||
|
position: relative ;
|
||||||
|
}
|
||||||
|
.tooltip:hover::after {
|
||||||
|
content: attr(data-tooltip) ;
|
||||||
|
position: absolute ;
|
||||||
|
top: 1.1em ;
|
||||||
|
left: 1em ;
|
||||||
|
min-width: 200px ;
|
||||||
|
border: 1px #808080 solid ;
|
||||||
|
padding: 8px ;
|
||||||
|
color: black ;
|
||||||
|
background-color: rgb(255, 255, 204) ;
|
||||||
|
z-index: 1 ;
|
||||||
|
}
|
||||||
@@ -10,77 +10,99 @@
|
|||||||
{% include 'allianceauth/admin-status/include.html' %}
|
{% include 'allianceauth/admin-status/include.html' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="row vertical-flexbox-row">
|
<div class="row vertical-flexbox-row2">
|
||||||
<div class="col-sm-6 text-center">
|
<div class="col-sm-6 text-center">
|
||||||
<div class="panel panel-primary" style="height:100%">
|
<div class="panel panel-primary" style="height:100%">
|
||||||
<div class="panel-heading"><h3 class="panel-title">{% trans "Main Character" %}</h3></div>
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">{% trans "Main Character" %}</h3>
|
||||||
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% if request.user.profile.main_character %}
|
{% if request.user.profile.main_character %}
|
||||||
{% with request.user.profile.main_character as main %}
|
{% with request.user.profile.main_character as main %}
|
||||||
<div class="col-lg-4 col-sm-2">
|
<div class="hidden-xs">
|
||||||
<table class="table">
|
<div class="col-lg-4 col-sm-2">
|
||||||
<tr>
|
<table class="table">
|
||||||
<td class="text-center"><img class="ra-avatar"
|
<tr>
|
||||||
src="{{ main.portrait_url_128 }}">
|
<td class="text-center">
|
||||||
</td>
|
<img class="ra-avatar"src="{{ main.portrait_url_128 }}">
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td class="text-center">{{ main.character_name }}</td>
|
<tr>
|
||||||
</tr>
|
<td class="text-center">{{ main.character_name }}</td>
|
||||||
</table>
|
</tr>
|
||||||
</div>
|
</table>
|
||||||
<div class="col-lg-4 col-sm-2">
|
</div>
|
||||||
<table class="table">
|
<div class="col-lg-4 col-sm-2">
|
||||||
<tr>
|
<table class="table">
|
||||||
<td class="text-center"><img class="ra-avatar"
|
<tr>
|
||||||
src="https://image.eveonline.com/Corporation/{{ main.corporation_id }}_128.png">
|
<td class="text-center">
|
||||||
</td>
|
<img class="ra-avatar"src="{{ main.corporation_logo_url_128 }}">
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td class="text-center">{{ main.corporation_name }}</td>
|
<tr>
|
||||||
</tr>
|
<td class="text-center">{{ main.corporation_name }}</td>
|
||||||
</table>
|
</tr>
|
||||||
</div>
|
</table>
|
||||||
<div class="col-lg-4 col-sm-2">
|
</div>
|
||||||
{% if main.alliance_id %}
|
<div class="col-lg-4 col-sm-2">
|
||||||
<table class="table">
|
{% if main.alliance_id %}
|
||||||
<tr>
|
<table class="table">
|
||||||
<td class="text-center"><img class="ra-avatar"
|
<tr>
|
||||||
src="https://image.eveonline.com/Alliance/{{ main.alliance_id }}_128.png">
|
<td class="text-center">
|
||||||
</td>
|
<img class="ra-avatar"src="{{ main.alliance_logo_url_128 }}">
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td class="text-center">{{ main.alliance_name }}</td>
|
<tr>
|
||||||
<tr>
|
<td class="text-center">{{ main.alliance_name }}</td>
|
||||||
</table>
|
<tr>
|
||||||
{% endif %}
|
</table>
|
||||||
</div>
|
{% endif %}
|
||||||
{% endwith %}
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table visible-xs-block">
|
||||||
|
<p>
|
||||||
|
<img class="ra-avatar" src="{{ main.portrait_url_64 }}">
|
||||||
|
<img class="ra-avatar" src="{{ main.corporation_logo_url_64 }}">
|
||||||
|
<img class="ra-avatar" src="{{ main.alliance_logo_url_64 }}">
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>{{ main.character_name }}</strong><br>
|
||||||
|
{{ main.corporation_name }}<br>
|
||||||
|
{{ main.alliance_name }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="alert alert-danger" role="alert">{% trans "No main character set." %}</div>
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{% trans "No main character set." %}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
<div class="col-xs-6">
|
<div class="row">
|
||||||
<a href="{% url 'authentication:add_character' %}" class="btn btn-block btn-info"
|
<div class="col-sm-6 button-wrapper">
|
||||||
title="Add Character">{% trans 'Add Character' %}</a>
|
<a href="{% url 'authentication:add_character' %}" class="btn btn-block btn-info"
|
||||||
</div>
|
title="Add Character">{% trans 'Add Character' %}</a>
|
||||||
<div class="col-xs-6">
|
</div>
|
||||||
<a href="{% url 'authentication:change_main_character' %}" class="btn btn-block btn-info"
|
<div class="col-sm-6 button-wrapper">
|
||||||
title="Change Main Character">{% trans "Change Main" %}</a>
|
<a href="{% url 'authentication:change_main_character' %}" class="btn btn-block btn-info"
|
||||||
|
title="Change Main Character">{% trans "Change Main" %}</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<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"><h3 class="panel-title">{% trans "Groups" %}</h3></div>
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">{% trans "Group Memberships" %}</h3>
|
||||||
|
</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;">
|
||||||
<table class="table table-striped">
|
<table class="table table-aa">
|
||||||
{% for group in user.groups.all %}
|
{% for group in groups %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ group.name }}</td>
|
<td>{{ group.name }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,26 +112,50 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading" style="display:flex;"><h3 class="panel-title">{% trans 'Characters' %}</h3></div>
|
<div class="panel-heading">
|
||||||
<div class="panel-body">
|
<h3 class="panel-title text-center" style="text-align: center">
|
||||||
<table class="table table-hover">
|
{% trans 'Characters' %}
|
||||||
<tr>
|
</h3>
|
||||||
<th class="text-center"></th>
|
</div>
|
||||||
<th class="text-center">{% trans 'Name' %}</th>
|
<div class="panel-body">
|
||||||
<th class="text-center">{% trans 'Corp' %}</th>
|
<table class="table table-aa hidden-xs">
|
||||||
<th class="text-center">{% trans 'Alliance' %}</th>
|
<thead>
|
||||||
</tr>
|
<tr>
|
||||||
{% for ownership in request.user.character_ownerships.all %}
|
<th class="text-center"></th>
|
||||||
{% with ownership.character as char %}
|
<th class="text-center">{% trans 'Name' %}</th>
|
||||||
<tr>
|
<th class="text-center">{% trans 'Corp' %}</th>
|
||||||
<td class="text-center"><img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}">
|
<th class="text-center">{% trans 'Alliance' %}</th>
|
||||||
</td>
|
</tr>
|
||||||
<td class="text-center">{{ char.character_name }}</td>
|
</thead>
|
||||||
<td class="text-center">{{ char.corporation_name }}</td>
|
<tbody>
|
||||||
<td class="text-center">{{ char.alliance_name }}</td>
|
{% for char in characters %}
|
||||||
</tr>
|
<tr>
|
||||||
{% endwith %}
|
<td class="text-center"><img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}">
|
||||||
{% endfor %}
|
</td>
|
||||||
|
<td class="text-center">{{ char.character_name }}</td>
|
||||||
|
<td class="text-center">{{ char.corporation_name }}</td>
|
||||||
|
<td class="text-center">{{ char.alliance_name }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="table table-aa visible-xs-block" style="width: 100%">
|
||||||
|
<tbody>
|
||||||
|
{% for ownership in request.user.character_ownerships.all %}
|
||||||
|
{% with ownership.character as char %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-center" style="vertical-align: middle">
|
||||||
|
<img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}">
|
||||||
|
</td>
|
||||||
|
<td class="text-center" style="vertical-align: middle; width: 100%">
|
||||||
|
<strong>{{ char.character_name }}</strong><br>
|
||||||
|
{{ char.corporation_name }}<br>
|
||||||
|
{{ char.alliance_name|default:"" }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
{% extends 'public/middle_box.html' %}
|
{% extends 'public/middle_box.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% block page_title %}Login{% endblock %}
|
{% block page_title %}Login{% endblock %}
|
||||||
{% block middle_box_content %}
|
{% block middle_box_content %}
|
||||||
<p style="text-align:center">
|
<a href="{% url 'auth_sso_login' %}{% if request.GET.next %}?next={{request.GET.next}}{%endif%}">
|
||||||
<a href="{% url 'auth_sso_login' %}">
|
<img class="img-responsive center-block" src="{% static 'img/sso/EVE_SSO_Login_Buttons_Large_Black.png' %}" border=0>
|
||||||
<img src="{% static 'img/sso/EVE_SSO_Login_Buttons_Large_Black.png' %}" border=0>
|
</a>
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
You're receiving this email because someone has entered this email address while registering for an account on {{ site.domain }}
|
You're receiving this email because someone has entered this email address while registering for an account on {{ site.domain }}
|
||||||
|
|
||||||
If this was you, please go to the following URL 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 }}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% load i18n %}{% autoescape off %}
|
{% load i18n %}{% autoescape off %}
|
||||||
{% blocktrans %}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:" %}
|
{% trans "Please go to the following page and choose a new password:" %}
|
||||||
|
|||||||
11
allianceauth/authentication/tests/__init__.py
Normal file
11
allianceauth/authentication/tests/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
|
||||||
|
def get_admin_change_view_url(obj: object) -> str:
|
||||||
|
return reverse(
|
||||||
|
'admin:{}_{}_change'.format(
|
||||||
|
obj._meta.app_label,
|
||||||
|
type(obj).__name__.lower()
|
||||||
|
),
|
||||||
|
args=(obj.pk,)
|
||||||
|
)
|
||||||
543
allianceauth/authentication/tests/test_admin.py
Normal file
543
allianceauth/authentication/tests/test_admin.py
Normal file
@@ -0,0 +1,543 @@
|
|||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.admin.sites import AdminSite
|
||||||
|
from django.contrib.auth.models import User as BaseUser, Group
|
||||||
|
from django.test import TestCase, RequestFactory, Client
|
||||||
|
|
||||||
|
from allianceauth.authentication.models import CharacterOwnership, State, \
|
||||||
|
OwnershipRecord
|
||||||
|
from allianceauth.eveonline.models import (
|
||||||
|
EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||||
|
)
|
||||||
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
|
|
||||||
|
from ..admin import (
|
||||||
|
BaseUserAdmin,
|
||||||
|
CharacterOwnershipAdmin,
|
||||||
|
PermissionAdmin,
|
||||||
|
StateAdmin,
|
||||||
|
MainCorporationsFilter,
|
||||||
|
MainAllianceFilter,
|
||||||
|
OwnershipRecordAdmin,
|
||||||
|
User,
|
||||||
|
UserAdmin,
|
||||||
|
user_main_organization,
|
||||||
|
user_profile_pic,
|
||||||
|
user_username,
|
||||||
|
update_main_character_model
|
||||||
|
)
|
||||||
|
from . import get_admin_change_view_url
|
||||||
|
|
||||||
|
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
|
||||||
|
_has_auto_groups = True
|
||||||
|
from allianceauth.eveonline.autogroups.models import AutogroupsConfig
|
||||||
|
else:
|
||||||
|
_has_auto_groups = False
|
||||||
|
|
||||||
|
MODULE_PATH = 'allianceauth.authentication.admin'
|
||||||
|
|
||||||
|
|
||||||
|
class MockRequest(object):
|
||||||
|
def __init__(self, user=None):
|
||||||
|
self.user = user
|
||||||
|
|
||||||
|
|
||||||
|
def create_test_data():
|
||||||
|
# groups
|
||||||
|
group_1 = Group.objects.create(
|
||||||
|
name='Group 1'
|
||||||
|
)
|
||||||
|
group_2 = Group.objects.create(
|
||||||
|
name='Group 2'
|
||||||
|
)
|
||||||
|
|
||||||
|
# user 1 - corp and alliance, normal user
|
||||||
|
character_1 = EveCharacter.objects.create(
|
||||||
|
character_id='1001',
|
||||||
|
character_name='Bruce Wayne',
|
||||||
|
corporation_id='2001',
|
||||||
|
corporation_name='Wayne Technologies',
|
||||||
|
corporation_ticker='WT',
|
||||||
|
alliance_id='3001',
|
||||||
|
alliance_name='Wayne Enterprises',
|
||||||
|
alliance_ticker='WE',
|
||||||
|
)
|
||||||
|
character_1a = EveCharacter.objects.create(
|
||||||
|
character_id='1002',
|
||||||
|
character_name='Batman',
|
||||||
|
corporation_id='2001',
|
||||||
|
corporation_name='Wayne Technologies',
|
||||||
|
corporation_ticker='WT',
|
||||||
|
alliance_id='3001',
|
||||||
|
alliance_name='Wayne Enterprises',
|
||||||
|
alliance_ticker='WE',
|
||||||
|
)
|
||||||
|
alliance = EveAllianceInfo.objects.create(
|
||||||
|
alliance_id='3001',
|
||||||
|
alliance_name='Wayne Enterprises',
|
||||||
|
alliance_ticker='WE',
|
||||||
|
executor_corp_id='2001'
|
||||||
|
)
|
||||||
|
EveCorporationInfo.objects.create(
|
||||||
|
corporation_id='2001',
|
||||||
|
corporation_name='Wayne Technologies',
|
||||||
|
corporation_ticker='WT',
|
||||||
|
member_count=42,
|
||||||
|
alliance=alliance
|
||||||
|
)
|
||||||
|
user_1 = User.objects.create_user(
|
||||||
|
character_1.character_name.replace(' ', '_'),
|
||||||
|
'abc@example.com',
|
||||||
|
'password'
|
||||||
|
)
|
||||||
|
CharacterOwnership.objects.create(
|
||||||
|
character=character_1,
|
||||||
|
owner_hash='x1' + character_1.character_name,
|
||||||
|
user=user_1
|
||||||
|
)
|
||||||
|
CharacterOwnership.objects.create(
|
||||||
|
character=character_1a,
|
||||||
|
owner_hash='x1' + character_1a.character_name,
|
||||||
|
user=user_1
|
||||||
|
)
|
||||||
|
user_1.profile.main_character = character_1
|
||||||
|
user_1.profile.save()
|
||||||
|
user_1.groups.add(group_1)
|
||||||
|
|
||||||
|
# user 2 - corp only, staff
|
||||||
|
character_2 = EveCharacter.objects.create(
|
||||||
|
character_id=1003,
|
||||||
|
character_name='Clark Kent',
|
||||||
|
corporation_id=2002,
|
||||||
|
corporation_name='Daily Planet',
|
||||||
|
corporation_ticker='DP',
|
||||||
|
alliance_id=None
|
||||||
|
)
|
||||||
|
EveCorporationInfo.objects.create(
|
||||||
|
corporation_id=2002,
|
||||||
|
corporation_name='Daily Plane',
|
||||||
|
corporation_ticker='DP',
|
||||||
|
member_count=99,
|
||||||
|
alliance=None
|
||||||
|
)
|
||||||
|
user_2 = User.objects.create_user(
|
||||||
|
character_2.character_name.replace(' ', '_'),
|
||||||
|
'abc@example.com',
|
||||||
|
'password'
|
||||||
|
)
|
||||||
|
CharacterOwnership.objects.create(
|
||||||
|
character=character_2,
|
||||||
|
owner_hash='x1' + character_2.character_name,
|
||||||
|
user=user_2
|
||||||
|
)
|
||||||
|
user_2.profile.main_character = character_2
|
||||||
|
user_2.profile.save()
|
||||||
|
user_2.groups.add(group_2)
|
||||||
|
user_2.is_staff = True
|
||||||
|
user_2.save()
|
||||||
|
|
||||||
|
# user 3 - no main, no group, superuser
|
||||||
|
character_3 = EveCharacter.objects.create(
|
||||||
|
character_id=1101,
|
||||||
|
character_name='Lex Luthor',
|
||||||
|
corporation_id=2101,
|
||||||
|
corporation_name='Lex Corp',
|
||||||
|
corporation_ticker='LC',
|
||||||
|
alliance_id=None
|
||||||
|
)
|
||||||
|
EveCorporationInfo.objects.create(
|
||||||
|
corporation_id=2101,
|
||||||
|
corporation_name='Lex Corp',
|
||||||
|
corporation_ticker='LC',
|
||||||
|
member_count=666,
|
||||||
|
alliance=None
|
||||||
|
)
|
||||||
|
EveAllianceInfo.objects.create(
|
||||||
|
alliance_id='3101',
|
||||||
|
alliance_name='Lex World Domination',
|
||||||
|
alliance_ticker='LWD',
|
||||||
|
executor_corp_id=''
|
||||||
|
)
|
||||||
|
user_3 = User.objects.create_user(
|
||||||
|
character_3.character_name.replace(' ', '_'),
|
||||||
|
'abc@example.com',
|
||||||
|
'password'
|
||||||
|
)
|
||||||
|
CharacterOwnership.objects.create(
|
||||||
|
character=character_3,
|
||||||
|
owner_hash='x1' + character_3.character_name,
|
||||||
|
user=user_3
|
||||||
|
)
|
||||||
|
user_3.is_superuser = True
|
||||||
|
user_3.save()
|
||||||
|
return user_1, user_2, user_3, group_1, group_2
|
||||||
|
|
||||||
|
|
||||||
|
class TestCharacterOwnershipAdmin(TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
cls.user_1, _, _, _, _ = create_test_data()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.modeladmin = CharacterOwnershipAdmin(
|
||||||
|
model=User, admin_site=AdminSite()
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_change_view_loads_normally(self):
|
||||||
|
User.objects.create_superuser(
|
||||||
|
username='superuser', password='secret', email='admin@example.com'
|
||||||
|
)
|
||||||
|
c = Client()
|
||||||
|
c.login(username='superuser', password='secret')
|
||||||
|
ownership = self.user_1.character_ownerships.first()
|
||||||
|
response = c.get(get_admin_change_view_url(ownership))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class TestOwnershipRecordAdmin(TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
cls.user_1, _, _, _, _ = create_test_data()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.modeladmin = OwnershipRecordAdmin(
|
||||||
|
model=User, admin_site=AdminSite()
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_change_view_loads_normally(self):
|
||||||
|
User.objects.create_superuser(
|
||||||
|
username='superuser', password='secret', email='admin@example.com'
|
||||||
|
)
|
||||||
|
c = Client()
|
||||||
|
c.login(username='superuser', password='secret')
|
||||||
|
ownership_record = OwnershipRecord.objects\
|
||||||
|
.filter(user=self.user_1)\
|
||||||
|
.first()
|
||||||
|
response = c.get(get_admin_change_view_url(ownership_record))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class TestStateAdmin(TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
create_test_data()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.modeladmin = StateAdmin(
|
||||||
|
model=User, admin_site=AdminSite()
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_change_view_loads_normally(self):
|
||||||
|
User.objects.create_superuser(
|
||||||
|
username='superuser', password='secret', email='admin@example.com'
|
||||||
|
)
|
||||||
|
c = Client()
|
||||||
|
c.login(username='superuser', password='secret')
|
||||||
|
|
||||||
|
guest_state = AuthUtils.get_guest_state()
|
||||||
|
response = c.get(get_admin_change_view_url(guest_state))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
member_state = AuthUtils.get_member_state()
|
||||||
|
response = c.get(get_admin_change_view_url(member_state))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class TestUserAdmin(TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
cls.user_1, cls.user_2, cls.user_3, cls.group_1, cls.group_2 = \
|
||||||
|
create_test_data()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.factory = RequestFactory()
|
||||||
|
self.modeladmin = UserAdmin(
|
||||||
|
model=User, admin_site=AdminSite()
|
||||||
|
)
|
||||||
|
self.character_1 = self.user_1.character_ownerships.first().character
|
||||||
|
|
||||||
|
def _create_autogroups(self):
|
||||||
|
"""create autogroups for corps and alliances"""
|
||||||
|
if _has_auto_groups:
|
||||||
|
autogroups_config = AutogroupsConfig(
|
||||||
|
corp_groups = True,
|
||||||
|
alliance_groups = True
|
||||||
|
)
|
||||||
|
autogroups_config.save()
|
||||||
|
for state in State.objects.all():
|
||||||
|
autogroups_config.states.add(state)
|
||||||
|
autogroups_config.update_corp_group_membership(self.user_1)
|
||||||
|
|
||||||
|
# column rendering
|
||||||
|
|
||||||
|
def test_user_profile_pic_u1(self):
|
||||||
|
expected = ('<img src="https://images.evetech.net/characters/1001/'
|
||||||
|
'portrait?size=32" class="img-circle">')
|
||||||
|
self.assertEqual(user_profile_pic(self.user_1), expected)
|
||||||
|
|
||||||
|
def test_user_profile_pic_u3(self):
|
||||||
|
self.assertIsNone(user_profile_pic(self.user_3))
|
||||||
|
|
||||||
|
def test_user_username_u1(self):
|
||||||
|
expected = (
|
||||||
|
'<strong><a href="/admin/authentication/user/{}/change/">'
|
||||||
|
'Bruce_Wayne</a></strong><br>Bruce Wayne'.format(self.user_1.pk)
|
||||||
|
)
|
||||||
|
self.assertEqual(user_username(self.user_1), expected)
|
||||||
|
|
||||||
|
def test_user_username_u3(self):
|
||||||
|
expected = (
|
||||||
|
'<strong><a href="/admin/authentication/user/{}/change/">'
|
||||||
|
'Lex_Luthor</a></strong>'.format(self.user_3.pk)
|
||||||
|
)
|
||||||
|
self.assertEqual(user_username(self.user_3), expected)
|
||||||
|
|
||||||
|
def test_user_main_organization_u1(self):
|
||||||
|
expected = 'Wayne Technologies<br>Wayne Enterprises'
|
||||||
|
self.assertEqual(user_main_organization(self.user_1), expected)
|
||||||
|
|
||||||
|
def test_user_main_organization_u2(self):
|
||||||
|
expected = 'Daily Planet'
|
||||||
|
self.assertEqual(user_main_organization(self.user_2), expected)
|
||||||
|
|
||||||
|
def test_user_main_organization_u3(self):
|
||||||
|
expected = None
|
||||||
|
self.assertEqual(user_main_organization(self.user_3), expected)
|
||||||
|
|
||||||
|
def test_characters_u1(self):
|
||||||
|
expected = 'Batman, Bruce Wayne'
|
||||||
|
result = self.modeladmin._characters(self.user_1)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_characters_u2(self):
|
||||||
|
expected = 'Clark Kent'
|
||||||
|
result = self.modeladmin._characters(self.user_2)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_characters_u3(self):
|
||||||
|
expected = 'Lex Luthor'
|
||||||
|
result = self.modeladmin._characters(self.user_3)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_groups_u1(self):
|
||||||
|
self._create_autogroups()
|
||||||
|
expected = 'Group 1'
|
||||||
|
result = self.modeladmin._groups(self.user_1)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_groups_u2(self):
|
||||||
|
self._create_autogroups()
|
||||||
|
expected = 'Group 2'
|
||||||
|
result = self.modeladmin._groups(self.user_2)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_groups_u3(self):
|
||||||
|
self._create_autogroups()
|
||||||
|
result = self.modeladmin._groups(self.user_3)
|
||||||
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '._has_auto_groups', False)
|
||||||
|
def test_groups_u1_no_autogroups(self):
|
||||||
|
expected = 'Group 1'
|
||||||
|
result = self.modeladmin._groups(self.user_1)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '._has_auto_groups', False)
|
||||||
|
def test_groups_u2_no_autogroups(self):
|
||||||
|
expected = 'Group 2'
|
||||||
|
result = self.modeladmin._groups(self.user_2)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '._has_auto_groups', False)
|
||||||
|
def test_groups_u3_no_autogroups(self):
|
||||||
|
result = self.modeladmin._groups(self.user_3)
|
||||||
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
def test_state(self):
|
||||||
|
expected = 'Guest'
|
||||||
|
result = self.modeladmin._state(self.user_1)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_role_u1(self):
|
||||||
|
expected = 'User'
|
||||||
|
result = self.modeladmin._role(self.user_1)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_role_u2(self):
|
||||||
|
expected = 'Staff'
|
||||||
|
result = self.modeladmin._role(self.user_2)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_role_u3(self):
|
||||||
|
expected = 'Superuser'
|
||||||
|
result = self.modeladmin._role(self.user_3)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_list_2_html_w_tooltips_no_cutoff(self):
|
||||||
|
items = ['one', 'two', 'three']
|
||||||
|
expected = 'one, two, three'
|
||||||
|
result = self.modeladmin._list_2_html_w_tooltips(items, 5)
|
||||||
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
|
def test_list_2_html_w_tooltips_w_cutoff(self):
|
||||||
|
items = ['one', 'two', 'three']
|
||||||
|
expected = ('<span data-tooltip="one, two, three" '
|
||||||
|
'class="tooltip">one, two, (...)</span>')
|
||||||
|
result = self.modeladmin._list_2_html_w_tooltips(items, 2)
|
||||||
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
|
def test_list_2_html_w_tooltips_empty_list(self):
|
||||||
|
items = []
|
||||||
|
expected = None
|
||||||
|
result = self.modeladmin._list_2_html_w_tooltips(items, 5)
|
||||||
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
|
# actions
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.UserAdmin.message_user', auto_spec=True)
|
||||||
|
@patch(MODULE_PATH + '.update_character')
|
||||||
|
def test_action_update_main_character_model(
|
||||||
|
self, mock_task, mock_message_user
|
||||||
|
):
|
||||||
|
users_qs = User.objects.filter(pk__in=[self.user_1.pk, self.user_2.pk])
|
||||||
|
update_main_character_model(
|
||||||
|
self.modeladmin, MockRequest(self.user_1), users_qs
|
||||||
|
)
|
||||||
|
self.assertEqual(mock_task.delay.call_count, 2)
|
||||||
|
self.assertTrue(mock_message_user.called)
|
||||||
|
|
||||||
|
# filters
|
||||||
|
|
||||||
|
def test_filter_real_groups_with_autogroups(self):
|
||||||
|
|
||||||
|
class UserAdminTest(BaseUserAdmin):
|
||||||
|
list_filter = (UserAdmin.RealGroupsFilter,)
|
||||||
|
|
||||||
|
self._create_autogroups()
|
||||||
|
my_modeladmin = UserAdminTest(User, AdminSite())
|
||||||
|
|
||||||
|
# Make sure the lookups are correct
|
||||||
|
request = self.factory.get('/')
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
filters = changelist.get_filters(request)
|
||||||
|
filterspec = filters[0][0]
|
||||||
|
expected = [
|
||||||
|
(self.group_1.pk, self.group_1.name),
|
||||||
|
(self.group_2.pk, self.group_2.name),
|
||||||
|
]
|
||||||
|
self.assertEqual(filterspec.lookup_choices, expected)
|
||||||
|
|
||||||
|
# Make sure the correct queryset is returned
|
||||||
|
request = self.factory.get('/', {'group_id__exact': self.group_1.pk})
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
queryset = changelist.get_queryset(request)
|
||||||
|
expected = User.objects.filter(groups__in=[self.group_1])
|
||||||
|
self.assertSetEqual(set(queryset), set(expected))
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '._has_auto_groups', False)
|
||||||
|
def test_filter_real_groups_no_autogroups(self):
|
||||||
|
|
||||||
|
class UserAdminTest(BaseUserAdmin):
|
||||||
|
list_filter = (UserAdmin.RealGroupsFilter,)
|
||||||
|
|
||||||
|
my_modeladmin = UserAdminTest(User, AdminSite())
|
||||||
|
|
||||||
|
# Make sure the lookups are correct
|
||||||
|
request = self.factory.get('/')
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
filters = changelist.get_filters(request)
|
||||||
|
filterspec = filters[0][0]
|
||||||
|
expected = [
|
||||||
|
(self.group_1.pk, self.group_1.name),
|
||||||
|
(self.group_2.pk, self.group_2.name),
|
||||||
|
]
|
||||||
|
self.assertEqual(filterspec.lookup_choices, expected)
|
||||||
|
|
||||||
|
# Make sure the correct queryset is returned
|
||||||
|
request = self.factory.get('/', {'group_id__exact': self.group_1.pk})
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
queryset = changelist.get_queryset(request)
|
||||||
|
expected = User.objects.filter(groups__in=[self.group_1])
|
||||||
|
self.assertSetEqual(set(queryset), set(expected))
|
||||||
|
|
||||||
|
def test_filter_main_corporations(self):
|
||||||
|
|
||||||
|
class UserAdminTest(BaseUserAdmin):
|
||||||
|
list_filter = (MainCorporationsFilter,)
|
||||||
|
|
||||||
|
my_modeladmin = UserAdminTest(User, AdminSite())
|
||||||
|
|
||||||
|
# Make sure the lookups are correct
|
||||||
|
request = self.factory.get('/')
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
filters = changelist.get_filters(request)
|
||||||
|
filterspec = filters[0][0]
|
||||||
|
expected = [
|
||||||
|
('2002', 'Daily Planet'),
|
||||||
|
('2001', 'Wayne Technologies'),
|
||||||
|
]
|
||||||
|
self.assertEqual(filterspec.lookup_choices, expected)
|
||||||
|
|
||||||
|
# Make sure the correct queryset is returned
|
||||||
|
request = self.factory.get(
|
||||||
|
'/',
|
||||||
|
{'main_corporation_id__exact': self.character_1.corporation_id}
|
||||||
|
)
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
queryset = changelist.get_queryset(request)
|
||||||
|
expected = [self.user_1]
|
||||||
|
self.assertSetEqual(set(queryset), set(expected))
|
||||||
|
|
||||||
|
def test_filter_main_alliances(self):
|
||||||
|
|
||||||
|
class UserAdminTest(BaseUserAdmin):
|
||||||
|
list_filter = (MainAllianceFilter,)
|
||||||
|
|
||||||
|
my_modeladmin = UserAdminTest(User, AdminSite())
|
||||||
|
|
||||||
|
# Make sure the lookups are correct
|
||||||
|
request = self.factory.get('/')
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
filters = changelist.get_filters(request)
|
||||||
|
filterspec = filters[0][0]
|
||||||
|
expected = [
|
||||||
|
('3001', 'Wayne Enterprises'),
|
||||||
|
]
|
||||||
|
self.assertEqual(filterspec.lookup_choices, expected)
|
||||||
|
|
||||||
|
# Make sure the correct queryset is returned
|
||||||
|
request = self.factory.get(
|
||||||
|
'/',
|
||||||
|
{'main_alliance_id__exact': self.character_1.alliance_id}
|
||||||
|
)
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
queryset = changelist.get_queryset(request)
|
||||||
|
expected = [self.user_1]
|
||||||
|
self.assertSetEqual(set(queryset), set(expected))
|
||||||
|
|
||||||
|
def test_change_view_loads_normally(self):
|
||||||
|
User.objects.create_superuser(
|
||||||
|
username='superuser', password='secret', email='admin@example.com'
|
||||||
|
)
|
||||||
|
c = Client()
|
||||||
|
c.login(username='superuser', password='secret')
|
||||||
|
response = c.get(get_admin_change_view_url(self.user_1))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
@@ -1,23 +1,28 @@
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from django.test import TestCase
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from allianceauth.tests.auth_utils import AuthUtils
|
|
||||||
from .models import CharacterOwnership, UserProfile, State, get_guest_state, OwnershipRecord
|
|
||||||
from .backends import StateBackend
|
|
||||||
from .tasks import check_character_ownership
|
|
||||||
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
|
||||||
from esi.models import Token
|
|
||||||
from esi.errors import IncompleteResponseError
|
|
||||||
from allianceauth.authentication.decorators import main_character_required
|
|
||||||
from django.test.client import RequestFactory
|
|
||||||
from django.http.response import HttpResponse
|
|
||||||
from django.contrib.auth.models import AnonymousUser
|
|
||||||
from django.conf import settings
|
|
||||||
from django.shortcuts import reverse
|
|
||||||
from django.core.management import call_command
|
|
||||||
from urllib import parse
|
from urllib import parse
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.models import AnonymousUser, User
|
||||||
|
from django.core.management import call_command
|
||||||
|
from django.http.response import HttpResponse
|
||||||
|
from django.shortcuts import reverse
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.test.client import RequestFactory
|
||||||
|
|
||||||
|
|
||||||
|
from allianceauth.authentication.decorators import main_character_required
|
||||||
|
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo,\
|
||||||
|
EveAllianceInfo
|
||||||
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
|
from esi.errors import IncompleteResponseError
|
||||||
|
from esi.models import Token
|
||||||
|
|
||||||
|
from ..backends import StateBackend
|
||||||
|
from ..models import CharacterOwnership, UserProfile, State, get_guest_state,\
|
||||||
|
OwnershipRecord
|
||||||
|
from ..tasks import check_character_ownership
|
||||||
|
|
||||||
MODULE_PATH = 'allianceauth.authentication'
|
MODULE_PATH = 'allianceauth.authentication'
|
||||||
|
|
||||||
|
|
||||||
@@ -119,7 +124,7 @@ class BackendTestCase(TestCase):
|
|||||||
def test_authenticate_character_record(self):
|
def test_authenticate_character_record(self):
|
||||||
t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='4')
|
t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='4')
|
||||||
record = OwnershipRecord.objects.create(user=self.old_user, character=self.unclaimed_character, owner_hash='4')
|
record = OwnershipRecord.objects.create(user=self.old_user, character=self.unclaimed_character, owner_hash='4')
|
||||||
user = StateBackend().authenticate(t)
|
user = StateBackend().authenticate(token=t)
|
||||||
self.assertEqual(user, self.old_user)
|
self.assertEqual(user, self.old_user)
|
||||||
self.assertTrue(CharacterOwnership.objects.filter(owner_hash='4', user=self.old_user).exists())
|
self.assertTrue(CharacterOwnership.objects.filter(owner_hash='4', user=self.old_user).exists())
|
||||||
self.assertTrue(user.profile.main_character)
|
self.assertTrue(user.profile.main_character)
|
||||||
102
allianceauth/authentication/tests/test_app_settings.py
Normal file
102
allianceauth/authentication/tests/test_app_settings.py
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
from unittest.mock import Mock, patch
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from .. import app_settings
|
||||||
|
|
||||||
|
MODULE_PATH = 'allianceauth.authentication'
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetAppSetting(TestCase):
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.app_settings.settings')
|
||||||
|
def test_default_if_not_set(self, mock_settings):
|
||||||
|
mock_settings.TEST_SETTING_DUMMY = Mock(spec=None)
|
||||||
|
result = app_settings._clean_setting(
|
||||||
|
'TEST_SETTING_DUMMY',
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
self.assertEqual(result, False)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.app_settings.settings')
|
||||||
|
def test_default_if_not_set_for_none(self, mock_settings):
|
||||||
|
mock_settings.TEST_SETTING_DUMMY = Mock(spec=None)
|
||||||
|
result = app_settings._clean_setting(
|
||||||
|
'TEST_SETTING_DUMMY',
|
||||||
|
None,
|
||||||
|
required_type=int
|
||||||
|
)
|
||||||
|
self.assertEqual(result, None)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.app_settings.settings')
|
||||||
|
def test_true_stays_true(self, mock_settings):
|
||||||
|
mock_settings.TEST_SETTING_DUMMY = True
|
||||||
|
result = app_settings._clean_setting(
|
||||||
|
'TEST_SETTING_DUMMY',
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.app_settings.settings')
|
||||||
|
def test_false_stays_false(self, mock_settings):
|
||||||
|
mock_settings.TEST_SETTING_DUMMY = False
|
||||||
|
result = app_settings._clean_setting(
|
||||||
|
'TEST_SETTING_DUMMY',
|
||||||
|
False
|
||||||
|
)
|
||||||
|
self.assertEqual(result, False)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.app_settings.settings')
|
||||||
|
def test_default_for_invalid_type_bool(self, mock_settings):
|
||||||
|
mock_settings.TEST_SETTING_DUMMY = 'invalid type'
|
||||||
|
result = app_settings._clean_setting(
|
||||||
|
'TEST_SETTING_DUMMY',
|
||||||
|
False
|
||||||
|
)
|
||||||
|
self.assertEqual(result, False)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.app_settings.settings')
|
||||||
|
def test_default_for_invalid_type_int(self, mock_settings):
|
||||||
|
mock_settings.TEST_SETTING_DUMMY = 'invalid type'
|
||||||
|
result = app_settings._clean_setting(
|
||||||
|
'TEST_SETTING_DUMMY',
|
||||||
|
50
|
||||||
|
)
|
||||||
|
self.assertEqual(result, 50)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.app_settings.settings')
|
||||||
|
def test_default_if_below_minimum_1(self, mock_settings):
|
||||||
|
mock_settings.TEST_SETTING_DUMMY = -5
|
||||||
|
result = app_settings._clean_setting(
|
||||||
|
'TEST_SETTING_DUMMY',
|
||||||
|
default_value=50
|
||||||
|
)
|
||||||
|
self.assertEqual(result, 50)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.app_settings.settings')
|
||||||
|
def test_default_if_below_minimum_2(self, mock_settings):
|
||||||
|
mock_settings.TEST_SETTING_DUMMY = -50
|
||||||
|
result = app_settings._clean_setting(
|
||||||
|
'TEST_SETTING_DUMMY',
|
||||||
|
default_value=50,
|
||||||
|
min_value=-10
|
||||||
|
)
|
||||||
|
self.assertEqual(result, 50)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.app_settings.settings')
|
||||||
|
def test_default_for_invalid_type_int(self, mock_settings):
|
||||||
|
mock_settings.TEST_SETTING_DUMMY = 1000
|
||||||
|
result = app_settings._clean_setting(
|
||||||
|
'TEST_SETTING_DUMMY',
|
||||||
|
default_value=50,
|
||||||
|
max_value=100
|
||||||
|
)
|
||||||
|
self.assertEqual(result, 50)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.app_settings.settings')
|
||||||
|
def test_default_is_none_needs_required_type(self, mock_settings):
|
||||||
|
mock_settings.TEST_SETTING_DUMMY = 'invalid type'
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
result = app_settings._clean_setting(
|
||||||
|
'TEST_SETTING_DUMMY',
|
||||||
|
default_value=None
|
||||||
|
)
|
||||||
@@ -7,11 +7,28 @@ from . import views
|
|||||||
app_name = 'authentication'
|
app_name = 'authentication'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', login_required(TemplateView.as_view(template_name='authentication/dashboard.html')),),
|
url(r'^$', views.index, name='index'),
|
||||||
url(r'^account/login/$', TemplateView.as_view(template_name='public/login.html'), name='login'),
|
url(
|
||||||
url(r'^account/characters/main/$', views.main_character_change, name='change_main_character'),
|
r'^account/login/$',
|
||||||
url(r'^account/characters/add/$', views.add_character, name='add_character'),
|
TemplateView.as_view(template_name='public/login.html'),
|
||||||
url(r'^help/$', login_required(TemplateView.as_view(template_name='allianceauth/help.html')), name='help'),
|
name='login'
|
||||||
url(r'^dashboard/$',
|
),
|
||||||
login_required(TemplateView.as_view(template_name='authentication/dashboard.html')), name='dashboard'),
|
url(
|
||||||
|
r'^account/characters/main/$',
|
||||||
|
views.main_character_change,
|
||||||
|
name='change_main_character'
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
r'^account/characters/add/$',
|
||||||
|
views.add_character,
|
||||||
|
name='add_character'
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
r'^help/$',
|
||||||
|
login_required(
|
||||||
|
TemplateView.as_view(template_name='allianceauth/help.html')
|
||||||
|
),
|
||||||
|
name='help'
|
||||||
|
),
|
||||||
|
url(r'^dashboard/$', views.dashboard, name='dashboard'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,20 +7,58 @@ 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.urls import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect, render
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from allianceauth.eveonline.models import EveCharacter
|
||||||
from esi.decorators import token_required
|
from esi.decorators import token_required
|
||||||
from esi.models import Token
|
from esi.models import Token
|
||||||
from registration.backends.hmac.views import RegistrationView as BaseRegistrationView, \
|
|
||||||
ActivationView as BaseActivationView, REGISTRATION_SALT
|
from registration.backends.hmac.views import (
|
||||||
|
RegistrationView as BaseRegistrationView,
|
||||||
|
ActivationView as BaseActivationView,
|
||||||
|
REGISTRATION_SALT
|
||||||
|
)
|
||||||
from registration.signals import user_registered
|
from registration.signals import user_registered
|
||||||
|
|
||||||
from .models import CharacterOwnership
|
from .models import CharacterOwnership
|
||||||
from .forms import RegistrationForm
|
from .forms import RegistrationForm
|
||||||
|
|
||||||
|
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
|
||||||
|
_has_auto_groups = True
|
||||||
|
from allianceauth.eveonline.autogroups.models import *
|
||||||
|
else:
|
||||||
|
_has_auto_groups = False
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def index(request):
|
||||||
|
return redirect('authentication:dashboard')
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def dashboard(request):
|
||||||
|
groups = request.user.groups.all()
|
||||||
|
if _has_auto_groups:
|
||||||
|
groups = groups\
|
||||||
|
.filter(managedalliancegroup__isnull=True)\
|
||||||
|
.filter(managedcorpgroup__isnull=True)
|
||||||
|
groups = groups.order_by('name')
|
||||||
|
characters = EveCharacter.objects\
|
||||||
|
.filter(character_ownership__user=request.user)\
|
||||||
|
.select_related()\
|
||||||
|
.order_by('character_name')
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'groups': groups,
|
||||||
|
'characters': characters
|
||||||
|
}
|
||||||
|
return render(request, 'authentication/dashboard.html', context)
|
||||||
|
|
||||||
|
|
||||||
@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):
|
||||||
|
|||||||
@@ -16,10 +16,16 @@ class CorpStatsQuerySet(models.QuerySet):
|
|||||||
assert char
|
assert char
|
||||||
# build all accepted queries
|
# build all accepted queries
|
||||||
queries = [models.Q(token__user=user)]
|
queries = [models.Q(token__user=user)]
|
||||||
if user.has_perm('corputils.view_corp_corpstats'):
|
|
||||||
queries.append(models.Q(corp__corporation_id=char.corporation_id))
|
|
||||||
if user.has_perm('corputils.view_alliance_corpstats'):
|
if user.has_perm('corputils.view_alliance_corpstats'):
|
||||||
queries.append(models.Q(corp__alliance__alliance_id=char.alliance_id))
|
if char.alliance_id is not None:
|
||||||
|
queries.append(models.Q(corp__alliance__alliance_id=char.alliance_id))
|
||||||
|
else:
|
||||||
|
queries.append(models.Q(corp__corporation_id=char.corporation_id))
|
||||||
|
if user.has_perm('corputils.view_corp_corpstats'):
|
||||||
|
if user.has_perm('corputils.view_alliance_corpstats'):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
queries.append(models.Q(corp__corporation_id=char.corporation_id))
|
||||||
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()))
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ from bravado.exception import HTTPForbidden
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from esi.errors import TokenError
|
from esi.errors import TokenError
|
||||||
from esi.models import Token
|
from esi.models import Token
|
||||||
from allianceauth.eveonline.models import EveCorporationInfo, EveCharacter
|
from allianceauth.eveonline.models import EveCorporationInfo, EveCharacter,\
|
||||||
|
EveAllianceInfo
|
||||||
from allianceauth.notifications import notify
|
from allianceauth.notifications import notify
|
||||||
|
|
||||||
from allianceauth.corputils.managers import CorpStatsManager
|
from allianceauth.corputils.managers import CorpStatsManager
|
||||||
@@ -15,9 +16,11 @@ SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sw
|
|||||||
"""
|
"""
|
||||||
Swagger spec operations:
|
Swagger spec operations:
|
||||||
|
|
||||||
|
Character
|
||||||
get_characters_character_id
|
get_characters_character_id
|
||||||
get_corporations_corporation_id_members
|
get_corporations_corporation_id_members
|
||||||
get_characters_names
|
Universe
|
||||||
|
post_universe_names
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -55,11 +58,11 @@ class CorpStats(models.Model):
|
|||||||
# the swagger spec doesn't have a maxItems count
|
# the swagger spec doesn't have a maxItems count
|
||||||
# manual testing says we can do over 350, but let's not risk it
|
# manual testing says we can do over 350, but let's not risk it
|
||||||
member_id_chunks = [member_ids[i:i + 255] for i in range(0, len(member_ids), 255)]
|
member_id_chunks = [member_ids[i:i + 255] for i in range(0, len(member_ids), 255)]
|
||||||
member_name_chunks = [c.Character.get_characters_names(character_ids=id_chunk).result() for id_chunk in
|
member_name_chunks = [c.Universe.post_universe_names(ids=id_chunk).result() for id_chunk in
|
||||||
member_id_chunks]
|
member_id_chunks]
|
||||||
member_list = {}
|
member_list = {}
|
||||||
for name_chunk in member_name_chunks:
|
for name_chunk in member_name_chunks:
|
||||||
member_list.update({m['character_id']: m['character_name'] for m in name_chunk})
|
member_list.update({m['id']: m['name'] for m in name_chunk})
|
||||||
|
|
||||||
# bulk create new member models
|
# bulk create new member models
|
||||||
missing_members = [m_id for m_id in member_ids if
|
missing_members = [m_id for m_id in member_ids if
|
||||||
@@ -135,13 +138,13 @@ class CorpStats(models.Model):
|
|||||||
return self.token.user == user or self.visible_to(user)
|
return self.token.user == user or self.visible_to(user)
|
||||||
|
|
||||||
def corp_logo(self, size=128):
|
def corp_logo(self, size=128):
|
||||||
return "https://image.eveonline.com/Corporation/%s_%s.png" % (self.corp.corporation_id, size)
|
return self.corp.logo_url(size)
|
||||||
|
|
||||||
def alliance_logo(self, size=128):
|
def alliance_logo(self, size=128):
|
||||||
if self.corp.alliance:
|
if self.corp.alliance:
|
||||||
return "https://image.eveonline.com/Alliance/%s_%s.png" % (self.corp.alliance.alliance_id, size)
|
return self.corp.alliance.logo_url(size)
|
||||||
else:
|
else:
|
||||||
return "https://image.eveonline.com/Alliance/1_%s.png" % size
|
return EveAllianceInfo.generic_logo_url(1, size)
|
||||||
|
|
||||||
|
|
||||||
class CorpMember(models.Model):
|
class CorpMember(models.Model):
|
||||||
@@ -183,10 +186,16 @@ class CorpMember(models.Model):
|
|||||||
return CharacterOwnership.objects.filter(character__character_id=self.character_id).exists()
|
return CharacterOwnership.objects.filter(character__character_id=self.character_id).exists()
|
||||||
|
|
||||||
def portrait_url(self, size=32):
|
def portrait_url(self, size=32):
|
||||||
return "https://image.eveonline.com/Character/%s_%s.jpg" % (self.character_id, size)
|
return EveCharacter.generic_portrait_url(self.character_id, size)
|
||||||
|
|
||||||
def __getattr__(self, item):
|
@property
|
||||||
if item.startswith('portrait_url_'):
|
def portrait_url_32(self):
|
||||||
size = item.strip('portrait_url_')
|
return self.portrait_url(32)
|
||||||
return self.portrait_url(size)
|
|
||||||
return self.__getattribute__(item)
|
@property
|
||||||
|
def portrait_url_64(self):
|
||||||
|
return self.portrait_url(64)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def portrait_url_128(self):
|
||||||
|
return self.portrait_url(128)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -7,11 +7,12 @@
|
|||||||
<div class="col-lg-12 text-center">
|
<div class="col-lg-12 text-center">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center col-lg-6
|
<td class="text-center col-lg-6{% if corpstats.corp.alliance %}{% else %}col-lg-offset-3{% endif %}">
|
||||||
{% if corpstats.corp.alliance %}{% else %}col-lg-offset-3{% endif %}"><img
|
<img class="ra-avatar" src="{{ corpstats.corp.logo_url_64 }}">
|
||||||
class="ra-avatar" src="{{ corpstats.corp.logo_url_128 }}"></td>
|
</td>
|
||||||
{% if corpstats.corp.alliance %}
|
{% if corpstats.corp.alliance %}
|
||||||
<td class="text-center col-lg-6"><img class="ra-avatar" src="{{ corpstats.corp.alliance.logo_url_128 }}">
|
<td class="text-center col-lg-6">
|
||||||
|
<img class="ra-avatar" src="{{ corpstats.corp.alliance.logo_url_64 }}">
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
@@ -29,15 +30,15 @@
|
|||||||
<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' %} ({{ corpstats.main_count }})</a></li>
|
<li class="active"><a href="#mains" data-toggle="pill">{% trans '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">{% trans 'Members' %} ({{ corpstats.member_count }})</a></li>
|
||||||
<li><a href="#unregistered" data-toggle="pill">{% trans 'Unregistered' %} ({{ corpstats.unregistered_member_count }})</a></li>
|
<li><a href="#unregistered" data-toggle="pill">{% trans 'Unregistered' %} ({{ unregistered.count }})</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="pull-right">
|
<div class="pull-right hidden-xs">
|
||||||
{% trans "Last update:" %} {{ corpstats.last_update|naturaltime }}
|
{% trans "Last update:" %} {{ corpstats.last_update|naturaltime }}
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -54,14 +55,14 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for main in mains %}
|
{% for id, main in mains.items %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center" style="vertical-align:middle">
|
<td class="text-center" style="vertical-align:middle">
|
||||||
<div class="thumbnail"
|
<div class="thumbnail"
|
||||||
style="border: 0 none; box-shadow: none; background: transparent;">
|
style="border: 0 none; box-shadow: none; background: transparent;">
|
||||||
<img src="{{ main.portrait_url_64 }}" class="img-circle">
|
<img src="{{ main.main.portrait_url_64 }}" class="img-circle">
|
||||||
<div class="caption text-center">
|
<div class="caption text-center">
|
||||||
{{ main }}
|
{{ main.main }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -80,7 +81,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="text-center" style="width:5%">
|
<td class="text-center" style="width:5%">
|
||||||
<div class="thumbnail" style="border: 0 none; box-shadow: none; background: transparent;">
|
<div class="thumbnail" style="border: 0 none; box-shadow: none; background: transparent;">
|
||||||
<img src="https://image.eveonline.com/Character/{{ alt.character_id }}_32.jpg" class="img-circle">
|
<img src="{{ alt.portrait_url_32 }}" class="img-circle">
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center" style="width:30%">{{ alt.character_name }}</td>
|
<td class="text-center" style="width:30%">{{ alt.character_name }}</td>
|
||||||
@@ -119,16 +120,29 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for member in members %}
|
{% for member in members %}
|
||||||
<tr {% if not member.registered %}class="danger"{% endif %}>
|
<tr>
|
||||||
|
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
|
||||||
|
<td class="text-center">{{ member }}</td>
|
||||||
|
<td class="text-center"><a
|
||||||
|
href="https://zkillboard.com/character/{{ member.character_id }}/"
|
||||||
|
class="label label-danger"
|
||||||
|
target="_blank">{% trans "Killboard" %}</a></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.alliance_name }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% for member in unregistered %}
|
||||||
|
<tr class="danger">
|
||||||
<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"><a
|
<td class="text-center"><a
|
||||||
href="https://zkillboard.com/character/{{ member.character_id }}/"
|
href="https://zkillboard.com/character/{{ member.character_id }}/"
|
||||||
class="label label-danger"
|
class="label label-danger"
|
||||||
target="_blank">{% trans "Killboard" %}</a></td>
|
target="_blank">{% trans "Killboard" %}</a></td>
|
||||||
<td class="text-center">{{ member.main_character.character_name }}</td>
|
<td class="text-center"></td>
|
||||||
<td class="text-center">{{ member.main_character.corporation_name }}</td>
|
<td class="text-center"></td>
|
||||||
<td class="text-center">{{ member.main_character.alliance_name }}</td>
|
<td class="text-center"></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class CorpStatsUpdateTestCase(TestCase):
|
|||||||
def test_update_add_member(self, SwaggerClient):
|
def test_update_add_member(self, SwaggerClient):
|
||||||
SwaggerClient.from_spec.return_value.Character.get_characters_character_id.return_value.result.return_value = {'corporation_id': 2}
|
SwaggerClient.from_spec.return_value.Character.get_characters_character_id.return_value.result.return_value = {'corporation_id': 2}
|
||||||
SwaggerClient.from_spec.return_value.Corporation.get_corporations_corporation_id_members.return_value.result.return_value = [1]
|
SwaggerClient.from_spec.return_value.Corporation.get_corporations_corporation_id_members.return_value.result.return_value = [1]
|
||||||
SwaggerClient.from_spec.return_value.Character.get_characters_names.return_value.result.return_value = [{'character_id': 1, 'character_name': 'test character'}]
|
SwaggerClient.from_spec.return_value.Universe.post_universe_names.return_value.result.return_value = [{'id': 1, 'name': 'test character'}]
|
||||||
self.corpstats.update()
|
self.corpstats.update()
|
||||||
self.assertTrue(CorpMember.objects.filter(character_id='1', character_name='test character', corpstats=self.corpstats).exists())
|
self.assertTrue(CorpMember.objects.filter(character_id='1', character_name='test character', corpstats=self.corpstats).exists())
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ class CorpStatsUpdateTestCase(TestCase):
|
|||||||
CorpMember.objects.create(character_id='2', character_name='old test character', corpstats=self.corpstats)
|
CorpMember.objects.create(character_id='2', character_name='old test character', corpstats=self.corpstats)
|
||||||
SwaggerClient.from_spec.return_value.Character.get_characters_character_id.return_value.result.return_value = {'corporation_id': 2}
|
SwaggerClient.from_spec.return_value.Character.get_characters_character_id.return_value.result.return_value = {'corporation_id': 2}
|
||||||
SwaggerClient.from_spec.return_value.Corporation.get_corporations_corporation_id_members.return_value.result.return_value = [1]
|
SwaggerClient.from_spec.return_value.Corporation.get_corporations_corporation_id_members.return_value.result.return_value = [1]
|
||||||
SwaggerClient.from_spec.return_value.Character.get_characters_names.return_value.result.return_value = [{'character_id': 1, 'character_name': 'test character'}]
|
SwaggerClient.from_spec.return_value.Universe.post_universe_names.return_value.result.return_value = [{'id': 1, 'name': 'test character'}]
|
||||||
self.corpstats.update()
|
self.corpstats.update()
|
||||||
self.assertFalse(CorpMember.objects.filter(character_id='2', corpstats=self.corpstats).exists())
|
self.assertFalse(CorpMember.objects.filter(character_id='2', corpstats=self.corpstats).exists())
|
||||||
|
|
||||||
@@ -205,13 +205,13 @@ class CorpStatsPropertiesTestCase(TestCase):
|
|||||||
AuthUtils.connect_signals()
|
AuthUtils.connect_signals()
|
||||||
|
|
||||||
def test_logos(self):
|
def test_logos(self):
|
||||||
self.assertEqual(self.corpstats.corp_logo(size=128), 'https://image.eveonline.com/Corporation/2_128.png')
|
self.assertEqual(self.corpstats.corp_logo(size=128), 'https://images.evetech.net/corporations/2/logo?size=128')
|
||||||
self.assertEqual(self.corpstats.alliance_logo(size=128), 'https://image.eveonline.com/Alliance/1_128.png')
|
self.assertEqual(self.corpstats.alliance_logo(size=128), 'https://images.evetech.net/alliances/1/logo?size=128')
|
||||||
|
|
||||||
alliance = EveAllianceInfo.objects.create(alliance_name='test alliance', alliance_id='3', alliance_ticker='TEST', executor_corp_id='2')
|
alliance = EveAllianceInfo.objects.create(alliance_name='test alliance', alliance_id='3', alliance_ticker='TEST', executor_corp_id='2')
|
||||||
self.corp.alliance = alliance
|
self.corp.alliance = alliance
|
||||||
self.corp.save()
|
self.corp.save()
|
||||||
self.assertEqual(self.corpstats.alliance_logo(size=128), 'https://image.eveonline.com/Alliance/3_128.png')
|
self.assertEqual(self.corpstats.alliance_logo(size=128), 'https://images.evetech.net/alliances/3/logo?size=128')
|
||||||
alliance.delete()
|
alliance.delete()
|
||||||
|
|
||||||
|
|
||||||
@@ -273,5 +273,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://image.eveonline.com/Character/2_32.jpg')
|
self.assertEquals(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.assertEquals(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.assertEquals(self.member.portrait_url(size=128), self.member.portrait_url_128)
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import os
|
|||||||
from bravado.exception import HTTPError
|
from bravado.exception import HTTPError
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
|
from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from esi.decorators import token_required
|
from esi.decorators import token_required
|
||||||
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo
|
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo
|
||||||
|
|
||||||
from .models import CorpStats
|
from .models import CorpStats, CorpMember
|
||||||
|
|
||||||
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
|
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
|
||||||
"""
|
"""
|
||||||
@@ -68,7 +68,7 @@ def corpstats_view(request, corp_id=None):
|
|||||||
corpstats = get_object_or_404(CorpStats, corp=corp)
|
corpstats = get_object_or_404(CorpStats, corp=corp)
|
||||||
|
|
||||||
# get available models
|
# get available models
|
||||||
available = CorpStats.objects.visible_to(request.user).order_by('corp__corporation_name')
|
available = CorpStats.objects.visible_to(request.user).order_by('corp__corporation_name').select_related('corp')
|
||||||
|
|
||||||
# ensure we can see the requested model
|
# ensure we can see the requested model
|
||||||
if corpstats and corpstats not in available:
|
if corpstats and corpstats not in available:
|
||||||
@@ -89,13 +89,50 @@ def corpstats_view(request, corp_id=None):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if corpstats:
|
if corpstats:
|
||||||
members = corpstats.members.all()
|
character_list = CorpMember.objects.filter(corpstats=corpstats)
|
||||||
mains = corpstats.mains.all()
|
linked_chars = EveCharacter.objects.filter(
|
||||||
unregistered = corpstats.unregistered_members.all()
|
character_id__in=character_list.values_list('character_id', flat=True))
|
||||||
|
linked_chars = linked_chars | EveCharacter.objects.filter(
|
||||||
|
character_ownership__user__profile__main_character__corporation_id=corpstats.corp.corporation_id)
|
||||||
|
|
||||||
|
linked_chars = linked_chars.select_related('character_ownership',
|
||||||
|
'character_ownership__user__profile__main_character') \
|
||||||
|
.prefetch_related('character_ownership__user__character_ownerships') \
|
||||||
|
.prefetch_related('character_ownership__user__character_ownerships__character')
|
||||||
|
|
||||||
|
members = []
|
||||||
|
mains = {}
|
||||||
|
|
||||||
|
temp_ids = []
|
||||||
|
for char in linked_chars:
|
||||||
|
try:
|
||||||
|
main = char.character_ownership.user.profile.main_character
|
||||||
|
if main is not None:
|
||||||
|
if main.corporation_id == corpstats.corp.corporation_id:
|
||||||
|
if main.character_id not in mains:
|
||||||
|
mains[main.character_id] = {'main':main, 'alts':[]}
|
||||||
|
|
||||||
|
mains[main.character_id]['alts'].append(char)
|
||||||
|
|
||||||
|
if char.corporation_id == corpstats.corp.corporation_id:
|
||||||
|
members.append(char)
|
||||||
|
|
||||||
|
temp_ids.append(char.character_id)
|
||||||
|
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
unregistered = character_list.exclude(character_id__in=temp_ids)
|
||||||
|
|
||||||
|
members = members
|
||||||
|
mains = mains
|
||||||
|
total_mains = len(mains)
|
||||||
|
unregistered = unregistered
|
||||||
context.update({
|
context.update({
|
||||||
'corpstats': corpstats,
|
'corpstats': corpstats,
|
||||||
'members': members,
|
'members': members,
|
||||||
'mains': mains,
|
'mains': mains,
|
||||||
|
'total_mains': total_mains,
|
||||||
'unregistered': unregistered,
|
'unregistered': unregistered,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from .models import AutogroupsConfig
|
from .models import AutogroupsConfig, ManagedCorpGroup, ManagedAllianceGroup
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@@ -37,3 +37,6 @@ class AutogroupsConfigAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
|
|
||||||
admin.site.register(AutogroupsConfig, AutogroupsConfigAdmin)
|
admin.site.register(AutogroupsConfig, AutogroupsConfigAdmin)
|
||||||
|
admin.site.register(ManagedCorpGroup)
|
||||||
|
admin.site.register(ManagedAllianceGroup)
|
||||||
|
|
||||||
|
|||||||
@@ -179,15 +179,13 @@ class AutogroupsConfig(models.Model):
|
|||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create_alliance_group(self, alliance: EveAllianceInfo) -> Group:
|
def create_alliance_group(self, alliance: EveAllianceInfo) -> Group:
|
||||||
group, created = Group.objects.get_or_create(name=self.get_alliance_group_name(alliance))
|
group, created = Group.objects.get_or_create(name=self.get_alliance_group_name(alliance))
|
||||||
if created:
|
ManagedAllianceGroup.objects.get_or_create(group=group, config=self, alliance=alliance)
|
||||||
ManagedAllianceGroup.objects.create(group=group, config=self, alliance=alliance)
|
|
||||||
return group
|
return group
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create_corp_group(self, corp: EveCorporationInfo) -> Group:
|
def create_corp_group(self, corp: EveCorporationInfo) -> Group:
|
||||||
group, created = Group.objects.get_or_create(name=self.get_corp_group_name(corp))
|
group, created = Group.objects.get_or_create(name=self.get_corp_group_name(corp))
|
||||||
if created:
|
ManagedCorpGroup.objects.get_or_create(group=group, config=self, corp=corp)
|
||||||
ManagedCorpGroup.objects.create(group=group, config=self, corp=corp)
|
|
||||||
return group
|
return group
|
||||||
|
|
||||||
def delete_alliance_managed_groups(self):
|
def delete_alliance_managed_groups(self):
|
||||||
@@ -240,6 +238,8 @@ class ManagedGroup(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Managed Group: %s" % self.group.name
|
||||||
|
|
||||||
class ManagedCorpGroup(ManagedGroup):
|
class ManagedCorpGroup(ManagedGroup):
|
||||||
corp = models.ForeignKey(EveCorporationInfo, on_delete=models.CASCADE)
|
corp = models.ForeignKey(EveCorporationInfo, on_delete=models.CASCADE)
|
||||||
|
|||||||
@@ -25,14 +25,35 @@ class AutogroupsConfigManagerTestCase(TestCase):
|
|||||||
obj = AutogroupsConfig.objects.create()
|
obj = AutogroupsConfig.objects.create()
|
||||||
obj.states.add(member.profile.state)
|
obj.states.add(member.profile.state)
|
||||||
|
|
||||||
with patch('.models.AutogroupsConfig.update_group_membership_for_user') as update_group_membership_for_user:
|
with patch('.models.AutogroupsConfig.update_group_membership_for_user') \
|
||||||
AutogroupsConfig.objects.update_groups_for_user(member)
|
as update_group_membership_for_user:
|
||||||
|
AutogroupsConfig.objects.update_groups_for_user(
|
||||||
|
user=member
|
||||||
|
)
|
||||||
|
|
||||||
self.assertTrue(update_group_membership_for_user.called)
|
self.assertTrue(update_group_membership_for_user.called)
|
||||||
self.assertEqual(update_group_membership_for_user.call_count, 1)
|
self.assertEqual(update_group_membership_for_user.call_count, 1)
|
||||||
args, kwargs = update_group_membership_for_user.call_args
|
args, kwargs = update_group_membership_for_user.call_args
|
||||||
self.assertEqual(args[0], member)
|
self.assertEqual(args[0], member)
|
||||||
|
|
||||||
|
def test_update_groups_for_user_no_state(self):
|
||||||
|
member = AuthUtils.create_member('test member')
|
||||||
|
obj = AutogroupsConfig.objects.create()
|
||||||
|
obj.states.add(member.profile.state)
|
||||||
|
|
||||||
|
with patch('.models.AutogroupsConfig.update_group_membership_for_user') \
|
||||||
|
as update_group_membership_for_user:
|
||||||
|
AutogroupsConfig.objects.update_groups_for_user(
|
||||||
|
user=member,
|
||||||
|
state=member.profile.state
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertTrue(update_group_membership_for_user.called)
|
||||||
|
self.assertEqual(update_group_membership_for_user.call_count, 1)
|
||||||
|
args, kwargs = update_group_membership_for_user.call_args
|
||||||
|
self.assertEqual(args[0], member)
|
||||||
|
|
||||||
|
|
||||||
@patch('.models.AutogroupsConfig.update_group_membership_for_user')
|
@patch('.models.AutogroupsConfig.update_group_membership_for_user')
|
||||||
@patch('.models.AutogroupsConfig.remove_user_from_alliance_groups')
|
@patch('.models.AutogroupsConfig.remove_user_from_alliance_groups')
|
||||||
@patch('.models.AutogroupsConfig.remove_user_from_corp_groups')
|
@patch('.models.AutogroupsConfig.remove_user_from_corp_groups')
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
from allianceauth.tests.auth_utils import AuthUtils
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
|
|
||||||
@@ -50,7 +51,11 @@ class AutogroupsConfigTestCase(TestCase):
|
|||||||
|
|
||||||
@patch('.models.AutogroupsConfig.update_alliance_group_membership')
|
@patch('.models.AutogroupsConfig.update_alliance_group_membership')
|
||||||
@patch('.models.AutogroupsConfig.update_corp_group_membership')
|
@patch('.models.AutogroupsConfig.update_corp_group_membership')
|
||||||
def test_update_group_membership(self, update_corp, update_alliance):
|
def test_update_group_membership_for_user(
|
||||||
|
self,
|
||||||
|
update_corp,
|
||||||
|
update_alliance
|
||||||
|
):
|
||||||
agc = AutogroupsConfig.objects.create()
|
agc = AutogroupsConfig.objects.create()
|
||||||
agc.update_group_membership_for_user(self.member)
|
agc.update_group_membership_for_user(self.member)
|
||||||
|
|
||||||
@@ -101,8 +106,27 @@ class AutogroupsConfigTestCase(TestCase):
|
|||||||
|
|
||||||
self.assertNotIn(group, self.member.groups.all())
|
self.assertNotIn(group, self.member.groups.all())
|
||||||
|
|
||||||
def test_update_alliance_group_membership_no_alliance_model(self):
|
# todo: this test case currently does not work, because it forces
|
||||||
obj = AutogroupsConfig.objects.create()
|
# an exception during a transaction, which is not easily testable
|
||||||
|
# the production code itself should be fine though
|
||||||
|
# I therefore commented out the test case for now
|
||||||
|
"""
|
||||||
|
@patch('.models.EveAllianceInfo.objects.create_alliance')
|
||||||
|
def test_update_alliance_group_membership_no_alliance_model(
|
||||||
|
self,
|
||||||
|
mock_create_alliance
|
||||||
|
):
|
||||||
|
def mock_create_alliance_side_effect(*args, **kwargs):
|
||||||
|
return EveAllianceInfo.objects.create(
|
||||||
|
alliance_id='3459',
|
||||||
|
alliance_name='alliance name',
|
||||||
|
alliance_ticker='alliance_ticker',
|
||||||
|
executor_corp_id='2345'
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_create_alliance.side_effect = mock_create_alliance_side_effect
|
||||||
|
|
||||||
|
obj = AutogroupsConfig.objects.create(alliance_groups=True)
|
||||||
obj.states.add(AuthUtils.get_member_state())
|
obj.states.add(AuthUtils.get_member_state())
|
||||||
char = EveCharacter.objects.create(
|
char = EveCharacter.objects.create(
|
||||||
character_id='1234',
|
character_id='1234',
|
||||||
@@ -116,12 +140,13 @@ class AutogroupsConfigTestCase(TestCase):
|
|||||||
self.member.profile.main_character = char
|
self.member.profile.main_character = char
|
||||||
self.member.profile.save()
|
self.member.profile.save()
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
obj.update_alliance_group_membership(self.member)
|
obj.update_alliance_group_membership(self.member)
|
||||||
|
|
||||||
group = obj.get_alliance_group(self.alliance)
|
group = obj.get_alliance_group(self.alliance)
|
||||||
|
|
||||||
self.assertNotIn(group, self.member.groups.all())
|
self.assertNotIn(group, self.member.groups.all())
|
||||||
|
"""
|
||||||
|
|
||||||
def test_update_corp_group_membership(self):
|
def test_update_corp_group_membership(self):
|
||||||
obj = AutogroupsConfig.objects.create(corp_groups=True)
|
obj = AutogroupsConfig.objects.create(corp_groups=True)
|
||||||
|
|||||||
17
allianceauth/eveonline/evelinks/__init__.py
Normal file
17
allianceauth/eveonline/evelinks/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# this package generates profile URL for eve entities
|
||||||
|
# on 3rd party websites like evewho and zKillboard
|
||||||
|
#
|
||||||
|
# It contains of modules for views and templatetags for templates
|
||||||
|
|
||||||
|
# list of all eve entity categories as defined in ESI
|
||||||
|
ESI_CATEGORY_AGENT = "agent"
|
||||||
|
ESI_CATEGORY_ALLIANCE = "alliance"
|
||||||
|
ESI_CATEGORY_CHARACTER = "character"
|
||||||
|
ESI_CATEGORY_CONSTELLATION = "constellation"
|
||||||
|
ESI_CATEGORY_CORPORATION = "corporation"
|
||||||
|
ESI_CATEGORY_FACTION = "faction"
|
||||||
|
ESI_CATEGORY_INVENTORYTYPE = "inventory_type"
|
||||||
|
ESI_CATEGORY_REGION = "region"
|
||||||
|
ESI_CATEGORY_SOLARSYSTEM = "solar_system"
|
||||||
|
ESI_CATEGORY_STATION = "station"
|
||||||
|
ESI_CATEGORY_WORMHOLE = "wormhole"
|
||||||
52
allianceauth/eveonline/evelinks/dotlan.py
Normal file
52
allianceauth/eveonline/evelinks/dotlan.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# this module generates profile URLs for dotlan
|
||||||
|
|
||||||
|
from urllib.parse import urljoin, quote
|
||||||
|
|
||||||
|
from . import *
|
||||||
|
|
||||||
|
BASE_URL = 'http://evemaps.dotlan.net'
|
||||||
|
|
||||||
|
|
||||||
|
def _build_url(category: str, name: str) -> str:
|
||||||
|
"""return url to profile page for an eve entity"""
|
||||||
|
|
||||||
|
if category == ESI_CATEGORY_ALLIANCE:
|
||||||
|
partial = 'alliance'
|
||||||
|
|
||||||
|
elif category == ESI_CATEGORY_CORPORATION:
|
||||||
|
partial = 'corp'
|
||||||
|
|
||||||
|
elif category == ESI_CATEGORY_REGION:
|
||||||
|
partial = 'map'
|
||||||
|
|
||||||
|
elif category == ESI_CATEGORY_SOLARSYSTEM:
|
||||||
|
partial = 'system'
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(
|
||||||
|
"Not implemented yet for category:" + category
|
||||||
|
)
|
||||||
|
|
||||||
|
url = urljoin(
|
||||||
|
BASE_URL,
|
||||||
|
'{}/{}'.format(partial, quote(str(name).replace(" ", "_")))
|
||||||
|
|
||||||
|
)
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
def alliance_url(name: str) -> str:
|
||||||
|
"""url for page about given alliance on dotlan"""
|
||||||
|
return _build_url(ESI_CATEGORY_ALLIANCE, name)
|
||||||
|
|
||||||
|
def corporation_url(name: str) -> str:
|
||||||
|
"""url for page about given corporation on dotlan"""
|
||||||
|
return _build_url(ESI_CATEGORY_CORPORATION, name)
|
||||||
|
|
||||||
|
def region_url(name: str) -> str:
|
||||||
|
"""url for page about given region on dotlan"""
|
||||||
|
return _build_url(ESI_CATEGORY_REGION, name)
|
||||||
|
|
||||||
|
def solar_system_url(name: str) -> str:
|
||||||
|
"""url for page about given solar system on dotlan"""
|
||||||
|
return _build_url(ESI_CATEGORY_SOLARSYSTEM, name)
|
||||||
44
allianceauth/eveonline/evelinks/evewho.py
Normal file
44
allianceauth/eveonline/evelinks/evewho.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# this module generates profile URLs for evewho
|
||||||
|
|
||||||
|
from urllib.parse import urljoin, quote
|
||||||
|
|
||||||
|
from . import *
|
||||||
|
|
||||||
|
BASE_URL = 'https://evewho.com'
|
||||||
|
|
||||||
|
|
||||||
|
def _build_url(category: str, eve_id: int) -> str:
|
||||||
|
"""return url to profile page for an eve entity"""
|
||||||
|
|
||||||
|
if category == ESI_CATEGORY_ALLIANCE:
|
||||||
|
partial = 'alliance'
|
||||||
|
|
||||||
|
elif category == ESI_CATEGORY_CORPORATION:
|
||||||
|
partial = 'corporation'
|
||||||
|
|
||||||
|
elif category == ESI_CATEGORY_CHARACTER:
|
||||||
|
partial = 'character'
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(
|
||||||
|
"Not implemented yet for category:" + category
|
||||||
|
)
|
||||||
|
|
||||||
|
url = urljoin(
|
||||||
|
BASE_URL,
|
||||||
|
'{}/{}'.format(partial, int(eve_id))
|
||||||
|
)
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
def alliance_url(eve_id: int) -> str:
|
||||||
|
"""url for page about given alliance on evewho"""
|
||||||
|
return _build_url(ESI_CATEGORY_ALLIANCE, eve_id)
|
||||||
|
|
||||||
|
def character_url(eve_id: int) -> str:
|
||||||
|
"""url for page about given character on evewho"""
|
||||||
|
return _build_url(ESI_CATEGORY_CHARACTER, eve_id)
|
||||||
|
|
||||||
|
def corporation_url(eve_id: int) -> str:
|
||||||
|
"""url for page about given corporation on evewho"""
|
||||||
|
return _build_url(ESI_CATEGORY_CORPORATION, eve_id)
|
||||||
92
allianceauth/eveonline/evelinks/tests/test_evelinks.py
Normal file
92
allianceauth/eveonline/evelinks/tests/test_evelinks.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from ...models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||||
|
from .. import dotlan, zkillboard, evewho
|
||||||
|
from ...templatetags import evelinks
|
||||||
|
|
||||||
|
|
||||||
|
class TestEveWho(TestCase):
|
||||||
|
|
||||||
|
def test_alliance_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evewho.alliance_url(12345678),
|
||||||
|
'https://evewho.com/alliance/12345678'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_corporation_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evewho.corporation_url(12345678),
|
||||||
|
'https://evewho.com/corporation/12345678'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_character_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evewho.character_url(12345678),
|
||||||
|
'https://evewho.com/character/12345678'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDotlan(TestCase):
|
||||||
|
|
||||||
|
def test_alliance_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
dotlan.alliance_url('Wayne Enterprices'),
|
||||||
|
'http://evemaps.dotlan.net/alliance/Wayne_Enterprices'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_corporation_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
dotlan.corporation_url('Wayne Technology'),
|
||||||
|
'http://evemaps.dotlan.net/corp/Wayne_Technology'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
dotlan.corporation_url('Crédit Agricole'),
|
||||||
|
'http://evemaps.dotlan.net/corp/Cr%C3%A9dit_Agricole'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_region_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
dotlan.region_url('Black Rise'),
|
||||||
|
'http://evemaps.dotlan.net/map/Black_Rise'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_solar_system_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
dotlan.solar_system_url('Jita'),
|
||||||
|
'http://evemaps.dotlan.net/system/Jita'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestZkillboard(TestCase):
|
||||||
|
|
||||||
|
def test_alliance_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
zkillboard.alliance_url(12345678),
|
||||||
|
'https://zkillboard.com/alliance/12345678/'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_corporation_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
zkillboard.corporation_url(12345678),
|
||||||
|
'https://zkillboard.com/corporation/12345678/'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_character_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
zkillboard.character_url(12345678),
|
||||||
|
'https://zkillboard.com/character/12345678/'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_region_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
zkillboard.region_url(12345678),
|
||||||
|
'https://zkillboard.com/region/12345678/'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_solar_system_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
zkillboard.solar_system_url(12345678),
|
||||||
|
'https://zkillboard.com/system/12345678/'
|
||||||
|
)
|
||||||
|
|
||||||
334
allianceauth/eveonline/evelinks/tests/test_templatetags.py
Normal file
334
allianceauth/eveonline/evelinks/tests/test_templatetags.py
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from ...models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||||
|
from .. import dotlan, zkillboard, evewho
|
||||||
|
from ...templatetags import evelinks
|
||||||
|
|
||||||
|
|
||||||
|
class TestTemplateTags(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.my_character = EveCharacter.objects.create(
|
||||||
|
character_id=1001,
|
||||||
|
character_name='Bruce Wayne',
|
||||||
|
corporation_id=2001,
|
||||||
|
corporation_name='Dummy Corporation 1',
|
||||||
|
corporation_ticker='DC1',
|
||||||
|
alliance_id=3001,
|
||||||
|
alliance_name='Dummy Alliance 1',
|
||||||
|
alliance_ticker='DA1',
|
||||||
|
)
|
||||||
|
self.my_character_2 = EveCharacter.objects.create(
|
||||||
|
character_id=1002,
|
||||||
|
character_name='Peter Parker',
|
||||||
|
corporation_id=2002,
|
||||||
|
corporation_name='Dummy Corporation 2',
|
||||||
|
corporation_ticker='DC2',
|
||||||
|
)
|
||||||
|
self.my_alliance = EveAllianceInfo.objects.create(
|
||||||
|
alliance_id=3001,
|
||||||
|
alliance_name='Dummy Alliance 1',
|
||||||
|
alliance_ticker='DA1',
|
||||||
|
executor_corp_id=2001
|
||||||
|
)
|
||||||
|
self.my_corporation = EveCorporationInfo(
|
||||||
|
corporation_id=2001,
|
||||||
|
corporation_name='Dummy Corporation 1',
|
||||||
|
corporation_ticker='DC1',
|
||||||
|
member_count=42,
|
||||||
|
alliance=self.my_alliance
|
||||||
|
)
|
||||||
|
|
||||||
|
self.my_region_id = 8001
|
||||||
|
self.my_region_name = 'Southpark'
|
||||||
|
|
||||||
|
self.my_solar_system_id = 9001
|
||||||
|
self.my_solar_system_name = 'Gotham'
|
||||||
|
|
||||||
|
|
||||||
|
def test_evewho_character_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.evewho_character_url(self.my_character),
|
||||||
|
evewho.character_url(self.my_character.character_id),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.evewho_character_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.evewho_character_url(self.my_character.character_id),
|
||||||
|
evewho.character_url(self.my_character.character_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_evewho_corporation_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.evewho_corporation_url(self.my_character),
|
||||||
|
evewho.corporation_url(self.my_character.corporation_id),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.evewho_corporation_url(self.my_corporation),
|
||||||
|
evewho.corporation_url(self.my_corporation.corporation_id),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.evewho_corporation_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.evewho_corporation_url(self.my_character.corporation_id),
|
||||||
|
evewho.corporation_url(self.my_character.corporation_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_evewho_alliance_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.evewho_alliance_url(self.my_character),
|
||||||
|
evewho.alliance_url(self.my_character.alliance_id),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.evewho_alliance_url(self.my_character_2),
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.evewho_alliance_url(self.my_alliance),
|
||||||
|
evewho.alliance_url(self.my_alliance.alliance_id),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.evewho_alliance_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.evewho_alliance_url(self.my_character.alliance_id),
|
||||||
|
evewho.alliance_url(self.my_character.alliance_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# dotlan
|
||||||
|
|
||||||
|
def test_dotlan_corporation_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_corporation_url(self.my_character),
|
||||||
|
dotlan.corporation_url(self.my_character.corporation_name),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_corporation_url(self.my_corporation),
|
||||||
|
dotlan.corporation_url(self.my_corporation.corporation_name),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_corporation_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_corporation_url(self.my_character.corporation_name),
|
||||||
|
dotlan.corporation_url(self.my_character.corporation_name),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dotlan_alliance_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_alliance_url(self.my_character),
|
||||||
|
dotlan.alliance_url(self.my_character.alliance_name),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_alliance_url(self.my_character_2),
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_alliance_url(self.my_alliance),
|
||||||
|
dotlan.alliance_url(self.my_alliance.alliance_name),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_alliance_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_alliance_url(self.my_character.alliance_name),
|
||||||
|
dotlan.alliance_url(self.my_character.alliance_name),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_dotlan_region_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_region_url(self.my_region_name),
|
||||||
|
dotlan.region_url(self.my_region_name),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_region_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_dotlan_solar_system_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_solar_system_url(self.my_solar_system_name),
|
||||||
|
dotlan.solar_system_url(self.my_solar_system_name),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.dotlan_solar_system_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# zkillboard
|
||||||
|
|
||||||
|
def test_zkillboard_character_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_character_url(self.my_character),
|
||||||
|
zkillboard.character_url(self.my_character.character_id),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_character_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_character_url(self.my_character.character_id),
|
||||||
|
zkillboard.character_url(self.my_character.character_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_zkillboard_corporation_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_corporation_url(self.my_character),
|
||||||
|
zkillboard.corporation_url(self.my_character.corporation_id),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_corporation_url(self.my_corporation),
|
||||||
|
zkillboard.corporation_url(self.my_corporation.corporation_id),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_corporation_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_corporation_url(self.my_character.corporation_id),
|
||||||
|
zkillboard.corporation_url(self.my_character.corporation_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_zkillboard_alliance_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_alliance_url(self.my_character),
|
||||||
|
zkillboard.alliance_url(self.my_character.alliance_id),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_alliance_url(self.my_character_2),
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_alliance_url(self.my_alliance),
|
||||||
|
zkillboard.alliance_url(self.my_alliance.alliance_id),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_alliance_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_alliance_url(self.my_character.alliance_id),
|
||||||
|
zkillboard.alliance_url(self.my_character.alliance_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_zkillboard_region_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_region_url(self.my_region_id),
|
||||||
|
zkillboard.region_url(self.my_region_id),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_region_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_zkillboard_solar_system_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_solar_system_url(self.my_solar_system_id),
|
||||||
|
zkillboard.solar_system_url(self.my_solar_system_id),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.zkillboard_solar_system_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# image URLs
|
||||||
|
|
||||||
|
def test_character_portrait_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.character_portrait_url(123),
|
||||||
|
EveCharacter.generic_portrait_url(123)
|
||||||
|
|
||||||
|
),
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.character_portrait_url(123, 128),
|
||||||
|
EveCharacter.generic_portrait_url(123, 128)
|
||||||
|
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.character_portrait_url(123, 99),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.character_portrait_url(self.my_character),
|
||||||
|
self.my_character.portrait_url()
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.character_portrait_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_corporation_logo_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.corporation_logo_url(123),
|
||||||
|
EveCorporationInfo.generic_logo_url(123)
|
||||||
|
),
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.corporation_logo_url(123, 128),
|
||||||
|
EveCorporationInfo.generic_logo_url(123, 128)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.corporation_logo_url(123, 99),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.corporation_logo_url(self.my_corporation),
|
||||||
|
self.my_corporation.logo_url()
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.corporation_logo_url(self.my_character),
|
||||||
|
self.my_character.corporation_logo_url()
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.corporation_logo_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_alliance_logo_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.alliance_logo_url(123),
|
||||||
|
EveAllianceInfo.generic_logo_url(123)
|
||||||
|
),
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.alliance_logo_url(123, 128),
|
||||||
|
EveAllianceInfo.generic_logo_url(123, 128)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.alliance_logo_url(123, 99),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.alliance_logo_url(self.my_alliance),
|
||||||
|
self.my_alliance.logo_url()
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.alliance_logo_url(self.my_character),
|
||||||
|
self.my_character.alliance_logo_url()
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.alliance_logo_url(None),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
evelinks.alliance_logo_url(self.my_character_2),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
|
||||||
57
allianceauth/eveonline/evelinks/zkillboard.py
Normal file
57
allianceauth/eveonline/evelinks/zkillboard.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# this module generates profile URLs for zKillboard
|
||||||
|
|
||||||
|
from urllib.parse import urljoin, quote
|
||||||
|
|
||||||
|
from . import *
|
||||||
|
|
||||||
|
BASE_URL = 'https://zkillboard.com'
|
||||||
|
|
||||||
|
|
||||||
|
def _build_url(category: str, eve_id: int) -> str:
|
||||||
|
"""return url to profile page for an eve entity"""
|
||||||
|
|
||||||
|
if category == ESI_CATEGORY_ALLIANCE:
|
||||||
|
partial = 'alliance'
|
||||||
|
|
||||||
|
elif category == ESI_CATEGORY_CORPORATION:
|
||||||
|
partial = 'corporation'
|
||||||
|
|
||||||
|
elif category == ESI_CATEGORY_CHARACTER:
|
||||||
|
partial = 'character'
|
||||||
|
|
||||||
|
elif category == ESI_CATEGORY_REGION:
|
||||||
|
partial = 'region'
|
||||||
|
|
||||||
|
elif category == ESI_CATEGORY_SOLARSYSTEM:
|
||||||
|
partial = 'system'
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(
|
||||||
|
"Not implemented yet for category:" + category
|
||||||
|
)
|
||||||
|
|
||||||
|
url = urljoin(
|
||||||
|
BASE_URL,
|
||||||
|
'{}/{}/'.format(partial, int(eve_id))
|
||||||
|
)
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
def alliance_url(eve_id: int) -> str:
|
||||||
|
"""url for page about given alliance on zKillboard"""
|
||||||
|
return _build_url(ESI_CATEGORY_ALLIANCE, eve_id)
|
||||||
|
|
||||||
|
def character_url(eve_id: int) -> str:
|
||||||
|
"""url for page about given character on zKillboard"""
|
||||||
|
return _build_url(ESI_CATEGORY_CHARACTER, eve_id)
|
||||||
|
|
||||||
|
def corporation_url(eve_id: int) -> str:
|
||||||
|
"""url for page about given corporation on zKillboard"""
|
||||||
|
return _build_url(ESI_CATEGORY_CORPORATION, eve_id)
|
||||||
|
|
||||||
|
def region_url(eve_id: int) -> str:
|
||||||
|
"""url for page about given region on zKillboard"""
|
||||||
|
return _build_url(ESI_CATEGORY_REGION, eve_id)
|
||||||
|
|
||||||
|
def solar_system_url(eve_id: int) -> str:
|
||||||
|
return _build_url(ESI_CATEGORY_SOLARSYSTEM, eve_id)
|
||||||
@@ -7,6 +7,90 @@ from .managers import EveAllianceManager, EveAllianceProviderManager
|
|||||||
from . import providers
|
from . import providers
|
||||||
|
|
||||||
|
|
||||||
|
EVE_IMAGE_SERVER_URL = 'https://images.evetech.net'
|
||||||
|
|
||||||
|
|
||||||
|
def _eve_entity_image_url(
|
||||||
|
category: str,
|
||||||
|
id: int,
|
||||||
|
size: int = 32,
|
||||||
|
variant: str = None,
|
||||||
|
tenant: str = None,
|
||||||
|
) -> str:
|
||||||
|
"""returns image URL for an Eve Online ID.
|
||||||
|
Supported categories: `alliance`, `corporation`, `character`
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
- category: category of the ID
|
||||||
|
- id: Eve ID of the entity
|
||||||
|
- size: (optional) render size of the image.must be between 32 (default) and 1024
|
||||||
|
- variant: (optional) image variant for category. currently not relevant.
|
||||||
|
- tentant: (optional) Eve Server, either `tranquility`(default) or `singularity`
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- URL string for the requested image on the Eve image server
|
||||||
|
|
||||||
|
Exceptions:
|
||||||
|
- Throws ValueError on invalid input
|
||||||
|
"""
|
||||||
|
|
||||||
|
# input validations
|
||||||
|
categories = {
|
||||||
|
'alliance': {
|
||||||
|
'endpoint': 'alliances',
|
||||||
|
'variants': [
|
||||||
|
'logo'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'corporation': {
|
||||||
|
'endpoint': 'corporations',
|
||||||
|
'variants': [
|
||||||
|
'logo'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'character': {
|
||||||
|
'endpoint': 'characters',
|
||||||
|
'variants': [
|
||||||
|
'portrait'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tenants = ['tranquility', 'singularity']
|
||||||
|
|
||||||
|
if size < 32 or size > 1024 or (size & (size - 1) != 0):
|
||||||
|
raise ValueError('Invalid size: {}'.format(size))
|
||||||
|
|
||||||
|
if category not in categories:
|
||||||
|
raise ValueError('Invalid category {}'.format(category))
|
||||||
|
else:
|
||||||
|
endpoint = categories[category]['endpoint']
|
||||||
|
|
||||||
|
if variant:
|
||||||
|
if variant not in categories[category]['variants']:
|
||||||
|
raise ValueError('Invalid variant {} for category {}'.format(
|
||||||
|
variant,
|
||||||
|
category
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
variant = categories[category]['variants'][0]
|
||||||
|
|
||||||
|
if tenant and tenant not in tenants:
|
||||||
|
raise ValueError('Invalid tentant {}'.format(tenant))
|
||||||
|
|
||||||
|
# compose result URL
|
||||||
|
result = '{}/{}/{}/{}?size={}'.format(
|
||||||
|
EVE_IMAGE_SERVER_URL,
|
||||||
|
endpoint,
|
||||||
|
id,
|
||||||
|
variant,
|
||||||
|
size
|
||||||
|
)
|
||||||
|
if tenant:
|
||||||
|
result += '&tenant={}'.format(tenant)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class EveAllianceInfo(models.Model):
|
class EveAllianceInfo(models.Model):
|
||||||
alliance_id = models.CharField(max_length=254, unique=True)
|
alliance_id = models.CharField(max_length=254, unique=True)
|
||||||
alliance_name = models.CharField(max_length=254, unique=True)
|
alliance_name = models.CharField(max_length=254, unique=True)
|
||||||
@@ -35,14 +119,34 @@ class EveAllianceInfo(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.alliance_name
|
return self.alliance_name
|
||||||
|
|
||||||
def logo_url(self, size=32):
|
@staticmethod
|
||||||
return "https://image.eveonline.com/Alliance/%s_%s.png" % (self.alliance_id, size)
|
def generic_logo_url(alliance_id: int, size: int = 32) -> str:
|
||||||
|
"""image URL for the given alliance ID"""
|
||||||
|
return _eve_entity_image_url('alliance', alliance_id, size)
|
||||||
|
|
||||||
|
def logo_url(self, size:int = 32) -> str:
|
||||||
|
"""image URL of this alliance"""
|
||||||
|
return self.generic_logo_url(self.alliance_id, size)
|
||||||
|
|
||||||
def __getattr__(self, item):
|
@property
|
||||||
if item.startswith('logo_url_'):
|
def logo_url_32(self) -> str:
|
||||||
size = item.strip('logo_url_')
|
"""image URL for this alliance"""
|
||||||
return self.logo_url(size)
|
return self.logo_url(32)
|
||||||
return self.__getattribute__(item)
|
|
||||||
|
@property
|
||||||
|
def logo_url_64(self) -> str:
|
||||||
|
"""image URL for this alliance"""
|
||||||
|
return self.logo_url(64)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def logo_url_128(self) -> str:
|
||||||
|
"""image URL for this alliance"""
|
||||||
|
return self.logo_url(128)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def logo_url_256(self) -> str:
|
||||||
|
"""image URL for this alliance"""
|
||||||
|
return self.logo_url(256)
|
||||||
|
|
||||||
|
|
||||||
class EveCorporationInfo(models.Model):
|
class EveCorporationInfo(models.Model):
|
||||||
@@ -69,14 +173,34 @@ class EveCorporationInfo(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.corporation_name
|
return self.corporation_name
|
||||||
|
|
||||||
def logo_url(self, size=32):
|
@staticmethod
|
||||||
return "https://image.eveonline.com/Corporation/%s_%s.png" % (self.corporation_id, size)
|
def generic_logo_url(corporation_id: int, size: int = 32) -> str:
|
||||||
|
"""image URL for the given corporation ID"""
|
||||||
|
return _eve_entity_image_url('corporation', corporation_id, size)
|
||||||
|
|
||||||
def __getattr__(self, item):
|
def logo_url(self, size:int = 32) -> str:
|
||||||
if item.startswith('logo_url_'):
|
"""image URL for this corporation"""
|
||||||
size = item.strip('logo_url_')
|
return self.generic_logo_url(self.corporation_id, size)
|
||||||
return self.logo_url(size)
|
|
||||||
return self.__getattribute__(item)
|
@property
|
||||||
|
def logo_url_32(self) -> str:
|
||||||
|
"""image URL for this corporation"""
|
||||||
|
return self.logo_url(32)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def logo_url_64(self) -> str:
|
||||||
|
"""image URL for this corporation"""
|
||||||
|
return self.logo_url(64)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def logo_url_128(self) -> str:
|
||||||
|
"""image URL for this corporation"""
|
||||||
|
return self.logo_url(128)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def logo_url_256(self) -> str:
|
||||||
|
"""image URL for this corporation"""
|
||||||
|
return self.logo_url(256)
|
||||||
|
|
||||||
|
|
||||||
class EveCharacter(models.Model):
|
class EveCharacter(models.Model):
|
||||||
@@ -128,11 +252,82 @@ class EveCharacter(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.character_name
|
return self.character_name
|
||||||
|
|
||||||
def portrait_url(self, size=32):
|
@staticmethod
|
||||||
return "https://image.eveonline.com/Character/%s_%s.jpg" % (self.character_id, size)
|
def generic_portrait_url(character_id: int, size: int = 32) -> str:
|
||||||
|
"""image URL for the given character ID"""
|
||||||
|
return _eve_entity_image_url('character', character_id, size)
|
||||||
|
|
||||||
def __getattr__(self, item):
|
def portrait_url(self, size = 32) -> str:
|
||||||
if item.startswith('portrait_url_'):
|
"""image URL for this character"""
|
||||||
size = item.strip('portrait_url_')
|
return self.generic_portrait_url(self.character_id, size)
|
||||||
return self.portrait_url(size)
|
|
||||||
return self.__getattribute__(item)
|
@property
|
||||||
|
def portrait_url_32(self) -> str:
|
||||||
|
"""image URL for this character"""
|
||||||
|
return self.portrait_url(32)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def portrait_url_64(self) -> str:
|
||||||
|
"""image URL for this character"""
|
||||||
|
return self.portrait_url(64)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def portrait_url_128(self) -> str:
|
||||||
|
"""image URL for this character"""
|
||||||
|
return self.portrait_url(128)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def portrait_url_256(self) -> str:
|
||||||
|
"""image URL for this character"""
|
||||||
|
return self.portrait_url(256)
|
||||||
|
|
||||||
|
def corporation_logo_url(self, size = 32) -> str:
|
||||||
|
"""image URL for corporation of this character"""
|
||||||
|
return EveCorporationInfo.generic_logo_url(self.corporation_id, size)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def corporation_logo_url_32(self) -> str:
|
||||||
|
"""image URL for corporation of this character"""
|
||||||
|
return self.corporation_logo_url(32)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def corporation_logo_url_64(self) -> str:
|
||||||
|
"""image URL for corporation of this character"""
|
||||||
|
return self.corporation_logo_url(64)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def corporation_logo_url_128(self) -> str:
|
||||||
|
"""image URL for corporation of this character"""
|
||||||
|
return self.corporation_logo_url(128)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def corporation_logo_url_256(self) -> str:
|
||||||
|
"""image URL for corporation of this character"""
|
||||||
|
return self.corporation_logo_url(256)
|
||||||
|
|
||||||
|
def alliance_logo_url(self, size = 32) -> str:
|
||||||
|
"""image URL for alliance of this character or empty string"""
|
||||||
|
if self.alliance_id:
|
||||||
|
return EveAllianceInfo.generic_logo_url(self.alliance_id, size)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
@property
|
||||||
|
def alliance_logo_url_32(self) -> str:
|
||||||
|
"""image URL for alliance of this character or empty string"""
|
||||||
|
return self.alliance_logo_url(32)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def alliance_logo_url_64(self) -> str:
|
||||||
|
"""image URL for alliance of this character or empty string"""
|
||||||
|
return self.alliance_logo_url(64)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def alliance_logo_url_128(self) -> str:
|
||||||
|
"""image URL for alliance of this character or empty string"""
|
||||||
|
return self.alliance_logo_url(128)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def alliance_logo_url_256(self) -> str:
|
||||||
|
"""image URL for alliance of this character or empty string"""
|
||||||
|
return self.alliance_logo_url(256)
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
from esi.clients import esi_client_factory
|
|
||||||
from bravado.exception import HTTPNotFound, HTTPUnprocessableEntity
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
|
from bravado.exception import HTTPNotFound, HTTPUnprocessableEntity, HTTPError
|
||||||
|
from jsonschema.exceptions import RefResolutionError
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from esi.clients import esi_client_factory
|
||||||
|
|
||||||
|
|
||||||
|
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
||||||
|
os.path.abspath(__file__)), 'swagger.json'
|
||||||
|
)
|
||||||
"""
|
"""
|
||||||
Swagger spec operations:
|
Swagger spec operations:
|
||||||
|
|
||||||
@@ -12,6 +19,7 @@ get_alliances_alliance_id_corporations
|
|||||||
get_corporations_corporation_id
|
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
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -149,10 +157,35 @@ class EveProvider(object):
|
|||||||
|
|
||||||
|
|
||||||
class EveSwaggerProvider(EveProvider):
|
class EveSwaggerProvider(EveProvider):
|
||||||
def __init__(self, token=None, adapter=None):
|
def __init__(self, token=None, adapter=None):
|
||||||
self.client = esi_client_factory(token=token, spec_file=SWAGGER_SPEC_PATH)
|
if settings.DEBUG:
|
||||||
|
self._client = None
|
||||||
|
logger.info(
|
||||||
|
'DEBUG mode detected: ESI client will be loaded on-demand.'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self._client = esi_client_factory(
|
||||||
|
token=token, spec_file=SWAGGER_SPEC_PATH
|
||||||
|
)
|
||||||
|
except (HTTPError, RefResolutionError):
|
||||||
|
logger.exception(
|
||||||
|
'Failed to load ESI client on startup. '
|
||||||
|
'Switching to on-demand loading for ESI client.'
|
||||||
|
)
|
||||||
|
self._client = None
|
||||||
|
|
||||||
|
self._token = token
|
||||||
self.adapter = adapter or self
|
self.adapter = adapter or self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def client(self):
|
||||||
|
if self._client is None:
|
||||||
|
self._client = esi_client_factory(
|
||||||
|
token=self._token, spec_file=SWAGGER_SPEC_PATH
|
||||||
|
)
|
||||||
|
return self._client
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'esi'
|
return 'esi'
|
||||||
|
|
||||||
@@ -189,11 +222,13 @@ class EveSwaggerProvider(EveProvider):
|
|||||||
def get_character(self, character_id):
|
def get_character(self, character_id):
|
||||||
try:
|
try:
|
||||||
data = self.client.Character.get_characters_character_id(character_id=character_id).result()
|
data = self.client.Character.get_characters_character_id(character_id=character_id).result()
|
||||||
|
affiliation = self.client.Character.post_characters_affiliation(characters=[character_id]).result()[0]
|
||||||
|
|
||||||
model = Character(
|
model = Character(
|
||||||
id=character_id,
|
id=character_id,
|
||||||
name=data['name'],
|
name=data['name'],
|
||||||
corp_id=data['corporation_id'],
|
corp_id=affiliation['corporation_id'],
|
||||||
alliance_id=data['alliance_id'] if 'alliance_id' in data else None,
|
alliance_id=affiliation['alliance_id'] if 'alliance_id' in affiliation else None,
|
||||||
)
|
)
|
||||||
return model
|
return model
|
||||||
except (HTTPNotFound, HTTPUnprocessableEntity):
|
except (HTTPNotFound, HTTPUnprocessableEntity):
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
286
allianceauth/eveonline/templatetags/evelinks.py
Normal file
286
allianceauth/eveonline/templatetags/evelinks.py
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
# This module defines template tags for evelinks URLs and eve image URLs
|
||||||
|
#
|
||||||
|
# Many tags will work both with their respective eveonline object
|
||||||
|
# and their respective eve entity ID
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# character URL on evewho: {{ my_character|evewho_character_url}}
|
||||||
|
# character URL on evewho: {{ 1456384556|evewho_character_url}}
|
||||||
|
#
|
||||||
|
# For more examples see examples.html
|
||||||
|
#
|
||||||
|
# To add templatetags for additional providers just add the respective
|
||||||
|
# template functions and let them call the generic functions
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
|
||||||
|
from ..models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||||
|
from ..evelinks import evewho, dotlan, zkillboard
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
_DEFAULT_IMAGE_SIZE = 32
|
||||||
|
|
||||||
|
|
||||||
|
# generic functions
|
||||||
|
|
||||||
|
def _generic_character_url(
|
||||||
|
provider: object,
|
||||||
|
obj_prop: str,
|
||||||
|
eve_obj: EveCharacter
|
||||||
|
) -> str:
|
||||||
|
"""returns character URL for given provider and object"""
|
||||||
|
my_func = getattr(provider, 'character_url')
|
||||||
|
if isinstance(eve_obj, EveCharacter):
|
||||||
|
return my_func(getattr(eve_obj, obj_prop))
|
||||||
|
|
||||||
|
elif eve_obj is None:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
else:
|
||||||
|
return my_func(eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
def _generic_corporation_url(
|
||||||
|
provider: object,
|
||||||
|
obj_prop: str,
|
||||||
|
eve_obj: object
|
||||||
|
) -> str:
|
||||||
|
"""returns corporation URL for given provider and object"""
|
||||||
|
my_func = getattr(provider, 'corporation_url')
|
||||||
|
if isinstance(eve_obj, (EveCharacter, EveCorporationInfo)):
|
||||||
|
return my_func(getattr(eve_obj, obj_prop))
|
||||||
|
|
||||||
|
elif eve_obj is None:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
else:
|
||||||
|
return my_func(eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
def _generic_alliance_url(
|
||||||
|
provider: object,
|
||||||
|
obj_prop: str,
|
||||||
|
eve_obj: object
|
||||||
|
) -> str:
|
||||||
|
"""returns alliance URL for given provider and object"""
|
||||||
|
my_func = getattr(provider, 'alliance_url')
|
||||||
|
|
||||||
|
if isinstance(eve_obj, EveCharacter):
|
||||||
|
if eve_obj.alliance_id:
|
||||||
|
return my_func(getattr(eve_obj, obj_prop))
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
elif isinstance(eve_obj, EveAllianceInfo):
|
||||||
|
return my_func(getattr(eve_obj, obj_prop))
|
||||||
|
|
||||||
|
elif eve_obj is None:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
else:
|
||||||
|
return my_func(eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
def _generic_evelinks_url(
|
||||||
|
provider: object,
|
||||||
|
provider_func: str,
|
||||||
|
eve_obj: object
|
||||||
|
) -> str:
|
||||||
|
"""returns evelinks URL for given provider, function and object"""
|
||||||
|
my_func = getattr(provider, provider_func)
|
||||||
|
if eve_obj is None:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
else:
|
||||||
|
return my_func(eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
# evewho
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def evewho_character_url(eve_obj: EveCharacter) -> str:
|
||||||
|
"""generates an evewho URL for the given object
|
||||||
|
Works with allianceauth.eveonline objects and eve entity IDs
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
return _generic_character_url(evewho, 'character_id', eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def evewho_corporation_url(eve_obj: object) -> str:
|
||||||
|
"""generates an evewho URL for the given object
|
||||||
|
Works with allianceauth.eveonline objects and eve entity IDs
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
return _generic_corporation_url(evewho, 'corporation_id', eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def evewho_alliance_url(eve_obj: object) -> str:
|
||||||
|
"""generates an evewho URL for the given object
|
||||||
|
Works with allianceauth.eveonline objects and eve entity IDs
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
return _generic_alliance_url(evewho, 'alliance_id', eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
# dotlan
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def dotlan_corporation_url(eve_obj: object) -> str:
|
||||||
|
"""generates a dotlan URL for the given object
|
||||||
|
Works with allianceauth.eveonline objects and eve entity names
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
return _generic_corporation_url(dotlan, 'corporation_name', eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def dotlan_alliance_url(eve_obj: object) -> str:
|
||||||
|
"""generates a dotlan URL for the given object
|
||||||
|
Works with allianceauth.eveonline objects and eve entity names
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
return _generic_alliance_url(dotlan, 'alliance_name', eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def dotlan_region_url(eve_obj: object) -> str:
|
||||||
|
"""generates a dotlan URL for the given object
|
||||||
|
Works with eve entity names
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
return _generic_evelinks_url(dotlan, 'region_url', eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def dotlan_solar_system_url(eve_obj: object) -> str:
|
||||||
|
"""generates a dotlan URL for the given object
|
||||||
|
Works with eve entity names
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
return _generic_evelinks_url(dotlan, 'solar_system_url', eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
#zkillboard
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def zkillboard_character_url(eve_obj: EveCharacter) -> str:
|
||||||
|
"""generates a zkillboard URL for the given object
|
||||||
|
Works with allianceauth.eveonline objects and eve entity IDs
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
return _generic_character_url(zkillboard, 'character_id', eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def zkillboard_corporation_url(eve_obj: object) -> str:
|
||||||
|
"""generates a zkillboard URL for the given object
|
||||||
|
Works with allianceauth.eveonline objects and eve entity IDs
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
return _generic_corporation_url(zkillboard, 'corporation_id', eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def zkillboard_alliance_url(eve_obj: object) -> str:
|
||||||
|
"""generates a zkillboard URL for the given object
|
||||||
|
Works with allianceauth.eveonline objects and eve entity IDs
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
return _generic_alliance_url(zkillboard, 'alliance_id', eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def zkillboard_region_url(eve_obj: object) -> str:
|
||||||
|
"""generates a zkillboard URL for the given object
|
||||||
|
Works with eve entity IDs
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
return _generic_evelinks_url(zkillboard, 'region_url', eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def zkillboard_solar_system_url(eve_obj: object) -> str:
|
||||||
|
"""generates zkillboard URL for the given object
|
||||||
|
Works with eve entity IDs
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
return _generic_evelinks_url(zkillboard, 'solar_system_url', eve_obj)
|
||||||
|
|
||||||
|
|
||||||
|
# image urls
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def character_portrait_url(
|
||||||
|
eve_obj: object,
|
||||||
|
size: int = _DEFAULT_IMAGE_SIZE
|
||||||
|
) -> str:
|
||||||
|
"""generates an image URL for the given object
|
||||||
|
Works with EveCharacter objects or character IDs
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
if isinstance(eve_obj, EveCharacter):
|
||||||
|
return eve_obj.portrait_url(size)
|
||||||
|
|
||||||
|
elif eve_obj is None:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return EveCharacter.generic_portrait_url(eve_obj, size)
|
||||||
|
except ValueError:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def corporation_logo_url(
|
||||||
|
eve_obj: object,
|
||||||
|
size: int = _DEFAULT_IMAGE_SIZE
|
||||||
|
) -> str:
|
||||||
|
"""generates image URL for the given object
|
||||||
|
Works with EveCharacter, EveCorporationInfo objects or corporation IDs
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
if isinstance(eve_obj, EveCorporationInfo):
|
||||||
|
return eve_obj.logo_url(size)
|
||||||
|
|
||||||
|
elif isinstance(eve_obj, EveCharacter):
|
||||||
|
return eve_obj.corporation_logo_url(size)
|
||||||
|
|
||||||
|
elif eve_obj is None:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return EveCorporationInfo.generic_logo_url(eve_obj, size)
|
||||||
|
except ValueError:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def alliance_logo_url(
|
||||||
|
eve_obj: object,
|
||||||
|
size: int = _DEFAULT_IMAGE_SIZE
|
||||||
|
) -> str:
|
||||||
|
"""generates image URL for the given object
|
||||||
|
Works with EveCharacter, EveAllianceInfo objects or alliance IDs
|
||||||
|
Returns URL or empty string
|
||||||
|
"""
|
||||||
|
if isinstance(eve_obj, EveAllianceInfo):
|
||||||
|
return eve_obj.logo_url(size)
|
||||||
|
|
||||||
|
elif isinstance(eve_obj, EveCharacter):
|
||||||
|
return eve_obj.alliance_logo_url(size)
|
||||||
|
|
||||||
|
elif eve_obj is None:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return EveAllianceInfo.generic_logo_url(eve_obj, size)
|
||||||
|
except ValueError:
|
||||||
|
return ''
|
||||||
|
|
||||||
84
allianceauth/eveonline/templatetags/examples.html
Normal file
84
allianceauth/eveonline/templatetags/examples.html
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<!-- This is an example template for the evelinks template tags
|
||||||
|
|
||||||
|
Needs to be called with a context containing three objects:
|
||||||
|
|
||||||
|
- EveCharacter: my_character
|
||||||
|
- EveCorporationInfo: my_corporation
|
||||||
|
- EveAllianceInfo: my_alliance
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
{% extends 'allianceauth/base.html' %}
|
||||||
|
{% load evelinks %}
|
||||||
|
|
||||||
|
{% block page_title %}Evelinks examples{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<h1 class="page-header text-center">Evelinks templatetags examples</h1>
|
||||||
|
<div class="col-lg-12 container">
|
||||||
|
|
||||||
|
<h2>profile URLs</h2>
|
||||||
|
|
||||||
|
<div class="rows">
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h3>evewho</h3>
|
||||||
|
<p><a href="{{ my_character|evewho_character_url}}">character from character object</a></p>
|
||||||
|
<p><a href="{{ my_corporation|evewho_corporation_url}}">corporation form corporation object</a></p>
|
||||||
|
<p><a href="{{ my_character|evewho_corporation_url}}">corporation from charachter object</a></p>
|
||||||
|
<p><a href="{{ my_alliance|evewho_alliance_url}}">alliance from alliance object</a></p>
|
||||||
|
<p><a href="{{ my_character|evewho_alliance_url}}">alliance from character object</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h3>dotlan</h3>
|
||||||
|
<p><a href="{{ my_character|dotlan_corporation_url}}">corporation form character object</a></p>
|
||||||
|
<p><a href="{{ my_corporation|dotlan_corporation_url}}">corporation form corporation object</a></p>
|
||||||
|
<p><a href="{{ my_character|dotlan_alliance_url}}">alliance from character object</a></p>
|
||||||
|
<p><a href="{{ my_alliance|dotlan_alliance_url}}">alliance from alliance object</a></p>
|
||||||
|
<p><a href="{{ 'Black Rise'|dotlan_region_url}}">region from name string</a></p>
|
||||||
|
<p><a href="{{ 'Tama'|dotlan_solar_system_url}}">solar system from name string</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h3>zkillboard</h3>
|
||||||
|
<p><a href="{{ my_character|zkillboard_character_url}}">character from character object</a></p>
|
||||||
|
<p><a href="{{ my_character|zkillboard_corporation_url}}">corporation from character object</a></p>
|
||||||
|
<p><a href="{{ my_corporation|zkillboard_corporation_url}}">corporation form corporation object</a></p>
|
||||||
|
<p><a href="{{ my_character|zkillboard_alliance_url}}">alliance from character object</a></p>
|
||||||
|
<p><a href="{{ my_alliance|zkillboard_alliance_url}}">alliance from alliance object</a></p>
|
||||||
|
<p><a href="{{ 10000069|zkillboard_region_url}}">region from ID</a></p>
|
||||||
|
<p><a href="{{ 30002813|zkillboard_solar_system_url}}">solar sytem from ID</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>image URLs</h2>
|
||||||
|
|
||||||
|
<div class="rows">
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<p>character from ID: <img src="{{ my_character.character_id|character_portrait_url:128}}"></p>
|
||||||
|
<p>character from character object: <img src="{{ my_character|character_portrait_url:128}}"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<p>corporation from ID: <img src="{{ my_character.corporation_id|corporation_logo_url:128}}"></p>
|
||||||
|
<p>corporation from character object: <img src="{{ my_character|corporation_logo_url:128}}"></p>
|
||||||
|
<p>corporation from corporation object: <img src="{{ my_corporation|corporation_logo_url:128}}"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<p>alliance from ID: <img src="{{ my_character.alliance_id|alliance_logo_url:128}}"></p>
|
||||||
|
<p>alliance from character object: <img src="{{ my_character|alliance_logo_url:128}}"></p>
|
||||||
|
<p>alliance from alliance object: <img src="{{ my_alliance|alliance_logo_url:128}}"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def set_logger(logger_name: str, name: str) -> object:
|
||||||
|
"""set logger for current test module
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- logger: current logger object
|
||||||
|
- name: name of current module, e.g. __file__
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- amended logger
|
||||||
|
"""
|
||||||
|
|
||||||
|
# reconfigure logger so we get logging from tested module
|
||||||
|
f_format = logging.Formatter(
|
||||||
|
'%(asctime)s - %(levelname)s - %(module)s:%(funcName)s - %(message)s'
|
||||||
|
)
|
||||||
|
f_handler = logging.FileHandler(
|
||||||
|
'{}.log'.format(os.path.splitext(name)[0]),
|
||||||
|
'w+'
|
||||||
|
)
|
||||||
|
f_handler.setFormatter(f_format)
|
||||||
|
logger = logging.getLogger(logger_name)
|
||||||
|
logger.level = logging.DEBUG
|
||||||
|
logger.addHandler(f_handler)
|
||||||
|
logger.propagate = False
|
||||||
|
return logger
|
||||||
|
|||||||
1
allianceauth/eveonline/tests/swagger_old.json
Normal file
1
allianceauth/eveonline/tests/swagger_old.json
Normal file
File diff suppressed because one or more lines are too long
@@ -26,12 +26,22 @@ class EveCharacterManagerTestCase(TestCase):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def corp(self):
|
def corp(self):
|
||||||
return Corporation(id='2345', name='Test Corp', alliance_id='3456', ticker='0BUGS')
|
return Corporation(
|
||||||
|
id='2345',
|
||||||
|
name='Test Corp',
|
||||||
|
alliance_id='3456',
|
||||||
|
ticker='0BUGS'
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch('allianceauth.eveonline.managers.providers.provider')
|
@mock.patch('allianceauth.eveonline.managers.providers.provider')
|
||||||
def test_create_character(self, provider):
|
def test_create_character(self, provider):
|
||||||
# Also covers create_character_obj
|
# Also covers create_character_obj
|
||||||
expected = self.TestCharacter(id='1234', name='Test Character', corp_id='2345', alliance_id='3456')
|
expected = self.TestCharacter(
|
||||||
|
id='1234',
|
||||||
|
name='Test Character',
|
||||||
|
corp_id='2345',
|
||||||
|
alliance_id='3456'
|
||||||
|
)
|
||||||
|
|
||||||
provider.get_character.return_value = expected
|
provider.get_character.return_value = expected
|
||||||
|
|
||||||
@@ -58,7 +68,12 @@ class EveCharacterManagerTestCase(TestCase):
|
|||||||
alliance_name='character.alliance.name',
|
alliance_name='character.alliance.name',
|
||||||
)
|
)
|
||||||
|
|
||||||
expected = self.TestCharacter(id='1234', name='Test Character', corp_id='2345', alliance_id='3456')
|
expected = self.TestCharacter(
|
||||||
|
id='1234',
|
||||||
|
name='Test Character',
|
||||||
|
corp_id='2345',
|
||||||
|
alliance_id='3456'
|
||||||
|
)
|
||||||
|
|
||||||
provider.get_character.return_value = expected
|
provider.get_character.return_value = expected
|
||||||
|
|
||||||
@@ -73,6 +88,7 @@ class EveCharacterManagerTestCase(TestCase):
|
|||||||
self.assertEqual(result.alliance_name, expected.alliance.name)
|
self.assertEqual(result.alliance_name, expected.alliance.name)
|
||||||
|
|
||||||
def test_get_character_by_id(self):
|
def test_get_character_by_id(self):
|
||||||
|
EveCharacter.objects.all().delete()
|
||||||
EveCharacter.objects.create(
|
EveCharacter.objects.create(
|
||||||
character_id='1234',
|
character_id='1234',
|
||||||
character_name='character.name',
|
character_name='character.name',
|
||||||
@@ -83,11 +99,15 @@ class EveCharacterManagerTestCase(TestCase):
|
|||||||
alliance_name='character.alliance.name',
|
alliance_name='character.alliance.name',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# try to get existing character
|
||||||
result = EveCharacter.objects.get_character_by_id('1234')
|
result = EveCharacter.objects.get_character_by_id('1234')
|
||||||
|
|
||||||
self.assertEqual(result.character_id, '1234')
|
self.assertEqual(result.character_id, '1234')
|
||||||
self.assertEqual(result.character_name, 'character.name')
|
self.assertEqual(result.character_name, 'character.name')
|
||||||
|
|
||||||
|
# try to get non existing character
|
||||||
|
self.assertIsNone(EveCharacter.objects.get_character_by_id('9999'))
|
||||||
|
|
||||||
|
|
||||||
class EveAllianceProviderManagerTestCase(TestCase):
|
class EveAllianceProviderManagerTestCase(TestCase):
|
||||||
@mock.patch('allianceauth.eveonline.managers.providers.provider')
|
@mock.patch('allianceauth.eveonline.managers.providers.provider')
|
||||||
@@ -110,8 +130,13 @@ class EveAllianceManagerTestCase(TestCase):
|
|||||||
@mock.patch('allianceauth.eveonline.managers.providers.provider')
|
@mock.patch('allianceauth.eveonline.managers.providers.provider')
|
||||||
def test_create_alliance(self, provider, populate_alliance):
|
def test_create_alliance(self, provider, populate_alliance):
|
||||||
# Also covers create_alliance_obj
|
# Also covers create_alliance_obj
|
||||||
expected = self.TestAlliance(id='3456', name='Test Alliance', ticker='TEST',
|
expected = self.TestAlliance(
|
||||||
corp_ids=['2345'], executor_corp_id='2345')
|
id='3456',
|
||||||
|
name='Test Alliance',
|
||||||
|
ticker='TEST',
|
||||||
|
corp_ids=['2345'],
|
||||||
|
executor_corp_id='2345'
|
||||||
|
)
|
||||||
|
|
||||||
provider.get_alliance.return_value = expected
|
provider.get_alliance.return_value = expected
|
||||||
|
|
||||||
@@ -132,8 +157,13 @@ class EveAllianceManagerTestCase(TestCase):
|
|||||||
alliance_ticker='alliance.ticker',
|
alliance_ticker='alliance.ticker',
|
||||||
executor_corp_id='alliance.executor_corp_id',
|
executor_corp_id='alliance.executor_corp_id',
|
||||||
)
|
)
|
||||||
expected = self.TestAlliance(id='3456', name='Test Alliance', ticker='TEST',
|
expected = self.TestAlliance(
|
||||||
corp_ids=['2345'], executor_corp_id='2345')
|
id='3456',
|
||||||
|
name='Test Alliance',
|
||||||
|
ticker='TEST',
|
||||||
|
corp_ids=['2345'],
|
||||||
|
executor_corp_id='2345'
|
||||||
|
)
|
||||||
|
|
||||||
provider.get_alliance.return_value = expected
|
provider.get_alliance.return_value = expected
|
||||||
|
|
||||||
@@ -159,13 +189,22 @@ class EveCorporationManagerTestCase(TestCase):
|
|||||||
class TestCorporation(Corporation):
|
class TestCorporation(Corporation):
|
||||||
@property
|
@property
|
||||||
def alliance(self):
|
def alliance(self):
|
||||||
return EveAllianceManagerTestCase.TestAlliance(id='3456', name='Test Alliance', ticker='TEST',
|
return EveAllianceManagerTestCase.TestAlliance(
|
||||||
corp_ids=['2345'], executor_corp_id='2345')
|
id='3456',
|
||||||
|
name='Test Alliance',
|
||||||
|
ticker='TEST',
|
||||||
|
corp_ids=['2345'],
|
||||||
|
executor_corp_id='2345'
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ceo(self):
|
def ceo(self):
|
||||||
return EveCharacterManagerTestCase.TestCharacter(id='1234', name='Test Character',
|
return EveCharacterManagerTestCase.TestCharacter(
|
||||||
corp_id='2345', alliance_id='3456')
|
id='1234',
|
||||||
|
name='Test Character',
|
||||||
|
corp_id='2345',
|
||||||
|
alliance_id='3456'
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch('allianceauth.eveonline.managers.providers.provider')
|
@mock.patch('allianceauth.eveonline.managers.providers.provider')
|
||||||
def test_create_corporation(self, provider):
|
def test_create_corporation(self, provider):
|
||||||
@@ -177,8 +216,14 @@ class EveCorporationManagerTestCase(TestCase):
|
|||||||
executor_corp_id='alliance.executor_corp_id',
|
executor_corp_id='alliance.executor_corp_id',
|
||||||
)
|
)
|
||||||
|
|
||||||
expected = self.TestCorporation(id='2345', name='Test Corp', ticker='0BUGS',
|
expected = self.TestCorporation(
|
||||||
ceo_id='1234', members=1, alliance_id='3456')
|
id='2345',
|
||||||
|
name='Test Corp',
|
||||||
|
ticker='0BUGS',
|
||||||
|
ceo_id='1234',
|
||||||
|
members=1,
|
||||||
|
alliance_id='3456'
|
||||||
|
)
|
||||||
|
|
||||||
provider.get_corp.return_value = expected
|
provider.get_corp.return_value = expected
|
||||||
|
|
||||||
@@ -191,7 +236,30 @@ class EveCorporationManagerTestCase(TestCase):
|
|||||||
self.assertEqual(result.alliance, exp_alliance)
|
self.assertEqual(result.alliance, exp_alliance)
|
||||||
|
|
||||||
@mock.patch('allianceauth.eveonline.managers.providers.provider')
|
@mock.patch('allianceauth.eveonline.managers.providers.provider')
|
||||||
def test_create_corporation(self, provider):
|
def test_create_corporation_no_alliance(self, provider):
|
||||||
|
# variant to test no alliance case
|
||||||
|
# Also covers create_corp_obj
|
||||||
|
expected = self.TestCorporation(
|
||||||
|
id='2345',
|
||||||
|
name='Test Corp',
|
||||||
|
ticker='0BUGS',
|
||||||
|
ceo_id='1234',
|
||||||
|
members=1,
|
||||||
|
alliance_id='3456'
|
||||||
|
)
|
||||||
|
|
||||||
|
provider.get_corp.return_value = expected
|
||||||
|
|
||||||
|
result = EveCorporationInfo.objects.create_corporation('2345')
|
||||||
|
|
||||||
|
self.assertEqual(result.corporation_id, expected.id)
|
||||||
|
self.assertEqual(result.corporation_name, expected.name)
|
||||||
|
self.assertEqual(result.corporation_ticker, expected.ticker)
|
||||||
|
self.assertEqual(result.member_count, expected.members)
|
||||||
|
self.assertIsNone(result.alliance)
|
||||||
|
|
||||||
|
@mock.patch('allianceauth.eveonline.managers.providers.provider')
|
||||||
|
def test_update_corporation(self, provider):
|
||||||
# Also covers Model.update_corporation
|
# Also covers Model.update_corporation
|
||||||
exp_alliance = EveAllianceInfo.objects.create(
|
exp_alliance = EveAllianceInfo.objects.create(
|
||||||
alliance_id='3456',
|
alliance_id='3456',
|
||||||
@@ -208,8 +276,14 @@ class EveCorporationManagerTestCase(TestCase):
|
|||||||
alliance=None,
|
alliance=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
expected = self.TestCorporation(id='2345', name='Test Corp', ticker='0BUGS',
|
expected = self.TestCorporation(
|
||||||
ceo_id='1234', members=1, alliance_id='3456')
|
id='2345',
|
||||||
|
name='Test Corp',
|
||||||
|
ticker='0BUGS',
|
||||||
|
ceo_id='1234',
|
||||||
|
members=1,
|
||||||
|
alliance_id='3456'
|
||||||
|
)
|
||||||
|
|
||||||
provider.get_corp.return_value = expected
|
provider.get_corp.return_value = expected
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,104 @@
|
|||||||
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from ..models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
from ..models import EveCharacter, EveCorporationInfo, \
|
||||||
|
EveAllianceInfo, _eve_entity_image_url
|
||||||
|
from ..providers import Alliance, Corporation, Character
|
||||||
|
|
||||||
|
|
||||||
|
class EveUniverseImageUrlTestCase(TestCase):
|
||||||
|
"""unit test for _eve_entity_image_url()"""
|
||||||
|
|
||||||
|
def test_sizes(self):
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('character', 42),
|
||||||
|
'https://images.evetech.net/characters/42/portrait?size=32'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('character', 42, size=32),
|
||||||
|
'https://images.evetech.net/characters/42/portrait?size=32'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('character', 42, size=64),
|
||||||
|
'https://images.evetech.net/characters/42/portrait?size=64'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('character', 42, size=128),
|
||||||
|
'https://images.evetech.net/characters/42/portrait?size=128'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('character', 42, size=256),
|
||||||
|
'https://images.evetech.net/characters/42/portrait?size=256'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('character', 42, size=512),
|
||||||
|
'https://images.evetech.net/characters/42/portrait?size=512'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('character', 42, size=1024),
|
||||||
|
'https://images.evetech.net/characters/42/portrait?size=1024'
|
||||||
|
)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_eve_entity_image_url('corporation', 42, size=-5)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_eve_entity_image_url('corporation', 42, size=0)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_eve_entity_image_url('corporation', 42, size=31)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_eve_entity_image_url('corporation', 42, size=1025)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_eve_entity_image_url('corporation', 42, size=2048)
|
||||||
|
|
||||||
|
|
||||||
|
def test_variant(self):
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('character', 42, variant='portrait'),
|
||||||
|
'https://images.evetech.net/characters/42/portrait?size=32'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('alliance', 42, variant='logo'),
|
||||||
|
'https://images.evetech.net/alliances/42/logo?size=32'
|
||||||
|
)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_eve_entity_image_url('character', 42, variant='logo')
|
||||||
|
|
||||||
|
|
||||||
|
def test_alliance(self):
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('alliance', 42),
|
||||||
|
'https://images.evetech.net/alliances/42/logo?size=32'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('corporation', 42),
|
||||||
|
'https://images.evetech.net/corporations/42/logo?size=32'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('character', 42),
|
||||||
|
'https://images.evetech.net/characters/42/portrait?size=32'
|
||||||
|
)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_eve_entity_image_url('station', 42)
|
||||||
|
|
||||||
|
|
||||||
|
def test_tenants(self):
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('character', 42, tenant='tranquility'),
|
||||||
|
'https://images.evetech.net/characters/42/portrait?size=32&tenant=tranquility'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
_eve_entity_image_url('character', 42, tenant='singularity'),
|
||||||
|
'https://images.evetech.net/characters/42/portrait?size=32&tenant=singularity'
|
||||||
|
)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_eve_entity_image_url('character', 42, tenant='xxx')
|
||||||
|
|
||||||
|
|
||||||
class EveCharacterTestCase(TestCase):
|
class EveCharacterTestCase(TestCase):
|
||||||
def test_corporation_prop(self):
|
def test_corporation_prop(self):
|
||||||
"""
|
"""
|
||||||
@@ -119,3 +215,410 @@ class EveCharacterTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertIsNone(character.alliance)
|
self.assertIsNone(character.alliance)
|
||||||
|
|
||||||
|
@patch('allianceauth.eveonline.providers.provider')
|
||||||
|
def test_update_character(self, mock_provider):
|
||||||
|
mock_provider.get_corp.return_value = Corporation(
|
||||||
|
id=2002,
|
||||||
|
name='Dummy Corp 2',
|
||||||
|
ticker='DC2',
|
||||||
|
ceo_id=1001,
|
||||||
|
members=34,
|
||||||
|
)
|
||||||
|
|
||||||
|
my_character = EveCharacter.objects.create(
|
||||||
|
character_id='1001',
|
||||||
|
character_name='Bruce Wayne',
|
||||||
|
corporation_id='2001',
|
||||||
|
corporation_name='Dummy Corp 1',
|
||||||
|
corporation_ticker='DC1',
|
||||||
|
alliance_id='3001',
|
||||||
|
alliance_name='Dummy Alliance 1',
|
||||||
|
)
|
||||||
|
my_updated_character = Character(
|
||||||
|
name='Bruce X. Wayne',
|
||||||
|
corp_id=2002
|
||||||
|
)
|
||||||
|
my_character.update_character(my_updated_character)
|
||||||
|
self.assertEqual(my_character.character_name, 'Bruce X. Wayne')
|
||||||
|
|
||||||
|
# todo: add test cases not yet covered, e.g. with alliance
|
||||||
|
|
||||||
|
|
||||||
|
def test_image_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
EveCharacter.generic_portrait_url(42),
|
||||||
|
_eve_entity_image_url('character', 42)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
EveCharacter.generic_portrait_url(42, 256),
|
||||||
|
_eve_entity_image_url('character', 42, 256)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_portrait_urls(self):
|
||||||
|
x = EveCharacter(
|
||||||
|
character_id='42',
|
||||||
|
character_name='character.name',
|
||||||
|
corporation_id='123',
|
||||||
|
corporation_name='corporation.name',
|
||||||
|
corporation_ticker='ABC',
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.portrait_url(),
|
||||||
|
_eve_entity_image_url('character', 42)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.portrait_url(64),
|
||||||
|
_eve_entity_image_url('character', 42, size=64)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.portrait_url_32,
|
||||||
|
_eve_entity_image_url('character', 42, size=32)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.portrait_url_64,
|
||||||
|
_eve_entity_image_url('character', 42, size=64)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.portrait_url_128,
|
||||||
|
_eve_entity_image_url('character', 42, size=128)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.portrait_url_256,
|
||||||
|
_eve_entity_image_url('character', 42, size=256)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_corporation_logo_urls(self):
|
||||||
|
x = EveCharacter(
|
||||||
|
character_id='42',
|
||||||
|
character_name='character.name',
|
||||||
|
corporation_id='123',
|
||||||
|
corporation_name='corporation.name',
|
||||||
|
corporation_ticker='ABC',
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.corporation_logo_url(),
|
||||||
|
_eve_entity_image_url('corporation', 123)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.corporation_logo_url(256),
|
||||||
|
_eve_entity_image_url('corporation', 123, size=256)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.corporation_logo_url_32,
|
||||||
|
_eve_entity_image_url('corporation', 123, size=32)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.corporation_logo_url_64,
|
||||||
|
_eve_entity_image_url('corporation', 123, size=64)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.corporation_logo_url_128,
|
||||||
|
_eve_entity_image_url('corporation', 123, size=128)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.corporation_logo_url_256,
|
||||||
|
_eve_entity_image_url('corporation', 123, size=256)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_alliance_logo_urls(self):
|
||||||
|
x = EveCharacter(
|
||||||
|
character_id='42',
|
||||||
|
character_name='character.name',
|
||||||
|
corporation_id='123',
|
||||||
|
corporation_name='corporation.name',
|
||||||
|
corporation_ticker='ABC',
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.alliance_logo_url(),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.alliance_logo_url_32,
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.alliance_logo_url_64,
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.alliance_logo_url_128,
|
||||||
|
''
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.alliance_logo_url_256,
|
||||||
|
''
|
||||||
|
)
|
||||||
|
x.alliance_id = 987
|
||||||
|
self.assertEqual(
|
||||||
|
x.alliance_logo_url(),
|
||||||
|
_eve_entity_image_url('alliance', 987)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.alliance_logo_url(128),
|
||||||
|
_eve_entity_image_url('alliance', 987, size=128)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.alliance_logo_url_32,
|
||||||
|
_eve_entity_image_url('alliance', 987, size=32)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.alliance_logo_url_64,
|
||||||
|
_eve_entity_image_url('alliance', 987, size=64)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.alliance_logo_url_128,
|
||||||
|
_eve_entity_image_url('alliance', 987, size=128)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.alliance_logo_url_256,
|
||||||
|
_eve_entity_image_url('alliance', 987, size=256)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class EveAllianceTestCase(TestCase):
|
||||||
|
|
||||||
|
def test_str(self):
|
||||||
|
my_alliance = EveAllianceInfo(
|
||||||
|
alliance_id=3001,
|
||||||
|
alliance_name='Dummy Alliance 1',
|
||||||
|
alliance_ticker='DA1',
|
||||||
|
executor_corp_id=2001
|
||||||
|
)
|
||||||
|
self.assertEqual(str(my_alliance), 'Dummy Alliance 1')
|
||||||
|
|
||||||
|
@patch(
|
||||||
|
'allianceauth.eveonline.models.EveCorporationInfo.objects.create_corporation'
|
||||||
|
)
|
||||||
|
def test_populate_alliance(self, mock_create_corporation):
|
||||||
|
|
||||||
|
def create_corp(corp_id):
|
||||||
|
if corp_id == 2002:
|
||||||
|
EveCorporationInfo.objects.create(
|
||||||
|
corporation_id=2002,
|
||||||
|
corporation_name='Dummy Corporation 2',
|
||||||
|
corporation_ticker='DC2',
|
||||||
|
member_count=87,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError()
|
||||||
|
|
||||||
|
mock_EveAllianceProviderManager = Mock()
|
||||||
|
mock_EveAllianceProviderManager.get_alliance.return_value = \
|
||||||
|
Alliance(
|
||||||
|
id=3001,
|
||||||
|
name='Dummy Alliance 1',
|
||||||
|
corp_ids=[2001, 2002]
|
||||||
|
)
|
||||||
|
mock_create_corporation.side_effect = create_corp
|
||||||
|
|
||||||
|
EveCorporationInfo.objects.create(
|
||||||
|
corporation_id=2001,
|
||||||
|
corporation_name='Dummy Corporation 1',
|
||||||
|
corporation_ticker='DC1',
|
||||||
|
member_count=42,
|
||||||
|
)
|
||||||
|
|
||||||
|
my_alliance = EveAllianceInfo(
|
||||||
|
alliance_id=3001,
|
||||||
|
alliance_name='Dummy Alliance 1',
|
||||||
|
alliance_ticker='DA1',
|
||||||
|
executor_corp_id=2001
|
||||||
|
)
|
||||||
|
my_alliance.provider = mock_EveAllianceProviderManager
|
||||||
|
my_alliance.save()
|
||||||
|
my_alliance.populate_alliance()
|
||||||
|
|
||||||
|
for corporation in EveCorporationInfo.objects\
|
||||||
|
.filter(corporation_id__in=[2001, 2002]
|
||||||
|
):
|
||||||
|
self.assertEqual(corporation.alliance, my_alliance)
|
||||||
|
|
||||||
|
def test_update_alliance_with_object(self):
|
||||||
|
my_alliance = EveAllianceInfo.objects.create(
|
||||||
|
alliance_id=3001,
|
||||||
|
alliance_name='Dummy Alliance 1',
|
||||||
|
alliance_ticker='DA1',
|
||||||
|
executor_corp_id=2001
|
||||||
|
)
|
||||||
|
updated_alliance = Alliance(
|
||||||
|
id=3002,
|
||||||
|
name='Dummy Alliance 2',
|
||||||
|
corp_ids=[2004],
|
||||||
|
executor_corp_id=2004
|
||||||
|
)
|
||||||
|
my_alliance.update_alliance(updated_alliance)
|
||||||
|
my_alliance.refresh_from_db()
|
||||||
|
self.assertEqual(int(my_alliance.executor_corp_id), 2004)
|
||||||
|
|
||||||
|
# potential bug
|
||||||
|
# update_alliance() is only updateting executor_corp_id when object is given
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_alliance_wo_object(self):
|
||||||
|
mock_EveAllianceProviderManager = Mock()
|
||||||
|
mock_EveAllianceProviderManager.get_alliance.return_value = \
|
||||||
|
Alliance(
|
||||||
|
id=3002,
|
||||||
|
name='Dummy Alliance 2',
|
||||||
|
corp_ids=[2004],
|
||||||
|
executor_corp_id=2004
|
||||||
|
)
|
||||||
|
|
||||||
|
my_alliance = EveAllianceInfo.objects.create(
|
||||||
|
alliance_id=3001,
|
||||||
|
alliance_name='Dummy Alliance 1',
|
||||||
|
alliance_ticker='DA1',
|
||||||
|
executor_corp_id=2001
|
||||||
|
)
|
||||||
|
my_alliance.provider = mock_EveAllianceProviderManager
|
||||||
|
my_alliance.save()
|
||||||
|
updated_alliance = Alliance(
|
||||||
|
name='Dummy Alliance 2',
|
||||||
|
corp_ids=[2004],
|
||||||
|
executor_corp_id=2004
|
||||||
|
)
|
||||||
|
my_alliance.update_alliance()
|
||||||
|
my_alliance.refresh_from_db()
|
||||||
|
self.assertEqual(int(my_alliance.executor_corp_id), 2004)
|
||||||
|
|
||||||
|
# potential bug
|
||||||
|
# update_alliance() is only updateting executor_corp_id nothing else ???
|
||||||
|
|
||||||
|
|
||||||
|
def test_image_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
EveAllianceInfo.generic_logo_url(42),
|
||||||
|
_eve_entity_image_url('alliance', 42)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
EveAllianceInfo.generic_logo_url(42, 256),
|
||||||
|
_eve_entity_image_url('alliance', 42, 256)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_logo_url(self):
|
||||||
|
x = EveAllianceInfo(
|
||||||
|
alliance_id='42',
|
||||||
|
alliance_name='alliance.name',
|
||||||
|
alliance_ticker='ABC',
|
||||||
|
executor_corp_id='123'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.logo_url(),
|
||||||
|
'https://images.evetech.net/alliances/42/logo?size=32'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.logo_url(64),
|
||||||
|
'https://images.evetech.net/alliances/42/logo?size=64'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.logo_url_32,
|
||||||
|
'https://images.evetech.net/alliances/42/logo?size=32'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.logo_url_64,
|
||||||
|
'https://images.evetech.net/alliances/42/logo?size=64'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.logo_url_128,
|
||||||
|
'https://images.evetech.net/alliances/42/logo?size=128'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
x.logo_url_256,
|
||||||
|
'https://images.evetech.net/alliances/42/logo?size=256'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class EveCorporationTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
my_alliance = EveAllianceInfo.objects.create(
|
||||||
|
alliance_id=3001,
|
||||||
|
alliance_name='Dummy Alliance 1',
|
||||||
|
alliance_ticker='DA1',
|
||||||
|
executor_corp_id=2001
|
||||||
|
)
|
||||||
|
self.my_corp = EveCorporationInfo(
|
||||||
|
corporation_id=2001,
|
||||||
|
corporation_name='Dummy Corporation 1',
|
||||||
|
corporation_ticker='DC1',
|
||||||
|
member_count=42,
|
||||||
|
alliance=my_alliance
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_str(self):
|
||||||
|
self.assertEqual(str(self.my_corp), 'Dummy Corporation 1')
|
||||||
|
|
||||||
|
def test_update_corporation_from_object_w_alliance(self):
|
||||||
|
updated_corp = Corporation(
|
||||||
|
members=87
|
||||||
|
)
|
||||||
|
self.my_corp.update_corporation(updated_corp)
|
||||||
|
self.assertEqual(self.my_corp.member_count, 87)
|
||||||
|
|
||||||
|
# potential bug
|
||||||
|
# update_corporation updates member_count only
|
||||||
|
|
||||||
|
def test_update_corporation_no_object_w_alliance(self):
|
||||||
|
mock_provider = Mock()
|
||||||
|
mock_provider.get_corporation.return_value = Corporation(
|
||||||
|
members=87
|
||||||
|
)
|
||||||
|
self.my_corp.provider = mock_provider
|
||||||
|
|
||||||
|
self.my_corp.update_corporation()
|
||||||
|
self.assertEqual(self.my_corp.member_count, 87)
|
||||||
|
|
||||||
|
def test_update_corporation_from_object_wo_alliance(self):
|
||||||
|
my_corp2 = EveCorporationInfo(
|
||||||
|
corporation_id=2011,
|
||||||
|
corporation_name='Dummy Corporation 11',
|
||||||
|
corporation_ticker='DC11',
|
||||||
|
member_count=6
|
||||||
|
)
|
||||||
|
updated_corp = Corporation(
|
||||||
|
members=8
|
||||||
|
)
|
||||||
|
my_corp2.update_corporation(updated_corp)
|
||||||
|
self.assertEqual(my_corp2.member_count, 8)
|
||||||
|
self.assertIsNone(my_corp2.alliance)
|
||||||
|
|
||||||
|
|
||||||
|
def test_image_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
EveCorporationInfo.generic_logo_url(42),
|
||||||
|
_eve_entity_image_url('corporation', 42)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
EveCorporationInfo.generic_logo_url(42, 256),
|
||||||
|
_eve_entity_image_url('corporation', 42, 256)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_logo_url(self):
|
||||||
|
self.assertEqual(
|
||||||
|
self.my_corp.logo_url(),
|
||||||
|
'https://images.evetech.net/corporations/2001/logo?size=32'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.my_corp.logo_url(64),
|
||||||
|
'https://images.evetech.net/corporations/2001/logo?size=64'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.my_corp.logo_url_32,
|
||||||
|
'https://images.evetech.net/corporations/2001/logo?size=32'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.my_corp.logo_url_64,
|
||||||
|
'https://images.evetech.net/corporations/2001/logo?size=64'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.my_corp.logo_url_128,
|
||||||
|
'https://images.evetech.net/corporations/2001/logo?size=128'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.my_corp.logo_url_256,
|
||||||
|
'https://images.evetech.net/corporations/2001/logo?size=256'
|
||||||
|
)
|
||||||
|
|
||||||
|
|||||||
605
allianceauth/eveonline/tests/test_providers.py
Normal file
605
allianceauth/eveonline/tests/test_providers.py
Normal file
@@ -0,0 +1,605 @@
|
|||||||
|
import os
|
||||||
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
|
from bravado.exception import HTTPNotFound, HTTPUnprocessableEntity
|
||||||
|
from jsonschema.exceptions import RefResolutionError
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from . import set_logger
|
||||||
|
from ..models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||||
|
from ..providers import ObjectNotFound, Entity, Character, Corporation, \
|
||||||
|
Alliance, ItemType, EveProvider, EveSwaggerProvider
|
||||||
|
|
||||||
|
|
||||||
|
MODULE_PATH = 'allianceauth.eveonline.providers'
|
||||||
|
SWAGGER_OLD_SPEC_PATH = os.path.join(os.path.dirname(
|
||||||
|
os.path.abspath(__file__)), 'swagger_old.json'
|
||||||
|
)
|
||||||
|
set_logger(MODULE_PATH, __file__)
|
||||||
|
|
||||||
|
|
||||||
|
class TestObjectNotFound(TestCase):
|
||||||
|
|
||||||
|
def test_str(self):
|
||||||
|
x = ObjectNotFound(1001, 'Character')
|
||||||
|
self.assertEqual(str(x), 'Character with ID 1001 not found.')
|
||||||
|
|
||||||
|
|
||||||
|
class TestEntity(TestCase):
|
||||||
|
|
||||||
|
def test_str(self):
|
||||||
|
x = Entity(1001, 'Bruce Wayne')
|
||||||
|
self.assertEqual(str(x), 'Bruce Wayne')
|
||||||
|
|
||||||
|
# bug - does not return a string
|
||||||
|
"""
|
||||||
|
x = Entity(1001)
|
||||||
|
self.assertEqual(str(x), '')
|
||||||
|
|
||||||
|
x = Entity()
|
||||||
|
self.assertEqual(str(x), '')
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_repr(self):
|
||||||
|
x = Entity(1001, 'Bruce Wayne')
|
||||||
|
self.assertEqual(repr(x), '<Entity (1001): Bruce Wayne>')
|
||||||
|
|
||||||
|
x = Entity(1001)
|
||||||
|
self.assertEqual(repr(x), '<Entity (1001): None>')
|
||||||
|
|
||||||
|
x = Entity()
|
||||||
|
self.assertEqual(repr(x), '<Entity (None): None>')
|
||||||
|
|
||||||
|
|
||||||
|
def test_bool(self):
|
||||||
|
x = Entity(1001)
|
||||||
|
self.assertTrue(bool(x))
|
||||||
|
|
||||||
|
x = Entity()
|
||||||
|
self.assertFalse(bool(x))
|
||||||
|
|
||||||
|
def test_eq(self):
|
||||||
|
x1 = Entity(1001)
|
||||||
|
x2 = Entity(1001)
|
||||||
|
y = Entity(1002)
|
||||||
|
z1 = Entity()
|
||||||
|
z2 = Entity()
|
||||||
|
|
||||||
|
self.assertEqual(x1, x2)
|
||||||
|
self.assertNotEqual(x1, y)
|
||||||
|
self.assertNotEqual(x1, z1)
|
||||||
|
self.assertEqual(z1, z2)
|
||||||
|
|
||||||
|
# bug: missing _neq_ in Equity to compliment _eq_
|
||||||
|
|
||||||
|
|
||||||
|
class TestCorporation(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 = Corporation(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 = Corporation()
|
||||||
|
self.assertEqual(
|
||||||
|
x.alliance,
|
||||||
|
Entity(None, None)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.EveSwaggerProvider.get_character')
|
||||||
|
def test_ceo(self, mock_provider_get_character):
|
||||||
|
my_ceo = Character(
|
||||||
|
id=1001,
|
||||||
|
name='Bruce Wayne',
|
||||||
|
corp_id=2001,
|
||||||
|
alliance_id=3001
|
||||||
|
)
|
||||||
|
mock_provider_get_character.return_value = my_ceo
|
||||||
|
|
||||||
|
# fetch from provider if not defined
|
||||||
|
x = Corporation()
|
||||||
|
self.assertEqual(
|
||||||
|
x.ceo,
|
||||||
|
my_ceo
|
||||||
|
)
|
||||||
|
|
||||||
|
# return existing if defined
|
||||||
|
mock_provider_get_character.return_value = None
|
||||||
|
self.assertEqual(
|
||||||
|
x.ceo,
|
||||||
|
my_ceo
|
||||||
|
)
|
||||||
|
self.assertEqual(mock_provider_get_character.call_count, 1)
|
||||||
|
|
||||||
|
# bug in ceo(): will try to fetch character even if ceo_id is None
|
||||||
|
|
||||||
|
|
||||||
|
class TestAlliance(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.my_alliance = Alliance(
|
||||||
|
id=3001,
|
||||||
|
name='Dummy Alliance',
|
||||||
|
ticker='Dummy',
|
||||||
|
corp_ids=[2001, 2002, 2003],
|
||||||
|
executor_corp_id=2001
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_corp(corp_id):
|
||||||
|
corps = {
|
||||||
|
2001: Corporation(
|
||||||
|
id=2001,
|
||||||
|
name='Dummy Corp 1',
|
||||||
|
alliance_id=3001
|
||||||
|
),
|
||||||
|
2002: Corporation(
|
||||||
|
id=2002,
|
||||||
|
name='Dummy Corp 2',
|
||||||
|
alliance_id=3001
|
||||||
|
),
|
||||||
|
2003: Corporation(
|
||||||
|
id=2003,
|
||||||
|
name='Dummy Corp 3',
|
||||||
|
alliance_id=3001
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
if corp_id:
|
||||||
|
return corps[int(corp_id)]
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.EveSwaggerProvider.get_corp')
|
||||||
|
def test_corp(self, mock_provider_get_corp):
|
||||||
|
mock_provider_get_corp.side_effect = TestAlliance._get_corp
|
||||||
|
|
||||||
|
# should fetch corp if not in the object
|
||||||
|
self.assertEqual(
|
||||||
|
self.my_alliance.corp(2001),
|
||||||
|
TestAlliance._get_corp(2001)
|
||||||
|
)
|
||||||
|
# should fetch corp if not in the object
|
||||||
|
self.assertEqual(
|
||||||
|
self.my_alliance.corp(2002),
|
||||||
|
TestAlliance._get_corp(2002)
|
||||||
|
)
|
||||||
|
# should return from the object if its there
|
||||||
|
self.assertEqual(
|
||||||
|
self.my_alliance.corp(2001),
|
||||||
|
TestAlliance._get_corp(2001)
|
||||||
|
)
|
||||||
|
# should return from the object if its there
|
||||||
|
self.assertEqual(
|
||||||
|
self.my_alliance.corp(2002),
|
||||||
|
TestAlliance._get_corp(2002)
|
||||||
|
)
|
||||||
|
# should be called once by used corp only
|
||||||
|
self.assertEqual(mock_provider_get_corp.call_count, 2)
|
||||||
|
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.EveSwaggerProvider.get_corp')
|
||||||
|
def test_corps(self, mock_provider_get_corp):
|
||||||
|
mock_provider_get_corp.side_effect = TestAlliance._get_corp
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
self.my_alliance.corps,
|
||||||
|
[
|
||||||
|
TestAlliance._get_corp(2001),
|
||||||
|
TestAlliance._get_corp(2002),
|
||||||
|
TestAlliance._get_corp(2003),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.EveSwaggerProvider.get_corp')
|
||||||
|
def test_executor_corp(self, mock_provider_get_corp):
|
||||||
|
mock_provider_get_corp.side_effect = TestAlliance._get_corp
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
self.my_alliance.executor_corp,
|
||||||
|
TestAlliance._get_corp(2001),
|
||||||
|
)
|
||||||
|
|
||||||
|
x = Alliance()
|
||||||
|
self.assertEqual(
|
||||||
|
x.executor_corp,
|
||||||
|
Entity(None, None),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCharacter(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.my_character = Character(
|
||||||
|
id=1001,
|
||||||
|
name='Bruce Wayne',
|
||||||
|
corp_id=2001,
|
||||||
|
alliance_id=3001
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.EveSwaggerProvider.get_corp')
|
||||||
|
def test_corp(self, mock_provider_get_corp):
|
||||||
|
my_corp = Corporation(
|
||||||
|
id=2001,
|
||||||
|
name='Dummy Corp 1'
|
||||||
|
)
|
||||||
|
mock_provider_get_corp.return_value = my_corp
|
||||||
|
|
||||||
|
self.assertEqual(self.my_character.corp, my_corp)
|
||||||
|
self.assertEqual(self.my_character.corp, my_corp)
|
||||||
|
|
||||||
|
# should call the provider one time only
|
||||||
|
self.assertEqual(mock_provider_get_corp.call_count, 1)
|
||||||
|
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.EveSwaggerProvider.get_alliance')
|
||||||
|
@patch(MODULE_PATH + '.EveSwaggerProvider.get_corp')
|
||||||
|
def test_alliance_has_one(
|
||||||
|
self,
|
||||||
|
mock_provider_get_corp,
|
||||||
|
mock_provider_get_alliance,
|
||||||
|
):
|
||||||
|
my_corp = Corporation(
|
||||||
|
id=2001,
|
||||||
|
name='Dummy Corp 1',
|
||||||
|
alliance_id=3001
|
||||||
|
)
|
||||||
|
mock_provider_get_corp.return_value = my_corp
|
||||||
|
my_alliance = Alliance(
|
||||||
|
id=3001,
|
||||||
|
name='Dummy Alliance 1',
|
||||||
|
executor_corp_id=2001,
|
||||||
|
corp_ids=[2001, 2002]
|
||||||
|
)
|
||||||
|
mock_provider_get_alliance.return_value = my_alliance
|
||||||
|
|
||||||
|
self.assertEqual(self.my_character.alliance, my_alliance)
|
||||||
|
self.assertEqual(self.my_character.alliance, my_alliance)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
|
||||||
|
def test_alliance_has_none(self):
|
||||||
|
self.my_character.alliance_id = None
|
||||||
|
self.assertEqual(self.my_character.alliance, Entity(None, None))
|
||||||
|
|
||||||
|
|
||||||
|
class TestItemType(TestCase):
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
x = ItemType(id=99, name='Dummy Item')
|
||||||
|
self.assertIsInstance(x, ItemType)
|
||||||
|
|
||||||
|
|
||||||
|
class TestEveProvider(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.my_provider = EveProvider()
|
||||||
|
|
||||||
|
def test_get_alliance(self):
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.my_provider.get_alliance(3001)
|
||||||
|
|
||||||
|
def test_get_corp(self):
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.my_provider.get_corp(2001)
|
||||||
|
|
||||||
|
def test_get_character(self):
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.my_provider.get_character(1001)
|
||||||
|
|
||||||
|
# bug: should be calling NotImplementedError() not NotImplemented
|
||||||
|
"""
|
||||||
|
def test_get_itemtype(self):
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.my_provider.get_itemtype(4001)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class TestEveSwaggerProvider(TestCase):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def esi_get_alliances_alliance_id(alliance_id):
|
||||||
|
alliances = {
|
||||||
|
3001: {
|
||||||
|
'name': 'Dummy Alliance 1',
|
||||||
|
'ticker': 'DA1',
|
||||||
|
'executor_corporation_id': 2001
|
||||||
|
},
|
||||||
|
3002: {
|
||||||
|
'name': 'Dummy Alliance 2',
|
||||||
|
'ticker': 'DA2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mock_result = Mock()
|
||||||
|
if alliance_id in alliances:
|
||||||
|
mock_result.result.return_value = alliances[alliance_id]
|
||||||
|
return mock_result
|
||||||
|
else:
|
||||||
|
raise HTTPNotFound(Mock())
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def esi_get_alliances_alliance_id_corporations(alliance_id):
|
||||||
|
alliances = {
|
||||||
|
3001: [2001, 2002, 2003],
|
||||||
|
3002: [2004, 2005]
|
||||||
|
}
|
||||||
|
mock_result = Mock()
|
||||||
|
if alliance_id in alliances:
|
||||||
|
mock_result.result.return_value = alliances[alliance_id]
|
||||||
|
return mock_result
|
||||||
|
else:
|
||||||
|
raise HTTPNotFound(Mock())
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def esi_get_corporations_corporation_id(corporation_id):
|
||||||
|
corporations = {
|
||||||
|
2001: {
|
||||||
|
'name': 'Dummy Corp 1',
|
||||||
|
'ticker': 'DC1',
|
||||||
|
'ceo_id': 1001,
|
||||||
|
'member_count': 42,
|
||||||
|
'alliance_id': 3001
|
||||||
|
},
|
||||||
|
2002: {
|
||||||
|
'name': 'Dummy Corp 2',
|
||||||
|
'ticker': 'DC2',
|
||||||
|
'ceo_id': 1011,
|
||||||
|
'member_count': 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mock_result = Mock()
|
||||||
|
if corporation_id in corporations:
|
||||||
|
mock_result.result.return_value = corporations[corporation_id]
|
||||||
|
return mock_result
|
||||||
|
else:
|
||||||
|
raise HTTPNotFound(Mock())
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def esi_get_characters_character_id(character_id):
|
||||||
|
characters = {
|
||||||
|
1001: {
|
||||||
|
'name': 'Bruce Wayne',
|
||||||
|
'corporation_id': 2001,
|
||||||
|
'alliance_id': 3001
|
||||||
|
},
|
||||||
|
1002: {
|
||||||
|
'name': 'Peter Parker',
|
||||||
|
'corporation_id': 2101
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mock_result = Mock()
|
||||||
|
if character_id in characters:
|
||||||
|
mock_result.result.return_value = characters[character_id]
|
||||||
|
return mock_result
|
||||||
|
else:
|
||||||
|
raise HTTPNotFound(Mock())
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def esi_post_characters_affiliation(characters):
|
||||||
|
character_data = {
|
||||||
|
1001: {
|
||||||
|
'corporation_id': 2001,
|
||||||
|
'alliance_id': 3001
|
||||||
|
},
|
||||||
|
1002: {
|
||||||
|
'corporation_id': 2101
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mock_result = Mock()
|
||||||
|
if isinstance(characters, list):
|
||||||
|
characters_result = list()
|
||||||
|
for character_id in characters:
|
||||||
|
if character_id in character_data:
|
||||||
|
characters_result.append(character_data[character_id])
|
||||||
|
else:
|
||||||
|
raise HTTPNotFound(Mock())
|
||||||
|
mock_result.result.return_value = characters_result
|
||||||
|
return mock_result
|
||||||
|
else:
|
||||||
|
raise TypeError()
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def esi_get_universe_types_type_id(type_id):
|
||||||
|
types = {
|
||||||
|
4001: {
|
||||||
|
'name': 'Dummy Type 1'
|
||||||
|
},
|
||||||
|
4002: {
|
||||||
|
'name': 'Dummy Type 2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mock_result = Mock()
|
||||||
|
if type_id in types:
|
||||||
|
mock_result.result.return_value = types[type_id]
|
||||||
|
return mock_result
|
||||||
|
else:
|
||||||
|
raise HTTPNotFound(Mock())
|
||||||
|
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.esi_client_factory')
|
||||||
|
def test_str(self, mock_esi_client_factory):
|
||||||
|
my_provider = EveSwaggerProvider()
|
||||||
|
self.assertEqual(str(my_provider), 'esi')
|
||||||
|
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.esi_client_factory')
|
||||||
|
def test_get_alliance(self, mock_esi_client_factory):
|
||||||
|
mock_esi_client_factory.return_value\
|
||||||
|
.Alliance.get_alliances_alliance_id \
|
||||||
|
= TestEveSwaggerProvider.esi_get_alliances_alliance_id
|
||||||
|
mock_esi_client_factory.return_value\
|
||||||
|
.Alliance.get_alliances_alliance_id_corporations \
|
||||||
|
= TestEveSwaggerProvider.esi_get_alliances_alliance_id_corporations
|
||||||
|
|
||||||
|
my_provider = EveSwaggerProvider()
|
||||||
|
|
||||||
|
# fully defined alliance
|
||||||
|
my_alliance = my_provider.get_alliance(3001)
|
||||||
|
self.assertEqual(my_alliance.id, 3001)
|
||||||
|
self.assertEqual(my_alliance.name, 'Dummy Alliance 1')
|
||||||
|
self.assertEqual(my_alliance.ticker, 'DA1')
|
||||||
|
self.assertListEqual(my_alliance.corp_ids, [2001, 2002, 2003])
|
||||||
|
self.assertEqual(my_alliance.executor_corp_id, 2001)
|
||||||
|
|
||||||
|
# alliance missing executor_corporation_id
|
||||||
|
my_alliance = my_provider.get_alliance(3002)
|
||||||
|
self.assertEqual(my_alliance.id, 3002)
|
||||||
|
self.assertEqual(my_alliance.executor_corp_id, None)
|
||||||
|
|
||||||
|
# alliance not found
|
||||||
|
with self.assertRaises(ObjectNotFound):
|
||||||
|
my_provider.get_alliance(3999)
|
||||||
|
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.esi_client_factory')
|
||||||
|
def test_get_corp(self, mock_esi_client_factory):
|
||||||
|
mock_esi_client_factory.return_value\
|
||||||
|
.Corporation.get_corporations_corporation_id \
|
||||||
|
= TestEveSwaggerProvider.esi_get_corporations_corporation_id
|
||||||
|
|
||||||
|
my_provider = EveSwaggerProvider()
|
||||||
|
|
||||||
|
# corporation with alliance
|
||||||
|
my_corp = my_provider.get_corp(2001)
|
||||||
|
self.assertEqual(my_corp.id, 2001)
|
||||||
|
self.assertEqual(my_corp.name, 'Dummy Corp 1')
|
||||||
|
self.assertEqual(my_corp.ticker, 'DC1')
|
||||||
|
self.assertEqual(my_corp.ceo_id, 1001)
|
||||||
|
self.assertEqual(my_corp.members, 42)
|
||||||
|
self.assertEqual(my_corp.alliance_id, 3001)
|
||||||
|
|
||||||
|
# corporation wo/ alliance
|
||||||
|
my_corp = my_provider.get_corp(2002)
|
||||||
|
self.assertEqual(my_corp.id, 2002)
|
||||||
|
self.assertEqual(my_corp.alliance_id, None)
|
||||||
|
|
||||||
|
# corporation not found
|
||||||
|
with self.assertRaises(ObjectNotFound):
|
||||||
|
my_provider.get_corp(2999)
|
||||||
|
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.esi_client_factory')
|
||||||
|
def test_get_character(self, mock_esi_client_factory):
|
||||||
|
mock_esi_client_factory.return_value\
|
||||||
|
.Character.get_characters_character_id \
|
||||||
|
= TestEveSwaggerProvider.esi_get_characters_character_id
|
||||||
|
mock_esi_client_factory.return_value\
|
||||||
|
.Character.post_characters_affiliation \
|
||||||
|
= TestEveSwaggerProvider.esi_post_characters_affiliation
|
||||||
|
|
||||||
|
my_provider = EveSwaggerProvider()
|
||||||
|
|
||||||
|
# character with alliance
|
||||||
|
my_character = my_provider.get_character(1001)
|
||||||
|
self.assertEqual(my_character.id, 1001)
|
||||||
|
self.assertEqual(my_character.name, 'Bruce Wayne')
|
||||||
|
self.assertEqual(my_character.corp_id, 2001)
|
||||||
|
self.assertEqual(my_character.alliance_id, 3001)
|
||||||
|
|
||||||
|
# character wo/ alliance
|
||||||
|
my_character = my_provider.get_character(1002)
|
||||||
|
self.assertEqual(my_character.id, 1002)
|
||||||
|
self.assertEqual(my_character.alliance_id, None)
|
||||||
|
|
||||||
|
# character not found
|
||||||
|
with self.assertRaises(ObjectNotFound):
|
||||||
|
my_provider.get_character(1999)
|
||||||
|
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.esi_client_factory')
|
||||||
|
def test_get_itemtype(self, mock_esi_client_factory):
|
||||||
|
mock_esi_client_factory.return_value\
|
||||||
|
.Universe.get_universe_types_type_id \
|
||||||
|
= TestEveSwaggerProvider.esi_get_universe_types_type_id
|
||||||
|
|
||||||
|
my_provider = EveSwaggerProvider()
|
||||||
|
|
||||||
|
# type exists
|
||||||
|
my_type = my_provider.get_itemtype(4001)
|
||||||
|
self.assertEqual(my_type.id, 4001)
|
||||||
|
self.assertEqual(my_type.name, 'Dummy Type 1')
|
||||||
|
|
||||||
|
# type not found
|
||||||
|
with self.assertRaises(ObjectNotFound):
|
||||||
|
my_provider.get_itemtype(4999)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.settings.DEBUG', False)
|
||||||
|
@patch(MODULE_PATH + '.esi_client_factory')
|
||||||
|
def test_create_client_on_normal_startup(self, mock_esi_client_factory):
|
||||||
|
my_provider = EveSwaggerProvider()
|
||||||
|
self.assertTrue(mock_esi_client_factory.called)
|
||||||
|
self.assertIsNotNone(my_provider._client)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.SWAGGER_SPEC_PATH', SWAGGER_OLD_SPEC_PATH)
|
||||||
|
@patch(MODULE_PATH + '.settings.DEBUG', False)
|
||||||
|
@patch('socket.socket')
|
||||||
|
def test_create_client_on_normal_startup_w_old_swagger_spec(
|
||||||
|
self, mock_socket
|
||||||
|
):
|
||||||
|
mock_socket.side_effect = Exception('Network blocked for testing')
|
||||||
|
my_provider = EveSwaggerProvider()
|
||||||
|
self.assertIsNone(my_provider._client)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.settings.DEBUG', True)
|
||||||
|
@patch(MODULE_PATH + '.esi_client_factory')
|
||||||
|
def test_dont_create_client_on_debug_startup(self, mock_esi_client_factory):
|
||||||
|
my_provider = EveSwaggerProvider()
|
||||||
|
self.assertFalse(mock_esi_client_factory.called)
|
||||||
|
self.assertIsNone(my_provider._client)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.settings.DEBUG', False)
|
||||||
|
@patch(MODULE_PATH + '.esi_client_factory')
|
||||||
|
def test_dont_create_client_if_client_creation_fails_on_normal_startup(
|
||||||
|
self, mock_esi_client_factory
|
||||||
|
):
|
||||||
|
mock_esi_client_factory.side_effect = RefResolutionError(cause='Test')
|
||||||
|
my_provider = EveSwaggerProvider()
|
||||||
|
self.assertTrue(mock_esi_client_factory.called)
|
||||||
|
self.assertIsNone(my_provider._client)
|
||||||
|
|
||||||
|
@patch(MODULE_PATH + '.settings.DEBUG', True)
|
||||||
|
@patch(MODULE_PATH + '.esi_client_factory')
|
||||||
|
def test_client_loads_on_demand(
|
||||||
|
self, mock_esi_client_factory
|
||||||
|
):
|
||||||
|
mock_esi_client_factory.return_value = 'my_client'
|
||||||
|
my_provider = EveSwaggerProvider()
|
||||||
|
self.assertFalse(mock_esi_client_factory.called)
|
||||||
|
self.assertIsNone(my_provider._client)
|
||||||
|
my_client = my_provider.client
|
||||||
|
self.assertTrue(mock_esi_client_factory.called)
|
||||||
|
self.assertIsNotNone(my_provider._client)
|
||||||
|
self.assertEqual(my_client, 'my_client')
|
||||||
|
|
||||||
|
|
||||||
110
allianceauth/eveonline/tests/test_tasks.py
Normal file
110
allianceauth/eveonline/tests/test_tasks.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
from unittest.mock import patch, Mock
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from ..models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||||
|
from ..tasks import update_alliance, update_corp, update_character, \
|
||||||
|
run_model_update
|
||||||
|
|
||||||
|
|
||||||
|
class TestTasks(TestCase):
|
||||||
|
|
||||||
|
@patch('allianceauth.eveonline.tasks.EveCorporationInfo')
|
||||||
|
def test_update_corp(self, mock_EveCorporationInfo):
|
||||||
|
update_corp(42)
|
||||||
|
self.assertEqual(
|
||||||
|
mock_EveCorporationInfo.objects.update_corporation.call_count,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
mock_EveCorporationInfo.objects.update_corporation.call_args[0][0],
|
||||||
|
42
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@patch('allianceauth.eveonline.tasks.EveAllianceInfo')
|
||||||
|
def test_update_alliance(self, mock_EveAllianceInfo):
|
||||||
|
update_alliance(42)
|
||||||
|
self.assertEqual(
|
||||||
|
mock_EveAllianceInfo.objects.update_alliance.call_args[0][0],
|
||||||
|
42
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
mock_EveAllianceInfo.objects\
|
||||||
|
.update_alliance.return_value.populate_alliance.call_count,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@patch('allianceauth.eveonline.tasks.EveCharacter')
|
||||||
|
def test_update_character(self, mock_EveCharacter):
|
||||||
|
update_character(42)
|
||||||
|
self.assertEqual(
|
||||||
|
mock_EveCharacter.objects.update_character.call_count,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
mock_EveCharacter.objects.update_character.call_args[0][0],
|
||||||
|
42
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@patch('allianceauth.eveonline.tasks.update_character')
|
||||||
|
@patch('allianceauth.eveonline.tasks.update_alliance')
|
||||||
|
@patch('allianceauth.eveonline.tasks.update_corp')
|
||||||
|
def test_run_model_update(
|
||||||
|
self,
|
||||||
|
mock_update_corp,
|
||||||
|
mock_update_alliance,
|
||||||
|
mock_update_character,
|
||||||
|
):
|
||||||
|
EveCorporationInfo.objects.all().delete()
|
||||||
|
EveAllianceInfo.objects.all().delete()
|
||||||
|
EveCharacter.objects.all().delete()
|
||||||
|
|
||||||
|
EveCorporationInfo.objects.create(
|
||||||
|
corporation_id='2345',
|
||||||
|
corporation_name='corp.name',
|
||||||
|
corporation_ticker='corp.ticker',
|
||||||
|
member_count=10,
|
||||||
|
alliance=None,
|
||||||
|
)
|
||||||
|
EveAllianceInfo.objects.create(
|
||||||
|
alliance_id='3456',
|
||||||
|
alliance_name='alliance.name',
|
||||||
|
alliance_ticker='alliance.ticker',
|
||||||
|
executor_corp_id='alliance.executor_corp_id',
|
||||||
|
)
|
||||||
|
EveCharacter.objects.create(
|
||||||
|
character_id='1234',
|
||||||
|
character_name='character.name',
|
||||||
|
corporation_id='character.corp.id',
|
||||||
|
corporation_name='character.corp.name',
|
||||||
|
corporation_ticker='character.corp.ticker',
|
||||||
|
alliance_id='character.alliance.id',
|
||||||
|
alliance_name='character.alliance.name',
|
||||||
|
)
|
||||||
|
|
||||||
|
run_model_update()
|
||||||
|
|
||||||
|
self.assertEqual(mock_update_corp.delay.call_count, 1)
|
||||||
|
self.assertEqual(
|
||||||
|
int(mock_update_corp.delay.call_args[0][0]),
|
||||||
|
2345
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(mock_update_alliance.delay.call_count, 1)
|
||||||
|
self.assertEqual(
|
||||||
|
int(mock_update_alliance.delay.call_args[0][0]),
|
||||||
|
3456
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(mock_update_character.delay.call_count, 1)
|
||||||
|
self.assertEqual(
|
||||||
|
int(mock_update_character.delay.call_args[0][0]),
|
||||||
|
1234
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 2.0.6 on 2018-08-03 04:30
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('fleetactivitytracking', '0005_remove_fat_name'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='fat',
|
||||||
|
name='shiptype',
|
||||||
|
field=models.CharField(max_length=100),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -24,7 +24,7 @@ class Fat(models.Model):
|
|||||||
character = models.ForeignKey(EveCharacter, on_delete=models.CASCADE)
|
character = models.ForeignKey(EveCharacter, on_delete=models.CASCADE)
|
||||||
fatlink = models.ForeignKey(Fatlink, on_delete=models.CASCADE)
|
fatlink = models.ForeignKey(Fatlink, on_delete=models.CASCADE)
|
||||||
system = models.CharField(max_length=30)
|
system = models.CharField(max_length=30)
|
||||||
shiptype = models.CharField(max_length=30)
|
shiptype = models.CharField(max_length=100)
|
||||||
station = models.CharField(max_length=125)
|
station = models.CharField(max_length=125)
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -12,7 +12,7 @@
|
|||||||
<div class="panel-heading">{{ character_name }}</div>
|
<div class="panel-heading">{{ character_name }}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="col-lg-2 col-sm-2">
|
<div class="col-lg-2 col-sm-2">
|
||||||
<img class="ra-avatar img-responsive" src="https://image.eveonline.com/Character/{{ character_id }}_128.jpg">
|
<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">{% trans "Character not registered!" %}</div>
|
||||||
|
|||||||
@@ -15,7 +15,13 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h1>
|
</h1>
|
||||||
<h2>{% blocktrans %}{{ user }} has collected {{ n_fats }} link{{ n_fats|pluralize }} this month.{% endblocktrans %}</h2>
|
<h2>
|
||||||
|
{% blocktrans count links=n_fats trimmed %}
|
||||||
|
{{ user }} has collected one link this month.
|
||||||
|
{% plural %}
|
||||||
|
{{ user }} has collected {{ links }} links this month.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</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">{% trans "Ship" %}</th>
|
||||||
@@ -29,7 +35,13 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% if created_fats %}
|
{% if created_fats %}
|
||||||
<h2>{% blocktrans %}{{ user }} has created {{ n_created_fats }} link{{ n_created_fats|pluralize }} this month.{% endblocktrans %}</h2>
|
<h2>
|
||||||
|
{% blocktrans count links=n_created_fats trimmed %}
|
||||||
|
{{ user }} has created one link this month.
|
||||||
|
{% plural %}
|
||||||
|
{{ user }} has created {{ links }} links this month.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</h2>
|
||||||
{% if created_fats %}
|
{% if created_fats %}
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
{% for memberStat in fatStats %}
|
{% for memberStat in fatStats %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="https://image.eveonline.com/Character/{{ memberStat.mainchid }}_32.jpg" class="ra-avatar img-responsive">
|
<img src="{{ memberStat.mainchar.portrait_url_32 }}" class="ra-avatar img-responsive">
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">{{ memberStat.mainchar.character_name }}</td>
|
<td class="text-center">{{ memberStat.mainchar.character_name }}</td>
|
||||||
<td class="text-center">{{ memberStat.n_chars }}</td>
|
<td class="text-center">{{ memberStat.n_chars }}</td>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
{% for corpStat in fatStats %}
|
{% for corpStat in fatStats %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="https://image.eveonline.com/Corporation/{{ corpStat.corp.corporation_id }}_32.png" class="ra-avatar img-responsive">
|
<img src="{{ corpStat.corp.logo_url_32 }}" class="ra-avatar img-responsive">
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center"><a href="{% url 'fatlink:statistics_corp' corpStat.corp.corporation_id %}">[{{ corpStat.corp.corporation_ticker }}]</td>
|
<td class="text-center"><a href="{% url 'fatlink:statistics_corp' corpStat.corp.corporation_id %}">[{{ corpStat.corp.corporation_ticker }}]</td>
|
||||||
<td class="text-center">{{ corpStat.corp.corporation_name }}</td>
|
<td class="text-center">{{ corpStat.corp.corporation_name }}</td>
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ def fatlink_statistics_corp_view(request, corpid, year=None, month=None):
|
|||||||
start_of_next_month = first_day_of_next_month(year, month)
|
start_of_next_month = first_day_of_next_month(year, month)
|
||||||
start_of_previous_month = first_day_of_previous_month(year, month)
|
start_of_previous_month = first_day_of_previous_month(year, month)
|
||||||
fat_stats = {}
|
fat_stats = {}
|
||||||
corp_members = CharacterOwnership.objects.filter(character__corporation_id=corpid).values('user_id').distinct()
|
corp_members = CharacterOwnership.objects.filter(character__corporation_id=corpid).order_by('user_id').values('user_id').distinct()
|
||||||
|
|
||||||
for member in corp_members:
|
for member in corp_members:
|
||||||
try:
|
try:
|
||||||
@@ -287,8 +287,13 @@ def click_fatlink_view(request, token, fat_hash=None):
|
|||||||
err_messages.append(message[0])
|
err_messages.append(message[0])
|
||||||
messages.error(request, ' '.join(err_messages))
|
messages.error(request, ' '.join(err_messages))
|
||||||
else:
|
else:
|
||||||
context = {'character_id': token.character_id,
|
context = {
|
||||||
'character_name': token.character_name}
|
'character_id': token.character_id,
|
||||||
|
'character_name': token.character_name,
|
||||||
|
'character_portrait_url': EveCharacter.generic_portrait_url(
|
||||||
|
token.character_id, 128
|
||||||
|
),
|
||||||
|
}
|
||||||
return render(request, 'fleetactivitytracking/characternotexisting.html', context=context)
|
return render(request, 'fleetactivitytracking/characternotexisting.html', context=context)
|
||||||
else:
|
else:
|
||||||
messages.error(request, _('FAT link has expired.'))
|
messages.error(request, _('FAT link has expired.'))
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
default_app_config = 'allianceauth.fleetup.apps.FleetupConfig'
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class FleetupConfig(AppConfig):
|
|
||||||
name = 'allianceauth.fleetup'
|
|
||||||
label = 'fleetup'
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
from allianceauth.services.hooks import MenuItemHook, UrlHook
|
|
||||||
|
|
||||||
from allianceauth import hooks
|
|
||||||
from . import urls
|
|
||||||
|
|
||||||
|
|
||||||
class FleetUpMenu(MenuItemHook):
|
|
||||||
def __init__(self):
|
|
||||||
MenuItemHook.__init__(self, 'Fleet-Up',
|
|
||||||
'fa fa-arrow-up fa-fw',
|
|
||||||
'fleetup:view',
|
|
||||||
navactive=['fleetup:'])
|
|
||||||
|
|
||||||
def render(self, request):
|
|
||||||
if request.user.has_perm('auth.view_fleetup'):
|
|
||||||
return MenuItemHook.render(self, request)
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
@hooks.register('menu_item_hook')
|
|
||||||
def register_menu():
|
|
||||||
return FleetUpMenu()
|
|
||||||
|
|
||||||
|
|
||||||
@hooks.register('url_hook')
|
|
||||||
def register_url():
|
|
||||||
return UrlHook(urls, 'fleetup', r'^fleetup/')
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
from django.conf import settings
|
|
||||||
from django.core.cache import cache
|
|
||||||
from django.utils import timezone
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import requests
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class FleetUpManager:
|
|
||||||
APP_KEY = settings.FLEETUP_APP_KEY
|
|
||||||
USER_ID = settings.FLEETUP_USER_ID
|
|
||||||
API_ID = settings.FLEETUP_API_ID
|
|
||||||
GROUP_ID = settings.FLEETUP_GROUP_ID
|
|
||||||
BASE_URL = "http://api.fleet-up.com/Api.svc/{}/{}/{}".format(APP_KEY, USER_ID, API_ID)
|
|
||||||
|
|
||||||
TZ = timezone.utc
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _request_cache_key(cls, url):
|
|
||||||
h = hashlib.sha1()
|
|
||||||
h.update(url.encode('utf-8'))
|
|
||||||
return 'FLEETUP_ENDPOINT_' + h.hexdigest()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _cache_until_seconds(cls, cache_until_json):
|
|
||||||
# Format comes in like "/Date(1493896236163)/"
|
|
||||||
try:
|
|
||||||
epoch_ms = int(cache_until_json[6:-2])
|
|
||||||
cache_delta = datetime.fromtimestamp(epoch_ms/1000) - datetime.now()
|
|
||||||
cache_delta_seconds = cache_delta.total_seconds()
|
|
||||||
if cache_delta_seconds < 0:
|
|
||||||
return 0
|
|
||||||
elif cache_delta_seconds > 3600:
|
|
||||||
return 3600
|
|
||||||
else:
|
|
||||||
return cache_delta_seconds
|
|
||||||
except TypeError:
|
|
||||||
logger.debug("Couldn't convert CachedUntil time, defaulting to 600 seconds")
|
|
||||||
return 600
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_endpoint(cls, url):
|
|
||||||
try:
|
|
||||||
cache_key = cls._request_cache_key(url)
|
|
||||||
cached = cache.get(cache_key)
|
|
||||||
if cached:
|
|
||||||
return cached
|
|
||||||
|
|
||||||
r = requests.get(url)
|
|
||||||
r.raise_for_status()
|
|
||||||
|
|
||||||
json = r.json()
|
|
||||||
|
|
||||||
if json['Success']:
|
|
||||||
cache.set(cache_key, json, cls._cache_until_seconds(json['CachedUntilUTC']))
|
|
||||||
return json
|
|
||||||
except requests.exceptions.ConnectionError:
|
|
||||||
logger.warning("Can't connect to Fleet-Up API, is it offline?!")
|
|
||||||
except requests.HTTPError:
|
|
||||||
logger.exception("Error accessing Fleetup API")
|
|
||||||
return None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fleetup_members(cls):
|
|
||||||
url = "{}/GroupCharacters/{}".format(cls.BASE_URL, cls.GROUP_ID)
|
|
||||||
try:
|
|
||||||
fmembers = cls.get_endpoint(url)
|
|
||||||
if not fmembers:
|
|
||||||
return None
|
|
||||||
return {row["UserId"]: {"user_id": row["UserId"],
|
|
||||||
"char_name": row["EveCharName"],
|
|
||||||
"char_id": row["EveCharId"],
|
|
||||||
"corporation": row["Corporation"]} for row in fmembers["Data"]}
|
|
||||||
except (ValueError, UnicodeDecodeError, TypeError):
|
|
||||||
logger.debug("No fleetup members retrieved.")
|
|
||||||
return {}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fleetup_operations(cls):
|
|
||||||
url = "{}/Operations/{}".format(cls.BASE_URL, cls.GROUP_ID)
|
|
||||||
foperations = cls.get_endpoint(url)
|
|
||||||
if foperations is None:
|
|
||||||
return None
|
|
||||||
return {row["StartString"]: {"subject": row["Subject"],
|
|
||||||
"start": timezone.make_aware(
|
|
||||||
datetime.strptime(row["StartString"], "%Y-%m-%d %H:%M:%S"), cls.TZ),
|
|
||||||
"end": timezone.make_aware(
|
|
||||||
datetime.strptime(row["EndString"], "%Y-%m-%d %H:%M:%S"), cls.TZ),
|
|
||||||
"operation_id": row["OperationId"],
|
|
||||||
"location": row["Location"],
|
|
||||||
"location_info": row["LocationInfo"],
|
|
||||||
"details": row["Details"],
|
|
||||||
"url": row["Url"],
|
|
||||||
"doctrine": row["Doctrines"],
|
|
||||||
"organizer": row["Organizer"]} for row in foperations["Data"]}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fleetup_timers(cls):
|
|
||||||
url = "{}/Timers/{}".format(cls.BASE_URL, cls.GROUP_ID)
|
|
||||||
ftimers = cls.get_endpoint(url)
|
|
||||||
if not ftimers:
|
|
||||||
return None
|
|
||||||
return {row["ExpiresString"]: {"solarsystem": row["SolarSystem"],
|
|
||||||
"planet": row["Planet"],
|
|
||||||
"moon": row["Moon"],
|
|
||||||
"owner": row["Owner"],
|
|
||||||
"type": row["Type"],
|
|
||||||
"timer_type": row["TimerType"],
|
|
||||||
"expires": timezone.make_aware(
|
|
||||||
datetime.strptime(row["ExpiresString"], "%Y-%m-%d %H:%M:%S"), cls.TZ),
|
|
||||||
"notes": row["Notes"]} for row in ftimers["Data"]}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fleetup_doctrines(cls):
|
|
||||||
url = "{}/Doctrines/{}".format(cls.BASE_URL, cls.GROUP_ID)
|
|
||||||
fdoctrines = cls.get_endpoint(url)
|
|
||||||
if not fdoctrines:
|
|
||||||
return None
|
|
||||||
return {"fleetup_doctrines": fdoctrines["Data"]}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fleetup_doctrine(cls, doctrinenumber):
|
|
||||||
url = "{}/DoctrineFittings/{}".format(cls.BASE_URL, doctrinenumber)
|
|
||||||
fdoctrine = cls.get_endpoint(url)
|
|
||||||
if not fdoctrine:
|
|
||||||
return None
|
|
||||||
return {"fitting_doctrine": fdoctrine}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fleetup_fittings(cls):
|
|
||||||
url = "{}/Fittings/{}".format(cls.BASE_URL, cls.GROUP_ID)
|
|
||||||
ffittings = cls.get_endpoint(url)
|
|
||||||
if not ffittings:
|
|
||||||
return None
|
|
||||||
return {row["FittingId"]: {"fitting_id": row["FittingId"],
|
|
||||||
"name": row["Name"],
|
|
||||||
"icon_id": row["EveTypeId"],
|
|
||||||
"hull": row["HullType"],
|
|
||||||
"shiptype": row["ShipType"],
|
|
||||||
"estimated": row["EstPrice"],
|
|
||||||
"faction": row["Faction"],
|
|
||||||
"categories": row["Categories"],
|
|
||||||
"last_update":
|
|
||||||
timezone.make_aware(
|
|
||||||
datetime.strptime(row["LastUpdatedString"], "%Y-%m-%d %H:%M:%S"), cls.TZ)}
|
|
||||||
for row in ffittings["Data"]}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fleetup_fitting(cls, fittingnumber):
|
|
||||||
url = "{}/Fitting/{}".format(cls.BASE_URL, fittingnumber)
|
|
||||||
try:
|
|
||||||
ffitting = cls.get_endpoint(url)
|
|
||||||
if not ffitting:
|
|
||||||
return None
|
|
||||||
return {"fitting_data": ffitting["Data"]}
|
|
||||||
except KeyError:
|
|
||||||
logger.warning("Failed to retrieve fleetup fitting number %s" % fittingnumber)
|
|
||||||
return {"fitting_data": {}}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fleetup_doctrineid(cls, fittingnumber):
|
|
||||||
url = "{}/Fitting/{}".format(cls.BASE_URL, fittingnumber)
|
|
||||||
try:
|
|
||||||
fdoctrineid = cls.get_endpoint(url)
|
|
||||||
if not fdoctrineid:
|
|
||||||
return None
|
|
||||||
return fdoctrineid['Data']['Doctrines'][0]['DoctrineId']
|
|
||||||
except (KeyError, IndexError):
|
|
||||||
logger.debug("Fleetup fitting number %s not in a doctrine." % fittingnumber)
|
|
||||||
return {}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fleetup_fitting_eft(cls, fittingnumber):
|
|
||||||
url = "{}/Fitting/{}/eft".format(cls.BASE_URL, fittingnumber)
|
|
||||||
try:
|
|
||||||
ffittingeft = cls.get_endpoint(url)
|
|
||||||
if not ffittingeft:
|
|
||||||
return None
|
|
||||||
return {"fitting_eft": ffittingeft["Data"]["FittingData"]}
|
|
||||||
except KeyError:
|
|
||||||
logger.warning("Fleetup fitting eft not found for fitting number %s" % fittingnumber)
|
|
||||||
return {"fitting_eft": {}}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
|
||||||
{% load bootstrap %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block page_title %}Characters - FleetUp{% endblock page_title %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="col-lg-12">
|
|
||||||
{% if perms.auth.corp_stats %}
|
|
||||||
{% include "fleetup/menu.html" %}
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title">{% trans "Characters registered on Fleet-Up.com" %}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<tr>
|
|
||||||
<th class="col-md-1"></th>
|
|
||||||
<th class="col-md-1">{% trans "Character" %}</th>
|
|
||||||
<th class="col-md-1">{% trans "Corporation" %}</th>
|
|
||||||
<th class="col-md-1">Fleet-Up(id)</th>
|
|
||||||
</tr>
|
|
||||||
{% for char_name, user_id in member_list %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<img src="http://image.eveonline.com/Character/{{ user_id.char_id }}_32.jpg" class="img-circle">
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<p>{{ user_id.char_name }}</p>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<p>{{ user_id.corporation }}</p>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<p>{{ user_id.user_id }}</p>
|
|
||||||
</td>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
|
||||||
{% load bootstrap %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block page_title %}Doctrine - FleetUp{% endblock page_title %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="col-lg-12">
|
|
||||||
{% include "fleetup/menu.html" %}
|
|
||||||
<div class="panel">
|
|
||||||
{% for a, j in doctrine.items %}
|
|
||||||
{% regroup j.Data|dictsort:"Role" by Role as role_list %}
|
|
||||||
|
|
||||||
{% for Role in role_list %}
|
|
||||||
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title"><b>{{ Role.grouper }}</b></h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<tr>
|
|
||||||
<th class="col-md-1"></th>
|
|
||||||
<th class="col-md-1">{% trans "Name" %}</th>
|
|
||||||
<th class="col-md-1">{% trans "Role" %}</th>
|
|
||||||
<th class="col-md-1">{% trans "Hull type" %}</th>
|
|
||||||
<th class="col-md-1">{% trans "Ship type" %}</th>
|
|
||||||
<th class="col-md-1">{% trans "Estimated ISK" %}</th>
|
|
||||||
<th class="col-md-2">{% trans "Categories" %}</th>
|
|
||||||
</tr>
|
|
||||||
{% for item in Role.list %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="{% url 'fleetup:fitting' item.FittingId %}"><img src="https://image.eveonline.com/InventoryType/{{ item.EveTypeId }}_32.png"></a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ item.Name }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ item.Role }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ item.HullType }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ item.ShipType }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% load humanize %}{{ item.EstPrice|intword }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% for categories in item.Categories %}
|
|
||||||
{{ categories }},
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock content %}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
|
||||||
{% load bootstrap %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block page_title %}Doctrines - FleetUp{% endblock page_title %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="col-lg-12">
|
|
||||||
{% include "fleetup/menu.html" %}
|
|
||||||
<div class="panel">
|
|
||||||
{% if doctrines_list %}
|
|
||||||
{% for a, j in doctrines_list.items %}
|
|
||||||
{% regroup j|dictsort:"FolderName" by FolderName as folder_list %}
|
|
||||||
{% for FolderName in folder_list %}
|
|
||||||
<div class="col-lg-8">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title"><b>{{ FolderName.grouper }}</b></h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<tr>
|
|
||||||
<th class="col-lg-1"></th>
|
|
||||||
<th class="col-lg-4">{% trans "Name" %}</th>
|
|
||||||
<th class="col-lg-3">{% trans "Doctrine" %}</th>
|
|
||||||
<th class="col-lg-4">{% trans "Last updated" %}</th>
|
|
||||||
<!--<th class="col-lg-1">Owner</th>
|
|
||||||
<th class="col-lg-2">Note</th>-->
|
|
||||||
</tr>
|
|
||||||
{% for item in FolderName.list %}
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="{% url 'fleetup:doctrine' item.DoctrineId %}"><img src="https://image.eveonline.com/InventoryType/{{ item.IconId }}_32.png"></a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ item.Name }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a href="{% url 'fleetup:doctrine' item.DoctrineId %}" class="btn btn-info btn-sm">{{ item.FolderName }}</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ item.LastUpdatedString }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
<h3>{% trans "There seems to be no Doctrines in here at the moment!" %}</h3>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock content %}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
|
||||||
{% load bootstrap %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block page_title %}{% trans "Doctrine - FleetUp" %}{% endblock page_title %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="col-lg-12">
|
|
||||||
{% include "fleetup/menu.html" %}
|
|
||||||
<div class="tab-content">
|
|
||||||
<div id="fit" class="tab-pane fade in active">
|
|
||||||
<div class="col-lg-3">
|
|
||||||
{% for x, y in fitting_data.items %}
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title">{% trans "This fit is part of a doctrine" %}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
{% for doctrin in y.Doctrines %}
|
|
||||||
<h4>{{ doctrin.Name }}</h4>
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<p>{% trans "Role in doctrine:" %} {{ doctrin.Role }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-4">
|
|
||||||
<p>{% trans "Priority:" %}</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-8">
|
|
||||||
<div class="progress">
|
|
||||||
<div class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="{{ doctrin.Priority }}" aria-valuemin="0" aria-valuemax="5" style="width: {% widthratio doctrin.Priority 5 100 %}%;">
|
|
||||||
{{ doctrin.Priority }}/5
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="pull-right">
|
|
||||||
<a class="btn btn-primary" href="{% url 'fleetup:doctrine' doctrin.DoctrineId %}">{% trans "See doctrine" %}</a>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title">{% trans "Fit categories" %}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
{% for category in y.Categories %}
|
|
||||||
<span class="label label-success">{{ category }}</span>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title">{% trans "All fits in this Doctrine" %}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="list-group">
|
|
||||||
{% for arbit, orbit in doctrines_list.items %}
|
|
||||||
|
|
||||||
{% for fitting in orbit.Data %}
|
|
||||||
<a href="{% url 'fleetup:fitting' fitting.FittingId %}" class="list-group-item">
|
|
||||||
|
|
||||||
<h4 class="list-group-item-heading">{{ fitting.Name }}<span class="pull-right"><img src="https://image.eveonline.com/InventoryType/{{ fitting.EveTypeId }}_32.png" class="img-circle"></span></h4>
|
|
||||||
<p class="list-group-item-heading">{{ fitting.Role }} - {{ fitting.ShipType }}</p>
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-4">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
{% for a, j in fitting_data.items %}
|
|
||||||
<h3 class="panel-title">{{ j.Name }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<img src="https://image.eveonline.com/InventoryType/{{ j.EveTypeId }}_64.png" class="img-responsive">
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-9">
|
|
||||||
<p>{% trans "Hull:" %} <b>{{ j.HullType }}</b></p>
|
|
||||||
<p>{% trans "Ship:" %} <b>{{ j.ShipType }}</b></p>
|
|
||||||
{% load humanize %}
|
|
||||||
<p>{% trans "Estimated price:" %} <b>{{ j.EstPrice|intword }} ISK</b></p>
|
|
||||||
</div>
|
|
||||||
{% regroup j.FittingData by Slot as fitting_list %}
|
|
||||||
<table class="table table-condensed table-hover">
|
|
||||||
<tr>
|
|
||||||
<th class="col-lg-1"></th>
|
|
||||||
<th class="col-lg-11"></th>
|
|
||||||
</tr>
|
|
||||||
{% for Slot in fitting_list %}
|
|
||||||
<tr class="info">
|
|
||||||
<td></td><td><b>{{ Slot.grouper }}</b></td>
|
|
||||||
</tr>
|
|
||||||
{% for item in Slot.list %}
|
|
||||||
<tr>
|
|
||||||
<td><img src="https://image.eveonline.com/InventoryType/{{ item.TypeId }}_32.png" class="img-responsive"></td>
|
|
||||||
<td> {{ item.Quantity }}x {{ item.TypeName }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title">{% trans "EFT/Export" %}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
{% for data in fitting_eft.items %}
|
|
||||||
{% autoescape off %}
|
|
||||||
<textarea class="form-control" rows="25" spellcheck="false" onclick="this.focus();this.select()" readonly>{{ fitting_eft.fitting_eft }}</textarea>
|
|
||||||
{% endautoescape %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock content %}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
|
||||||
{% load bootstrap %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block page_title %}Fittings - FleetUp{% endblock page_title %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="col-lg-12">
|
|
||||||
{% include "fleetup/menu.html" %}
|
|
||||||
<div class="panel">
|
|
||||||
{% if fitting_list %}
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<tr>
|
|
||||||
<th class="col-md-1"></th>
|
|
||||||
<th class="col-md-1">{% trans "Name" %}</th>
|
|
||||||
<th class="col-md-1">{% trans "Hull" %}</th>
|
|
||||||
<th class="col-md-1">{% trans "Ship type" %}</th>
|
|
||||||
<th class="col-md-1">{% trans "Estimated ISK" %}</th>
|
|
||||||
<th class="col-md-2">{% trans "Categories" %}</th>
|
|
||||||
</tr>
|
|
||||||
{% for id, fittings in fitting_list %}
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="{% url 'fleetup:fitting' fittings.fitting_id %}"><img src="https://image.eveonline.com/InventoryType/{{ fittings.icon_id }}_32.png"></a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ fittings.name }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ fittings.hull }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ fittings.shiptype }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% load humanize %}{{ fittings.estimated|intword }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% for categories in fittings.categories %}
|
|
||||||
{{ categories }},
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
{% else %}
|
|
||||||
<h3>{% trans "There seems to be no Fittings in here at the moment!" %}</h3>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock content %}
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
|
||||||
{% load bootstrap %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block page_title %}FleetUp{% endblock page_title %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="col-lg-12">
|
|
||||||
{% include "fleetup/menu.html" %}
|
|
||||||
<div class="panel">
|
|
||||||
<ul class="nav nav-tabs">
|
|
||||||
<li class="active"><a data-toggle="tab" href="#operations">{% trans "Operations" %}</a></li>
|
|
||||||
<li><a data-toggle="tab" href="#timers">{% trans "Timers" %}</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="tab-content">
|
|
||||||
<div id="operations" class="tab-pane fade in active">
|
|
||||||
<div class="col-lg-7">
|
|
||||||
{% if operations_list %}
|
|
||||||
{% for subject, start in operations_list %}
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title"><b>{{ start.subject }}</b></h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<table class="table table-condensed">
|
|
||||||
<tr>
|
|
||||||
<th class="col-md-6">{% trans "Start" %}</th>
|
|
||||||
<th class="col-md-6">{% trans "End" %}</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="col-md-6">{{ start.start|date:"l d M H:i" }} <span class="label label-success">{% trans "Eve Time" %}</span></td>
|
|
||||||
|
|
||||||
<td class="col-md-6">{{ start.end|date:"l d M H:i" }} <span class="label label-success">{% trans "Eve Time" %}</span></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="col-md-6">
|
|
||||||
<span id="localtime{{ start.operation_id }}"></span> <span class='label label-success'>Local time</span><br>
|
|
||||||
<div id="countdown{{ start.operation_id }}"></div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="col-md-6"></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<p>{{ start.details }}</p>
|
|
||||||
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<table class="table table-condensed table-striped">
|
|
||||||
<tr>
|
|
||||||
<th class="col-md-4">{% trans "Location" %}</th>
|
|
||||||
<th class="col-md-4">{% trans "Doctrine" %}</th>
|
|
||||||
<th class="col-md-2">{% trans "Organizer" %}</th>
|
|
||||||
<th class="col-md-2">{% trans "URL" %}</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ start.location }} - {{ start.location_info }} <a href="http://evemaps.dotlan.net/system/{{ start.location }}" target="_blank" class="label label-success">Dotlan</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if start.doctrine %}
|
|
||||||
{% for doctrine in start.doctrine %}
|
|
||||||
|
|
||||||
<a href="{% url 'fleetup:doctrine' doctrine.Id %}" class="label label-success">{{ doctrine.Name }}</a>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
<span class="label label-danger">{% trans "TBA" %}</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ start.organizer }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% ifequal start.url "" %}
|
|
||||||
<div class="label label-danger">{% trans "No link" %}</div>
|
|
||||||
{% else %}
|
|
||||||
<a href="{{ start.url }}" target="_blank" class="label label-success">{% trans "External link" %}</a>
|
|
||||||
{% endifequal %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
<h3>{% trans "There seems to be no Operations in the near future." %}</h3>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h2 class="panel-title">{% trans "Current Eve Time:" %}</h2>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div id="current-time"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% if timers_list %}
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h2 class="panel-title">{% trans "Timers" %}</h2>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
{% for notes, type in timers_list %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ type.solarsystem }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ type.expires|date:"l d M H:i" }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="timers" class="tab-pane fade in">
|
|
||||||
<div class="col-lg-12">
|
|
||||||
{% if timers_list %}
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h2 class="panel-title">{% trans "Timers" %}</h2>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<table class="table table-condensed table-hover table-striped">
|
|
||||||
<tr>
|
|
||||||
<th class="col-lg-1">{% trans "Type" %}</th>
|
|
||||||
<th class="col-lg-1">{% trans "Structure" %}</th>
|
|
||||||
<th class="col-lg-2">{% trans "Location" %}</th>
|
|
||||||
<th class="col-lg-2">{% trans "Expires(EVE-time)" %}</th>
|
|
||||||
<th class="col-lg-1">{% trans "Owner" %}</th>
|
|
||||||
<th class="col-lg-2">{% trans "Note" %}</th>
|
|
||||||
</tr>
|
|
||||||
{% for notes, type in timers_list %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{% ifequal type.type "Final" %}
|
|
||||||
<span class="label label-danger">
|
|
||||||
{{ type.type }}</span>{% else %}{{ type.type }}{% endifequal %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ type.timer_type }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ type.solarsystem }} - Planet:{{ type.planet }} Moon:{{ type.moon }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ type.expires|date:"l d M H:i" }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ type.owner }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ type.notes }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<h3>{% trans "There seems to be no Timers in the near future." %}</h3>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% include 'bundles/moment-js.html' with locale=True %}
|
|
||||||
<script src="{% static 'js/timers.js' %}"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
// Data
|
|
||||||
var timers = [
|
|
||||||
{% for start, op in operations_list %}
|
|
||||||
{
|
|
||||||
'id': {{ op.operation_id }},
|
|
||||||
'start': moment("{{ op.start | date:"c" }}"),
|
|
||||||
'end': moment("{{ op.end | date:"c" }}"),
|
|
||||||
'expired': false
|
|
||||||
},
|
|
||||||
{% endfor %}
|
|
||||||
]
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
timedUpdate();
|
|
||||||
setAllLocalTimes();
|
|
||||||
|
|
||||||
// Start timed updates
|
|
||||||
setInterval(timedUpdate, 1000);
|
|
||||||
|
|
||||||
function timedUpdate() {
|
|
||||||
updateClock();
|
|
||||||
updateAllTimers();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateAllTimers () {
|
|
||||||
var l = timers.length;
|
|
||||||
for (var i=0; i < l; ++i) {
|
|
||||||
if (timers[i].expired) continue;
|
|
||||||
updateTimer(timers[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a timer
|
|
||||||
* @param timer Timer information
|
|
||||||
* @param timer.start Date of the timer
|
|
||||||
* @param timer.id Id number of the timer
|
|
||||||
* @param timer.expired
|
|
||||||
*/
|
|
||||||
function updateTimer(timer) {
|
|
||||||
if (timer.start.isAfter(Date.now())) {
|
|
||||||
var duration = moment.duration(timer.start - moment(), 'milliseconds');
|
|
||||||
document.getElementById("countdown" + timer.id).innerHTML = getDurationString(duration);
|
|
||||||
} else {
|
|
||||||
timer.expired = true;
|
|
||||||
document.getElementById("countdown" + timer.id).innerHTML = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set all local time fields
|
|
||||||
*/
|
|
||||||
function setAllLocalTimes() {
|
|
||||||
var l = timers.length;
|
|
||||||
for (var i=0; i < l; ++i) {
|
|
||||||
setLocalTime(timers[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the local time info for the timer
|
|
||||||
* @param timer Timer information
|
|
||||||
* @param timer.start Date of the timer
|
|
||||||
* @param timer.id Id number of the timer
|
|
||||||
*/
|
|
||||||
function setLocalTime(timer) {
|
|
||||||
document.getElementById("localtime" + timer.id).innerHTML = timer.start.format("ddd @ LT");
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateClock() {
|
|
||||||
document.getElementById("current-time").innerHTML = "<b>" + moment.utc().format('ddd, ll HH:mm:ss z') + "</b>";
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock content %}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
{% load i18n %}
|
|
||||||
{% load navactive %}
|
|
||||||
|
|
||||||
<nav class="navbar navbar-default">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="navbar-header">
|
|
||||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
|
|
||||||
<span class="sr-only">{% trans "Toggle navigation" %}</span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
</button>
|
|
||||||
<a class="navbar-brand" href="#">Fleet-Up</a>
|
|
||||||
</div>
|
|
||||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
|
||||||
<ul class="nav navbar-nav">
|
|
||||||
<li class="{% navactive request 'fleetup:view' %}"><a href="{% url 'fleetup:view' %}">{% trans "Ops and Timers" %}</a></li>
|
|
||||||
<li class="{% navactive request 'fleetup:doctrines fleetup:doctrine' %}"><a href="{% url 'fleetup:doctrines' %}">{% trans "Doctrines" %}</a></li>
|
|
||||||
<li class="{% navactive request 'fleetup:fittings fleetup:fitting' %}"><a href="{% url 'fleetup:fittings' %}">{% trans "Fittings" %}</a></li>
|
|
||||||
{% if perms.auth.corp_stats %}
|
|
||||||
<li class="{% navactive request 'fleetup:characters' %}"><a href="{% url 'fleetup:characters' %}">{% trans "Characters" %}</a></li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
@@ -1,503 +0,0 @@
|
|||||||
from unittest import mock
|
|
||||||
|
|
||||||
import requests_mock
|
|
||||||
import json
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from django.test import TestCase
|
|
||||||
from django.utils.timezone import make_aware, utc
|
|
||||||
|
|
||||||
from allianceauth.fleetup.managers import FleetUpManager
|
|
||||||
|
|
||||||
|
|
||||||
class FleetupManagerTestCase(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test__request_cache_key(self):
|
|
||||||
|
|
||||||
cache_key = FleetUpManager._request_cache_key('testurl')
|
|
||||||
|
|
||||||
self.assertEqual('FLEETUP_ENDPOINT_a39562b6ef5b858220be13d2adb61d3f10cf8d61',
|
|
||||||
cache_key)
|
|
||||||
|
|
||||||
@mock.patch('allianceauth.fleetup.managers.cache')
|
|
||||||
@requests_mock.Mocker()
|
|
||||||
def test_get_endpoint(self, cache, m):
|
|
||||||
url = "http://example.com/test/endpoint/"
|
|
||||||
json_data = {'data': "123456", 'CachedUntilUTC': '/Date(1493896236163)/', 'Success': True}
|
|
||||||
m.register_uri('GET', url,
|
|
||||||
text=json.dumps(json_data))
|
|
||||||
|
|
||||||
cache.get.return_value = None # No cached value
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_endpoint(url)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertTrue(cache.get.called)
|
|
||||||
self.assertTrue(cache.set.called)
|
|
||||||
args, kwargs = cache.set.call_args
|
|
||||||
self.assertDictEqual(json_data, args[1])
|
|
||||||
|
|
||||||
self.assertDictEqual(json_data, result)
|
|
||||||
|
|
||||||
@mock.patch('allianceauth.fleetup.managers.cache')
|
|
||||||
@requests_mock.Mocker()
|
|
||||||
def test_get_endpoint_error(self, cache, m):
|
|
||||||
url = "http://example.com/test/endpoint/"
|
|
||||||
json_data = {'data': [], 'Success': False}
|
|
||||||
m.register_uri('GET', url,
|
|
||||||
text=json.dumps(json_data),
|
|
||||||
status_code=400)
|
|
||||||
|
|
||||||
cache.get.return_value = None # No cached value
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_endpoint(url)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertTrue(cache.get.called)
|
|
||||||
self.assertFalse(cache.set.called)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
@mock.patch('allianceauth.fleetup.managers.FleetUpManager.get_endpoint')
|
|
||||||
def test_get_fleetup_members(self, get_endpoint):
|
|
||||||
|
|
||||||
get_endpoint.return_value = {"Data": [
|
|
||||||
{
|
|
||||||
'UserId': 1234,
|
|
||||||
'EveCharName': 'test_name',
|
|
||||||
'EveCharId': 5678,
|
|
||||||
'Corporation': 'test_corporation',
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_members()
|
|
||||||
|
|
||||||
# Asset
|
|
||||||
self.assertTrue(get_endpoint.called)
|
|
||||||
args, kwargs = get_endpoint.call_args
|
|
||||||
self.assertEqual(args[0],
|
|
||||||
FleetUpManager.BASE_URL + '/GroupCharacters/' +
|
|
||||||
FleetUpManager.GROUP_ID)
|
|
||||||
expected_result = {
|
|
||||||
1234: {
|
|
||||||
'user_id': 1234,
|
|
||||||
'char_name': 'test_name',
|
|
||||||
'char_id': 5678,
|
|
||||||
'corporation': 'test_corporation',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.assertDictEqual(expected_result, result)
|
|
||||||
|
|
||||||
# Test None response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = None
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_members()
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
# Test Empty response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = {'Data': []}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_members()
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertDictEqual({}, result)
|
|
||||||
|
|
||||||
@mock.patch('allianceauth.fleetup.managers.FleetUpManager.get_endpoint')
|
|
||||||
def test_get_fleetup_operations(self, get_endpoint):
|
|
||||||
|
|
||||||
get_endpoint.return_value = {"Data": [
|
|
||||||
{
|
|
||||||
'Subject': 'test_operation',
|
|
||||||
'StartString': '2017-05-06 11:11:11',
|
|
||||||
'EndString': '2017-05-06 12:12:12',
|
|
||||||
'OperationId': 1234,
|
|
||||||
'Location': 'Jita',
|
|
||||||
'LocationInfo': '4-4',
|
|
||||||
'Details': 'This is a test operation',
|
|
||||||
'Url': 'http://example.com/1234',
|
|
||||||
'Doctrines': 'Foxcats',
|
|
||||||
'Organizer': 'Example FC'
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_operations()
|
|
||||||
self.maxDiff = None
|
|
||||||
# Asset
|
|
||||||
self.assertTrue(get_endpoint.called)
|
|
||||||
args, kwargs = get_endpoint.call_args
|
|
||||||
self.assertEqual(args[0],
|
|
||||||
FleetUpManager.BASE_URL + '/Operations/' +
|
|
||||||
FleetUpManager.GROUP_ID)
|
|
||||||
expected_result = {
|
|
||||||
'2017-05-06 11:11:11': {
|
|
||||||
'subject': 'test_operation',
|
|
||||||
'start': make_aware(datetime.datetime(2017, 5, 6, 11, 11, 11), utc),
|
|
||||||
'end': make_aware(datetime.datetime(2017, 5, 6, 12, 12, 12), utc),
|
|
||||||
'operation_id': 1234,
|
|
||||||
'location': 'Jita',
|
|
||||||
'location_info': '4-4',
|
|
||||||
'details': 'This is a test operation',
|
|
||||||
'url': 'http://example.com/1234',
|
|
||||||
'doctrine': 'Foxcats',
|
|
||||||
'organizer': 'Example FC'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.assertDictEqual(expected_result, result)
|
|
||||||
|
|
||||||
# Test None response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = None
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_operations()
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
# Test Empty response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = {'Data': []}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_operations()
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertDictEqual({}, result)
|
|
||||||
|
|
||||||
@mock.patch('allianceauth.fleetup.managers.FleetUpManager.get_endpoint')
|
|
||||||
def test_get_fleetup_timers(self, get_endpoint):
|
|
||||||
|
|
||||||
get_endpoint.return_value = {"Data": [
|
|
||||||
{
|
|
||||||
'ExpiresString': '2017-05-06 11:11:11',
|
|
||||||
'SolarSystem': 'Jita',
|
|
||||||
'Planet': '4',
|
|
||||||
'Moon': '4',
|
|
||||||
'Owner': 'Caldari Navy',
|
|
||||||
'Type': 'Caldari Station',
|
|
||||||
'TimerType': 'Armor',
|
|
||||||
'Notes': 'Burn Jita?'
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_timers()
|
|
||||||
|
|
||||||
# Asset
|
|
||||||
self.assertTrue(get_endpoint.called)
|
|
||||||
args, kwargs = get_endpoint.call_args
|
|
||||||
self.assertEqual(args[0],
|
|
||||||
FleetUpManager.BASE_URL + '/Timers/' +
|
|
||||||
FleetUpManager.GROUP_ID)
|
|
||||||
expected_result = {
|
|
||||||
'2017-05-06 11:11:11': {
|
|
||||||
'expires': make_aware(datetime.datetime(2017, 5, 6, 11, 11, 11), utc),
|
|
||||||
'solarsystem': 'Jita',
|
|
||||||
'planet': '4',
|
|
||||||
'moon': '4',
|
|
||||||
'owner': 'Caldari Navy',
|
|
||||||
'type': 'Caldari Station',
|
|
||||||
'timer_type': 'Armor',
|
|
||||||
'notes': 'Burn Jita?'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.assertDictEqual(expected_result, result)
|
|
||||||
|
|
||||||
# Test None response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = None
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_timers()
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
# Test Empty response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = {'Data': []}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_timers()
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertDictEqual({}, result)
|
|
||||||
|
|
||||||
@mock.patch('allianceauth.fleetup.managers.FleetUpManager.get_endpoint')
|
|
||||||
def test_get_fleetup_doctrines(self, get_endpoint):
|
|
||||||
|
|
||||||
get_endpoint.return_value = {"Data": [
|
|
||||||
{
|
|
||||||
'TestData': True
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_doctrines()
|
|
||||||
|
|
||||||
# Asset
|
|
||||||
self.assertTrue(get_endpoint.called)
|
|
||||||
args, kwargs = get_endpoint.call_args
|
|
||||||
self.assertEqual(args[0],
|
|
||||||
FleetUpManager.BASE_URL + '/Doctrines/' +
|
|
||||||
FleetUpManager.GROUP_ID)
|
|
||||||
expected_result = {
|
|
||||||
'fleetup_doctrines': [{
|
|
||||||
'TestData': True
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
self.assertDictEqual(expected_result, result)
|
|
||||||
|
|
||||||
# Test None response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = None
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_doctrines()
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
# Test Empty response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = {'Data': []}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_doctrines()
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertDictEqual({"fleetup_doctrines": []}, result)
|
|
||||||
|
|
||||||
@mock.patch('allianceauth.fleetup.managers.FleetUpManager.get_endpoint')
|
|
||||||
def test_get_fleetup_doctrine(self, get_endpoint):
|
|
||||||
|
|
||||||
get_endpoint.return_value = {"Data": [
|
|
||||||
{
|
|
||||||
'TestData': True
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_doctrine(1234)
|
|
||||||
|
|
||||||
# Asset
|
|
||||||
self.assertTrue(get_endpoint.called)
|
|
||||||
args, kwargs = get_endpoint.call_args
|
|
||||||
self.assertEqual(args[0],
|
|
||||||
FleetUpManager.BASE_URL + '/DoctrineFittings/1234')
|
|
||||||
expected_result = {
|
|
||||||
'fitting_doctrine': {'Data': [{
|
|
||||||
'TestData': True
|
|
||||||
}]}
|
|
||||||
}
|
|
||||||
self.assertDictEqual(expected_result, result)
|
|
||||||
|
|
||||||
# Test None response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = None
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_doctrine(1234)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
# Test Empty response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = {'Data': []}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_doctrine(1234)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertDictEqual({"fitting_doctrine": {'Data': []}}, result)
|
|
||||||
|
|
||||||
@mock.patch('allianceauth.fleetup.managers.FleetUpManager.get_endpoint')
|
|
||||||
def test_get_fleetup_fittings(self, get_endpoint):
|
|
||||||
|
|
||||||
get_endpoint.return_value = {"Data": [
|
|
||||||
{
|
|
||||||
'FittingId': 1234,
|
|
||||||
'Name': 'Foxcat',
|
|
||||||
'EveTypeId': 17726,
|
|
||||||
'HullType': 'Battleship',
|
|
||||||
'ShipType': 'Apocalypse Navy Issue',
|
|
||||||
'EstPrice': 500000000,
|
|
||||||
'Faction': 'Amarr',
|
|
||||||
'Categories': ["Armor", "Laser"],
|
|
||||||
'LastUpdatedString': '2017-05-06 11:11:11',
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_fittings()
|
|
||||||
|
|
||||||
# Asset
|
|
||||||
self.assertTrue(get_endpoint.called)
|
|
||||||
expected_result = {
|
|
||||||
1234: {
|
|
||||||
'fitting_id': 1234,
|
|
||||||
'name': 'Foxcat',
|
|
||||||
'icon_id': 17726,
|
|
||||||
'hull': 'Battleship',
|
|
||||||
'shiptype': 'Apocalypse Navy Issue',
|
|
||||||
'estimated': 500000000,
|
|
||||||
'faction': 'Amarr',
|
|
||||||
'categories': ["Armor", "Laser"],
|
|
||||||
'last_update': make_aware(datetime.datetime(2017, 5, 6, 11, 11, 11), utc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.assertDictEqual(expected_result, result)
|
|
||||||
|
|
||||||
# Test None response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = None
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_fittings()
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
# Test Empty response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = {'Data': []}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_fittings()
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertDictEqual({}, result)
|
|
||||||
|
|
||||||
@mock.patch('allianceauth.fleetup.managers.FleetUpManager.get_endpoint')
|
|
||||||
def test_get_fleetup_fitting(self, get_endpoint):
|
|
||||||
|
|
||||||
get_endpoint.return_value = {"Data":
|
|
||||||
{
|
|
||||||
'FittingData': [{}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_fitting(1234)
|
|
||||||
|
|
||||||
# Asset
|
|
||||||
self.assertTrue(get_endpoint.called)
|
|
||||||
args, kwargs = get_endpoint.call_args
|
|
||||||
self.assertEqual(args[0], FleetUpManager.BASE_URL + '/Fitting/1234')
|
|
||||||
expected_result = {
|
|
||||||
'fitting_data': {
|
|
||||||
'FittingData': [{}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.assertDictEqual(expected_result, result)
|
|
||||||
|
|
||||||
# Test None response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = None
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_fitting(1234)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
# Test Empty response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = {'Data': {}}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_fitting(1234)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertDictEqual({"fitting_data": {}}, result)
|
|
||||||
|
|
||||||
@mock.patch('allianceauth.fleetup.managers.FleetUpManager.get_endpoint')
|
|
||||||
def test_get_fleetup_doctrineid(self, get_endpoint):
|
|
||||||
|
|
||||||
get_endpoint.return_value = {
|
|
||||||
"Data": {
|
|
||||||
'Doctrines': [{'DoctrineId': 4567}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_doctrineid(1234)
|
|
||||||
|
|
||||||
# Asset
|
|
||||||
self.assertTrue(get_endpoint.called)
|
|
||||||
args, kwargs = get_endpoint.call_args
|
|
||||||
self.assertEqual(args[0], FleetUpManager.BASE_URL + '/Fitting/1234')
|
|
||||||
|
|
||||||
self.assertEqual(4567, result)
|
|
||||||
|
|
||||||
# Test None response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = None
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_doctrineid(1234)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
# Test Empty response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = {'Data': {}}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_doctrineid(1234)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertDictEqual({}, result)
|
|
||||||
|
|
||||||
@mock.patch('allianceauth.fleetup.managers.FleetUpManager.get_endpoint')
|
|
||||||
def test_get_fleetup_fitting_eft(self, get_endpoint):
|
|
||||||
|
|
||||||
get_endpoint.return_value = {
|
|
||||||
"Data": {
|
|
||||||
'FittingData': '[Apocalypse Navy Issue, Foxcat]'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_fitting_eft(1234)
|
|
||||||
|
|
||||||
# Asset
|
|
||||||
self.assertTrue(get_endpoint.called)
|
|
||||||
args, kwargs = get_endpoint.call_args
|
|
||||||
self.assertEqual(args[0], FleetUpManager.BASE_URL + '/Fitting/1234/eft')
|
|
||||||
|
|
||||||
self.assertDictEqual({"fitting_eft": '[Apocalypse Navy Issue, Foxcat]'},
|
|
||||||
result)
|
|
||||||
|
|
||||||
# Test None response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = None
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_fitting_eft(1234)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
# Test Empty response
|
|
||||||
# Arrange
|
|
||||||
get_endpoint.return_value = {'Data': {}}
|
|
||||||
|
|
||||||
# Act
|
|
||||||
result = FleetUpManager.get_fleetup_fitting_eft(1234)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertDictEqual({"fitting_eft": {}}, result)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
from django.conf.urls import url
|
|
||||||
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
app_name = 'fleetup'
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
url(r'^$', views.fleetup_view, name='view'),
|
|
||||||
url(r'^fittings/$', views.fleetup_fittings, name='fittings'),
|
|
||||||
url(r'^fittings/(?P<fittingnumber>[0-9]+)/$', views.fleetup_fitting, name='fitting'),
|
|
||||||
url(r'^doctrines/$', views.fleetup_doctrines, name='doctrines'),
|
|
||||||
url(r'^characters/$', views.fleetup_characters, name='characters'),
|
|
||||||
url(r'^doctrines/(?P<doctrinenumber>[0-9]+)/$', views.fleetup_doctrine, name='doctrine'),
|
|
||||||
]
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
import datetime
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.contrib.auth.decorators import permission_required
|
|
||||||
from django.shortcuts import render
|
|
||||||
from django.template.defaulttags import register
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from .managers import FleetUpManager
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
|
||||||
def get_item(dictionary, key):
|
|
||||||
return dictionary.get(key)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@permission_required('auth.view_fleetup')
|
|
||||||
def fleetup_view(request):
|
|
||||||
logger.debug("fleetup_view called by user %s" % request.user)
|
|
||||||
|
|
||||||
operations_list = FleetUpManager.get_fleetup_operations()
|
|
||||||
if operations_list is None:
|
|
||||||
messages.add_message(request, messages.ERROR, _("Failed to get operations list, contact your administrator"))
|
|
||||||
operations_list = {}
|
|
||||||
timers_list = FleetUpManager.get_fleetup_timers()
|
|
||||||
if timers_list is None:
|
|
||||||
messages.add_message(request, messages.ERROR, _("Failed to get timers list, contact your administrator"))
|
|
||||||
timers_list = {}
|
|
||||||
now = datetime.datetime.now().strftime('%H:%M:%S')
|
|
||||||
|
|
||||||
context = {"timers_list": sorted(timers_list.items()),
|
|
||||||
"operations_list": sorted(operations_list.items()),
|
|
||||||
"now": now}
|
|
||||||
|
|
||||||
return render(request, 'fleetup/index.html', context=context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@permission_required('auth.human_resources')
|
|
||||||
@permission_required('auth.view_fleetup')
|
|
||||||
def fleetup_characters(request):
|
|
||||||
logger.debug("fleetup_characters called by user %s" % request.user)
|
|
||||||
|
|
||||||
member_list = FleetUpManager.get_fleetup_members()
|
|
||||||
if member_list is None:
|
|
||||||
messages.add_message(request, messages.ERROR, _("Failed to get member list, contact your administrator"))
|
|
||||||
member_list = {}
|
|
||||||
|
|
||||||
context = {"member_list": sorted(member_list.items())}
|
|
||||||
|
|
||||||
return render(request, 'fleetup/characters.html', context=context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@permission_required('auth.view_fleetup')
|
|
||||||
def fleetup_fittings(request):
|
|
||||||
logger.debug("fleetup_fittings called by user %s" % request.user)
|
|
||||||
fitting_list = FleetUpManager.get_fleetup_fittings()
|
|
||||||
|
|
||||||
if fitting_list is None:
|
|
||||||
messages.add_message(request, messages.ERROR, _("Failed to get fitting list, contact your administrator"))
|
|
||||||
fitting_list = {}
|
|
||||||
|
|
||||||
context = {"fitting_list": sorted(fitting_list.items())}
|
|
||||||
return render(request, 'fleetup/fittingsview.html', context=context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@permission_required('auth.view_fleetup')
|
|
||||||
def fleetup_fitting(request, fittingnumber):
|
|
||||||
logger.debug("fleetup_fitting called by user %s" % request.user)
|
|
||||||
fitting_eft = FleetUpManager.get_fleetup_fitting_eft(fittingnumber)
|
|
||||||
fitting_data = FleetUpManager.get_fleetup_fitting(fittingnumber)
|
|
||||||
doctrinenumber = FleetUpManager.get_fleetup_doctrineid(fittingnumber)
|
|
||||||
doctrines_list = FleetUpManager.get_fleetup_doctrine(doctrinenumber)
|
|
||||||
|
|
||||||
if fitting_eft is None or fitting_data is None or doctrinenumber is None:
|
|
||||||
messages.add_message(request, messages.ERROR, _("There was an error getting some of the data for this fitting. "
|
|
||||||
"Contact your administrator"))
|
|
||||||
|
|
||||||
context = {"fitting_eft": fitting_eft,
|
|
||||||
"fitting_data": fitting_data,
|
|
||||||
"doctrines_list": doctrines_list}
|
|
||||||
return render(request, 'fleetup/fitting.html', context=context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@permission_required('auth.view_fleetup')
|
|
||||||
def fleetup_doctrines(request):
|
|
||||||
logger.debug("fleetup_doctrines called by user %s" % request.user)
|
|
||||||
doctrines_list = FleetUpManager.get_fleetup_doctrines()
|
|
||||||
if doctrines_list is None:
|
|
||||||
messages.add_message(request, messages.ERROR, _("Failed to get doctrines list, contact your administrator"))
|
|
||||||
|
|
||||||
context = {"doctrines_list": doctrines_list}
|
|
||||||
return render(request, 'fleetup/doctrinesview.html', context=context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@permission_required('auth.view_fleetup')
|
|
||||||
def fleetup_doctrine(request, doctrinenumber):
|
|
||||||
logger.debug("fleetup_doctrine called by user %s" % request.user)
|
|
||||||
doctrine = FleetUpManager.get_fleetup_doctrine(doctrinenumber)
|
|
||||||
if doctrine is None:
|
|
||||||
messages.add_message(request, messages.ERROR, _("Failed to get doctine, contact your administrator"))
|
|
||||||
context = {"doctrine": doctrine}
|
|
||||||
return render(request, 'fleetup/doctrine.html', context=context)
|
|
||||||
@@ -1,18 +1,46 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth.models import Group as BaseGroup
|
from django.contrib.auth.models import Group as BaseGroup, User
|
||||||
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete, m2m_changed
|
from django.db.models import Count
|
||||||
|
from django.db.models.functions import Lower
|
||||||
|
from django.db.models.signals import pre_save, post_save, pre_delete, \
|
||||||
|
post_delete, m2m_changed
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from .models import AuthGroup
|
from .models import AuthGroup
|
||||||
from .models import GroupRequest
|
from .models import GroupRequest
|
||||||
|
from . import signals
|
||||||
|
|
||||||
|
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
|
||||||
|
_has_auto_groups = True
|
||||||
|
else:
|
||||||
|
_has_auto_groups = False
|
||||||
|
|
||||||
|
|
||||||
class AuthGroupInlineAdmin(admin.StackedInline):
|
class AuthGroupInlineAdmin(admin.StackedInline):
|
||||||
model = AuthGroup
|
model = AuthGroup
|
||||||
filter_horizontal = ('group_leaders',)
|
filter_horizontal = ('group_leaders', 'group_leader_groups', 'states',)
|
||||||
fields = ('description', 'group_leaders', 'internal', 'hidden', 'open', 'public')
|
fields = (
|
||||||
|
'description',
|
||||||
|
'group_leaders',
|
||||||
|
'group_leader_groups',
|
||||||
|
'states', 'internal',
|
||||||
|
'hidden',
|
||||||
|
'open',
|
||||||
|
'public'
|
||||||
|
)
|
||||||
verbose_name_plural = 'Auth Settings'
|
verbose_name_plural = 'Auth Settings'
|
||||||
verbose_name = ''
|
verbose_name = ''
|
||||||
|
|
||||||
|
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||||
|
"""overriding this formfield to have sorted lists in the form"""
|
||||||
|
if db_field.name == "group_leaders":
|
||||||
|
kwargs["queryset"] = User.objects.order_by(Lower('username'))
|
||||||
|
elif db_field.name == "group_leader_groups":
|
||||||
|
kwargs["queryset"] = Group.objects.order_by(Lower('name'))
|
||||||
|
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -23,7 +51,118 @@ class AuthGroupInlineAdmin(admin.StackedInline):
|
|||||||
return request.user.has_perm('auth.change_group')
|
return request.user.has_perm('auth.change_group')
|
||||||
|
|
||||||
|
|
||||||
class GroupAdmin(admin.ModelAdmin):
|
if _has_auto_groups:
|
||||||
|
class IsAutoGroupFilter(admin.SimpleListFilter):
|
||||||
|
title = 'auto group'
|
||||||
|
parameter_name = 'is_auto_group__exact'
|
||||||
|
|
||||||
|
def lookups(self, request, model_admin):
|
||||||
|
return (
|
||||||
|
('yes', 'Yes'),
|
||||||
|
('no', 'No'),
|
||||||
|
)
|
||||||
|
|
||||||
|
def queryset(self, request, queryset):
|
||||||
|
value = self.value()
|
||||||
|
if value == 'yes':
|
||||||
|
return queryset.exclude(
|
||||||
|
managedalliancegroup__isnull=True,
|
||||||
|
managedcorpgroup__isnull=True
|
||||||
|
)
|
||||||
|
elif value == 'no':
|
||||||
|
return queryset.filter(
|
||||||
|
managedalliancegroup__isnull=True,
|
||||||
|
managedcorpgroup__isnull=True
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class HasLeaderFilter(admin.SimpleListFilter):
|
||||||
|
title = 'has leader'
|
||||||
|
parameter_name = 'has_leader__exact'
|
||||||
|
|
||||||
|
def lookups(self, request, model_admin):
|
||||||
|
return (
|
||||||
|
('yes', 'Yes'),
|
||||||
|
('no', 'No'),
|
||||||
|
)
|
||||||
|
|
||||||
|
def queryset(self, request, queryset):
|
||||||
|
value = self.value()
|
||||||
|
if value == 'yes':
|
||||||
|
return queryset.filter(authgroup__group_leaders__isnull=False)
|
||||||
|
elif value == 'no':
|
||||||
|
return queryset.filter(authgroup__group_leaders__isnull=True)
|
||||||
|
else:
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
class GroupAdmin(admin.ModelAdmin):
|
||||||
|
list_select_related = True
|
||||||
|
ordering = ('name', )
|
||||||
|
list_display = (
|
||||||
|
'name',
|
||||||
|
'_description',
|
||||||
|
'_properties',
|
||||||
|
'_member_count',
|
||||||
|
'has_leader'
|
||||||
|
)
|
||||||
|
list_filter = [
|
||||||
|
'authgroup__internal',
|
||||||
|
'authgroup__hidden',
|
||||||
|
'authgroup__open',
|
||||||
|
'authgroup__public',
|
||||||
|
]
|
||||||
|
if _has_auto_groups:
|
||||||
|
list_filter.append(IsAutoGroupFilter)
|
||||||
|
list_filter.append(HasLeaderFilter)
|
||||||
|
|
||||||
|
search_fields = ('name', 'authgroup__description')
|
||||||
|
|
||||||
|
def get_queryset(self, request):
|
||||||
|
qs = super().get_queryset(request)
|
||||||
|
qs = qs.annotate(
|
||||||
|
member_count=Count('user', distinct=True),
|
||||||
|
)
|
||||||
|
return qs
|
||||||
|
|
||||||
|
def _description(self, obj):
|
||||||
|
return obj.authgroup.description
|
||||||
|
|
||||||
|
def _member_count(self, obj):
|
||||||
|
return obj.member_count
|
||||||
|
|
||||||
|
_member_count.short_description = 'Members'
|
||||||
|
_member_count.admin_order_field = 'member_count'
|
||||||
|
|
||||||
|
def has_leader(self, obj):
|
||||||
|
return obj.authgroup.group_leaders.exists()
|
||||||
|
|
||||||
|
has_leader.boolean = True
|
||||||
|
|
||||||
|
def _properties(self, obj):
|
||||||
|
properties = list()
|
||||||
|
if _has_auto_groups and (
|
||||||
|
obj.managedalliancegroup_set.exists()
|
||||||
|
or obj.managedcorpgroup_set.exists()
|
||||||
|
):
|
||||||
|
properties.append('Auto Group')
|
||||||
|
elif obj.authgroup.internal:
|
||||||
|
properties.append('Internal')
|
||||||
|
else:
|
||||||
|
if obj.authgroup.hidden:
|
||||||
|
properties.append('Hidden')
|
||||||
|
if obj.authgroup.open:
|
||||||
|
properties.append('Open')
|
||||||
|
if obj.authgroup.public:
|
||||||
|
properties.append('Public')
|
||||||
|
if not properties:
|
||||||
|
properties.append('Default')
|
||||||
|
|
||||||
|
return properties
|
||||||
|
|
||||||
|
_properties.short_description = "properties"
|
||||||
|
|
||||||
filter_horizontal = ('permissions',)
|
filter_horizontal = ('permissions',)
|
||||||
inlines = (AuthGroupInlineAdmin,)
|
inlines = (AuthGroupInlineAdmin,)
|
||||||
|
|
||||||
@@ -65,4 +204,4 @@ def redirect_post_delete(sender, signal=None, *args, **kwargs):
|
|||||||
|
|
||||||
@receiver(m2m_changed, sender=Group.permissions.through)
|
@receiver(m2m_changed, sender=Group.permissions.through)
|
||||||
def redirect_m2m_changed_permissions(sender, signal=None, *args, **kwargs):
|
def redirect_m2m_changed_permissions(sender, signal=None, *args, **kwargs):
|
||||||
m2m_changed.send(BaseGroup, *args, **kwargs)
|
m2m_changed.send(BaseGroup, *args, **kwargs)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
|
||||||
class GroupManager:
|
class GroupManager:
|
||||||
@@ -6,21 +7,49 @@ class GroupManager:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_joinable_groups():
|
def get_joinable_groups(state):
|
||||||
|
return Group.objects.select_related('authgroup').exclude(authgroup__internal=True)\
|
||||||
|
.filter(Q(authgroup__states=state) | Q(authgroup__states=None))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_all_non_internal_groups():
|
||||||
return Group.objects.select_related('authgroup').exclude(authgroup__internal=True)
|
return Group.objects.select_related('authgroup').exclude(authgroup__internal=True)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_group_leaders_groups(user):
|
def get_group_leaders_groups(user):
|
||||||
return Group.objects.select_related('authgroup').filter(authgroup__group_leaders__in=[user])
|
return Group.objects.select_related('authgroup').filter(authgroup__group_leaders__in=[user]) | \
|
||||||
|
Group.objects.select_related('authgroup').filter(authgroup__group_leader_groups__in=user.groups.all())
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def joinable_group(group):
|
def joinable_group(group, state):
|
||||||
"""
|
"""
|
||||||
Check if a group is a user joinable group, i.e.
|
Check if a group is a user/state joinable group, i.e.
|
||||||
not an internal group for Corp, Alliance, Members etc
|
not an internal group for Corp, Alliance, Members etc,
|
||||||
|
or restricted from the user's current state.
|
||||||
:param group: django.contrib.auth.models.Group object
|
:param group: django.contrib.auth.models.Group object
|
||||||
|
:param state: allianceauth.authentication.State object
|
||||||
:return: bool True if its joinable, False otherwise
|
:return: bool True if its joinable, False otherwise
|
||||||
"""
|
"""
|
||||||
|
if len(group.authgroup.states.all()) != 0 and state not in group.authgroup.states.all():
|
||||||
|
return False
|
||||||
|
return not group.authgroup.internal
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_internal_group(group):
|
||||||
|
"""
|
||||||
|
Check if a group is auditable, i.e not an internal group
|
||||||
|
:param group: django.contrib.auth.models.Group object
|
||||||
|
:return: bool True if it is auditable, false otherwise
|
||||||
|
"""
|
||||||
|
return not group.authgroup.internal
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_internal_group(group):
|
||||||
|
"""
|
||||||
|
Check if a group is auditable, i.e not an internal group
|
||||||
|
:param group: django.contrib.auth.models.Group object
|
||||||
|
:return: bool True if it is auditable, false otherwise
|
||||||
|
"""
|
||||||
return not group.authgroup.internal
|
return not group.authgroup.internal
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -38,7 +67,7 @@ class GroupManager:
|
|||||||
:return: bool True if user can manage groups, False otherwise
|
:return: bool True if user can manage groups, False otherwise
|
||||||
"""
|
"""
|
||||||
if user.is_authenticated:
|
if user.is_authenticated:
|
||||||
return cls.has_management_permission(user) or user.leads_groups.all()
|
return cls.has_management_permission(user) or cls.get_group_leaders_groups(user)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -50,5 +79,5 @@ class GroupManager:
|
|||||||
:return: True if the user can manage the group
|
:return: True if the user can manage the group
|
||||||
"""
|
"""
|
||||||
if user.is_authenticated:
|
if user.is_authenticated:
|
||||||
return cls.has_management_permission(user) or user.leads_groups.filter(group=group).exists()
|
return cls.has_management_permission(user) or cls.get_group_leaders_groups(user).filter(pk=group.pk).exists()
|
||||||
return False
|
return False
|
||||||
|
|||||||
28
allianceauth/groupmanagement/migrations/0009_requestlog.py
Normal file
28
allianceauth/groupmanagement/migrations/0009_requestlog.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 2.0.6 on 2018-06-04 02:45
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0008_alter_user_username_max_length'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('groupmanagement', '0008_remove_authgroup_permissions'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='RequestLog',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('request_type', models.NullBooleanField(default=0)),
|
||||||
|
('request_info', models.CharField(max_length=254)),
|
||||||
|
('action', models.BooleanField(default=0)),
|
||||||
|
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
|
||||||
|
('request_actor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 2.0.6 on 2018-07-11 00:17
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('authentication', '0016_ownershiprecord'),
|
||||||
|
('groupmanagement', '0009_requestlog'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='authgroup',
|
||||||
|
name='states',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='States listed here will have the ability to join this group provided they have the proper permissions.', related_name='valid_states', to='authentication.State'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 2.0.8 on 2018-12-07 08:56
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('groupmanagement', '0010_authgroup_states'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='requestlog',
|
||||||
|
name='date',
|
||||||
|
field=models.DateTimeField(default=datetime.datetime(2018, 12, 7, 8, 56, 33, 846342)),
|
||||||
|
),
|
||||||
|
]
|
||||||
19
allianceauth/groupmanagement/migrations/0012_group_leads.py
Normal file
19
allianceauth/groupmanagement/migrations/0012_group_leads.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 2.2.8 on 2020-01-06 11:00
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0011_update_proxy_permissions'),
|
||||||
|
('groupmanagement', '0011_requestlog_date'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='authgroup',
|
||||||
|
name='group_leader_groups',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Group leaders can process group requests for this group specifically. Use the auth.group_management permission to allow a user to manage all groups.', related_name='leads_group_groups', to='auth.Group'),
|
||||||
|
)
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 2.2.9 on 2020-01-30 13:14
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('groupmanagement', '0012_group_leads'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='requestlog',
|
||||||
|
name='date',
|
||||||
|
field=models.DateTimeField(auto_now_add=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -3,6 +3,8 @@ from django.contrib.auth.models import User
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
from allianceauth.authentication.models import State
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
class GroupRequest(models.Model):
|
class GroupRequest(models.Model):
|
||||||
@@ -23,6 +25,38 @@ class GroupRequest(models.Model):
|
|||||||
return self.user.username + ":" + self.group.name
|
return self.user.username + ":" + self.group.name
|
||||||
|
|
||||||
|
|
||||||
|
class RequestLog(models.Model):
|
||||||
|
request_type = models.NullBooleanField(default=0)
|
||||||
|
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||||
|
request_info = models.CharField(max_length=254)
|
||||||
|
action = models.BooleanField(default=0)
|
||||||
|
request_actor = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
date = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def requestor(self):
|
||||||
|
return self.request_info.split(":")[0]
|
||||||
|
|
||||||
|
def type_to_str(self):
|
||||||
|
if self.request_type is None:
|
||||||
|
return "Removed"
|
||||||
|
elif self.request_type is True:
|
||||||
|
return "Leave"
|
||||||
|
elif self.request_type is False:
|
||||||
|
return "Join"
|
||||||
|
|
||||||
|
def action_to_str(self):
|
||||||
|
if self.action is True:
|
||||||
|
return "Accept"
|
||||||
|
elif self.action is False:
|
||||||
|
return "Reject"
|
||||||
|
|
||||||
|
def req_char(self):
|
||||||
|
usr = self.requestor()
|
||||||
|
user = User.objects.get(username=usr)
|
||||||
|
return user.profile.main_character
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AuthGroup(models.Model):
|
class AuthGroup(models.Model):
|
||||||
"""
|
"""
|
||||||
Extends Django Group model with a one-to-one field
|
Extends Django Group model with a one-to-one field
|
||||||
@@ -64,6 +98,15 @@ class AuthGroup(models.Model):
|
|||||||
help_text="Group leaders can process group requests for this group "
|
help_text="Group leaders can process group requests for this group "
|
||||||
"specifically. Use the auth.group_management permission to allow "
|
"specifically. Use the auth.group_management permission to allow "
|
||||||
"a user to manage all groups.")
|
"a user to manage all groups.")
|
||||||
|
# allow groups to be *group leads*
|
||||||
|
group_leader_groups = models.ManyToManyField(Group, related_name='leads_group_groups', blank=True,
|
||||||
|
help_text="Group leaders can process group requests for this group "
|
||||||
|
"specifically. Use the auth.group_management permission to allow "
|
||||||
|
"a user to manage all groups.")
|
||||||
|
|
||||||
|
states = models.ManyToManyField(State, related_name='valid_states', blank=True,
|
||||||
|
help_text="States listed here will have the ability to join this group provided "
|
||||||
|
"they have the proper permissions.")
|
||||||
|
|
||||||
description = models.CharField(max_length=512, blank=True, help_text="Description of the group shown to users.")
|
description = models.CharField(max_length=512, blank=True, help_text="Description of the group shown to users.")
|
||||||
|
|
||||||
|
|||||||
16
allianceauth/groupmanagement/signals.py
Normal file
16
allianceauth/groupmanagement/signals.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from allianceauth.authentication.signals import state_changed
|
||||||
|
from .managers import GroupManager
|
||||||
|
from .models import Group
|
||||||
|
from django.dispatch import receiver
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@receiver(state_changed)
|
||||||
|
def check_groups_on_state_change(sender, user, state, **kwargs):
|
||||||
|
logger.debug("Updating auth groups for {}".format(user))
|
||||||
|
visible_groups = GroupManager.get_joinable_groups(state)
|
||||||
|
visible_groups = visible_groups | Group.objects.select_related('authgroup').filter(authgroup__internal=True)
|
||||||
|
groups = user.groups.all()
|
||||||
|
for g in groups:
|
||||||
|
if g not in visible_groups:
|
||||||
|
user.groups.remove(g)
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
{% extends "allianceauth/base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block page_title %}{{ group }} {% trans "Audit Log" %}{% endblock page_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<br>
|
||||||
|
{% include 'groupmanagement/menu.html' %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
{{ group }} - {% trans 'Audit Log' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-default" href="{% url 'groupmanagement:membership' %}" role="button">
|
||||||
|
Back
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{% if entries %}
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped" id="log-entries">
|
||||||
|
<thead>
|
||||||
|
<th class="text-center" scope="col">{% trans "Date/Time" %}</th>
|
||||||
|
<th class="text-center" scope="col">{% trans "Requestor" %}</th>
|
||||||
|
<th class="text-center" scope="col">{% trans "Character" %}</th>
|
||||||
|
<th class="text-center" scope="col">{% trans "Corporation" %}</th>
|
||||||
|
<th class="text-center" scope="col">{% trans "Type" %}</th>
|
||||||
|
<th class="text-center" scope="col">{% trans "Action" %}</th>
|
||||||
|
<th class="text-center" scope="col">{% trans "Actor" %}</th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for entry in entries %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-center">{{ entry.date }}</td>
|
||||||
|
<td class="text-center">{{ entry.requestor }}</td>
|
||||||
|
<td class="text-center">{{ entry.req_char }}</td>
|
||||||
|
<td class="text-center">{{ entry.req_char.corporation_name }}</td>
|
||||||
|
<td class="text-center">{{ entry.type_to_str }}</td>
|
||||||
|
{% if entry.request_type is None %}
|
||||||
|
<td class="text-center"> Removed</td>
|
||||||
|
{% else %}
|
||||||
|
<td class="text-center">{{ entry.action_to_str }}</td>
|
||||||
|
{% endif %}
|
||||||
|
<td class="text-center">{{ entry.request_actor }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="text-muted">
|
||||||
|
All times displayed are EVE/UTC.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
<br>
|
||||||
|
<div class="alert alert-warning text-center">
|
||||||
|
{% trans "No entries found for this group." %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_javascript %}
|
||||||
|
{% include 'bundles/datatables-js.html' %}
|
||||||
|
<script type="text/javascript" src="{% static 'js/filterDropDown/filterDropDown.min.js' %}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_css %}
|
||||||
|
{% include 'bundles/datatables-css.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_script %}
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#log-entries').DataTable({
|
||||||
|
order: [[ 0, 'desc' ], [ 1, 'asc' ] ],
|
||||||
|
filterDropDown:
|
||||||
|
{
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
idx: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
idx: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
idx: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
idx: 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
idx: 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
idx: 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
bootstrap: true
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
@@ -1,43 +1,102 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load evelinks %}
|
||||||
|
|
||||||
{% block page_title %}{% trans "Group Members" %}{% endblock page_title %}
|
{% block page_title %}{% trans "Group Members" %}{% endblock page_title %}
|
||||||
{% block extra_css %}{% endblock extra_css %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<br>
|
<br>
|
||||||
{% include 'groupmanagement/menu.html' %}
|
{% include 'groupmanagement/menu.html' %}
|
||||||
<h3>{{ group.name }} {% trans 'Members' %}</h3>
|
|
||||||
<div id="list" class="">
|
<div class="panel panel-default">
|
||||||
{% if group.user_set %}
|
<div class="panel-heading">
|
||||||
<table class="table">
|
{{ group.name }} - {% trans 'Members' %}
|
||||||
<tr>
|
</div>
|
||||||
<th class="text-center">{% trans "User" %}</th>
|
<div class="panel-body">
|
||||||
<th class="text-center">{% trans "Character" %}</th>
|
<p>
|
||||||
<th class="text-center">{% trans "Corp" %}</th>
|
<a class="btn btn-default" href="{% url 'groupmanagement:membership' %}" role="button">
|
||||||
<th class="text-center">{% trans "Alliance" %}</th>
|
Back
|
||||||
<th class="text-center">{% trans "Action" %}</th>
|
</a>
|
||||||
</tr>
|
</p>
|
||||||
{% for member in members %}
|
{% if group.user_set %}
|
||||||
<tr>
|
<div class="table-responsive">
|
||||||
<td class="text-center">{{ member.user.username }}</td>
|
<table class="table table-aa" id="tab_group_members">
|
||||||
<td class="text-center">{{ member.main_char.character_name }}</td>
|
<thead>
|
||||||
<td class="text-center">{{ member.main_char.corporation_name }}</td>
|
<tr>
|
||||||
<td class="text-center">{{ member.main_char.alliance_name }}</td>
|
<th class="text-right">{% trans "Portrait" %}</th>
|
||||||
<td class="text-center">
|
<th class="text-center">{% trans "Character" %}</th>
|
||||||
<a href="{% url 'groupmanagement:membership_remove' group.id member.user.id %}" class="btn btn-danger"
|
<th class="text-center">{% trans "Organization" %}</th>
|
||||||
title="{% trans "Remove from group" %}">
|
<th class="text-center"></th>
|
||||||
<i class="glyphicon glyphicon-remove"></i>
|
</tr>
|
||||||
</a>
|
</thead>
|
||||||
</td>
|
<tbody>
|
||||||
</tr>
|
{% for member in members %}
|
||||||
{% endfor %}
|
<tr>
|
||||||
</table>
|
<td class="text-right">
|
||||||
{% else %}
|
{% if member.is_leader %}
|
||||||
<div class="alert alert-warning text-center">{% trans "No group members to list." %}</div>
|
<i class="fa fa-star"></i>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<img src="{{ member.main_char|character_portrait_url:32 }}" class="img-circle">
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
{% if member.main_char %}
|
||||||
|
<a href="{{ member.main_char|evewho_character_url }}" target="_blank">
|
||||||
|
{{ member.main_char.character_name }}
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
{{ member.user.username }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
{% if member.main_char %}
|
||||||
|
<a href="{{ member.main_char|dotlan_corporation_url }}" target="_blank">
|
||||||
|
{{ member.main_char.corporation_name }}
|
||||||
|
</a><br>
|
||||||
|
{{ member.main_char.alliance_name|default_if_none:"" }}
|
||||||
|
{% else %}
|
||||||
|
(unknown)
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{% url 'groupmanagement:membership_remove' group.id member.user.id %}" class="btn btn-danger"
|
||||||
|
title="{% trans "Remove from group" %}">
|
||||||
|
<i class="glyphicon glyphicon-remove"></i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="text-muted"><i class="fa fa-star"></i>: Group leader</p>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="alert alert-warning text-center">
|
||||||
|
{% trans "No group members to list." %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
|
{% block extra_javascript %}
|
||||||
|
{% include 'bundles/datatables-js.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_css %}
|
||||||
|
{% include 'bundles/datatables-css.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_script %}
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#tab_group_members').DataTable({
|
||||||
|
order: [ [ 1, "asc" ] ],
|
||||||
|
columnDefs: [
|
||||||
|
{ "sortable": false, "targets": [0, 3] },
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
{% endblock %}
|
||||||
@@ -9,45 +9,62 @@
|
|||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<br>
|
<br>
|
||||||
{% include 'groupmanagement/menu.html' %}
|
{% include 'groupmanagement/menu.html' %}
|
||||||
<div>
|
<div class="panel panel-default">
|
||||||
{% if groups %}
|
<div class="panel-heading">
|
||||||
<h3>Groups</h3>
|
Groups
|
||||||
<table class="table">
|
</div>
|
||||||
<tr>
|
<div class="panel-body">
|
||||||
<th class="text-center">{% trans "Name" %}</th>
|
{% if groups %}
|
||||||
<th class="text-center">{% trans "Description" %}</th>
|
<div class="table-responsive">
|
||||||
<th class="text-center">{% trans "Status" %}</th>
|
<table class="table table-aa">
|
||||||
<th class="text-center">{% trans "Member Count" %}</th>
|
<thead>
|
||||||
<th class="text-center">{% trans "Action" %}</th>
|
<tr>
|
||||||
</tr>
|
<th class="text-center">{% trans "Name" %}</th>
|
||||||
{% for group in groups %}
|
<th class="text-center">{% trans "Description" %}</th>
|
||||||
<tr>
|
<th class="text-center">{% trans "Status" %}</th>
|
||||||
<td class="text-center">{{ group.name }}</td>
|
<th class="text-center">{% trans "Member Count" %}</th>
|
||||||
<td class="text-center">{{ group.authgroup.description }}</td>
|
<th class="text-center"></th>
|
||||||
<td class="text-center">
|
</tr>
|
||||||
{% if group.authgroup.hidden %}
|
</thead>
|
||||||
<span class="label label-info">{% trans "Hidden" %}</span>
|
<tbody>
|
||||||
{% elif group.authgroup.open %}
|
{% for group in groups %}
|
||||||
<span class="label label-success">{% trans "Open" %}</span>
|
<tr>
|
||||||
{% else %}
|
<td class="text-center">
|
||||||
<span class="label label-default">{% trans "Requestable" %}</span>
|
<a href="{% url 'groupmanagement:membership_list' group.id %}">{{ group.name }}</a>
|
||||||
{% endif %}
|
</td>
|
||||||
</td>
|
<td class="text-center">{{ group.authgroup.description }}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
{{ group.num_members }}
|
{% if group.authgroup.hidden %}
|
||||||
</td>
|
<span class="label label-info">{% trans "Hidden" %}</span>
|
||||||
<td class="text-center">
|
{% elif group.authgroup.open %}
|
||||||
<a href="{% url 'groupmanagement:membership_list' group.id %}" class="btn btn-primary"
|
<span class="label label-success">{% trans "Open" %}</span>
|
||||||
title="{% trans "View Members" %}">
|
{% else %}
|
||||||
<i class="glyphicon glyphicon-eye-open"></i>
|
<span class="label label-default">{% trans "Requestable" %}</span>
|
||||||
</a>
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
<td class="text-center">
|
||||||
{% endfor %}
|
{{ group.num_members }}
|
||||||
</table>
|
</td>
|
||||||
{% else %}
|
<td class="text-center">
|
||||||
<div class="alert alert-warning text-center">{% trans "No groups to list." %}</div>
|
<a href="{% url 'groupmanagement:membership_list' group.id %}" class="btn btn-primary"
|
||||||
{% endif %}
|
title="{% trans "View Members" %}">
|
||||||
|
<i class="glyphicon glyphicon-eye-open"></i>
|
||||||
|
</a>
|
||||||
|
<a href="{% url "groupmanagement:audit_log" group.id %}" class="btn btn-info" title="{% trans "Audit Members" %}">
|
||||||
|
<i class="glyphicon glyphicon-list-alt"></i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="alert alert-warning text-center">
|
||||||
|
{% trans "No groups to list." %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -9,43 +9,54 @@ url
|
|||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<h1 class="page-header text-center">{% trans "Available Groups" %}</h1>
|
<h1 class="page-header text-center">{% trans "Available Groups" %}</h1>
|
||||||
{% if groups %}
|
{% if groups %}
|
||||||
<table class="table">
|
<table class="table table-aa">
|
||||||
<tr>
|
<thead>
|
||||||
<th class="text-center">{% trans "Name" %}</th>
|
<tr>
|
||||||
<th class="text-center">{% trans "Description" %}</th>
|
<th class="text-center">{% trans "Name" %}</th>
|
||||||
<th class="text-center">{% trans "Action" %}</th>
|
<th class="text-center">{% trans "Description" %}</th>
|
||||||
</tr>
|
<th class="text-center">{% trans "Action" %}</th>
|
||||||
|
</tr>
|
||||||
{% for g in groups %}
|
</thead>
|
||||||
<tr>
|
<tbody>
|
||||||
<td class="text-center">{{ g.group.name }}</td>
|
{% for g in groups %}
|
||||||
<td class="text-center">{{ g.group.authgroup.description }}</td>
|
<tr>
|
||||||
<td class="text-center">
|
<td class="text-center">{{ g.group.name }}</td>
|
||||||
{% if g.group in user.groups.all %}
|
<td class="text-center">{{ g.group.authgroup.description|urlize }}</td>
|
||||||
{% if not g.request %}
|
<td class="text-center">
|
||||||
<a href="{% url 'groupmanagement:request_leave' g.group.id %}" class="btn btn-danger">
|
{% if g.group in user.groups.all %}
|
||||||
{% trans "Leave" %}
|
{% if not g.request %}
|
||||||
</a>
|
<a href="{% url 'groupmanagement:request_leave' g.group.id %}" class="btn btn-danger">
|
||||||
{% else %}
|
{% trans "Leave" %}
|
||||||
<button type="button" class="btn btn-primary" disabled>
|
</a>
|
||||||
{{ g.request.status }}
|
{% else %}
|
||||||
</button>
|
<button type="button" class="btn btn-primary" disabled>
|
||||||
{% endif %}
|
{{ g.request.status }}
|
||||||
{% elif not g.request %}
|
</button>
|
||||||
<a href="{% url 'groupmanagement:request_add' g.group.id %}" class="btn btn-success">
|
{% endif %}
|
||||||
{% trans "Request" %}
|
{% elif not g.request %}
|
||||||
</a>
|
{% if g.group.authgroup.open %}
|
||||||
{% else %}
|
<a href="{% url 'groupmanagement:request_add' g.group.id %}" class="btn btn-success">
|
||||||
<button type="button" class="btn btn-primary" disabled>
|
{% trans "Join" %}
|
||||||
{{ g.request.status }}
|
</a>
|
||||||
</button>
|
{% else %}
|
||||||
{% endif %}
|
<a href="{% url 'groupmanagement:request_add' g.group.id %}" class="btn btn-primary">
|
||||||
</td>
|
{% trans "Request" %}
|
||||||
</tr>
|
</a>
|
||||||
{% endfor %}
|
{% endif %}
|
||||||
</table>
|
{% else %}
|
||||||
|
<button type="button" class="btn btn-primary" disabled>
|
||||||
|
{{ g.request.status }}
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="alert alert-warning text-center">{% trans "No groups available." %}</div>
|
<div class="alert alert-warning text-center">
|
||||||
|
{% trans "No groups available." %}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,81 +1,149 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load evelinks %}
|
||||||
|
|
||||||
{% block page_title %}{% trans "Groups Management" %}{% endblock page_title %}
|
{% block page_title %}{% trans "Groups Management" %}{% endblock page_title %}
|
||||||
{% block extra_css %}{% endblock extra_css %}
|
{% block extra_css %}
|
||||||
|
<style>
|
||||||
|
.nav-tabs>li.active>a {
|
||||||
|
background-color: #ECF0F1 !important;
|
||||||
|
color: #2C3E50;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
{% endblock extra_css %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<br>
|
<br>
|
||||||
{% include 'groupmanagement/menu.html' %}
|
{% include 'groupmanagement/menu.html' %}
|
||||||
|
|
||||||
<ul class="nav nav-tabs">
|
<ul class="nav nav-tabs">
|
||||||
<li class="active"><a data-toggle="tab" href="#add">{% trans "Group Add Requests" %}</a></li>
|
<li class="active"><a data-toggle="tab" href="#add">{% trans "Join Requests" %}</a></li>
|
||||||
<li><a data-toggle="tab" href="#leave">{% trans "Group Leave Requests" %}</a></li>
|
<li><a data-toggle="tab" href="#leave">{% trans "Leave Requests" %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
|
||||||
<div id="add" class="tab-pane fade in active panel panel-default">
|
<div id="add" class="tab-pane fade in active panel panel-default">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% if acceptrequests %}
|
{% if acceptrequests %}
|
||||||
<table class="table">
|
<div class="table-responsive">
|
||||||
<tr>
|
<table class="table table-aa">
|
||||||
<th class="text-center">{% trans "RequestID" %}</th>
|
<thead>
|
||||||
<th class="text-center">{% trans "CharacterName" %}</th>
|
<tr>
|
||||||
<th class="text-center">{% trans "GroupName" %}</th>
|
<th class="text-center"></th>
|
||||||
<th class="text-center">{% trans "Action" %}</th>
|
<th class="text-center">{% trans "Character" %}</th>
|
||||||
</tr>
|
<th class="text-center">{% trans "Organization" %}</th>
|
||||||
{% for acceptrequest in acceptrequests %}
|
<th class="text-center">{% trans "Group" %}</th>
|
||||||
<tr>
|
<th class="text-center"></th>
|
||||||
<td class="text-center">{{ acceptrequest.id }}</td>
|
</tr>
|
||||||
<td class="text-center">{{ acceptrequest.main_char.character_name }}</td>
|
</thead>
|
||||||
<td class="text-center">{{ acceptrequest.group.name }}</td>
|
<tbody>
|
||||||
<td class="text-center">
|
{% for acceptrequest in acceptrequests %}
|
||||||
<a href="{% url 'groupmanagement:accept_request' acceptrequest.id %}" class="btn btn-success">
|
<tr>
|
||||||
{% trans "Accept" %}
|
<td class="text-right">
|
||||||
</a>
|
<img src="{{ acceptrequest.main_char|character_portrait_url:32 }}" class="img-circle">
|
||||||
<a href="{% url 'groupmanagement:reject_request' acceptrequest.id %}" class="btn btn-danger">
|
</td>
|
||||||
{% trans "Reject" %}
|
<td class="text-center">
|
||||||
</a>
|
{% if acceptrequest.main_char %}
|
||||||
</td>
|
<a href="{{ acceptrequest.main_char|evewho_character_url }}" target="_blank">
|
||||||
</tr>
|
{{ acceptrequest.main_char.character_name }}
|
||||||
{% endfor %}
|
</a>
|
||||||
</table>
|
{% else %}
|
||||||
|
{{ acceptrequest.user.username }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
{% if acceptrequest.main_char %}
|
||||||
|
<a href="{{ acceptrequest.main_char|dotlan_corporation_url }}" target="_blank">
|
||||||
|
{{ acceptrequest.main_char.corporation_name }}
|
||||||
|
</a><br>
|
||||||
|
{{ acceptrequest.main_char.alliance_name|default_if_none:"" }}
|
||||||
|
{% else %}
|
||||||
|
(unknown)
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">{{ acceptrequest.group.name }}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{% url 'groupmanagement:accept_request' acceptrequest.id %}" class="btn btn-success">
|
||||||
|
{% trans "Accept" %}
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'groupmanagement:reject_request' acceptrequest.id %}" class="btn btn-danger">
|
||||||
|
{% trans "Reject" %}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="alert alert-warning text-center">{% trans "No group add requests." %}</div>
|
<div class="alert alert-warning text-center">{% trans "No group add requests." %}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="leave" class="tab-pane fade panel panel-default">
|
<div id="leave" class="tab-pane fade panel panel-default">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% if leaverequests %}
|
{% if leaverequests %}
|
||||||
<table class="table">
|
<div class="table-responsive">
|
||||||
<tr>
|
<table class="table table-aa">
|
||||||
<th class="text-center">{% trans "RequestID" %}</th>
|
<thead>
|
||||||
<th class="text-center">{% trans "CharacterName" %}</th>
|
<tr>
|
||||||
<th class="text-center">{% trans "GroupName" %}</th>
|
<th class="text-center"></th>
|
||||||
<th class="text-center">{% trans "Action" %}</th>
|
<th class="text-center">{% trans "Character" %}</th>
|
||||||
</tr>
|
<th class="text-center">{% trans "Organization" %}</th>
|
||||||
{% for leaverequest in leaverequests %}
|
<th class="text-center">{% trans "Group" %}</th>
|
||||||
<tr>
|
<th class="text-center"></th>
|
||||||
<td class="text-center">{{ leaverequest.id }}</td>
|
</tr>
|
||||||
<td class="text-center">{{ leaverequest.main_char.character_name }}</td>
|
</thead>
|
||||||
<td class="text-center">{{ leaverequest.group.name }}</td>
|
<tbody>
|
||||||
<td class="text-center">
|
{% for leaverequest in leaverequests %}
|
||||||
<a href="{% url 'groupmanagement:leave_accept_request' leaverequest.id %}" class="btn btn-success">
|
<tr>
|
||||||
{% trans "Accept" %}
|
<td class="text-right">
|
||||||
</a>
|
<img src="{{ leaverequest.main_char|character_portrait_url:32 }}" class="img-circle">
|
||||||
<a href="{% url 'groupmanagement:leave_reject_request' leaverequest.id %}" class="btn btn-danger">
|
</td>
|
||||||
{% trans "Reject" %}
|
<td class="text-center">
|
||||||
</a>
|
{% if leaverequest.main_char %}
|
||||||
</td>
|
<a href="{{ leaverequest.main_char|evewho_character_url }}" target="_blank">
|
||||||
</tr>
|
{{ leaverequest.main_char.character_name }}
|
||||||
{% endfor %}
|
</a>
|
||||||
</table>
|
{% else %}
|
||||||
|
{{ leaverequest.user.username }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
{% if leaverequest.main_char %}
|
||||||
|
<a href="{{ leaverequest.main_char|dotlan_corporation_url }}" target="_blank">
|
||||||
|
{{ leaverequest.main_char.corporation_name }}
|
||||||
|
</a><br>
|
||||||
|
{{ leaverequest.main_char.alliance_name|default_if_none:"" }}
|
||||||
|
{% else %}
|
||||||
|
(unknown)
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">{{ leaverequest.group.name }}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{% url 'groupmanagement:leave_accept_request' leaverequest.id %}" class="btn btn-success">
|
||||||
|
{% trans "Accept" %}
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'groupmanagement:leave_reject_request' leaverequest.id %}" class="btn btn-danger">
|
||||||
|
{% trans "Reject" %}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="alert alert-warning text-center">{% trans "No group leave requests." %}</div>
|
<div class="alert alert-warning text-center">{% trans "No group leave requests." %}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -3,25 +3,28 @@
|
|||||||
{% load navactive %}
|
{% load navactive %}
|
||||||
|
|
||||||
<nav class="navbar navbar-default">
|
<nav class="navbar navbar-default">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="navbar-header">
|
|
||||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
|
<div class="navbar-header">
|
||||||
<span class="sr-only">{% trans "Toggle navigation" %}</span>
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
|
||||||
<span class="icon-bar"></span>
|
<span class="sr-only">{% trans "Toggle navigation" %}</span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</button>
|
<span class="icon-bar"></span>
|
||||||
<a class="navbar-brand" href="#">{% trans "Group Management" %}</a>
|
</button>
|
||||||
|
<a class="navbar-brand" href="">{% trans "Group Management" %}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||||
|
<ul class="nav navbar-nav">
|
||||||
|
<li class="{% navactive request 'groupmanagement:management' %}">
|
||||||
|
<a href="{% url 'groupmanagement:management' %}">{% trans "Group Requests" %}</a>
|
||||||
|
</li>
|
||||||
|
<li class="{% renavactive request '^/group/membership/' %}">
|
||||||
|
<a href="{% url 'groupmanagement:membership' %}">{% trans "Group Membership" %}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
|
||||||
<ul class="nav navbar-nav">
|
|
||||||
<li class="{% navactive request 'auth_group_management' %}">
|
|
||||||
<a href="{% url 'groupmanagement:management' %}">{% trans "Group Requests" %}</a>
|
|
||||||
</li>
|
|
||||||
<li class="{% navactive request 'auth_group_membership auth_group_membership_list' %}">
|
|
||||||
<a href="{% url 'groupmanagement:membership' %}">{% trans "Group Membership" %}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
11
allianceauth/groupmanagement/tests/__init__.py
Normal file
11
allianceauth/groupmanagement/tests/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
|
||||||
|
def get_admin_change_view_url(obj: object) -> str:
|
||||||
|
return reverse(
|
||||||
|
'admin:{}_{}_change'.format(
|
||||||
|
obj._meta.app_label,
|
||||||
|
type(obj).__name__.lower()
|
||||||
|
),
|
||||||
|
args=(obj.pk,)
|
||||||
|
)
|
||||||
397
allianceauth/groupmanagement/tests/test_admin.py
Normal file
397
allianceauth/groupmanagement/tests/test_admin.py
Normal file
@@ -0,0 +1,397 @@
|
|||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.admin.sites import AdminSite
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.test import TestCase, RequestFactory, Client
|
||||||
|
|
||||||
|
from allianceauth.authentication.models import CharacterOwnership, State
|
||||||
|
from allianceauth.eveonline.models import (
|
||||||
|
EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
from ..admin import (
|
||||||
|
HasLeaderFilter,
|
||||||
|
GroupAdmin,
|
||||||
|
Group
|
||||||
|
)
|
||||||
|
from . import get_admin_change_view_url
|
||||||
|
|
||||||
|
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
|
||||||
|
_has_auto_groups = True
|
||||||
|
from allianceauth.eveonline.autogroups.models import AutogroupsConfig
|
||||||
|
from ..admin import IsAutoGroupFilter
|
||||||
|
else:
|
||||||
|
_has_auto_groups = False
|
||||||
|
|
||||||
|
|
||||||
|
MODULE_PATH = 'allianceauth.groupmanagement.admin'
|
||||||
|
|
||||||
|
|
||||||
|
class MockRequest(object):
|
||||||
|
|
||||||
|
def __init__(self, user=None):
|
||||||
|
self.user = user
|
||||||
|
|
||||||
|
|
||||||
|
class TestGroupAdmin(TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
|
||||||
|
# group 1 - has leader
|
||||||
|
cls.group_1 = Group.objects.create(name='Group 1')
|
||||||
|
cls.group_1.authgroup.description = 'Default Group'
|
||||||
|
cls.group_1.authgroup.internal = False
|
||||||
|
cls.group_1.authgroup.hidden = False
|
||||||
|
cls.group_1.authgroup.save()
|
||||||
|
|
||||||
|
# group 2 - no leader
|
||||||
|
cls.group_2 = Group.objects.create(name='Group 2')
|
||||||
|
cls.group_2.authgroup.description = 'Internal Group'
|
||||||
|
cls.group_2.authgroup.internal = True
|
||||||
|
cls.group_2.authgroup.save()
|
||||||
|
|
||||||
|
# group 3 - has leader
|
||||||
|
cls.group_3 = Group.objects.create(name='Group 3')
|
||||||
|
cls.group_3.authgroup.description = 'Hidden Group'
|
||||||
|
cls.group_3.authgroup.internal = False
|
||||||
|
cls.group_3.authgroup.hidden = True
|
||||||
|
cls.group_3.authgroup.save()
|
||||||
|
|
||||||
|
# group 4 - no leader
|
||||||
|
cls.group_4 = Group.objects.create(name='Group 4')
|
||||||
|
cls.group_4.authgroup.description = 'Open Group'
|
||||||
|
cls.group_4.authgroup.internal = False
|
||||||
|
cls.group_4.authgroup.hidden = False
|
||||||
|
cls.group_4.authgroup.open = True
|
||||||
|
cls.group_4.authgroup.save()
|
||||||
|
|
||||||
|
# group 5 - no leader
|
||||||
|
cls.group_5 = Group.objects.create(name='Group 5')
|
||||||
|
cls.group_5.authgroup.description = 'Public Group'
|
||||||
|
cls.group_5.authgroup.internal = False
|
||||||
|
cls.group_5.authgroup.hidden = False
|
||||||
|
cls.group_5.authgroup.public = True
|
||||||
|
cls.group_5.authgroup.save()
|
||||||
|
|
||||||
|
# group 6 - no leader
|
||||||
|
cls.group_6 = Group.objects.create(name='Group 6')
|
||||||
|
cls.group_6.authgroup.description = 'Mixed Group'
|
||||||
|
cls.group_6.authgroup.internal = False
|
||||||
|
cls.group_6.authgroup.hidden = True
|
||||||
|
cls.group_6.authgroup.open = True
|
||||||
|
cls.group_6.authgroup.public = True
|
||||||
|
cls.group_6.authgroup.save()
|
||||||
|
|
||||||
|
# user 1 - corp and alliance, normal user
|
||||||
|
cls.character_1 = EveCharacter.objects.create(
|
||||||
|
character_id='1001',
|
||||||
|
character_name='Bruce Wayne',
|
||||||
|
corporation_id='2001',
|
||||||
|
corporation_name='Wayne Technologies',
|
||||||
|
corporation_ticker='WT',
|
||||||
|
alliance_id='3001',
|
||||||
|
alliance_name='Wayne Enterprises',
|
||||||
|
alliance_ticker='WE',
|
||||||
|
)
|
||||||
|
cls.character_1a = EveCharacter.objects.create(
|
||||||
|
character_id='1002',
|
||||||
|
character_name='Batman',
|
||||||
|
corporation_id='2001',
|
||||||
|
corporation_name='Wayne Technologies',
|
||||||
|
corporation_ticker='WT',
|
||||||
|
alliance_id='3001',
|
||||||
|
alliance_name='Wayne Enterprises',
|
||||||
|
alliance_ticker='WE',
|
||||||
|
)
|
||||||
|
alliance = EveAllianceInfo.objects.create(
|
||||||
|
alliance_id='3001',
|
||||||
|
alliance_name='Wayne Enterprises',
|
||||||
|
alliance_ticker='WE',
|
||||||
|
executor_corp_id='2001'
|
||||||
|
)
|
||||||
|
EveCorporationInfo.objects.create(
|
||||||
|
corporation_id='2001',
|
||||||
|
corporation_name='Wayne Technologies',
|
||||||
|
corporation_ticker='WT',
|
||||||
|
member_count=42,
|
||||||
|
alliance=alliance
|
||||||
|
)
|
||||||
|
cls.user_1 = User.objects.create_user(
|
||||||
|
cls.character_1.character_name.replace(' ', '_'),
|
||||||
|
'abc@example.com',
|
||||||
|
'password'
|
||||||
|
)
|
||||||
|
CharacterOwnership.objects.create(
|
||||||
|
character=cls.character_1,
|
||||||
|
owner_hash='x1' + cls.character_1.character_name,
|
||||||
|
user=cls.user_1
|
||||||
|
)
|
||||||
|
CharacterOwnership.objects.create(
|
||||||
|
character=cls.character_1a,
|
||||||
|
owner_hash='x1' + cls.character_1a.character_name,
|
||||||
|
user=cls.user_1
|
||||||
|
)
|
||||||
|
cls.user_1.profile.main_character = cls.character_1
|
||||||
|
cls.user_1.profile.save()
|
||||||
|
cls.user_1.groups.add(cls.group_1)
|
||||||
|
cls.group_1.authgroup.group_leaders.add(cls.user_1)
|
||||||
|
|
||||||
|
# user 2 - corp only, staff
|
||||||
|
cls.character_2 = EveCharacter.objects.create(
|
||||||
|
character_id=1003,
|
||||||
|
character_name='Clark Kent',
|
||||||
|
corporation_id=2002,
|
||||||
|
corporation_name='Daily Planet',
|
||||||
|
corporation_ticker='DP',
|
||||||
|
alliance_id=None
|
||||||
|
)
|
||||||
|
EveCorporationInfo.objects.create(
|
||||||
|
corporation_id=2002,
|
||||||
|
corporation_name='Daily Plane',
|
||||||
|
corporation_ticker='DP',
|
||||||
|
member_count=99,
|
||||||
|
alliance=None
|
||||||
|
)
|
||||||
|
cls.user_2 = User.objects.create_user(
|
||||||
|
cls.character_2.character_name.replace(' ', '_'),
|
||||||
|
'abc@example.com',
|
||||||
|
'password'
|
||||||
|
)
|
||||||
|
CharacterOwnership.objects.create(
|
||||||
|
character=cls.character_2,
|
||||||
|
owner_hash='x1' + cls.character_2.character_name,
|
||||||
|
user=cls.user_2
|
||||||
|
)
|
||||||
|
cls.user_2.profile.main_character = cls.character_2
|
||||||
|
cls.user_2.profile.save()
|
||||||
|
cls.user_2.groups.add(cls.group_2)
|
||||||
|
cls.user_2.is_staff = True
|
||||||
|
cls.user_2.save()
|
||||||
|
|
||||||
|
# user 3 - no main, no group, superuser
|
||||||
|
cls.character_3 = EveCharacter.objects.create(
|
||||||
|
character_id=1101,
|
||||||
|
character_name='Lex Luthor',
|
||||||
|
corporation_id=2101,
|
||||||
|
corporation_name='Lex Corp',
|
||||||
|
corporation_ticker='LC',
|
||||||
|
alliance_id=None
|
||||||
|
)
|
||||||
|
EveCorporationInfo.objects.create(
|
||||||
|
corporation_id=2101,
|
||||||
|
corporation_name='Lex Corp',
|
||||||
|
corporation_ticker='LC',
|
||||||
|
member_count=666,
|
||||||
|
alliance=None
|
||||||
|
)
|
||||||
|
EveAllianceInfo.objects.create(
|
||||||
|
alliance_id='3101',
|
||||||
|
alliance_name='Lex World Domination',
|
||||||
|
alliance_ticker='LWD',
|
||||||
|
executor_corp_id=''
|
||||||
|
)
|
||||||
|
cls.user_3 = User.objects.create_user(
|
||||||
|
cls.character_3.character_name.replace(' ', '_'),
|
||||||
|
'abc@example.com',
|
||||||
|
'password'
|
||||||
|
)
|
||||||
|
CharacterOwnership.objects.create(
|
||||||
|
character=cls.character_3,
|
||||||
|
owner_hash='x1' + cls.character_3.character_name,
|
||||||
|
user=cls.user_3
|
||||||
|
)
|
||||||
|
cls.user_3.is_superuser = True
|
||||||
|
cls.user_3.save()
|
||||||
|
cls.user_3.groups.add(cls.group_3)
|
||||||
|
cls.group_3.authgroup.group_leaders.add(cls.user_3)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.factory = RequestFactory()
|
||||||
|
self.modeladmin = GroupAdmin(
|
||||||
|
model=Group, admin_site=AdminSite()
|
||||||
|
)
|
||||||
|
|
||||||
|
def _create_autogroups(self):
|
||||||
|
"""create autogroups for corps and alliances"""
|
||||||
|
if _has_auto_groups:
|
||||||
|
autogroups_config = AutogroupsConfig(
|
||||||
|
corp_groups = True,
|
||||||
|
alliance_groups = True
|
||||||
|
)
|
||||||
|
autogroups_config.save()
|
||||||
|
for state in State.objects.all():
|
||||||
|
autogroups_config.states.add(state)
|
||||||
|
autogroups_config.update_corp_group_membership(self.user_1)
|
||||||
|
|
||||||
|
# column rendering
|
||||||
|
|
||||||
|
def test_description(self):
|
||||||
|
expected = 'Default Group'
|
||||||
|
result = self.modeladmin._description(self.group_1)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_member_count(self):
|
||||||
|
expected = 1
|
||||||
|
obj = self.modeladmin.get_queryset(MockRequest(user=self.user_1))\
|
||||||
|
.get(pk=self.group_1.pk)
|
||||||
|
result = self.modeladmin._member_count(obj)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_has_leader(self):
|
||||||
|
result = self.modeladmin.has_leader(self.group_1)
|
||||||
|
self.assertTrue(result)
|
||||||
|
|
||||||
|
def test_properties_1(self):
|
||||||
|
expected = ['Default']
|
||||||
|
result = self.modeladmin._properties(self.group_1)
|
||||||
|
self.assertListEqual(result, expected)
|
||||||
|
|
||||||
|
def test_properties_2(self):
|
||||||
|
expected = ['Internal']
|
||||||
|
result = self.modeladmin._properties(self.group_2)
|
||||||
|
self.assertListEqual(result, expected)
|
||||||
|
|
||||||
|
def test_properties_3(self):
|
||||||
|
expected = ['Hidden']
|
||||||
|
result = self.modeladmin._properties(self.group_3)
|
||||||
|
self.assertListEqual(result, expected)
|
||||||
|
|
||||||
|
def test_properties_4(self):
|
||||||
|
expected = ['Open']
|
||||||
|
result = self.modeladmin._properties(self.group_4)
|
||||||
|
self.assertListEqual(result, expected)
|
||||||
|
|
||||||
|
def test_properties_5(self):
|
||||||
|
expected = ['Public']
|
||||||
|
result = self.modeladmin._properties(self.group_5)
|
||||||
|
self.assertListEqual(result, expected)
|
||||||
|
|
||||||
|
def test_properties_6(self):
|
||||||
|
expected = ['Hidden', 'Open', 'Public']
|
||||||
|
result = self.modeladmin._properties(self.group_6)
|
||||||
|
self.assertListEqual(result, expected)
|
||||||
|
|
||||||
|
if _has_auto_groups:
|
||||||
|
@patch(MODULE_PATH + '._has_auto_groups', True)
|
||||||
|
def test_properties_6(self):
|
||||||
|
self._create_autogroups()
|
||||||
|
expected = ['Auto Group']
|
||||||
|
my_group = Group.objects\
|
||||||
|
.filter(managedcorpgroup__isnull=False)\
|
||||||
|
.first()
|
||||||
|
result = self.modeladmin._properties(my_group)
|
||||||
|
self.assertListEqual(result, expected)
|
||||||
|
|
||||||
|
# actions
|
||||||
|
|
||||||
|
# filters
|
||||||
|
|
||||||
|
if _has_auto_groups:
|
||||||
|
@patch(MODULE_PATH + '._has_auto_groups', True)
|
||||||
|
def test_filter_is_auto_group(self):
|
||||||
|
|
||||||
|
class GroupAdminTest(admin.ModelAdmin):
|
||||||
|
list_filter = (IsAutoGroupFilter,)
|
||||||
|
|
||||||
|
self._create_autogroups()
|
||||||
|
my_modeladmin = GroupAdminTest(Group, AdminSite())
|
||||||
|
|
||||||
|
# Make sure the lookups are correct
|
||||||
|
request = self.factory.get('/')
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
filters = changelist.get_filters(request)
|
||||||
|
filterspec = filters[0][0]
|
||||||
|
expected = [
|
||||||
|
('yes', 'Yes'),
|
||||||
|
('no', 'No'),
|
||||||
|
]
|
||||||
|
self.assertEqual(filterspec.lookup_choices, expected)
|
||||||
|
|
||||||
|
# Make sure the correct queryset is returned - no
|
||||||
|
request = self.factory.get(
|
||||||
|
'/', {'is_auto_group__exact': 'no'}
|
||||||
|
)
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
queryset = changelist.get_queryset(request)
|
||||||
|
expected = [
|
||||||
|
self.group_1,
|
||||||
|
self.group_2,
|
||||||
|
self.group_3,
|
||||||
|
self.group_4,
|
||||||
|
self.group_5,
|
||||||
|
self.group_6
|
||||||
|
]
|
||||||
|
self.assertSetEqual(set(queryset), set(expected))
|
||||||
|
|
||||||
|
# Make sure the correct queryset is returned - yes
|
||||||
|
request = self.factory.get(
|
||||||
|
'/', {'is_auto_group__exact': 'yes'}
|
||||||
|
)
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
queryset = changelist.get_queryset(request)
|
||||||
|
expected = Group.objects.exclude(
|
||||||
|
managedalliancegroup__isnull=True,
|
||||||
|
managedcorpgroup__isnull=True
|
||||||
|
)
|
||||||
|
self.assertSetEqual(set(queryset), set(expected))
|
||||||
|
|
||||||
|
def test_filter_has_leader(self):
|
||||||
|
|
||||||
|
class GroupAdminTest(admin.ModelAdmin):
|
||||||
|
list_filter = (HasLeaderFilter,)
|
||||||
|
|
||||||
|
self._create_autogroups()
|
||||||
|
my_modeladmin = GroupAdminTest(Group, AdminSite())
|
||||||
|
|
||||||
|
# Make sure the lookups are correct
|
||||||
|
request = self.factory.get('/')
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
filters = changelist.get_filters(request)
|
||||||
|
filterspec = filters[0][0]
|
||||||
|
expected = [
|
||||||
|
('yes', 'Yes'),
|
||||||
|
('no', 'No'),
|
||||||
|
]
|
||||||
|
self.assertEqual(filterspec.lookup_choices, expected)
|
||||||
|
|
||||||
|
# Make sure the correct queryset is returned - no
|
||||||
|
request = self.factory.get(
|
||||||
|
'/', {'has_leader__exact': 'no'}
|
||||||
|
)
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
queryset = changelist.get_queryset(request)
|
||||||
|
expected = Group.objects.exclude(pk__in=[
|
||||||
|
self.group_1.pk, self.group_3.pk
|
||||||
|
])
|
||||||
|
self.assertSetEqual(set(queryset), set(expected))
|
||||||
|
|
||||||
|
# Make sure the correct queryset is returned - yes
|
||||||
|
request = self.factory.get(
|
||||||
|
'/', {'has_leader__exact': 'yes'}
|
||||||
|
)
|
||||||
|
request.user = self.user_1
|
||||||
|
changelist = my_modeladmin.get_changelist_instance(request)
|
||||||
|
queryset = changelist.get_queryset(request)
|
||||||
|
expected = [
|
||||||
|
self.group_1,
|
||||||
|
self.group_3
|
||||||
|
]
|
||||||
|
self.assertSetEqual(set(queryset), set(expected))
|
||||||
|
|
||||||
|
def test_change_view_loads_normally(self):
|
||||||
|
User.objects.create_superuser(
|
||||||
|
username='superuser', password='secret', email='admin@example.com'
|
||||||
|
)
|
||||||
|
c = Client()
|
||||||
|
c.login(username='superuser', password='secret')
|
||||||
|
response = c.get(get_admin_change_view_url(self.group_1))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
107
allianceauth/groupmanagement/tests/test_all.py
Normal file
107
allianceauth/groupmanagement/tests/test_all.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
|
from allianceauth.eveonline.models import EveCorporationInfo, EveAllianceInfo, EveCharacter
|
||||||
|
from django.contrib.auth.models import User, Group
|
||||||
|
from allianceauth.groupmanagement.managers import GroupManager
|
||||||
|
from allianceauth.groupmanagement.signals import check_groups_on_state_change
|
||||||
|
|
||||||
|
class GroupManagementVisibilityTestCase(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.user = AuthUtils.create_user('test')
|
||||||
|
AuthUtils.add_main_character(cls.user, 'test character', '1', corp_id='2', corp_name='test_corp', corp_ticker='TEST', alliance_id='3', alliance_name='TEST')
|
||||||
|
cls.user.profile.refresh_from_db()
|
||||||
|
cls.alliance = EveAllianceInfo.objects.create(alliance_id='3', alliance_name='test alliance', alliance_ticker='TEST', executor_corp_id='2')
|
||||||
|
cls.corp = EveCorporationInfo.objects.create(corporation_id='2', corporation_name='test corp', corporation_ticker='TEST', alliance=cls.alliance, member_count=1)
|
||||||
|
cls.group1 = Group.objects.create(name='group1')
|
||||||
|
cls.group2 = Group.objects.create(name='group2')
|
||||||
|
cls.group3 = Group.objects.create(name='group3')
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.user.refresh_from_db()
|
||||||
|
|
||||||
|
def _refresh_user(self):
|
||||||
|
self.user = User.objects.get(pk=self.user.pk)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_group_leaders_groups(self):
|
||||||
|
self.group1.authgroup.group_leaders.add(self.user)
|
||||||
|
self.group2.authgroup.group_leader_groups.add(self.group1)
|
||||||
|
self._refresh_user()
|
||||||
|
groups = GroupManager.get_group_leaders_groups(self.user)
|
||||||
|
|
||||||
|
self.assertIn(self.group1, groups) #avail due to user
|
||||||
|
self.assertNotIn(self.group2, groups) #not avail due to group
|
||||||
|
self.assertNotIn(self.group3, groups) #not avail at all
|
||||||
|
|
||||||
|
self.user.groups.add(self.group1)
|
||||||
|
self._refresh_user()
|
||||||
|
groups = GroupManager.get_group_leaders_groups(self.user)
|
||||||
|
|
||||||
|
self.assertIn(self.group1, groups) #avail due to user
|
||||||
|
self.assertIn(self.group2, groups) #avail due to group1
|
||||||
|
self.assertNotIn(self.group3, groups) #not avail at all
|
||||||
|
|
||||||
|
def test_can_manage_group(self):
|
||||||
|
self.group1.authgroup.group_leaders.add(self.user)
|
||||||
|
self.user.groups.add(self.group1)
|
||||||
|
self._refresh_user()
|
||||||
|
|
||||||
|
self.assertTrue(GroupManager.can_manage_group(self.user, self.group1))
|
||||||
|
self.assertFalse(GroupManager.can_manage_group(self.user, self.group2))
|
||||||
|
self.assertFalse(GroupManager.can_manage_group(self.user, self.group3))
|
||||||
|
|
||||||
|
self.group2.authgroup.group_leader_groups.add(self.group1)
|
||||||
|
self.group1.authgroup.group_leaders.remove(self.user)
|
||||||
|
self._refresh_user()
|
||||||
|
|
||||||
|
self.assertFalse(GroupManager.can_manage_group(self.user, self.group1))
|
||||||
|
self.assertTrue(GroupManager.can_manage_group(self.user, self.group2))
|
||||||
|
self.assertFalse(GroupManager.can_manage_group(self.user, self.group3))
|
||||||
|
|
||||||
|
|
||||||
|
class GroupManagementStateTestCase(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.user = AuthUtils.create_user('test')
|
||||||
|
AuthUtils.add_main_character(cls.user, 'test character', '1', corp_id='2', corp_name='test_corp', corp_ticker='TEST', alliance_id='3', alliance_name='TEST')
|
||||||
|
cls.user.profile.refresh_from_db()
|
||||||
|
cls.alliance = EveAllianceInfo.objects.create(alliance_id='3', alliance_name='test alliance', alliance_ticker='TEST', executor_corp_id='2')
|
||||||
|
cls.corp = EveCorporationInfo.objects.create(corporation_id='2', corporation_name='test corp', corporation_ticker='TEST', alliance=cls.alliance, member_count=1)
|
||||||
|
cls.state_group = Group.objects.create(name='state_group')
|
||||||
|
cls.open_group = Group.objects.create(name='open_group')
|
||||||
|
cls.state = AuthUtils.create_state('test state', 500)
|
||||||
|
cls.state_group.authgroup.states.add(cls.state)
|
||||||
|
cls.state_group.authgroup.internal = False
|
||||||
|
cls.state_group.save()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.user.refresh_from_db()
|
||||||
|
self.state.refresh_from_db()
|
||||||
|
|
||||||
|
def _refresh_user(self):
|
||||||
|
self.user = User.objects.get(pk=self.user.pk)
|
||||||
|
|
||||||
|
def _refresh_test_group(self):
|
||||||
|
self.state_group = Group.objects.get(pk=self.state_group.pk)
|
||||||
|
|
||||||
|
def test_drop_state_group(self):
|
||||||
|
self.user.groups.add(self.open_group)
|
||||||
|
self.user.groups.add(self.state_group)
|
||||||
|
self.assertEqual(self.user.profile.state.name, "Guest")
|
||||||
|
|
||||||
|
self.state.member_corporations.add(self.corp)
|
||||||
|
self._refresh_user()
|
||||||
|
self.assertEqual(self.user.profile.state, self.state)
|
||||||
|
groups = self.user.groups.all()
|
||||||
|
self.assertIn(self.state_group, groups) #keeps group
|
||||||
|
self.assertIn(self.open_group, groups) #public group unafected
|
||||||
|
|
||||||
|
self.state.member_corporations.clear()
|
||||||
|
self._refresh_user()
|
||||||
|
self.assertEqual(self.user.profile.state.name, "Guest")
|
||||||
|
groups = self.user.groups.all()
|
||||||
|
self.assertNotIn(self.state_group, groups) #looses group
|
||||||
|
self.assertIn(self.open_group, groups) #public group unafected
|
||||||
@@ -12,6 +12,7 @@ urlpatterns = [
|
|||||||
name='membership'),
|
name='membership'),
|
||||||
url(r'^membership/(\w+)/$', views.group_membership_list,
|
url(r'^membership/(\w+)/$', views.group_membership_list,
|
||||||
name='membership_list'),
|
name='membership_list'),
|
||||||
|
url(r'^membership/(\w+)/audit/$', views.group_membership_audit, name="audit_log"),
|
||||||
url(r'^membership/(\w+)/remove/(\w+)/$', views.group_membership_remove,
|
url(r'^membership/(\w+)/remove/(\w+)/$', views.group_membership_remove,
|
||||||
name='membership_remove'),
|
name='membership_remove'),
|
||||||
url(r'^request_add/(\w+)', views.group_request_add,
|
url(r'^request_add/(\w+)', views.group_request_add,
|
||||||
|
|||||||
@@ -5,15 +5,18 @@ from django.contrib.auth.decorators import login_required
|
|||||||
from django.contrib.auth.decorators import user_passes_test
|
from django.contrib.auth.decorators import user_passes_test
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
|
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
|
||||||
|
from django.core.paginator import Paginator, EmptyPage
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from .managers import GroupManager
|
from .managers import GroupManager
|
||||||
from .models import GroupRequest
|
from .models import GroupRequest, RequestLog
|
||||||
|
|
||||||
from allianceauth.notifications import notify
|
from allianceauth.notifications import notify
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -30,7 +33,8 @@ def group_management(request):
|
|||||||
group_requests = base_group_query.all()
|
group_requests = base_group_query.all()
|
||||||
else:
|
else:
|
||||||
# Group specific leader
|
# Group specific leader
|
||||||
group_requests = base_group_query.filter(group__authgroup__group_leaders__in=[request.user])
|
users__groups = GroupManager.get_group_leaders_groups(request.user)
|
||||||
|
group_requests = base_group_query.filter(group__in=users__groups)
|
||||||
|
|
||||||
for grouprequest in group_requests:
|
for grouprequest in group_requests:
|
||||||
if grouprequest.leave_request:
|
if grouprequest.leave_request:
|
||||||
@@ -53,7 +57,7 @@ def group_membership(request):
|
|||||||
# Get all open and closed groups
|
# Get all open and closed groups
|
||||||
if GroupManager.has_management_permission(request.user):
|
if GroupManager.has_management_permission(request.user):
|
||||||
# Full access
|
# Full access
|
||||||
groups = GroupManager.get_joinable_groups()
|
groups = GroupManager.get_all_non_internal_groups()
|
||||||
else:
|
else:
|
||||||
# Group leader specific
|
# Group leader specific
|
||||||
groups = GroupManager.get_group_leaders_groups(request.user)
|
groups = GroupManager.get_group_leaders_groups(request.user)
|
||||||
@@ -67,33 +71,73 @@ def group_membership(request):
|
|||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@user_passes_test(GroupManager.can_manage_groups)
|
@user_passes_test(GroupManager.can_manage_groups)
|
||||||
def group_membership_list(request, group_id):
|
def group_membership_audit(request, group_id):
|
||||||
logger.debug("group_membership_list called by user %s for group id %s" % (request.user, group_id))
|
logger.debug("group_management_audit called by user %s" % request.user)
|
||||||
group = get_object_or_404(Group, id=group_id)
|
group = get_object_or_404(Group, id=group_id)
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# Check its a joinable group i.e. not corp or internal
|
# Check its a joinable group i.e. not corp or internal
|
||||||
# And the user has permission to manage it
|
# And the user has permission to manage it
|
||||||
if not GroupManager.joinable_group(group) or not GroupManager.can_manage_group(request.user, group):
|
if not GroupManager.check_internal_group(group) or not GroupManager.can_manage_group(request.user, group):
|
||||||
logger.warning("User %s attempted to view the membership of group %s but permission was denied" %
|
logger.warning("User %s attempted to view the membership of group %s but permission was denied" %
|
||||||
(request.user, group_id))
|
(request.user, group_id))
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
raise Http404("Group does not exist")
|
raise Http404("Group does not exist")
|
||||||
|
render_items = {'group': group.name}
|
||||||
|
entries = RequestLog.objects.filter(group=group).order_by('-date')
|
||||||
|
render_items['entries'] = entries
|
||||||
|
|
||||||
|
return render(request, 'groupmanagement/audit.html', context=render_items)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(GroupManager.can_manage_groups)
|
||||||
|
def group_membership_list(request, group_id):
|
||||||
|
logger.debug(
|
||||||
|
"group_membership_list called by user %s "
|
||||||
|
"for group id %s" % (request.user, group_id)
|
||||||
|
)
|
||||||
|
group = get_object_or_404(Group, id=group_id)
|
||||||
|
try:
|
||||||
|
|
||||||
|
# Check its a joinable group i.e. not corp or internal
|
||||||
|
# And the user has permission to manage it
|
||||||
|
if (not GroupManager.check_internal_group(group)
|
||||||
|
or not GroupManager.can_manage_group(request.user, group)
|
||||||
|
):
|
||||||
|
logger.warning(
|
||||||
|
"User %s attempted to view the membership of group %s "
|
||||||
|
"but permission was denied" % (request.user, group_id)
|
||||||
|
)
|
||||||
|
raise PermissionDenied
|
||||||
|
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
raise Http404("Group does not exist")
|
||||||
|
|
||||||
|
group_leaders = group.authgroup.group_leaders.all()
|
||||||
members = list()
|
members = list()
|
||||||
|
for member in \
|
||||||
for member in group.user_set.select_related('profile').all().order_by('username'):
|
group.user_set\
|
||||||
|
.all()\
|
||||||
|
.select_related('profile')\
|
||||||
|
.order_by('profile__main_character__character_name'):
|
||||||
|
|
||||||
members.append({
|
members.append({
|
||||||
'user': member,
|
'user': member,
|
||||||
'main_char': member.profile.main_character
|
'main_char': member.profile.main_character,
|
||||||
|
'is_leader': member in group_leaders
|
||||||
})
|
})
|
||||||
|
|
||||||
render_items = {'group': group, 'members': members}
|
render_items = {'group': group, 'members': members}
|
||||||
|
|
||||||
return render(request, 'groupmanagement/groupmembers.html', context=render_items)
|
return render(
|
||||||
|
request, 'groupmanagement/groupmembers.html',
|
||||||
|
context=render_items
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@@ -105,13 +149,16 @@ def group_membership_remove(request, group_id, user_id):
|
|||||||
try:
|
try:
|
||||||
# Check its a joinable group i.e. not corp or internal
|
# Check its a joinable group i.e. not corp or internal
|
||||||
# And the user has permission to manage it
|
# And the user has permission to manage it
|
||||||
if not GroupManager.joinable_group(group) or not GroupManager.can_manage_group(request.user, group):
|
if not GroupManager.check_internal_group(group) or not GroupManager.can_manage_group(request.user, group):
|
||||||
logger.warning("User %s attempted to remove a user from group %s but permission was denied" % (request.user,
|
logger.warning("User %s attempted to remove a user from group %s but permission was denied" % (request.user,
|
||||||
group_id))
|
group_id))
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user = group.user_set.get(id=user_id)
|
user = group.user_set.get(id=user_id)
|
||||||
|
request_info = user.username + ":" + group.name
|
||||||
|
log = RequestLog(request_type=None,group=group,request_info=request_info,action=1,request_actor=request.user)
|
||||||
|
log.save()
|
||||||
# Remove group from user
|
# Remove group from user
|
||||||
user.groups.remove(group)
|
user.groups.remove(group)
|
||||||
logger.info("User %s removed user %s from group %s" % (request.user, user, group))
|
logger.info("User %s removed user %s from group %s" % (request.user, user, group))
|
||||||
@@ -133,12 +180,14 @@ def group_accept_request(request, group_request_id):
|
|||||||
try:
|
try:
|
||||||
group, created = Group.objects.get_or_create(name=group_request.group.name)
|
group, created = Group.objects.get_or_create(name=group_request.group.name)
|
||||||
|
|
||||||
if not GroupManager.joinable_group(group_request.group) or \
|
if not GroupManager.joinable_group(group_request.group, group_request.user.profile.state) or \
|
||||||
not GroupManager.can_manage_group(request.user, group_request.group):
|
not GroupManager.can_manage_group(request.user, group_request.group):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
group_request.user.groups.add(group)
|
group_request.user.groups.add(group)
|
||||||
group_request.user.save()
|
group_request.user.save()
|
||||||
|
log = RequestLog(request_type=group_request.leave_request,group=group,request_info=group_request.__str__(),action=1,request_actor=request.user)
|
||||||
|
log.save()
|
||||||
group_request.delete()
|
group_request.delete()
|
||||||
logger.info("User %s accepted group request from user %s to group %s" % (
|
logger.info("User %s accepted group request from user %s to group %s" % (
|
||||||
request.user, group_request.user, group_request.group.name))
|
request.user, group_request.user, group_request.group.name))
|
||||||
@@ -172,6 +221,8 @@ def group_reject_request(request, group_request_id):
|
|||||||
if group_request:
|
if group_request:
|
||||||
logger.info("User %s rejected group request from user %s to group %s" % (
|
logger.info("User %s rejected group request from user %s to group %s" % (
|
||||||
request.user, group_request.user, group_request.group.name))
|
request.user, group_request.user, group_request.group.name))
|
||||||
|
log = RequestLog(request_type=group_request.leave_request,group=group_request.group,request_info=group_request.__str__(),action=0,request_actor=request.user)
|
||||||
|
log.save()
|
||||||
group_request.delete()
|
group_request.delete()
|
||||||
notify(group_request.user, "Group Application Rejected", level="danger",
|
notify(group_request.user, "Group Application Rejected", level="danger",
|
||||||
message="Your application to %s has been rejected." % group_request.group)
|
message="Your application to %s has been rejected." % group_request.group)
|
||||||
@@ -204,6 +255,8 @@ def group_leave_accept_request(request, group_request_id):
|
|||||||
group, created = Group.objects.get_or_create(name=group_request.group.name)
|
group, created = Group.objects.get_or_create(name=group_request.group.name)
|
||||||
group_request.user.groups.remove(group)
|
group_request.user.groups.remove(group)
|
||||||
group_request.user.save()
|
group_request.user.save()
|
||||||
|
log = RequestLog(request_type=group_request.leave_request,group=group_request.group,request_info=group_request.__str__(),action=1,request_actor=request.user)
|
||||||
|
log.save()
|
||||||
group_request.delete()
|
group_request.delete()
|
||||||
logger.info("User %s accepted group leave request from user %s to group %s" % (
|
logger.info("User %s accepted group leave request from user %s to group %s" % (
|
||||||
request.user, group_request.user, group_request.group.name))
|
request.user, group_request.user, group_request.group.name))
|
||||||
@@ -236,6 +289,8 @@ def group_leave_reject_request(request, group_request_id):
|
|||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
if group_request:
|
if group_request:
|
||||||
|
log = RequestLog(request_type=group_request.leave_request,group=group_request.group,request_info=group_request.__str__(),action=0,request_actor=request.user)
|
||||||
|
log.save()
|
||||||
group_request.delete()
|
group_request.delete()
|
||||||
logger.info("User %s rejected group leave request from user %s for group %s" % (
|
logger.info("User %s rejected group leave request from user %s for group %s" % (
|
||||||
request.user, group_request.user, group_request.group.name))
|
request.user, group_request.user, group_request.group.name))
|
||||||
@@ -262,7 +317,7 @@ def groups_view(request):
|
|||||||
logger.debug("groups_view called by user %s" % request.user)
|
logger.debug("groups_view called by user %s" % request.user)
|
||||||
groups = []
|
groups = []
|
||||||
|
|
||||||
group_query = GroupManager.get_joinable_groups()
|
group_query = GroupManager.get_joinable_groups(request.user.profile.state)
|
||||||
|
|
||||||
if not request.user.has_perm('groupmanagement.request_groups'):
|
if not request.user.has_perm('groupmanagement.request_groups'):
|
||||||
# Filter down to public groups only for non-members
|
# Filter down to public groups only for non-members
|
||||||
@@ -284,11 +339,18 @@ def groups_view(request):
|
|||||||
def group_request_add(request, group_id):
|
def group_request_add(request, group_id):
|
||||||
logger.debug("group_request_add called by user %s for group id %s" % (request.user, group_id))
|
logger.debug("group_request_add called by user %s for group id %s" % (request.user, group_id))
|
||||||
group = Group.objects.get(id=group_id)
|
group = Group.objects.get(id=group_id)
|
||||||
if not GroupManager.joinable_group(group):
|
state = request.user.profile.state
|
||||||
|
if not GroupManager.joinable_group(group, state):
|
||||||
logger.warning("User %s attempted to join group id %s but it is not a joinable group" %
|
logger.warning("User %s attempted to join group id %s but it is not a joinable group" %
|
||||||
(request.user, group_id))
|
(request.user, group_id))
|
||||||
messages.warning(request, _("You cannot join that group"))
|
messages.warning(request, _("You cannot join that group"))
|
||||||
return redirect('groupmanagement:groups')
|
return redirect('groupmanagement:groups')
|
||||||
|
if group in request.user.groups.all():
|
||||||
|
# User is already a member of this group.
|
||||||
|
logger.warning("User %s attempted to join group id %s but they are already a member." %
|
||||||
|
(request.user, group_id))
|
||||||
|
messages.warning(request, "You are already a member of that group.")
|
||||||
|
return redirect('groupmanagement:groups')
|
||||||
if not request.user.has_perm('groupmanagement.request_groups') and not group.authgroup.public:
|
if not request.user.has_perm('groupmanagement.request_groups') and not group.authgroup.public:
|
||||||
# Does not have the required permission, trying to join a non-public group
|
# Does not have the required permission, trying to join a non-public group
|
||||||
logger.warning("User %s attempted to join group id %s but it is not a public group" %
|
logger.warning("User %s attempted to join group id %s but it is not a public group" %
|
||||||
@@ -299,6 +361,11 @@ def group_request_add(request, group_id):
|
|||||||
logger.info("%s joining %s as is an open group" % (request.user, group))
|
logger.info("%s joining %s as is an open group" % (request.user, group))
|
||||||
request.user.groups.add(group)
|
request.user.groups.add(group)
|
||||||
return redirect("groupmanagement:groups")
|
return redirect("groupmanagement:groups")
|
||||||
|
req = GroupRequest.objects.filter(user=request.user, group=group)
|
||||||
|
if len(req) > 0:
|
||||||
|
logger.info("%s attempted to join %s but already has an open application" % (request.user, group))
|
||||||
|
messages.warning(request, "You already have a pending application for that group.")
|
||||||
|
return redirect("groupmanagement:groups")
|
||||||
grouprequest = GroupRequest()
|
grouprequest = GroupRequest()
|
||||||
grouprequest.status = _('Pending')
|
grouprequest.status = _('Pending')
|
||||||
grouprequest.group = group
|
grouprequest.group = group
|
||||||
@@ -314,7 +381,7 @@ def group_request_add(request, group_id):
|
|||||||
def group_request_leave(request, group_id):
|
def group_request_leave(request, group_id):
|
||||||
logger.debug("group_request_leave called by user %s for group id %s" % (request.user, group_id))
|
logger.debug("group_request_leave called by user %s for group id %s" % (request.user, group_id))
|
||||||
group = Group.objects.get(id=group_id)
|
group = Group.objects.get(id=group_id)
|
||||||
if not GroupManager.joinable_group(group):
|
if not GroupManager.check_internal_group(group):
|
||||||
logger.warning("User %s attempted to leave group id %s but it is not a joinable group" %
|
logger.warning("User %s attempted to leave group id %s but it is not a joinable group" %
|
||||||
(request.user, group_id))
|
(request.user, group_id))
|
||||||
messages.warning(request, _("You cannot leave that group"))
|
messages.warning(request, _("You cannot leave that group"))
|
||||||
@@ -328,6 +395,15 @@ def group_request_leave(request, group_id):
|
|||||||
logger.info("%s leaving %s as is an open group" % (request.user, group))
|
logger.info("%s leaving %s as is an open group" % (request.user, group))
|
||||||
request.user.groups.remove(group)
|
request.user.groups.remove(group)
|
||||||
return redirect("groupmanagement:groups")
|
return redirect("groupmanagement:groups")
|
||||||
|
req = GroupRequest.objects.filter(user=request.user, group=group)
|
||||||
|
if len(req) > 0:
|
||||||
|
logger.info("%s attempted to leave %s but already has an pending leave request." % (request.user, group))
|
||||||
|
messages.warning(request, "You already have a pending leave request for that group.")
|
||||||
|
return redirect("groupmanagement:groups")
|
||||||
|
if getattr(settings, 'AUTO_LEAVE', False):
|
||||||
|
logger.info("%s leaving joinable group %s due to auto_leave" % (request.user, group))
|
||||||
|
request.user.groups.remove(group)
|
||||||
|
return redirect('groupmanagement:groups')
|
||||||
grouprequest = GroupRequest()
|
grouprequest = GroupRequest()
|
||||||
grouprequest.status = _('Pending')
|
grouprequest.status = _('Pending')
|
||||||
grouprequest.group = group
|
grouprequest.group = group
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<img class="ra-avatar img-responsive img-circle"
|
<img class="ra-avatar img-responsive img-circle"
|
||||||
src="https://image.eveonline.com/Character/{{ char.character_id }}_32.jpg">
|
src="{{ char.portrait_url_32 }}">
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">{{ char.character_name }}</td>
|
<td class="text-center">{{ char.character_name }}</td>
|
||||||
<td class="text-center">{{ char.corporation_name }}</td>
|
<td class="text-center">{{ char.corporation_name }}</td>
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
allianceauth/locale/en/LC_MESSAGES/django.mo
Normal file
BIN
allianceauth/locale/en/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
1805
allianceauth/locale/en/LC_MESSAGES/django.po
Normal file
1805
allianceauth/locale/en/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user