mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-04 06:06:19 +01:00
Compare commits
380 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
564f4fb5f9 | ||
|
|
688c11ff18 | ||
|
|
4a9a2a670c | ||
|
|
5b25637de5 | ||
|
|
cf2e368978 | ||
|
|
0d67673542 | ||
|
|
d7e58fb557 | ||
|
|
f8cffb64a1 | ||
|
|
dc97fb1cc5 | ||
|
|
392a0c4dcb | ||
|
|
970a111386 | ||
|
|
cafa7cbf17 | ||
|
|
0f0c0441a9 | ||
|
|
a0db8e8e2c | ||
|
|
641356f31d | ||
|
|
191b238a04 | ||
|
|
f6936c5f33 | ||
|
|
e8f8cb8aa3 | ||
|
|
96170a668f | ||
|
|
4a3e807066 | ||
|
|
ab369d9aac | ||
|
|
742864fe6c | ||
|
|
c3df1c4d1f | ||
|
|
63d7fb80e6 | ||
|
|
a7f468efd1 | ||
|
|
9f4ab9540b | ||
|
|
1e133b7c5d | ||
|
|
4aa7530bbc | ||
|
|
2e0ddf2e7a | ||
|
|
e24bc2a05d | ||
|
|
a8c0db3fd7 | ||
|
|
7b77a6cd40 | ||
|
|
b8b8e470f2 | ||
|
|
ad92ea243d | ||
|
|
489a8456f7 | ||
|
|
122e389c38 | ||
|
|
8318add6d5 | ||
|
|
6c3650d9f2 | ||
|
|
37005b1c68 | ||
|
|
0897383e41 | ||
|
|
15db817382 | ||
|
|
eaa1cde01a | ||
|
|
7c1d1074f9 | ||
|
|
0f0f9b6062 | ||
|
|
839232dc98 | ||
|
|
356df45583 | ||
|
|
6b78992f07 | ||
|
|
5af33facb6 | ||
|
|
a7b9bcf3e8 | ||
|
|
eafc6074c1 | ||
|
|
99383a482b | ||
|
|
6f2807cba2 | ||
|
|
39a40a8c43 | ||
|
|
31123f7e97 | ||
|
|
96300a012a | ||
|
|
5f98b5350e | ||
|
|
8874eceb5f | ||
|
|
9de4d557e3 | ||
|
|
1d5f2634f1 | ||
|
|
710d26d36d | ||
|
|
47793e6198 | ||
|
|
3c799eb40b | ||
|
|
0d7b8e315f | ||
|
|
ae7cfbff35 | ||
|
|
a0a497ab33 | ||
|
|
fd86471a0f | ||
|
|
5fcb56a087 | ||
|
|
808080d5ee | ||
|
|
e6037f1680 | ||
|
|
5c3ded6b07 | ||
|
|
0c14e35d15 | ||
|
|
c13be5d39a | ||
|
|
e4b515c1d5 | ||
|
|
65e2c87e8f | ||
|
|
68ce25854a | ||
|
|
1f80a02be9 | ||
|
|
3df6643513 | ||
|
|
e6a4cea4de | ||
|
|
bad36a69e8 | ||
|
|
2697fb5317 | ||
|
|
b47392ba7f | ||
|
|
a99a375375 | ||
|
|
8c3df89d52 | ||
|
|
6ea0ebc9f9 | ||
|
|
26236f5886 | ||
|
|
1420c71ec5 | ||
|
|
10bd77d761 | ||
|
|
192d286cf2 | ||
|
|
8ab9d2d5ad | ||
|
|
3630812b92 | ||
|
|
2f320bc256 | ||
|
|
45b8d42b8e | ||
|
|
bd2d19f867 | ||
|
|
0be404baca | ||
|
|
e6cee9ac83 | ||
|
|
5a2c9243c4 | ||
|
|
fecd748198 | ||
|
|
85351b2c66 | ||
|
|
8b3e5b6462 | ||
|
|
93af920b8f | ||
|
|
b1248d9845 | ||
|
|
c86abef07d | ||
|
|
17ad5dfe33 | ||
|
|
e1843fe1f1 | ||
|
|
256a21f058 | ||
|
|
1473259e41 | ||
|
|
e935b91e93 | ||
|
|
07258a6914 | ||
|
|
cb35808508 | ||
|
|
937d656227 | ||
|
|
d173a59441 | ||
|
|
75a3adb2c9 | ||
|
|
148e208476 | ||
|
|
8f59f2549a | ||
|
|
630400fee4 | ||
|
|
1ec6929e91 | ||
|
|
8c6bdd8ae2 | ||
|
|
e04138bced | ||
|
|
db5ad85811 | ||
|
|
5b44fd376d | ||
|
|
0036e8b280 | ||
|
|
ea2b5bfecf | ||
|
|
aa7495fa60 | ||
|
|
162ec1bd86 | ||
|
|
2668884008 | ||
|
|
abdc3f3485 | ||
|
|
911f21ee7c | ||
|
|
2387c40254 | ||
|
|
76e18a79b3 | ||
|
|
9725c9c947 | ||
|
|
b1b79d1245 | ||
|
|
247ed7cc64 | ||
|
|
2fd2af793e | ||
|
|
3fc36b9ce1 | ||
|
|
c12fd2d7bc | ||
|
|
7fe1ba2fb2 | ||
|
|
ab7eb3e5df | ||
|
|
3452c3acd1 | ||
|
|
3c305fbf37 | ||
|
|
a5e0721ec1 | ||
|
|
164a0d5376 | ||
|
|
2bcf6ec39a | ||
|
|
40824156bf | ||
|
|
cec4495034 | ||
|
|
74eb8621d9 | ||
|
|
4394d25961 | ||
|
|
8113327d31 | ||
|
|
aeeb35bc60 | ||
|
|
630aa3f320 | ||
|
|
3487a945c2 | ||
|
|
1936ae44a3 | ||
|
|
3d7a84e786 | ||
|
|
a4d6730cb0 | ||
|
|
b724227a29 | ||
|
|
d72964fd7c | ||
|
|
d4a41cfb60 | ||
|
|
05859453df | ||
|
|
240d910c9b | ||
|
|
0c31bce7d0 | ||
|
|
f4c4ae36ed | ||
|
|
b9931b2ceb | ||
|
|
4fd6f06c0b | ||
|
|
09573ba7ef | ||
|
|
08e9676760 | ||
|
|
15ae737522 | ||
|
|
50ec9e563e | ||
|
|
cb40649f8b | ||
|
|
bccead0881 | ||
|
|
35ae710624 | ||
|
|
75de4518f2 | ||
|
|
bfcdfea6c8 | ||
|
|
471553fa88 | ||
|
|
db59f5f69b | ||
|
|
1b5413646e | ||
|
|
0337d2517c | ||
|
|
87e6eb9688 | ||
|
|
7b8c246ef8 | ||
|
|
a268a8980a | ||
|
|
3b516c338e | ||
|
|
9952685805 | ||
|
|
2f59c8df22 | ||
|
|
6cd0a42791 | ||
|
|
4c42153bfd | ||
|
|
603bd9c37c | ||
|
|
87c0c3ac73 | ||
|
|
8a91e7f6ac | ||
|
|
281dbdbb01 | ||
|
|
8980d8d32f | ||
|
|
9d6cf9a62e | ||
|
|
0fabb2b368 | ||
|
|
9801ca0314 | ||
|
|
fd86b26b39 | ||
|
|
b0dbef1587 | ||
|
|
a6e60bc23b | ||
|
|
137e8a876d | ||
|
|
fe9538253f | ||
|
|
edda2c248e | ||
|
|
62275639e3 | ||
|
|
d0c68b82f4 | ||
|
|
78b5953bdf | ||
|
|
25e565b099 | ||
|
|
1fe0f78ad7 | ||
|
|
fc8b68156f | ||
|
|
b47cd197ce | ||
|
|
3943426c4c | ||
|
|
08a9bd42a3 | ||
|
|
b2ff339efe | ||
|
|
c604131e04 | ||
|
|
f17607f126 | ||
|
|
94e455a57b | ||
|
|
3c9149db4a | ||
|
|
96cc615c07 | ||
|
|
cd1f4a1c2b | ||
|
|
e0f99a42db | ||
|
|
3506e417d4 | ||
|
|
36197e2212 | ||
|
|
fc5f42d01e | ||
|
|
e26d3767e0 | ||
|
|
046b37c76a | ||
|
|
925ff3e116 | ||
|
|
e5ede4f7b6 | ||
|
|
ded9301527 | ||
|
|
bd1ed6ff73 | ||
|
|
feb65980d4 | ||
|
|
e81d75a782 | ||
|
|
ff305d13ae | ||
|
|
8bcbc1a779 | ||
|
|
3874aa6fee | ||
|
|
103e9f3a11 | ||
|
|
d02c25f421 | ||
|
|
228af38a4a | ||
|
|
051a48885c | ||
|
|
6bcdc6052f | ||
|
|
af3527e64f | ||
|
|
17ef3dd07a | ||
|
|
1f165ecd2a | ||
|
|
70d1d450a9 | ||
|
|
b667892698 | ||
|
|
dc11add0e9 | ||
|
|
cb429a0b88 | ||
|
|
b51039cfc0 | ||
|
|
eadd959d95 | ||
|
|
1856e03d88 | ||
|
|
7dcfa622a3 | ||
|
|
64251b9b3c | ||
|
|
6b073dd5fc | ||
|
|
0911fabfb2 | ||
|
|
050d3f5e63 | ||
|
|
bbe3f78ad1 | ||
|
|
8204c18895 | ||
|
|
b91c788897 | ||
|
|
1d20a3029f | ||
|
|
9cfebc9ae3 | ||
|
|
ddabb4539b | ||
|
|
ada35e221b | ||
|
|
6fef9d904e | ||
|
|
67cf2b5904 | ||
|
|
3bebe792f6 | ||
|
|
00b4d89181 | ||
|
|
f729c6b650 | ||
|
|
df95f8c3f3 | ||
|
|
fe36e57d72 | ||
|
|
31197812b6 | ||
|
|
bd3fe01a12 | ||
|
|
39f7f32b7d | ||
|
|
b4522a1277 | ||
|
|
bb6a7e8327 | ||
|
|
9bd42a7579 | ||
|
|
b41430e5a3 | ||
|
|
595353e838 | ||
|
|
f1a21bb856 | ||
|
|
e44c2935f9 | ||
|
|
4d546f948d | ||
|
|
3bab349d7b | ||
|
|
eef6126ef8 | ||
|
|
5c7478fa39 | ||
|
|
64b72d0b06 | ||
|
|
b266a98b25 | ||
|
|
8a27de5df8 | ||
|
|
f9b5310fce | ||
|
|
fdce173969 | ||
|
|
7b9ddf90c1 | ||
|
|
580c8c19de | ||
|
|
55cc77140e | ||
|
|
93c89dd7cc | ||
|
|
c970cbbd2d | ||
|
|
9ea55fa51f | ||
|
|
5775a11b4e | ||
|
|
1a666b6584 | ||
|
|
35407a2108 | ||
|
|
71fb19aa22 | ||
|
|
b7d7f7b8ce | ||
|
|
59b983edcc | ||
|
|
1734d034e1 | ||
|
|
7f7500ff0c | ||
|
|
ce77c24e5c | ||
|
|
5469a591c0 | ||
|
|
a4befc5e59 | ||
|
|
1ee8065592 | ||
|
|
e4e3bd44fc | ||
|
|
c75de07c2e | ||
|
|
e928131809 | ||
|
|
4f802e82a9 | ||
|
|
0c90bd462e | ||
|
|
bbb70c93d9 | ||
|
|
f6e6ba775c | ||
|
|
06646be907 | ||
|
|
1b4c1a4b9e | ||
|
|
ae3f5a0f62 | ||
|
|
3a984e8a4d | ||
|
|
7d711a54bc | ||
|
|
d92d629c25 | ||
|
|
21e630209a | ||
|
|
e3933998ef | ||
|
|
667afe9051 | ||
|
|
26dc2881eb | ||
|
|
250cb33285 | ||
|
|
db51abec1f | ||
|
|
530716d458 | ||
|
|
f3065d79b3 | ||
|
|
bca5f0472e | ||
|
|
8e54c43917 | ||
|
|
946df1d7a0 | ||
|
|
55f00f742c | ||
|
|
97b2cb71b7 | ||
|
|
ba3a5ba53c | ||
|
|
953c09c999 | ||
|
|
b4cc325b07 | ||
|
|
28c1343f3e | ||
|
|
c16fd94c4a | ||
|
|
bb2cc20838 | ||
|
|
7b815fd010 | ||
|
|
18584974df | ||
|
|
15823b7785 | ||
|
|
6c275d4cd2 | ||
|
|
2d64ee5e2a | ||
|
|
3ae5ffa3f6 | ||
|
|
57d9ddc2c6 | ||
|
|
8b84def494 | ||
|
|
546f01ceb2 | ||
|
|
be720d0e0f | ||
|
|
72bed03244 | ||
|
|
38083ed284 | ||
|
|
53f1b94475 | ||
|
|
ed4270a0e3 | ||
|
|
f1d5cc8903 | ||
|
|
80efdec5d9 | ||
|
|
d49687400a | ||
|
|
e6e03b50da | ||
|
|
543fa3cfa9 | ||
|
|
899988c7c2 | ||
|
|
2f48dd449b | ||
|
|
f70fbbdfee | ||
|
|
2b09ca240d | ||
|
|
0626ff84ad | ||
|
|
62ec746ee3 | ||
|
|
d0f12d7d56 | ||
|
|
b806a69604 | ||
|
|
a609d6360b | ||
|
|
dafbfc8644 | ||
|
|
55413eea19 | ||
|
|
5247c181af | ||
|
|
321af5ec87 | ||
|
|
9ccf340b3d | ||
|
|
d7dcacb899 | ||
|
|
8addd483c2 | ||
|
|
4d27e5ac9b | ||
|
|
31290f6e80 | ||
|
|
c31cc4dbee | ||
|
|
cc1f94cf61 | ||
|
|
a9132b8d50 | ||
|
|
7b4a9891aa | ||
|
|
dcaaf38ecc | ||
|
|
653a8aa850 | ||
|
|
274af11385 | ||
|
|
170b246901 | ||
|
|
9ea79ea389 | ||
|
|
b6fdf840ef | ||
|
|
42948386ec | ||
|
|
1ce0dbde0e |
24
.editorconfig
Normal file
24
.editorconfig
Normal file
@@ -0,0 +1,24 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{yaml,yml,less}]
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
indent_size = 2
|
||||
|
||||
# Makefiles always use tabs for indentation
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.bat]
|
||||
indent_style = tab
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -72,3 +72,8 @@ celerybeat-schedule
|
||||
|
||||
#transifex
|
||||
.tx/
|
||||
|
||||
#other
|
||||
.flake8
|
||||
.pylintrc
|
||||
Makefile
|
||||
|
||||
100
.gitlab-ci.yml
100
.gitlab-ci.yml
@@ -1,61 +1,107 @@
|
||||
stages:
|
||||
- "test"
|
||||
- pre-commit
|
||||
- gitlab
|
||||
- test
|
||||
- deploy
|
||||
|
||||
include:
|
||||
- template: Dependency-Scanning.gitlab-ci.yml
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
|
||||
before_script:
|
||||
- python -V
|
||||
- pip install wheel tox
|
||||
- apt-get update && apt-get install redis-server -y
|
||||
- redis-server --daemonize yes
|
||||
- python -V
|
||||
- pip install wheel tox
|
||||
|
||||
test-3.5-core:
|
||||
image: python:3.5-buster
|
||||
script:
|
||||
- tox -e py35-core
|
||||
|
||||
test-3.6-core:
|
||||
pre-commit-check:
|
||||
stage: pre-commit
|
||||
image: python:3.6-buster
|
||||
variables:
|
||||
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
||||
cache:
|
||||
paths:
|
||||
- ${PRE_COMMIT_HOME}
|
||||
script:
|
||||
- tox -e py36-core
|
||||
- pip install pre-commit
|
||||
- pre-commit run --all-files
|
||||
|
||||
sast:
|
||||
stage: gitlab
|
||||
before_script: []
|
||||
|
||||
dependency_scanning:
|
||||
stage: gitlab
|
||||
before_script:
|
||||
- apt-get update && apt-get install redis-server libmariadb-dev -y
|
||||
- redis-server --daemonize yes
|
||||
- python -V
|
||||
- pip install wheel tox
|
||||
|
||||
test-3.7-core:
|
||||
image: python:3.7-buster
|
||||
image: python:3.7-bullseye
|
||||
script:
|
||||
- tox -e py37-core
|
||||
artifacts:
|
||||
when: always
|
||||
reports:
|
||||
cobertura: coverage.xml
|
||||
|
||||
test-3.8-core:
|
||||
image: python:3.8-buster
|
||||
image: python:3.8-bullseye
|
||||
script:
|
||||
- tox -e py38-core
|
||||
artifacts:
|
||||
when: always
|
||||
reports:
|
||||
cobertura: coverage.xml
|
||||
|
||||
test-3.5-all:
|
||||
image: python:3.5-buster
|
||||
test-3.9-core:
|
||||
image: python:3.9-bullseye
|
||||
script:
|
||||
- tox -e py35-all
|
||||
|
||||
test-3.6-all:
|
||||
image: python:3.6-buster
|
||||
script:
|
||||
- tox -e py36-all
|
||||
- tox -e py39-core
|
||||
artifacts:
|
||||
when: always
|
||||
reports:
|
||||
cobertura: coverage.xml
|
||||
|
||||
test-3.7-all:
|
||||
image: python:3.7-buster
|
||||
image: python:3.7-bullseye
|
||||
script:
|
||||
- tox -e py37-all
|
||||
artifacts:
|
||||
when: always
|
||||
reports:
|
||||
cobertura: coverage.xml
|
||||
|
||||
test-3.8-all:
|
||||
image: python:3.8-buster
|
||||
image: python:3.8-bullseye
|
||||
script:
|
||||
- tox -e py38-all
|
||||
artifacts:
|
||||
when: always
|
||||
reports:
|
||||
cobertura: coverage.xml
|
||||
|
||||
test-3.9-all:
|
||||
image: python:3.9-bullseye
|
||||
script:
|
||||
- tox -e py39-all
|
||||
artifacts:
|
||||
when: always
|
||||
reports:
|
||||
cobertura: coverage.xml
|
||||
|
||||
deploy_production:
|
||||
stage: deploy
|
||||
image: python:3.6-stretch
|
||||
image: python:3.9-bullseye
|
||||
|
||||
before_script:
|
||||
- pip install twine
|
||||
- pip install twine wheel
|
||||
|
||||
script:
|
||||
- python setup.py sdist
|
||||
- python setup.py sdist bdist_wheel
|
||||
- twine upload dist/*
|
||||
|
||||
only:
|
||||
- tags
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
|
||||
28
.pre-commit-config.yaml
Normal file
28
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
# Apply to all files without committing:
|
||||
# pre-commit run --all-files
|
||||
# Update this file:
|
||||
# pre-commit autoupdate
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.0.1
|
||||
hooks:
|
||||
- id: check-case-conflict
|
||||
- id: check-json
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
- id: fix-byte-order-marker
|
||||
- id: trailing-whitespace
|
||||
exclude: (\.min\.css|\.min\.js|\.mo|\.po|swagger\.json)$
|
||||
- id: end-of-file-fixer
|
||||
exclude: (\.min\.css|\.min\.js|\.mo|\.po|swagger\.json)$
|
||||
- id: mixed-line-ending
|
||||
args: [ '--fix=lf' ]
|
||||
- id: fix-encoding-pragma
|
||||
args: [ '--remove' ]
|
||||
|
||||
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
|
||||
rev: 2.3.5
|
||||
hooks:
|
||||
- id: editorconfig-checker
|
||||
exclude: ^(LICENSE|allianceauth\/static\/css\/themes\/bootstrap-locals.less|allianceauth\/eveonline\/swagger.json|(.*.po)|(.*.mo))
|
||||
23
.readthedocs.yml
Normal file
23
.readthedocs.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
# .readthedocs.yml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
||||
# Build documentation with MkDocs
|
||||
#mkdocs:
|
||||
# configuration: mkdocs.yml
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
formats: all
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
python:
|
||||
version: 3.7
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
1
LICENSE
1
LICENSE
@@ -337,4 +337,3 @@ proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ Main features:
|
||||
|
||||
- Can be easily extended with additional services and apps. Many are provided by the community and can be found here: [Community Creations](https://gitlab.com/allianceauth/community-creations)
|
||||
|
||||
- Chinese :cn:, English :us:, German :de: and Spanish :es: localization
|
||||
- English :flag_gb:, Chinese :flag_cn:, German :flag_de:, Spanish :flag_es:, Korean :flag_kr: and Russian :flag_ru: localization
|
||||
|
||||
For further details about AA - including an installation guide and a full list of included services and plugin apps - please see the [official documentation](http://allianceauth.rtfd.io).
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# This will make sure the app is always imported when
|
||||
# Django starts so that shared_task will use this app.
|
||||
|
||||
__version__ = '2.6.3'
|
||||
NAME = 'Alliance Auth v%s' % __version__
|
||||
__version__ = '2.9.0b1'
|
||||
__title__ = 'Alliance Auth'
|
||||
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
|
||||
NAME = '%s v%s' % (__title__, __version__)
|
||||
default_app_config = 'allianceauth.apps.AllianceAuthConfig'
|
||||
|
||||
1
allianceauth/analytics/__init__.py
Normal file
1
allianceauth/analytics/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
default_app_config = 'allianceauth.analytics.apps.AnalyticsConfig'
|
||||
21
allianceauth/analytics/admin.py
Normal file
21
allianceauth/analytics/admin.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import AnalyticsIdentifier, AnalyticsPath, AnalyticsTokens
|
||||
|
||||
|
||||
@admin.register(AnalyticsIdentifier)
|
||||
class AnalyticsIdentifierAdmin(admin.ModelAdmin):
|
||||
search_fields = ['identifier', ]
|
||||
list_display = ('identifier',)
|
||||
|
||||
|
||||
@admin.register(AnalyticsTokens)
|
||||
class AnalyticsTokensAdmin(admin.ModelAdmin):
|
||||
search_fields = ['name', ]
|
||||
list_display = ('name', 'type',)
|
||||
|
||||
|
||||
@admin.register(AnalyticsPath)
|
||||
class AnalyticsPathAdmin(admin.ModelAdmin):
|
||||
search_fields = ['ignore_path', ]
|
||||
list_display = ('ignore_path',)
|
||||
9
allianceauth/analytics/apps.py
Normal file
9
allianceauth/analytics/apps.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AnalyticsConfig(AppConfig):
|
||||
name = 'allianceauth.analytics'
|
||||
label = 'analytics'
|
||||
|
||||
def ready(self):
|
||||
import allianceauth.analytics.signals
|
||||
21
allianceauth/analytics/fixtures/disable_analytics.json
Normal file
21
allianceauth/analytics/fixtures/disable_analytics.json
Normal file
@@ -0,0 +1,21 @@
|
||||
[
|
||||
{
|
||||
"model": "analytics.AnalyticsTokens",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"name": "AA Team Public Google Analytics (Universal)",
|
||||
"type": "GA-V4",
|
||||
"token": "UA-186249766-2",
|
||||
"send_page_views": "False",
|
||||
"send_celery_tasks": "False",
|
||||
"send_stats": "False"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "analytics.AnalyticsIdentifier",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"identifier": "ab33e241fbf042b6aa77c7655a768af7"
|
||||
}
|
||||
}
|
||||
]
|
||||
41
allianceauth/analytics/middleware.py
Normal file
41
allianceauth/analytics/middleware.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
from .models import AnalyticsTokens, AnalyticsIdentifier
|
||||
from .tasks import send_ga_tracking_web_view
|
||||
|
||||
|
||||
class AnalyticsMiddleware(MiddlewareMixin):
|
||||
def process_response(self, request, response):
|
||||
"""Django Middleware: Process Page Views and creates Analytics Celery Tasks"""
|
||||
analyticstokens = AnalyticsTokens.objects.all()
|
||||
client_id = AnalyticsIdentifier.objects.get(id=1).identifier.hex
|
||||
try:
|
||||
title = BeautifulSoup(
|
||||
response.content, "html.parser").html.head.title.text
|
||||
except AttributeError:
|
||||
title = ''
|
||||
for token in analyticstokens:
|
||||
# Check if Page View Sending is Disabled
|
||||
if token.send_page_views is False:
|
||||
continue
|
||||
# Check Exclusions
|
||||
if request.path in token.ignore_paths.all():
|
||||
continue
|
||||
|
||||
tracking_id = token.token
|
||||
locale = request.LANGUAGE_CODE
|
||||
path = request.path
|
||||
try:
|
||||
useragent = request.headers["User-Agent"]
|
||||
except KeyError:
|
||||
useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
||||
|
||||
send_ga_tracking_web_view.s(tracking_id=tracking_id,
|
||||
client_id=client_id,
|
||||
page=path,
|
||||
title=title,
|
||||
locale=locale,
|
||||
useragent=useragent).\
|
||||
apply_async(priority=9)
|
||||
return response
|
||||
42
allianceauth/analytics/migrations/0001_initial.py
Normal file
42
allianceauth/analytics/migrations/0001_initial.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# Generated by Django 3.1.4 on 2020-12-30 13:11
|
||||
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AnalyticsIdentifier',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('identifier', models.UUIDField(default=uuid.uuid4, editable=False)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AnalyticsPath',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('ignore_path', models.CharField(default='/example/', max_length=254)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AnalyticsTokens',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=254)),
|
||||
('type', models.CharField(choices=[('GA-U', 'Google Analytics Universal'), ('GA-V4', 'Google Analytics V4')], max_length=254)),
|
||||
('token', models.CharField(max_length=254)),
|
||||
('send_page_views', models.BooleanField(default=False)),
|
||||
('send_celery_tasks', models.BooleanField(default=False)),
|
||||
('send_stats', models.BooleanField(default=False)),
|
||||
('ignore_paths', models.ManyToManyField(blank=True, to='analytics.AnalyticsPath')),
|
||||
],
|
||||
),
|
||||
]
|
||||
34
allianceauth/analytics/migrations/0002_add_AA_Team_Token.py
Normal file
34
allianceauth/analytics/migrations/0002_add_AA_Team_Token.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# Generated by Django 3.1.4 on 2020-12-30 08:53
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def add_aa_team_token(apps, schema_editor):
|
||||
# We can't import the Person model directly as it may be a newer
|
||||
# version than this migration expects. We use the historical version.
|
||||
Tokens = apps.get_model('analytics', 'AnalyticsTokens')
|
||||
token = Tokens()
|
||||
token.type = 'GA-U'
|
||||
token.token = 'UA-186249766-2'
|
||||
token.send_page_views = True
|
||||
token.send_celery_tasks = True
|
||||
token.send_stats = True
|
||||
token.name = 'AA Team Public Google Analytics (Universal)'
|
||||
token.save()
|
||||
|
||||
|
||||
def remove_aa_team_token(apps, schema_editor):
|
||||
# Have to define some code to remove this identifier
|
||||
# In case of migration rollback?
|
||||
Tokens = apps.get_model('analytics', 'AnalyticsTokens')
|
||||
token = Tokens.objects.filter(token="UA-186249766-2").delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('analytics', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [migrations.RunPython(add_aa_team_token, remove_aa_team_token)
|
||||
]
|
||||
@@ -0,0 +1,30 @@
|
||||
# Generated by Django 3.1.4 on 2020-12-30 08:53
|
||||
|
||||
from uuid import uuid4
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def generate_identifier(apps, schema_editor):
|
||||
# We can't import the Person model directly as it may be a newer
|
||||
# version than this migration expects. We use the historical version.
|
||||
Identifier = apps.get_model('analytics', 'AnalyticsIdentifier')
|
||||
identifier = Identifier()
|
||||
identifier.id = 1
|
||||
identifier.save()
|
||||
|
||||
|
||||
def zero_identifier(apps, schema_editor):
|
||||
# Have to define some code to remove this identifier
|
||||
# In case of migration rollback?
|
||||
Identifier = apps.get_model('analytics', 'AnalyticsIdentifier')
|
||||
Identifier.objects.filter(id=1).delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('analytics', '0002_add_AA_Team_Token'),
|
||||
]
|
||||
|
||||
operations = [migrations.RunPython(generate_identifier, zero_identifier)
|
||||
]
|
||||
0
allianceauth/analytics/migrations/__init__.py
Normal file
0
allianceauth/analytics/migrations/__init__.py
Normal file
38
allianceauth/analytics/models.py
Normal file
38
allianceauth/analytics/models.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from django.db import models
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
class AnalyticsIdentifier(models.Model):
|
||||
|
||||
identifier = models.UUIDField(default=uuid4,
|
||||
editable=False)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.pk and AnalyticsIdentifier.objects.exists():
|
||||
# Force a single object
|
||||
raise ValidationError('There is can be only one \
|
||||
AnalyticsIdentifier instance')
|
||||
self.pk = self.id = 1 # If this happens to be deleted and recreated, force it to be 1
|
||||
return super(AnalyticsIdentifier, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
class AnalyticsPath(models.Model):
|
||||
ignore_path = models.CharField(max_length=254, default="/example/")
|
||||
|
||||
|
||||
class AnalyticsTokens(models.Model):
|
||||
|
||||
class Analytics_Type(models.TextChoices):
|
||||
GA_U = 'GA-U', _('Google Analytics Universal')
|
||||
GA_V4 = 'GA-V4', _('Google Analytics V4')
|
||||
|
||||
name = models.CharField(max_length=254)
|
||||
type = models.CharField(max_length=254, choices=Analytics_Type.choices)
|
||||
token = models.CharField(max_length=254, blank=False)
|
||||
send_page_views = models.BooleanField(default=False)
|
||||
send_celery_tasks = models.BooleanField(default=False)
|
||||
send_stats = models.BooleanField(default=False)
|
||||
ignore_paths = models.ManyToManyField(AnalyticsPath, blank=True)
|
||||
50
allianceauth/analytics/signals.py
Normal file
50
allianceauth/analytics/signals.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from allianceauth.analytics.tasks import analytics_event
|
||||
from celery.signals import task_failure, task_success
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@task_failure.connect
|
||||
def process_failure_signal(
|
||||
exception, traceback,
|
||||
sender, task_id, signal,
|
||||
args, kwargs, einfo, **kw):
|
||||
logger.debug("Celery task_failure signal %s" % sender.__class__.__name__)
|
||||
|
||||
category = sender.__module__
|
||||
|
||||
if 'allianceauth.analytics' not in category:
|
||||
if category.endswith(".tasks"):
|
||||
category = category[:-6]
|
||||
|
||||
action = sender.__name__
|
||||
|
||||
label = f"{exception.__class__.__name__}: {str(exception)}"
|
||||
|
||||
analytics_event(category=category,
|
||||
action=action,
|
||||
label=label)
|
||||
|
||||
|
||||
@task_success.connect
|
||||
def celery_success_signal(sender, result=None, **kw):
|
||||
logger.debug("Celery task_success signal %s" % sender.__class__.__name__)
|
||||
|
||||
category = sender.__module__
|
||||
|
||||
if 'allianceauth.analytics' not in category:
|
||||
if category.endswith(".tasks"):
|
||||
category = category[:-6]
|
||||
|
||||
action = sender.__name__
|
||||
label = "Success"
|
||||
|
||||
value = 0
|
||||
if isinstance(result, int):
|
||||
value = result
|
||||
|
||||
analytics_event(category=category,
|
||||
action=action,
|
||||
label=label,
|
||||
value=value)
|
||||
207
allianceauth/analytics/tasks.py
Normal file
207
allianceauth/analytics/tasks.py
Normal file
@@ -0,0 +1,207 @@
|
||||
import requests
|
||||
import logging
|
||||
from django.conf import settings
|
||||
from django.apps import apps
|
||||
from celery import shared_task
|
||||
from allianceauth import __version__
|
||||
from .models import AnalyticsTokens, AnalyticsIdentifier
|
||||
from .utils import (
|
||||
install_stat_addons,
|
||||
install_stat_tokens,
|
||||
install_stat_users)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
BASE_URL = "https://www.google-analytics.com/"
|
||||
|
||||
DEBUG_URL = f"{BASE_URL}debug/collect"
|
||||
COLLECTION_URL = f"{BASE_URL}collect"
|
||||
|
||||
if getattr(settings, "ANALYTICS_ENABLE_DEBUG", False) and settings.DEBUG:
|
||||
# Force sending of analytics data during in a debug/test environemt
|
||||
# Usefull for developers working on this feature.
|
||||
logger.warning(
|
||||
"You have 'ANALYTICS_ENABLE_DEBUG' Enabled! "
|
||||
"This debug instance will send analytics data!")
|
||||
DEBUG_URL = COLLECTION_URL
|
||||
|
||||
ANALYTICS_URL = COLLECTION_URL
|
||||
|
||||
if settings.DEBUG is True:
|
||||
ANALYTICS_URL = DEBUG_URL
|
||||
|
||||
|
||||
def analytics_event(category: str,
|
||||
action: str,
|
||||
label: str,
|
||||
value: int = 0,
|
||||
event_type: str = 'Celery'):
|
||||
"""
|
||||
Send a Google Analytics Event for each token stored
|
||||
Includes check for if its enabled/disabled
|
||||
|
||||
Parameters
|
||||
-------
|
||||
`category` (str): Celery Namespace
|
||||
`action` (str): Task Name
|
||||
`label` (str): Optional, Task Success/Exception
|
||||
`value` (int): Optional, If bulk, Query size, can be a binary True/False
|
||||
`event_type` (str): Optional, Celery or Stats only, Default to Celery
|
||||
"""
|
||||
analyticstokens = AnalyticsTokens.objects.all()
|
||||
client_id = AnalyticsIdentifier.objects.get(id=1).identifier.hex
|
||||
for token in analyticstokens:
|
||||
if event_type == 'Celery':
|
||||
allowed = token.send_celery_tasks
|
||||
elif event_type == 'Stats':
|
||||
allowed = token.send_stats
|
||||
else:
|
||||
allowed = False
|
||||
|
||||
if allowed is True:
|
||||
tracking_id = token.token
|
||||
send_ga_tracking_celery_event.s(tracking_id=tracking_id,
|
||||
client_id=client_id,
|
||||
category=category,
|
||||
action=action,
|
||||
label=label,
|
||||
value=value).\
|
||||
apply_async(priority=9)
|
||||
|
||||
|
||||
@shared_task()
|
||||
def analytics_daily_stats():
|
||||
"""Celery Task: Do not call directly
|
||||
|
||||
Gathers a series of daily statistics and sends analytics events containing them"""
|
||||
users = install_stat_users()
|
||||
tokens = install_stat_tokens()
|
||||
addons = install_stat_addons()
|
||||
logger.debug("Running Daily Analytics Upload")
|
||||
|
||||
analytics_event(category='allianceauth.analytics',
|
||||
action='send_install_stats',
|
||||
label='existence',
|
||||
value=1,
|
||||
event_type='Stats')
|
||||
analytics_event(category='allianceauth.analytics',
|
||||
action='send_install_stats',
|
||||
label='users',
|
||||
value=users,
|
||||
event_type='Stats')
|
||||
analytics_event(category='allianceauth.analytics',
|
||||
action='send_install_stats',
|
||||
label='tokens',
|
||||
value=tokens,
|
||||
event_type='Stats')
|
||||
analytics_event(category='allianceauth.analytics',
|
||||
action='send_install_stats',
|
||||
label='addons',
|
||||
value=addons,
|
||||
event_type='Stats')
|
||||
|
||||
for appconfig in apps.get_app_configs():
|
||||
analytics_event(category='allianceauth.analytics',
|
||||
action='send_extension_stats',
|
||||
label=appconfig.label,
|
||||
value=1,
|
||||
event_type='Stats')
|
||||
|
||||
|
||||
@shared_task()
|
||||
def send_ga_tracking_web_view(
|
||||
tracking_id: str,
|
||||
client_id: str,
|
||||
page: str,
|
||||
title: str,
|
||||
locale: str,
|
||||
useragent: str) -> requests.Response:
|
||||
|
||||
"""Celery Task: Do not call directly
|
||||
|
||||
Sends Page View events to GA, Called only via analytics.middleware
|
||||
|
||||
Parameters
|
||||
----------
|
||||
`tracking_id` (str): Unique Server Identifier
|
||||
`client_id` (str): GA Token
|
||||
`page` (str): Page Path
|
||||
`title` (str): Page Title
|
||||
`locale` (str): Browser Language
|
||||
`useragent` (str): Browser UserAgent
|
||||
|
||||
Returns
|
||||
-------
|
||||
requests.Reponse Object
|
||||
"""
|
||||
headers = {"User-Agent": useragent}
|
||||
|
||||
payload = {
|
||||
'v': '1',
|
||||
'tid': tracking_id,
|
||||
'cid': client_id,
|
||||
't': 'pageview',
|
||||
'dp': page,
|
||||
'dt': title,
|
||||
'ul': locale,
|
||||
'ua': useragent,
|
||||
'aip': 1,
|
||||
'an': "allianceauth",
|
||||
'av': __version__
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
ANALYTICS_URL, data=payload,
|
||||
timeout=5, headers=headers)
|
||||
logger.debug(f"Analytics Page View HTTP{response.status_code}")
|
||||
return response
|
||||
|
||||
|
||||
@shared_task()
|
||||
def send_ga_tracking_celery_event(
|
||||
tracking_id: str,
|
||||
client_id: str,
|
||||
category: str,
|
||||
action: str,
|
||||
label: str,
|
||||
value: int) -> requests.Response:
|
||||
"""Celery Task: Do not call directly
|
||||
|
||||
Sends Page View events to GA, Called only via analytics.middleware
|
||||
|
||||
Parameters
|
||||
----------
|
||||
`tracking_id` (str): Unique Server Identifier
|
||||
`client_id` (str): GA Token
|
||||
`category` (str): Celery Namespace
|
||||
`action` (str): Task Name
|
||||
`label` (str): Optional, Task Success/Exception
|
||||
`value` (int): Optional, If bulk, Query size, can be a binary True/False
|
||||
|
||||
Returns
|
||||
-------
|
||||
requests.Reponse Object
|
||||
"""
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"}
|
||||
|
||||
payload = {
|
||||
'v': '1',
|
||||
'tid': tracking_id,
|
||||
'cid': client_id,
|
||||
't': 'event',
|
||||
'ec': category,
|
||||
'ea': action,
|
||||
'el': label,
|
||||
'ev': value,
|
||||
'aip': 1,
|
||||
'an': "allianceauth",
|
||||
'av': __version__
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
ANALYTICS_URL, data=payload,
|
||||
timeout=5, headers=headers)
|
||||
logger.debug(f"Analytics Celery/Stats Event HTTP{response.status_code}")
|
||||
return response
|
||||
0
allianceauth/analytics/tests/__init__.py
Normal file
0
allianceauth/analytics/tests/__init__.py
Normal file
23
allianceauth/analytics/tests/test_middleware.py
Normal file
23
allianceauth/analytics/tests/test_middleware.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from allianceauth.analytics.middleware import AnalyticsMiddleware
|
||||
from unittest.mock import Mock
|
||||
|
||||
from django.test.testcases import TestCase
|
||||
|
||||
|
||||
class TestAnalyticsMiddleware(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.middleware = AnalyticsMiddleware()
|
||||
self.request = Mock()
|
||||
self.request.headers = {
|
||||
"User-Agent": "AUTOMATED TEST"
|
||||
}
|
||||
self.request.path = '/testURL/'
|
||||
self.request.session = {}
|
||||
self.request.LANGUAGE_CODE = 'en'
|
||||
self.response = Mock()
|
||||
self.response.content = 'hello world'
|
||||
|
||||
def test_middleware(self):
|
||||
response = self.middleware.process_response(self.request, self.response)
|
||||
self.assertEqual(self.response, response)
|
||||
26
allianceauth/analytics/tests/test_models.py
Normal file
26
allianceauth/analytics/tests/test_models.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from allianceauth.analytics.models import AnalyticsIdentifier
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from django.test.testcases import TestCase
|
||||
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
|
||||
# Identifiers
|
||||
uuid_1 = "ab33e241fbf042b6aa77c7655a768af7"
|
||||
uuid_2 = "7aa6bd70701f44729af5e3095ff4b55c"
|
||||
|
||||
|
||||
class TestAnalyticsIdentifier(TestCase):
|
||||
|
||||
def test_identifier_random(self):
|
||||
self.assertNotEqual(AnalyticsIdentifier.objects.get(), uuid4)
|
||||
|
||||
def test_identifier_singular(self):
|
||||
AnalyticsIdentifier.objects.all().delete()
|
||||
AnalyticsIdentifier.objects.create(identifier=uuid_1)
|
||||
# Yeah i have multiple asserts here, they all do the same thing
|
||||
with self.assertRaises(ValidationError):
|
||||
AnalyticsIdentifier.objects.create(identifier=uuid_2)
|
||||
self.assertEqual(AnalyticsIdentifier.objects.count(), 1)
|
||||
self.assertEqual(AnalyticsIdentifier.objects.get(pk=1).identifier, UUID(uuid_1))
|
||||
119
allianceauth/analytics/tests/test_tasks.py
Normal file
119
allianceauth/analytics/tests/test_tasks.py
Normal file
@@ -0,0 +1,119 @@
|
||||
from allianceauth.analytics.tasks import (
|
||||
analytics_event,
|
||||
send_ga_tracking_celery_event,
|
||||
send_ga_tracking_web_view)
|
||||
from django.test.testcases import TestCase
|
||||
|
||||
|
||||
class TestAnalyticsTasks(TestCase):
|
||||
def test_analytics_event(self):
|
||||
analytics_event(
|
||||
category='allianceauth.analytics',
|
||||
action='send_tests',
|
||||
label='test',
|
||||
value=1,
|
||||
event_type='Stats')
|
||||
|
||||
def test_send_ga_tracking_web_view_sent(self):
|
||||
# This test sends if the event SENDS to google
|
||||
# Not if it was successful
|
||||
tracking_id = 'UA-186249766-2'
|
||||
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
||||
page = '/index/'
|
||||
title = 'Hello World'
|
||||
locale = 'en'
|
||||
useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
||||
response = send_ga_tracking_web_view(
|
||||
tracking_id,
|
||||
client_id,
|
||||
page,
|
||||
title,
|
||||
locale,
|
||||
useragent)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_send_ga_tracking_web_view_success(self):
|
||||
tracking_id = 'UA-186249766-2'
|
||||
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
||||
page = '/index/'
|
||||
title = 'Hello World'
|
||||
locale = 'en'
|
||||
useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
||||
json_response = send_ga_tracking_web_view(
|
||||
tracking_id,
|
||||
client_id,
|
||||
page,
|
||||
title,
|
||||
locale,
|
||||
useragent).json()
|
||||
self.assertTrue(json_response["hitParsingResult"][0]["valid"])
|
||||
|
||||
def test_send_ga_tracking_web_view_invalid_token(self):
|
||||
tracking_id = 'UA-IntentionallyBadTrackingID-2'
|
||||
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
||||
page = '/index/'
|
||||
title = 'Hello World'
|
||||
locale = 'en'
|
||||
useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
||||
json_response = send_ga_tracking_web_view(
|
||||
tracking_id,
|
||||
client_id,
|
||||
page,
|
||||
title,
|
||||
locale,
|
||||
useragent).json()
|
||||
self.assertFalse(json_response["hitParsingResult"][0]["valid"])
|
||||
self.assertEqual(json_response["hitParsingResult"][0]["parserMessage"][1]["description"], "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.")
|
||||
|
||||
# [{'valid': False, 'parserMessage': [{'messageType': 'INFO', 'description': 'IP Address from this hit was anonymized to 1.132.110.0.', 'messageCode': 'VALUE_MODIFIED'}, {'messageType': 'ERROR', 'description': "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.", 'messageCode': 'VALUE_INVALID', 'parameter': 'tid'}], 'hit': '/debug/collect?v=1&tid=UA-IntentionallyBadTrackingID-2&cid=ab33e241fbf042b6aa77c7655a768af7&t=pageview&dp=/index/&dt=Hello World&ul=en&ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36&aip=1&an=allianceauth&av=2.9.0a2'}]
|
||||
|
||||
def test_send_ga_tracking_celery_event_sent(self):
|
||||
tracking_id = 'UA-186249766-2'
|
||||
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
||||
category = 'test'
|
||||
action = 'test'
|
||||
label = 'test'
|
||||
value = '1'
|
||||
response = send_ga_tracking_celery_event(
|
||||
tracking_id,
|
||||
client_id,
|
||||
category,
|
||||
action,
|
||||
label,
|
||||
value)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_send_ga_tracking_celery_event_success(self):
|
||||
tracking_id = 'UA-186249766-2'
|
||||
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
||||
category = 'test'
|
||||
action = 'test'
|
||||
label = 'test'
|
||||
value = '1'
|
||||
json_response = send_ga_tracking_celery_event(
|
||||
tracking_id,
|
||||
client_id,
|
||||
category,
|
||||
action,
|
||||
label,
|
||||
value).json()
|
||||
self.assertTrue(json_response["hitParsingResult"][0]["valid"])
|
||||
|
||||
def test_send_ga_tracking_celery_event_invalid_token(self):
|
||||
tracking_id = 'UA-IntentionallyBadTrackingID-2'
|
||||
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
||||
category = 'test'
|
||||
action = 'test'
|
||||
label = 'test'
|
||||
value = '1'
|
||||
json_response = send_ga_tracking_celery_event(
|
||||
tracking_id,
|
||||
client_id,
|
||||
category,
|
||||
action,
|
||||
label,
|
||||
value).json()
|
||||
self.assertFalse(json_response["hitParsingResult"][0]["valid"])
|
||||
self.assertEqual(json_response["hitParsingResult"][0]["parserMessage"][1]["description"], "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.")
|
||||
|
||||
# [{'valid': False, 'parserMessage': [{'messageType': 'INFO', 'description': 'IP Address from this hit was anonymized to 1.132.110.0.', 'messageCode': 'VALUE_MODIFIED'}, {'messageType': 'ERROR', 'description': "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.", 'messageCode': 'VALUE_INVALID', 'parameter': 'tid'}], 'hit': '/debug/collect?v=1&tid=UA-IntentionallyBadTrackingID-2&cid=ab33e241fbf042b6aa77c7655a768af7&t=pageview&dp=/index/&dt=Hello World&ul=en&ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36&aip=1&an=allianceauth&av=2.9.0a2'}]
|
||||
55
allianceauth/analytics/tests/test_utils.py
Normal file
55
allianceauth/analytics/tests/test_utils.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from django.apps import apps
|
||||
from allianceauth.authentication.models import User
|
||||
from esi.models import Token
|
||||
from allianceauth.analytics.utils import install_stat_users, install_stat_tokens, install_stat_addons
|
||||
|
||||
from django.test.testcases import TestCase
|
||||
|
||||
|
||||
def create_testdata():
|
||||
User.objects.all().delete()
|
||||
User.objects.create_user(
|
||||
'user_1'
|
||||
'abc@example.com',
|
||||
'password'
|
||||
)
|
||||
User.objects.create_user(
|
||||
'user_2'
|
||||
'abc@example.com',
|
||||
'password'
|
||||
)
|
||||
#Token.objects.all().delete()
|
||||
#Token.objects.create(
|
||||
# character_id=101,
|
||||
# character_name='character1',
|
||||
# access_token='my_access_token'
|
||||
#)
|
||||
#Token.objects.create(
|
||||
# character_id=102,
|
||||
# character_name='character2',
|
||||
# access_token='my_access_token'
|
||||
#)
|
||||
|
||||
|
||||
class TestAnalyticsUtils(TestCase):
|
||||
|
||||
def test_install_stat_users(self):
|
||||
create_testdata()
|
||||
expected = 2
|
||||
|
||||
users = install_stat_users()
|
||||
self.assertEqual(users, expected)
|
||||
|
||||
#def test_install_stat_tokens(self):
|
||||
# create_testdata()
|
||||
# expected = 2
|
||||
#
|
||||
# tokens = install_stat_tokens()
|
||||
# self.assertEqual(tokens, expected)
|
||||
|
||||
def test_install_stat_addons(self):
|
||||
# this test does what its testing...
|
||||
# but helpful for existing as a sanity check
|
||||
expected = len(list(apps.get_app_configs()))
|
||||
addons = install_stat_addons()
|
||||
self.assertEqual(addons, expected)
|
||||
36
allianceauth/analytics/utils.py
Normal file
36
allianceauth/analytics/utils.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from django.apps import apps
|
||||
from allianceauth.authentication.models import User
|
||||
from esi.models import Token
|
||||
|
||||
|
||||
def install_stat_users() -> int:
|
||||
"""Count and Return the number of User accounts
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
The Number of User objects"""
|
||||
users = User.objects.count()
|
||||
return users
|
||||
|
||||
|
||||
def install_stat_tokens() -> int:
|
||||
"""Count and Return the number of ESI Tokens Stored
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
The Number of Token Objects"""
|
||||
tokens = Token.objects.count()
|
||||
return tokens
|
||||
|
||||
|
||||
def install_stat_addons() -> int:
|
||||
"""Count and Return the number of Django Applications Installed
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
The Number of Installed Apps"""
|
||||
addons = len(list(apps.get_app_configs()))
|
||||
return addons
|
||||
@@ -1,10 +1,8 @@
|
||||
from django.conf import settings
|
||||
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||
from django.contrib.auth.models import User as BaseUser, \
|
||||
Permission as BasePermission, Group
|
||||
from django.db.models import Q, F
|
||||
from django.db.models import Count, Q
|
||||
from allianceauth.services.hooks import ServicesHook
|
||||
from django.db.models.signals import pre_save, post_save, pre_delete, \
|
||||
post_delete, m2m_changed
|
||||
@@ -24,11 +22,6 @@ 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):
|
||||
"""
|
||||
@@ -37,6 +30,9 @@ def make_service_hooks_update_groups_action(service):
|
||||
:return: fn to update services groups for the selected users
|
||||
"""
|
||||
def update_service_groups(modeladmin, request, queryset):
|
||||
if hasattr(service, 'update_groups_bulk'):
|
||||
service.update_groups_bulk(queryset)
|
||||
else:
|
||||
for user in queryset: # queryset filtering doesn't work here?
|
||||
service.update_groups(user)
|
||||
|
||||
@@ -52,6 +48,9 @@ def make_service_hooks_sync_nickname_action(service):
|
||||
:return: fn to sync nickname for the selected users
|
||||
"""
|
||||
def sync_nickname(modeladmin, request, queryset):
|
||||
if hasattr(service, 'sync_nicknames_bulk'):
|
||||
service.sync_nicknames_bulk(queryset)
|
||||
else:
|
||||
for user in queryset: # queryset filtering doesn't work here?
|
||||
service.sync_nickname(user)
|
||||
|
||||
@@ -86,7 +85,6 @@ class UserProfileInline(admin.StackedInline):
|
||||
query |= Q(userprofile__isnull=True)
|
||||
else:
|
||||
query |= Q(character_ownership__user=obj)
|
||||
qs = EveCharacter.objects.filter(query)
|
||||
formset = super().get_formset(request, obj=obj, **kwargs)
|
||||
|
||||
def get_kwargs(self, index):
|
||||
@@ -94,7 +92,7 @@ class UserProfileInline(admin.StackedInline):
|
||||
formset.get_form_kwargs = get_kwargs
|
||||
return formset
|
||||
|
||||
def has_add_permission(self, request):
|
||||
def has_add_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
@@ -115,6 +113,8 @@ def user_profile_pic(obj):
|
||||
)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
user_profile_pic.short_description = ''
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@ def user_username(obj):
|
||||
user_obj.username,
|
||||
)
|
||||
|
||||
|
||||
user_username.short_description = 'user / main'
|
||||
user_username.admin_order_field = 'username'
|
||||
|
||||
@@ -162,7 +163,8 @@ def user_main_organization(obj):
|
||||
else:
|
||||
corporation = user_obj.profile.main_character.corporation_name
|
||||
if user_obj.profile.main_character.alliance_id:
|
||||
result = format_html('{}<br>{}',
|
||||
result = format_html(
|
||||
'{}<br>{}',
|
||||
corporation,
|
||||
user_obj.profile.main_character.alliance_name
|
||||
)
|
||||
@@ -170,6 +172,7 @@ def user_main_organization(obj):
|
||||
result = corporation
|
||||
return result
|
||||
|
||||
|
||||
user_main_organization.short_description = 'Corporation / Alliance (Main)'
|
||||
user_main_organization.admin_order_field = \
|
||||
'profile__main_character__corporation_name'
|
||||
@@ -199,13 +202,13 @@ class MainCorporationsFilter(admin.SimpleListFilter):
|
||||
return qs.all()
|
||||
else:
|
||||
if qs.model == User:
|
||||
return qs\
|
||||
.filter(profile__main_character__corporation_id=\
|
||||
self.value())
|
||||
return qs.filter(
|
||||
profile__main_character__corporation_id=self.value()
|
||||
)
|
||||
else:
|
||||
return qs\
|
||||
.filter(user__profile__main_character__corporation_id=\
|
||||
self.value())
|
||||
return qs.filter(
|
||||
user__profile__main_character__corporation_id=self.value()
|
||||
)
|
||||
|
||||
|
||||
class MainAllianceFilter(admin.SimpleListFilter):
|
||||
@@ -233,12 +236,11 @@ class MainAllianceFilter(admin.SimpleListFilter):
|
||||
return qs.all()
|
||||
else:
|
||||
if qs.model == User:
|
||||
return qs\
|
||||
.filter(profile__main_character__alliance_id=self.value())
|
||||
return qs.filter(profile__main_character__alliance_id=self.value())
|
||||
else:
|
||||
return qs\
|
||||
.filter(user__profile__main_character__alliance_id=\
|
||||
self.value())
|
||||
return qs.filter(
|
||||
user__profile__main_character__alliance_id=self.value()
|
||||
)
|
||||
|
||||
|
||||
def update_main_character_model(modeladmin, request, queryset):
|
||||
@@ -253,6 +255,7 @@ def update_main_character_model(modeladmin, request, queryset):
|
||||
'Update from ESI started for {} characters'.format(tasks_count)
|
||||
)
|
||||
|
||||
|
||||
update_main_character_model.short_description = \
|
||||
'Update main character model from ESI'
|
||||
|
||||
@@ -261,7 +264,6 @@ class UserAdmin(BaseUserAdmin):
|
||||
"""Extending Django's UserAdmin model
|
||||
|
||||
Behavior of groups and characters columns can be configured via settings
|
||||
|
||||
"""
|
||||
|
||||
class Media:
|
||||
@@ -269,24 +271,9 @@ class UserAdmin(BaseUserAdmin):
|
||||
"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_queryset(self, request):
|
||||
qs = super().get_queryset(request)
|
||||
return qs.prefetch_related("character_ownerships__character", "groups")
|
||||
|
||||
def get_actions(self, request):
|
||||
actions = super(BaseUserAdmin, self).get_actions(request)
|
||||
@@ -335,11 +322,9 @@ class UserAdmin(BaseUserAdmin):
|
||||
return result
|
||||
|
||||
inlines = BaseUserAdmin.inlines + [UserProfileInline]
|
||||
|
||||
ordering = ('username', )
|
||||
list_select_related = True
|
||||
list_select_related = ('profile__state', 'profile__main_character')
|
||||
show_full_result_count = True
|
||||
|
||||
list_display = (
|
||||
user_profile_pic,
|
||||
user_username,
|
||||
@@ -352,10 +337,9 @@ class UserAdmin(BaseUserAdmin):
|
||||
'_role'
|
||||
)
|
||||
list_display_links = None
|
||||
|
||||
list_filter = (
|
||||
'profile__state',
|
||||
RealGroupsFilter,
|
||||
'groups',
|
||||
MainCorporationsFilter,
|
||||
MainAllianceFilter,
|
||||
'is_active',
|
||||
@@ -369,21 +353,15 @@ class UserAdmin(BaseUserAdmin):
|
||||
)
|
||||
|
||||
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()
|
||||
]
|
||||
character_ownerships = list(obj.character_ownerships.all())
|
||||
characters = [obj.character.character_name for obj in character_ownerships]
|
||||
return self._list_2_html_w_tooltips(
|
||||
my_characters,
|
||||
sorted(characters),
|
||||
AUTHENTICATION_ADMIN_USERS_MAX_CHARS
|
||||
)
|
||||
|
||||
_characters.short_description = 'characters'
|
||||
|
||||
|
||||
def _state(self, obj):
|
||||
return obj.profile.state.name
|
||||
|
||||
@@ -391,19 +369,9 @@ class UserAdmin(BaseUserAdmin):
|
||||
_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')
|
||||
]
|
||||
|
||||
my_groups = sorted([group.name for group in list(obj.groups.all())])
|
||||
return self._list_2_html_w_tooltips(
|
||||
my_groups,
|
||||
AUTHENTICATION_ADMIN_USERS_MAX_GROUPS
|
||||
my_groups, AUTHENTICATION_ADMIN_USERS_MAX_GROUPS
|
||||
)
|
||||
|
||||
_groups.short_description = 'groups'
|
||||
@@ -440,9 +408,14 @@ class StateAdmin(admin.ModelAdmin):
|
||||
list_select_related = True
|
||||
list_display = ('name', 'priority', '_user_count')
|
||||
|
||||
def get_queryset(self, request):
|
||||
qs = super().get_queryset(request)
|
||||
return qs.annotate(user_count=Count("userprofile__id"))
|
||||
|
||||
def _user_count(self, obj):
|
||||
return obj.userprofile_set.all().count()
|
||||
return obj.user_count
|
||||
_user_count.short_description = 'Users'
|
||||
_user_count.admin_order_field = 'user_count'
|
||||
|
||||
fieldsets = (
|
||||
(None, {
|
||||
@@ -475,6 +448,8 @@ class StateAdmin(admin.ModelAdmin):
|
||||
elif db_field.name == "member_alliances":
|
||||
kwargs["queryset"] = EveAllianceInfo.objects.all()\
|
||||
.order_by(Lower('alliance_name'))
|
||||
elif db_field.name == "permissions":
|
||||
kwargs["queryset"] = Permission.objects.select_related("content_type").all()
|
||||
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
@@ -498,7 +473,8 @@ class BaseOwnershipAdmin(admin.ModelAdmin):
|
||||
"all": ("authentication/css/admin.css",)
|
||||
}
|
||||
|
||||
list_select_related = True
|
||||
list_select_related = (
|
||||
'user__profile__state', 'user__profile__main_character', 'character')
|
||||
list_display = (
|
||||
user_profile_pic,
|
||||
user_username,
|
||||
@@ -536,6 +512,7 @@ class CharacterOwnershipAdmin(BaseOwnershipAdmin):
|
||||
class PermissionAdmin(admin.ModelAdmin):
|
||||
actions = None
|
||||
readonly_fields = [field.name for field in BasePermission._meta.fields]
|
||||
search_fields = ('codename', )
|
||||
list_display = ('admin_name', 'name', 'codename', 'content_type')
|
||||
list_filter = ('content_type__app_label',)
|
||||
|
||||
@@ -543,7 +520,7 @@ class PermissionAdmin(admin.ModelAdmin):
|
||||
def admin_name(obj):
|
||||
return str(obj)
|
||||
|
||||
def has_add_permission(self, request):
|
||||
def has_add_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
|
||||
@@ -43,4 +43,3 @@ AUTHENTICATION_ADMIN_USERS_MAX_GROUPS = \
|
||||
|
||||
AUTHENTICATION_ADMIN_USERS_MAX_CHARS = \
|
||||
_clean_setting('AUTHENTICATION_ADMIN_USERS_MAX_CHARS', 5)
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.auth.models import User
|
||||
import logging
|
||||
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
from django.contrib.auth.models import User, Permission
|
||||
|
||||
from .models import UserProfile, CharacterOwnership, OwnershipRecord
|
||||
|
||||
|
||||
@@ -11,9 +12,11 @@ logger = logging.getLogger(__name__)
|
||||
class StateBackend(ModelBackend):
|
||||
@staticmethod
|
||||
def _get_state_permissions(user_obj):
|
||||
profile_state_field = UserProfile._meta.get_field('state')
|
||||
user_state_query = 'state__%s__user' % profile_state_field.related_query_name()
|
||||
return Permission.objects.filter(**{user_state_query: user_obj})
|
||||
"""returns permissions for state of given user object"""
|
||||
if hasattr(user_obj, "profile") and user_obj.profile:
|
||||
return Permission.objects.filter(state=user_obj.profile.state)
|
||||
else:
|
||||
return Permission.objects.none()
|
||||
|
||||
def get_state_permissions(self, user_obj, obj=None):
|
||||
return self._get_permissions(user_obj, obj, 'state')
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
from allianceauth.authentication.models import User
|
||||
class RegistrationForm(forms.Form):
|
||||
email = forms.EmailField(label=_('Email'), max_length=254, required=True)
|
||||
|
||||
class _meta:
|
||||
model = User
|
||||
|
||||
@@ -10,5 +10,5 @@ urlpatterns = [
|
||||
url(r'^register/$', views.RegistrationView.as_view(), name='registration_register'),
|
||||
url(r'^register/complete/$', views.registration_complete, name='registration_complete'),
|
||||
url(r'^register/closed/$', views.registration_closed, name='registration_disallowed'),
|
||||
url(r'', include('registration.auth_urls')),
|
||||
url(r'', include('django.contrib.auth.urls')),
|
||||
]
|
||||
@@ -23,8 +23,7 @@ class CharacterOwnershipManager(Manager):
|
||||
def create_by_token(self, token):
|
||||
if not EveCharacter.objects.filter(character_id=token.character_id).exists():
|
||||
EveCharacter.objects.create_character(token.character_id)
|
||||
return self.create(character=EveCharacter.objects.get(character_id=token.character_id), user=token.user,
|
||||
owner_hash=token.character_owner_hash)
|
||||
return self.create(character=EveCharacter.objects.get(character_id=token.character_id), user=token.user, owner_hash=token.character_owner_hash)
|
||||
|
||||
|
||||
class StateQuerySet(QuerySet):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-05 21:38
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-07 19:14
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-09 20:29
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-09 23:19
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-09 23:11
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-10 05:42
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-10 21:50
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-12 13:04
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.2 on 2016-10-21 02:28
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2017-01-07 06:47
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2017-01-07 07:11
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-01-12 00:59
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.2 on 2016-12-11 23:14
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-09 23:19
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-03-22 23:09
|
||||
from __future__ import unicode_literals
|
||||
|
||||
@@ -171,8 +170,7 @@ def recreate_authservicesinfo(apps, schema_editor):
|
||||
|
||||
# repopulate main characters
|
||||
for profile in UserProfile.objects.exclude(main_character__isnull=True).select_related('user', 'main_character'):
|
||||
AuthServicesInfo.objects.update_or_create(user=profile.user,
|
||||
defaults={'main_char_id': profile.main_character.character_id})
|
||||
AuthServicesInfo.objects.update_or_create(user=profile.user, defaults={'main_char_id': profile.main_character.character_id})
|
||||
|
||||
# repopulate states we understand
|
||||
for profile in UserProfile.objects.exclude(state__name='Guest').filter(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
@@ -14,15 +14,11 @@ logger = logging.getLogger(__name__)
|
||||
class State(models.Model):
|
||||
name = models.CharField(max_length=20, unique=True)
|
||||
permissions = models.ManyToManyField(Permission, blank=True)
|
||||
priority = models.IntegerField(unique=True,
|
||||
help_text="Users get assigned the state with the highest priority available to them.")
|
||||
priority = models.IntegerField(unique=True, help_text="Users get assigned the state with the highest priority available to them.")
|
||||
|
||||
member_characters = models.ManyToManyField(EveCharacter, blank=True,
|
||||
help_text="Characters to which this state is available.")
|
||||
member_corporations = models.ManyToManyField(EveCorporationInfo, blank=True,
|
||||
help_text="Corporations to whose members this state is available.")
|
||||
member_alliances = models.ManyToManyField(EveAllianceInfo, blank=True,
|
||||
help_text="Alliances to whose members this state is available.")
|
||||
member_characters = models.ManyToManyField(EveCharacter, blank=True, help_text="Characters to which this state is available.")
|
||||
member_corporations = models.ManyToManyField(EveCorporationInfo, blank=True, help_text="Corporations to whose members this state is available.")
|
||||
member_alliances = models.ManyToManyField(EveAllianceInfo, blank=True, help_text="Alliances to whose members this state is available.")
|
||||
public = models.BooleanField(default=False, help_text="Make this state available to any character.")
|
||||
|
||||
objects = StateManager()
|
||||
@@ -73,11 +69,22 @@ class UserProfile(models.Model):
|
||||
if commit:
|
||||
logger.info('Updating {} state to {}'.format(self.user, self.state))
|
||||
self.save(update_fields=['state'])
|
||||
notify(self.user, _('State Changed'),
|
||||
_('Your user state has been changed to %(state)s') % ({'state': state}),
|
||||
'info')
|
||||
notify(
|
||||
self.user,
|
||||
_('State changed to: %s' % state),
|
||||
_('Your user\'s state is now: %(state)s')
|
||||
% ({'state': state}),
|
||||
'info'
|
||||
)
|
||||
from allianceauth.authentication.signals import state_changed
|
||||
state_changed.send(sender=self.__class__, user=self.user, state=self.state)
|
||||
|
||||
# We need to ensure we get up to date perms here as they will have just changed.
|
||||
# Clear all attribute caches and reload the model that will get passed to the signals!
|
||||
self.refresh_from_db()
|
||||
|
||||
state_changed.send(
|
||||
sender=self.__class__, user=self.user, state=self.state
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.user)
|
||||
|
||||
@@ -23,9 +23,7 @@ def trigger_state_check(state):
|
||||
check_states = State.objects.filter(priority__lt=state.priority)
|
||||
for profile in UserProfile.objects.filter(state__in=check_states):
|
||||
if state.available_to_user(profile.user):
|
||||
profile.state = state
|
||||
profile.save(update_fields=['state'])
|
||||
state_changed.send(sender=state.__class__, user=profile.user, state=state)
|
||||
profile.assign_state(state)
|
||||
|
||||
|
||||
@receiver(m2m_changed, sender=State.member_characters.through)
|
||||
@@ -77,8 +75,7 @@ def create_required_models(sender, instance, created, *args, **kwargs):
|
||||
@receiver(post_save, sender=Token)
|
||||
def record_character_ownership(sender, instance, created, *args, **kwargs):
|
||||
if created:
|
||||
logger.debug('New token for {0} character {1} saved. Evaluating ownership.'.format(instance.user,
|
||||
instance.character_name))
|
||||
logger.debug('New token for {0} character {1} saved. Evaluating ownership.'.format(instance.user, instance.character_name))
|
||||
if instance.user:
|
||||
query = Q(owner_hash=instance.character_owner_hash) & Q(user=instance.user)
|
||||
else:
|
||||
@@ -87,18 +84,14 @@ def record_character_ownership(sender, instance, created, *args, **kwargs):
|
||||
CharacterOwnership.objects.filter(character__character_id=instance.character_id).exclude(query).delete()
|
||||
# create character if needed
|
||||
if EveCharacter.objects.filter(character_id=instance.character_id).exists() is False:
|
||||
logger.debug('Token is for a new character. Creating model for {0} ({1})'.format(instance.character_name,
|
||||
instance.character_id))
|
||||
logger.debug('Token is for a new character. Creating model for {0} ({1})'.format(instance.character_name, instance.character_id))
|
||||
EveCharacter.objects.create_character(instance.character_id)
|
||||
char = EveCharacter.objects.get(character_id=instance.character_id)
|
||||
# check if we need to create ownership
|
||||
if instance.user and not CharacterOwnership.objects.filter(
|
||||
character__character_id=instance.character_id).exists():
|
||||
logger.debug("Character {0} is not yet owned. Assigning ownership to {1}".format(instance.character_name,
|
||||
instance.user))
|
||||
CharacterOwnership.objects.update_or_create(character=char,
|
||||
defaults={'owner_hash': instance.character_owner_hash,
|
||||
'user': instance.user})
|
||||
logger.debug("Character {0} is not yet owned. Assigning ownership to {1}".format(instance.character_name, instance.user))
|
||||
CharacterOwnership.objects.update_or_create(character=char, defaults={'owner_hash': instance.character_owner_hash, 'user': instance.user})
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=CharacterOwnership)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "allianceauth/base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{% trans "Dashboard" %}{% endblock %}
|
||||
@@ -14,7 +14,11 @@
|
||||
<div class="col-sm-6 text-center">
|
||||
<div class="panel panel-primary" style="height:100%">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{% trans "Main Character" %}</h3>
|
||||
<h3 class="panel-title">
|
||||
{% blocktrans with state=request.user.profile.state %}
|
||||
Main Character (State: {{ state }})
|
||||
{% endblocktrans %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if request.user.profile.main_character %}
|
||||
@@ -141,8 +145,7 @@
|
||||
</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 %}
|
||||
{% for char in characters %}
|
||||
<tr>
|
||||
<td class="text-center" style="vertical-align: middle">
|
||||
<img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}">
|
||||
@@ -153,7 +156,6 @@
|
||||
{{ char.alliance_name|default:"" }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
<meta property="og:title" content="{{ SITE_NAME }}">
|
||||
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{% static 'icons/apple-touch-icon.png' %}">
|
||||
<meta property="og:description" content="Alliance Auth - An auth system for EVE Online to help in-game organizations manage online service access.">
|
||||
|
||||
{% include 'allianceauth/icons.html' %}
|
||||
|
||||
<title>{% block title %}{{ SITE_NAME }}{% endblock %}</title>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% load staticfiles %}
|
||||
{% extends 'public/base.html' %}
|
||||
{% load static %}
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
{% extends 'public/base.html' %}
|
||||
{% block page_title %}Registration{% endblock %}
|
||||
{% block extra_include %}
|
||||
{% include 'bundles/bootstrap-css.html' %}
|
||||
|
||||
@@ -10,6 +10,7 @@ def get_admin_change_view_url(obj: object) -> str:
|
||||
args=(obj.pk,)
|
||||
)
|
||||
|
||||
|
||||
def get_admin_search_url(ModelClass: type) -> str:
|
||||
"""returns URL to search URL for model of given object"""
|
||||
return '{}{}/'.format(
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
from urllib.parse import quote
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
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.contrib.auth.models import Group
|
||||
from django.test import TestCase, RequestFactory, Client
|
||||
|
||||
from allianceauth.authentication.models import (
|
||||
@@ -13,12 +11,12 @@ from allianceauth.authentication.models import (
|
||||
from allianceauth.eveonline.models import (
|
||||
EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||
)
|
||||
from allianceauth.services.hooks import ServicesHook
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
from ..admin import (
|
||||
BaseUserAdmin,
|
||||
CharacterOwnershipAdmin,
|
||||
PermissionAdmin,
|
||||
StateAdmin,
|
||||
MainCorporationsFilter,
|
||||
MainAllianceFilter,
|
||||
@@ -28,15 +26,12 @@ from ..admin import (
|
||||
user_main_organization,
|
||||
user_profile_pic,
|
||||
user_username,
|
||||
update_main_character_model
|
||||
update_main_character_model,
|
||||
make_service_hooks_update_groups_action,
|
||||
make_service_hooks_sync_nickname_action
|
||||
)
|
||||
from . import get_admin_change_view_url, get_admin_search_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'
|
||||
|
||||
@@ -46,50 +41,60 @@ class MockRequest(object):
|
||||
self.user = user
|
||||
|
||||
|
||||
def create_test_data():
|
||||
class TestCaseWithTestData(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
for MyModel in [
|
||||
EveAllianceInfo, EveCorporationInfo, EveCharacter, Group, User
|
||||
]:
|
||||
MyModel.objects.all().delete()
|
||||
|
||||
# groups
|
||||
group_1 = Group.objects.create(
|
||||
cls.group_1 = Group.objects.create(
|
||||
name='Group 1'
|
||||
)
|
||||
group_2 = Group.objects.create(
|
||||
cls.group_2 = Group.objects.create(
|
||||
name='Group 2'
|
||||
)
|
||||
|
||||
# user 1 - corp and alliance, normal user
|
||||
character_1 = EveCharacter.objects.create(
|
||||
character_id='1001',
|
||||
character_id=1001,
|
||||
character_name='Bruce Wayne',
|
||||
corporation_id='2001',
|
||||
corporation_id=2001,
|
||||
corporation_name='Wayne Technologies',
|
||||
corporation_ticker='WT',
|
||||
alliance_id='3001',
|
||||
alliance_id=3001,
|
||||
alliance_name='Wayne Enterprises',
|
||||
alliance_ticker='WE',
|
||||
)
|
||||
character_1a = EveCharacter.objects.create(
|
||||
character_id='1002',
|
||||
character_id=1002,
|
||||
character_name='Batman',
|
||||
corporation_id='2001',
|
||||
corporation_id=2001,
|
||||
corporation_name='Wayne Technologies',
|
||||
corporation_ticker='WT',
|
||||
alliance_id='3001',
|
||||
alliance_id=3001,
|
||||
alliance_name='Wayne Enterprises',
|
||||
alliance_ticker='WE',
|
||||
)
|
||||
alliance = EveAllianceInfo.objects.create(
|
||||
alliance_id='3001',
|
||||
alliance_id=3001,
|
||||
alliance_name='Wayne Enterprises',
|
||||
alliance_ticker='WE',
|
||||
executor_corp_id='2001'
|
||||
executor_corp_id=2001
|
||||
)
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id='2001',
|
||||
corporation_id=2001,
|
||||
corporation_name='Wayne Technologies',
|
||||
corporation_ticker='WT',
|
||||
member_count=42,
|
||||
alliance=alliance
|
||||
)
|
||||
user_1 = User.objects.create_user(
|
||||
cls.user_1 = User.objects.create_user(
|
||||
character_1.character_name.replace(' ', '_'),
|
||||
'abc@example.com',
|
||||
'password'
|
||||
@@ -97,16 +102,16 @@ def create_test_data():
|
||||
CharacterOwnership.objects.create(
|
||||
character=character_1,
|
||||
owner_hash='x1' + character_1.character_name,
|
||||
user=user_1
|
||||
user=cls.user_1
|
||||
)
|
||||
CharacterOwnership.objects.create(
|
||||
character=character_1a,
|
||||
owner_hash='x1' + character_1a.character_name,
|
||||
user=user_1
|
||||
user=cls.user_1
|
||||
)
|
||||
user_1.profile.main_character = character_1
|
||||
user_1.profile.save()
|
||||
user_1.groups.add(group_1)
|
||||
cls.user_1.profile.main_character = character_1
|
||||
cls.user_1.profile.save()
|
||||
cls.user_1.groups.add(cls.group_1)
|
||||
|
||||
# user 2 - corp only, staff
|
||||
character_2 = EveCharacter.objects.create(
|
||||
@@ -124,7 +129,7 @@ def create_test_data():
|
||||
member_count=99,
|
||||
alliance=None
|
||||
)
|
||||
user_2 = User.objects.create_user(
|
||||
cls.user_2 = User.objects.create_user(
|
||||
character_2.character_name.replace(' ', '_'),
|
||||
'abc@example.com',
|
||||
'password'
|
||||
@@ -132,13 +137,13 @@ def create_test_data():
|
||||
CharacterOwnership.objects.create(
|
||||
character=character_2,
|
||||
owner_hash='x1' + character_2.character_name,
|
||||
user=user_2
|
||||
user=cls.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()
|
||||
cls.user_2.profile.main_character = 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
|
||||
character_3 = EveCharacter.objects.create(
|
||||
@@ -157,12 +162,12 @@ def create_test_data():
|
||||
alliance=None
|
||||
)
|
||||
EveAllianceInfo.objects.create(
|
||||
alliance_id='3101',
|
||||
alliance_id=3101,
|
||||
alliance_name='Lex World Domination',
|
||||
alliance_ticker='LWD',
|
||||
executor_corp_id=''
|
||||
executor_corp_id=2101
|
||||
)
|
||||
user_3 = User.objects.create_user(
|
||||
cls.user_3 = User.objects.create_user(
|
||||
character_3.character_name.replace(' ', '_'),
|
||||
'abc@example.com',
|
||||
'password'
|
||||
@@ -170,11 +175,10 @@ def create_test_data():
|
||||
CharacterOwnership.objects.create(
|
||||
character=character_3,
|
||||
owner_hash='x1' + character_3.character_name,
|
||||
user=user_3
|
||||
user=cls.user_3
|
||||
)
|
||||
user_3.is_superuser = True
|
||||
user_3.save()
|
||||
return user_1, user_2, user_3, group_1, group_2
|
||||
cls.user_3.is_superuser = True
|
||||
cls.user_3.save()
|
||||
|
||||
|
||||
def make_generic_search_request(ModelClass: type, search_term: str):
|
||||
@@ -188,12 +192,7 @@ def make_generic_search_request(ModelClass: type, search_term: str):
|
||||
)
|
||||
|
||||
|
||||
class TestCharacterOwnershipAdmin(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.user_1, _, _, _, _ = create_test_data()
|
||||
class TestCharacterOwnershipAdmin(TestCaseWithTestData):
|
||||
|
||||
def setUp(self):
|
||||
self.modeladmin = CharacterOwnershipAdmin(
|
||||
@@ -219,12 +218,7 @@ class TestCharacterOwnershipAdmin(TestCase):
|
||||
self.assertEqual(response.status_code, expected)
|
||||
|
||||
|
||||
class TestOwnershipRecordAdmin(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.user_1, _, _, _, _ = create_test_data()
|
||||
class TestOwnershipRecordAdmin(TestCaseWithTestData):
|
||||
|
||||
def setUp(self):
|
||||
self.modeladmin = OwnershipRecordAdmin(
|
||||
@@ -250,12 +244,7 @@ class TestOwnershipRecordAdmin(TestCase):
|
||||
self.assertEqual(response.status_code, expected)
|
||||
|
||||
|
||||
class TestStateAdmin(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
create_test_data()
|
||||
class TestStateAdmin(TestCaseWithTestData):
|
||||
|
||||
def setUp(self):
|
||||
self.modeladmin = StateAdmin(
|
||||
@@ -283,13 +272,8 @@ class TestStateAdmin(TestCase):
|
||||
expected = 200
|
||||
self.assertEqual(response.status_code, expected)
|
||||
|
||||
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()
|
||||
class TestUserAdmin(TestCaseWithTestData):
|
||||
|
||||
def setUp(self):
|
||||
self.factory = RequestFactory()
|
||||
@@ -298,23 +282,11 @@ class TestUserAdmin(TestCase):
|
||||
)
|
||||
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">')
|
||||
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):
|
||||
@@ -362,36 +334,16 @@ class TestUserAdmin(TestCase):
|
||||
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)
|
||||
|
||||
@@ -423,8 +375,10 @@ class TestUserAdmin(TestCase):
|
||||
|
||||
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>')
|
||||
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)
|
||||
|
||||
@@ -450,62 +404,6 @@ class TestUserAdmin(TestCase):
|
||||
|
||||
# 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):
|
||||
@@ -520,8 +418,8 @@ class TestUserAdmin(TestCase):
|
||||
filters = changelist.get_filters(request)
|
||||
filterspec = filters[0][0]
|
||||
expected = [
|
||||
('2002', 'Daily Planet'),
|
||||
('2001', 'Wayne Technologies'),
|
||||
(2002, 'Daily Planet'),
|
||||
(2001, 'Wayne Technologies'),
|
||||
]
|
||||
self.assertEqual(filterspec.lookup_choices, expected)
|
||||
|
||||
@@ -550,7 +448,7 @@ class TestUserAdmin(TestCase):
|
||||
filters = changelist.get_filters(request)
|
||||
filterspec = filters[0][0]
|
||||
expected = [
|
||||
('3001', 'Wayne Enterprises'),
|
||||
(3001, 'Wayne Enterprises'),
|
||||
]
|
||||
self.assertEqual(filterspec.lookup_choices, expected)
|
||||
|
||||
@@ -579,3 +477,66 @@ class TestUserAdmin(TestCase):
|
||||
response = make_generic_search_request(type(obj), obj.username)
|
||||
expected = 200
|
||||
self.assertEqual(response.status_code, expected)
|
||||
|
||||
|
||||
class TestMakeServicesHooksActions(TestCaseWithTestData):
|
||||
|
||||
class MyServicesHookTypeA(ServicesHook):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'My Service A'
|
||||
|
||||
def update_groups(self, user):
|
||||
pass
|
||||
|
||||
def sync_nicknames(self, user):
|
||||
pass
|
||||
|
||||
class MyServicesHookTypeB(ServicesHook):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'My Service B'
|
||||
|
||||
def update_groups(self, user):
|
||||
pass
|
||||
|
||||
def update_groups_bulk(self, user):
|
||||
pass
|
||||
|
||||
def sync_nicknames(self, user):
|
||||
pass
|
||||
|
||||
def sync_nicknames_bulk(self, user):
|
||||
pass
|
||||
|
||||
def test_service_has_update_groups_only(self):
|
||||
service = self.MyServicesHookTypeA()
|
||||
mock_service = MagicMock(spec=service)
|
||||
action = make_service_hooks_update_groups_action(mock_service)
|
||||
action(MagicMock(), MagicMock(), [self.user_1])
|
||||
self.assertTrue(mock_service.update_groups.called)
|
||||
|
||||
def test_service_has_update_groups_bulk(self):
|
||||
service = self.MyServicesHookTypeB()
|
||||
mock_service = MagicMock(spec=service)
|
||||
action = make_service_hooks_update_groups_action(mock_service)
|
||||
action(MagicMock(), MagicMock(), [self.user_1])
|
||||
self.assertFalse(mock_service.update_groups.called)
|
||||
self.assertTrue(mock_service.update_groups_bulk.called)
|
||||
|
||||
def test_service_has_sync_nickname_only(self):
|
||||
service = self.MyServicesHookTypeA()
|
||||
mock_service = MagicMock(spec=service)
|
||||
action = make_service_hooks_sync_nickname_action(mock_service)
|
||||
action(MagicMock(), MagicMock(), [self.user_1])
|
||||
self.assertTrue(mock_service.sync_nickname.called)
|
||||
|
||||
def test_service_has_sync_nicknames_bulk(self):
|
||||
service = self.MyServicesHookTypeB()
|
||||
mock_service = MagicMock(spec=service)
|
||||
action = make_service_hooks_sync_nickname_action(mock_service)
|
||||
action(MagicMock(), MagicMock(), [self.user_1])
|
||||
self.assertFalse(mock_service.sync_nickname.called)
|
||||
self.assertTrue(mock_service.sync_nicknames_bulk.called)
|
||||
|
||||
149
allianceauth/authentication/tests/test_backend.py
Normal file
149
allianceauth/authentication/tests/test_backend.py
Normal file
@@ -0,0 +1,149 @@
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.test import TestCase
|
||||
|
||||
from allianceauth.eveonline.models import EveCharacter
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
from esi.models import Token
|
||||
|
||||
from ..backends import StateBackend
|
||||
from ..models import CharacterOwnership, UserProfile, OwnershipRecord
|
||||
|
||||
MODULE_PATH = 'allianceauth.authentication'
|
||||
|
||||
PERMISSION_1 = "authentication.add_user"
|
||||
PERMISSION_2 = "authentication.change_user"
|
||||
|
||||
|
||||
class TestStatePermissions(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# permissions
|
||||
self.permission_1 = AuthUtils.get_permission_by_name(PERMISSION_1)
|
||||
self.permission_2 = AuthUtils.get_permission_by_name(PERMISSION_2)
|
||||
|
||||
# group
|
||||
self.group_1 = Group.objects.create(name="Group 1")
|
||||
self.group_2 = Group.objects.create(name="Group 2")
|
||||
|
||||
# state
|
||||
self.state_1 = AuthUtils.get_member_state()
|
||||
self.state_2 = AuthUtils.create_state("Other State", 75)
|
||||
|
||||
# user
|
||||
self.user = AuthUtils.create_user("Bruce Wayne")
|
||||
self.main = AuthUtils.add_main_character_2(self.user, self.user.username, 123)
|
||||
|
||||
def test_user_has_user_permissions(self):
|
||||
self.user.user_permissions.add(self.permission_1)
|
||||
|
||||
user = User.objects.get(pk=self.user.pk)
|
||||
self.assertTrue(user.has_perm(PERMISSION_1))
|
||||
|
||||
def test_user_has_group_permissions(self):
|
||||
self.group_1.permissions.add(self.permission_1)
|
||||
self.user.groups.add(self.group_1)
|
||||
|
||||
user = User.objects.get(pk=self.user.pk)
|
||||
self.assertTrue(user.has_perm(PERMISSION_1))
|
||||
|
||||
def test_user_has_state_permissions(self):
|
||||
self.state_1.permissions.add(self.permission_1)
|
||||
self.state_1.member_characters.add(self.main)
|
||||
user = User.objects.get(pk=self.user.pk)
|
||||
|
||||
self.assertTrue(user.has_perm(PERMISSION_1))
|
||||
|
||||
def test_when_user_changes_state_perms_change_accordingly(self):
|
||||
self.state_1.permissions.add(self.permission_1)
|
||||
self.state_1.member_characters.add(self.main)
|
||||
user = User.objects.get(pk=self.user.pk)
|
||||
self.assertTrue(user.has_perm(PERMISSION_1))
|
||||
|
||||
self.state_2.permissions.add(self.permission_2)
|
||||
self.state_2.member_characters.add(self.main)
|
||||
self.state_1.member_characters.remove(self.main)
|
||||
user = User.objects.get(pk=self.user.pk)
|
||||
self.assertFalse(user.has_perm(PERMISSION_1))
|
||||
self.assertTrue(user.has_perm(PERMISSION_2))
|
||||
|
||||
def test_state_permissions_are_returned_for_current_user_object(self):
|
||||
# verify state permissions are returns for the current user object
|
||||
# and not for it's instance in the database, which might be outdated
|
||||
self.state_1.permissions.add(self.permission_1)
|
||||
self.state_2.permissions.add(self.permission_2)
|
||||
self.state_1.member_characters.add(self.main)
|
||||
user = User.objects.get(pk=self.user.pk)
|
||||
user.profile.state = self.state_2
|
||||
self.assertFalse(user.has_perm(PERMISSION_1))
|
||||
self.assertTrue(user.has_perm(PERMISSION_2))
|
||||
|
||||
|
||||
class TestAuthenticate(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.main_character = EveCharacter.objects.create(
|
||||
character_id=1,
|
||||
character_name='Main Character',
|
||||
corporation_id=1,
|
||||
corporation_name='Corp',
|
||||
corporation_ticker='CORP',
|
||||
)
|
||||
cls.alt_character = EveCharacter.objects.create(
|
||||
character_id=2,
|
||||
character_name='Alt Character',
|
||||
corporation_id=1,
|
||||
corporation_name='Corp',
|
||||
corporation_ticker='CORP',
|
||||
)
|
||||
cls.unclaimed_character = EveCharacter.objects.create(
|
||||
character_id=3,
|
||||
character_name='Unclaimed Character',
|
||||
corporation_id=1,
|
||||
corporation_name='Corp',
|
||||
corporation_ticker='CORP',
|
||||
)
|
||||
cls.user = AuthUtils.create_user('test_user', disconnect_signals=True)
|
||||
cls.old_user = AuthUtils.create_user('old_user', disconnect_signals=True)
|
||||
AuthUtils.disconnect_signals()
|
||||
CharacterOwnership.objects.create(user=cls.user, character=cls.main_character, owner_hash='1')
|
||||
CharacterOwnership.objects.create(user=cls.user, character=cls.alt_character, owner_hash='2')
|
||||
UserProfile.objects.update_or_create(user=cls.user, defaults={'main_character': cls.main_character})
|
||||
AuthUtils.connect_signals()
|
||||
|
||||
def test_authenticate_main_character(self):
|
||||
t = Token(character_id=self.main_character.character_id, character_owner_hash='1')
|
||||
user = StateBackend().authenticate(token=t)
|
||||
self.assertEquals(user, self.user)
|
||||
|
||||
def test_authenticate_alt_character(self):
|
||||
t = Token(character_id=self.alt_character.character_id, character_owner_hash='2')
|
||||
user = StateBackend().authenticate(token=t)
|
||||
self.assertEquals(user, self.user)
|
||||
|
||||
def test_authenticate_unclaimed_character(self):
|
||||
t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='3')
|
||||
user = StateBackend().authenticate(token=t)
|
||||
self.assertNotEqual(user, self.user)
|
||||
self.assertEqual(user.username, 'Unclaimed_Character')
|
||||
self.assertEqual(user.profile.main_character, self.unclaimed_character)
|
||||
|
||||
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')
|
||||
OwnershipRecord.objects.create(user=self.old_user, character=self.unclaimed_character, owner_hash='4')
|
||||
user = StateBackend().authenticate(token=t)
|
||||
self.assertEqual(user, self.old_user)
|
||||
self.assertTrue(CharacterOwnership.objects.filter(owner_hash='4', user=self.old_user).exists())
|
||||
self.assertTrue(user.profile.main_character)
|
||||
|
||||
def test_iterate_username(self):
|
||||
t = Token(character_id=self.unclaimed_character.character_id,
|
||||
character_name=self.unclaimed_character.character_name, character_owner_hash='3')
|
||||
username = StateBackend().authenticate(token=t).username
|
||||
t.character_owner_hash = '4'
|
||||
username_1 = StateBackend().authenticate(token=t).username
|
||||
t.character_owner_hash = '5'
|
||||
username_2 = StateBackend().authenticate(token=t).username
|
||||
self.assertNotEqual(username, username_1, username_2)
|
||||
self.assertTrue(username_1.endswith('_1'))
|
||||
self.assertTrue(username_2.endswith('_2'))
|
||||
35
allianceauth/authentication/tests/test_commands.py
Normal file
35
allianceauth/authentication/tests/test_commands.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from io import StringIO
|
||||
|
||||
from django.core.management import call_command
|
||||
from django.test import TestCase
|
||||
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
from ..models import CharacterOwnership, UserProfile
|
||||
|
||||
|
||||
class ManagementCommandTestCase(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.user = AuthUtils.create_user('test user', disconnect_signals=True)
|
||||
AuthUtils.add_main_character(cls.user, 'test character', '1', '2', 'test corp', 'test')
|
||||
character = UserProfile.objects.get(user=cls.user).main_character
|
||||
CharacterOwnership.objects.create(user=cls.user, character=character, owner_hash='test')
|
||||
|
||||
def setUp(self):
|
||||
self.stdout = StringIO()
|
||||
|
||||
def test_ownership(self):
|
||||
call_command('checkmains', stdout=self.stdout)
|
||||
self.assertFalse(UserProfile.objects.filter(main_character__isnull=True).count())
|
||||
self.assertNotIn(self.user.username, self.stdout.getvalue())
|
||||
self.assertIn('All main characters', self.stdout.getvalue())
|
||||
|
||||
def test_no_ownership(self):
|
||||
user = AuthUtils.create_user('v1 user', disconnect_signals=True)
|
||||
AuthUtils.add_main_character(user, 'v1 character', '10', '20', 'test corp', 'test')
|
||||
self.assertFalse(UserProfile.objects.filter(main_character__isnull=True).count())
|
||||
|
||||
call_command('checkmains', stdout=self.stdout)
|
||||
self.assertEqual(UserProfile.objects.filter(main_character__isnull=True).count(), 1)
|
||||
self.assertIn(user.username, self.stdout.getvalue())
|
||||
68
allianceauth/authentication/tests/test_decorators.py
Normal file
68
allianceauth/authentication/tests/test_decorators.py
Normal file
@@ -0,0 +1,68 @@
|
||||
from unittest import mock
|
||||
from urllib import parse
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.http.response import HttpResponse
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
from allianceauth.eveonline.models import EveCharacter
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
from ..decorators import main_character_required
|
||||
from ..models import CharacterOwnership
|
||||
|
||||
|
||||
MODULE_PATH = 'allianceauth.authentication'
|
||||
|
||||
|
||||
class DecoratorTestCase(TestCase):
|
||||
@staticmethod
|
||||
@main_character_required
|
||||
def dummy_view(*args, **kwargs):
|
||||
return HttpResponse(status=200)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.main_user = AuthUtils.create_user('main_user', disconnect_signals=True)
|
||||
cls.no_main_user = AuthUtils.create_user(
|
||||
'no_main_user', disconnect_signals=True
|
||||
)
|
||||
main_character = EveCharacter.objects.create(
|
||||
character_id=1,
|
||||
character_name='Main Character',
|
||||
corporation_id=1,
|
||||
corporation_name='Corp',
|
||||
corporation_ticker='CORP',
|
||||
)
|
||||
CharacterOwnership.objects.create(
|
||||
user=cls.main_user, character=main_character, owner_hash='1'
|
||||
)
|
||||
cls.main_user.profile.main_character = main_character
|
||||
|
||||
def setUp(self):
|
||||
self.request = RequestFactory().get('/test/')
|
||||
|
||||
@mock.patch(MODULE_PATH + '.decorators.messages')
|
||||
def test_login_redirect(self, m):
|
||||
setattr(self.request, 'user', AnonymousUser())
|
||||
response = self.dummy_view(self.request)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
url = getattr(response, 'url', None)
|
||||
self.assertEqual(parse.urlparse(url).path, reverse(settings.LOGIN_URL))
|
||||
|
||||
@mock.patch(MODULE_PATH + '.decorators.messages')
|
||||
def test_main_character_redirect(self, m):
|
||||
setattr(self.request, 'user', self.no_main_user)
|
||||
response = self.dummy_view(self.request)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
url = getattr(response, 'url', None)
|
||||
self.assertEqual(url, reverse('authentication:dashboard'))
|
||||
|
||||
@mock.patch(MODULE_PATH + '.decorators.messages')
|
||||
def test_successful_request(self, m):
|
||||
setattr(self.request, 'user', self.main_user)
|
||||
response = self.dummy_view(self.request)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@@ -1,147 +1,20 @@
|
||||
from unittest import mock
|
||||
from io import StringIO
|
||||
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.contrib.auth.models import User
|
||||
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 ..models import CharacterOwnership, State, get_guest_state
|
||||
from ..tasks import check_character_ownership
|
||||
|
||||
MODULE_PATH = 'allianceauth.authentication'
|
||||
|
||||
|
||||
class DecoratorTestCase(TestCase):
|
||||
@staticmethod
|
||||
@main_character_required
|
||||
def dummy_view(*args, **kwargs):
|
||||
return HttpResponse(status=200)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.main_user = AuthUtils.create_user('main_user', disconnect_signals=True)
|
||||
cls.no_main_user = AuthUtils.create_user('no_main_user', disconnect_signals=True)
|
||||
main_character = EveCharacter.objects.create(
|
||||
character_id=1,
|
||||
character_name='Main Character',
|
||||
corporation_id=1,
|
||||
corporation_name='Corp',
|
||||
corporation_ticker='CORP',
|
||||
)
|
||||
CharacterOwnership.objects.create(user=cls.main_user, character=main_character, owner_hash='1')
|
||||
cls.main_user.profile.main_character = main_character
|
||||
|
||||
def setUp(self):
|
||||
self.request = RequestFactory().get('/test/')
|
||||
|
||||
@mock.patch(MODULE_PATH + '.decorators.messages')
|
||||
def test_login_redirect(self, m):
|
||||
setattr(self.request, 'user', AnonymousUser())
|
||||
response = self.dummy_view(self.request)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
url = getattr(response, 'url', None)
|
||||
self.assertEqual(parse.urlparse(url).path, reverse(settings.LOGIN_URL))
|
||||
|
||||
@mock.patch(MODULE_PATH + '.decorators.messages')
|
||||
def test_main_character_redirect(self, m):
|
||||
setattr(self.request, 'user', self.no_main_user)
|
||||
response = self.dummy_view(self.request)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
url = getattr(response, 'url', None)
|
||||
self.assertEqual(url, reverse('authentication:dashboard'))
|
||||
|
||||
@mock.patch(MODULE_PATH + '.decorators.messages')
|
||||
def test_successful_request(self, m):
|
||||
setattr(self.request, 'user', self.main_user)
|
||||
response = self.dummy_view(self.request)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class BackendTestCase(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.main_character = EveCharacter.objects.create(
|
||||
character_id=1,
|
||||
character_name='Main Character',
|
||||
corporation_id=1,
|
||||
corporation_name='Corp',
|
||||
corporation_ticker='CORP',
|
||||
)
|
||||
cls.alt_character = EveCharacter.objects.create(
|
||||
character_id=2,
|
||||
character_name='Alt Character',
|
||||
corporation_id=1,
|
||||
corporation_name='Corp',
|
||||
corporation_ticker='CORP',
|
||||
)
|
||||
cls.unclaimed_character = EveCharacter.objects.create(
|
||||
character_id=3,
|
||||
character_name='Unclaimed Character',
|
||||
corporation_id=1,
|
||||
corporation_name='Corp',
|
||||
corporation_ticker='CORP',
|
||||
)
|
||||
cls.user = AuthUtils.create_user('test_user', disconnect_signals=True)
|
||||
cls.old_user = AuthUtils.create_user('old_user', disconnect_signals=True)
|
||||
AuthUtils.disconnect_signals()
|
||||
CharacterOwnership.objects.create(user=cls.user, character=cls.main_character, owner_hash='1')
|
||||
CharacterOwnership.objects.create(user=cls.user, character=cls.alt_character, owner_hash='2')
|
||||
UserProfile.objects.update_or_create(user=cls.user, defaults={'main_character': cls.main_character})
|
||||
AuthUtils.connect_signals()
|
||||
|
||||
def test_authenticate_main_character(self):
|
||||
t = Token(character_id=self.main_character.character_id, character_owner_hash='1')
|
||||
user = StateBackend().authenticate(token=t)
|
||||
self.assertEquals(user, self.user)
|
||||
|
||||
def test_authenticate_alt_character(self):
|
||||
t = Token(character_id=self.alt_character.character_id, character_owner_hash='2')
|
||||
user = StateBackend().authenticate(token=t)
|
||||
self.assertEquals(user, self.user)
|
||||
|
||||
def test_authenticate_unclaimed_character(self):
|
||||
t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='3')
|
||||
user = StateBackend().authenticate(token=t)
|
||||
self.assertNotEqual(user, self.user)
|
||||
self.assertEqual(user.username, 'Unclaimed_Character')
|
||||
self.assertEqual(user.profile.main_character, self.unclaimed_character)
|
||||
|
||||
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')
|
||||
record = OwnershipRecord.objects.create(user=self.old_user, character=self.unclaimed_character, owner_hash='4')
|
||||
user = StateBackend().authenticate(token=t)
|
||||
self.assertEqual(user, self.old_user)
|
||||
self.assertTrue(CharacterOwnership.objects.filter(owner_hash='4', user=self.old_user).exists())
|
||||
self.assertTrue(user.profile.main_character)
|
||||
|
||||
def test_iterate_username(self):
|
||||
t = Token(character_id=self.unclaimed_character.character_id,
|
||||
character_name=self.unclaimed_character.character_name, character_owner_hash='3')
|
||||
username = StateBackend().authenticate(token=t).username
|
||||
t.character_owner_hash = '4'
|
||||
username_1 = StateBackend().authenticate(token=t).username
|
||||
t.character_owner_hash = '5'
|
||||
username_2 = StateBackend().authenticate(token=t).username
|
||||
self.assertNotEqual(username, username_1, username_2)
|
||||
self.assertTrue(username_1.endswith('_1'))
|
||||
self.assertTrue(username_2.endswith('_2'))
|
||||
|
||||
|
||||
class CharacterOwnershipTestCase(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@@ -343,10 +216,10 @@ class CharacterOwnershipCheckTestCase(TestCase):
|
||||
cls.user = AuthUtils.create_user('test_user', disconnect_signals=True)
|
||||
AuthUtils.add_main_character(cls.user, 'Test Character', '1', corp_id='1', alliance_id='1',
|
||||
corp_name='Test Corp', alliance_name='Test Alliance')
|
||||
cls.character = EveCharacter.objects.get(character_id='1')
|
||||
cls.character = EveCharacter.objects.get(character_id=1)
|
||||
cls.token = Token.objects.create(
|
||||
user=cls.user,
|
||||
character_id='1',
|
||||
character_id=1,
|
||||
character_name='Test',
|
||||
character_owner_hash='1',
|
||||
)
|
||||
@@ -378,30 +251,3 @@ class CharacterOwnershipCheckTestCase(TestCase):
|
||||
filter.return_value.exists.return_value = False
|
||||
check_character_ownership(self.ownership)
|
||||
self.assertTrue(filter.return_value.delete.called)
|
||||
|
||||
|
||||
class ManagementCommandTestCase(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.user = AuthUtils.create_user('test user', disconnect_signals=True)
|
||||
AuthUtils.add_main_character(cls.user, 'test character', '1', '2', 'test corp', 'test')
|
||||
character = UserProfile.objects.get(user=cls.user).main_character
|
||||
CharacterOwnership.objects.create(user=cls.user, character=character, owner_hash='test')
|
||||
|
||||
def setUp(self):
|
||||
self.stdout = StringIO()
|
||||
|
||||
def test_ownership(self):
|
||||
call_command('checkmains', stdout=self.stdout)
|
||||
self.assertFalse(UserProfile.objects.filter(main_character__isnull=True).count())
|
||||
self.assertNotIn(self.user.username, self.stdout.getvalue())
|
||||
self.assertIn('All main characters', self.stdout.getvalue())
|
||||
|
||||
def test_no_ownership(self):
|
||||
user = AuthUtils.create_user('v1 user', disconnect_signals=True)
|
||||
AuthUtils.add_main_character(user, 'v1 character', '10', '20', 'test corp', 'test')
|
||||
self.assertFalse(UserProfile.objects.filter(main_character__isnull=True).count())
|
||||
|
||||
call_command('checkmains', stdout=self.stdout)
|
||||
self.assertEqual(UserProfile.objects.filter(main_character__isnull=True).count(), 1)
|
||||
self.assertIn(user.username, self.stdout.getvalue())
|
||||
319
allianceauth/authentication/tests/test_templatetags.py
Normal file
319
allianceauth/authentication/tests/test_templatetags.py
Normal file
@@ -0,0 +1,319 @@
|
||||
from math import ceil
|
||||
from unittest.mock import patch
|
||||
|
||||
import requests_mock
|
||||
from packaging.version import Version as Pep440Version
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.test import TestCase
|
||||
|
||||
from allianceauth.templatetags.admin_status import (
|
||||
status_overview,
|
||||
_fetch_list_from_gitlab,
|
||||
_current_notifications,
|
||||
_current_version_summary,
|
||||
_fetch_notification_issues_from_gitlab,
|
||||
_latests_versions
|
||||
)
|
||||
|
||||
MODULE_PATH = 'allianceauth.templatetags'
|
||||
|
||||
|
||||
def create_tags_list(tag_names: list):
|
||||
return [{'name': str(tag_name)} for tag_name in tag_names]
|
||||
|
||||
|
||||
GITHUB_TAGS = create_tags_list(['v2.4.6a1', 'v2.4.5', 'v2.4.0', 'v2.0.0', 'v1.1.1'])
|
||||
GITHUB_NOTIFICATION_ISSUES = [
|
||||
{
|
||||
'id': 1,
|
||||
'title': 'first issue'
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'title': 'second issue'
|
||||
},
|
||||
{
|
||||
'id': 3,
|
||||
'title': 'third issue'
|
||||
},
|
||||
{
|
||||
'id': 4,
|
||||
'title': 'forth issue'
|
||||
},
|
||||
{
|
||||
'id': 5,
|
||||
'title': 'fifth issue'
|
||||
},
|
||||
{
|
||||
'id': 6,
|
||||
'title': 'sixth issue'
|
||||
},
|
||||
]
|
||||
TEST_VERSION = '2.6.5'
|
||||
|
||||
|
||||
class TestStatusOverviewTag(TestCase):
|
||||
|
||||
@patch(MODULE_PATH + '.admin_status.__version__', TEST_VERSION)
|
||||
@patch(MODULE_PATH + '.admin_status._fetch_celery_queue_length')
|
||||
@patch(MODULE_PATH + '.admin_status._current_version_summary')
|
||||
@patch(MODULE_PATH + '.admin_status._current_notifications')
|
||||
def test_status_overview(
|
||||
self,
|
||||
mock_current_notifications,
|
||||
mock_current_version_info,
|
||||
mock_fetch_celery_queue_length
|
||||
):
|
||||
notifications = {
|
||||
'notifications': GITHUB_NOTIFICATION_ISSUES[:5]
|
||||
}
|
||||
mock_current_notifications.return_value = notifications
|
||||
version_info = {
|
||||
'latest_major': True,
|
||||
'latest_minor': True,
|
||||
'latest_patch': True,
|
||||
'latest_beta': False,
|
||||
'current_version': TEST_VERSION,
|
||||
'latest_major_version': '2.4.5',
|
||||
'latest_minor_version': '2.4.0',
|
||||
'latest_patch_version': '2.4.5',
|
||||
'latest_beta_version': '2.4.4a1',
|
||||
}
|
||||
mock_current_version_info.return_value = version_info
|
||||
mock_fetch_celery_queue_length.return_value = 3
|
||||
|
||||
result = status_overview()
|
||||
expected = {
|
||||
'notifications': GITHUB_NOTIFICATION_ISSUES[:5],
|
||||
'latest_major': True,
|
||||
'latest_minor': True,
|
||||
'latest_patch': True,
|
||||
'latest_beta': False,
|
||||
'current_version': TEST_VERSION,
|
||||
'latest_major_version': '2.4.5',
|
||||
'latest_minor_version': '2.4.0',
|
||||
'latest_patch_version': '2.4.5',
|
||||
'latest_beta_version': '2.4.4a1',
|
||||
'task_queue_length': 3,
|
||||
}
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
|
||||
class TestNotifications(TestCase):
|
||||
|
||||
def setUp(self) -> None:
|
||||
cache.clear()
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_fetch_notification_issues_from_gitlab(self, requests_mocker):
|
||||
# given
|
||||
url = (
|
||||
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth/issues'
|
||||
'?labels=announcement'
|
||||
)
|
||||
requests_mocker.get(url, json=GITHUB_NOTIFICATION_ISSUES)
|
||||
# when
|
||||
result = _fetch_notification_issues_from_gitlab()
|
||||
# then
|
||||
self.assertEqual(result, GITHUB_NOTIFICATION_ISSUES)
|
||||
|
||||
@patch(MODULE_PATH + '.admin_status.cache')
|
||||
def test_current_notifications_normal(self, mock_cache):
|
||||
# given
|
||||
mock_cache.get_or_set.return_value = GITHUB_NOTIFICATION_ISSUES
|
||||
# when
|
||||
result = _current_notifications()
|
||||
# then
|
||||
self.assertEqual(result['notifications'], GITHUB_NOTIFICATION_ISSUES[:5])
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_current_notifications_failed(self, requests_mocker):
|
||||
# given
|
||||
url = (
|
||||
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth/issues'
|
||||
'?labels=announcement'
|
||||
)
|
||||
requests_mocker.get(url, status_code=404)
|
||||
# when
|
||||
result = _current_notifications()
|
||||
# then
|
||||
self.assertEqual(result['notifications'], list())
|
||||
|
||||
@patch(MODULE_PATH + '.admin_status.cache')
|
||||
def test_current_notifications_is_none(self, mock_cache):
|
||||
# given
|
||||
mock_cache.get_or_set.return_value = None
|
||||
# when
|
||||
result = _current_notifications()
|
||||
# then
|
||||
self.assertEqual(result['notifications'], list())
|
||||
|
||||
|
||||
class TestCeleryQueueLength(TestCase):
|
||||
|
||||
def test_get_celery_queue_length(self):
|
||||
pass
|
||||
|
||||
|
||||
class TestVersionTags(TestCase):
|
||||
|
||||
def setUp(self) -> None:
|
||||
cache.clear()
|
||||
|
||||
@patch(MODULE_PATH + '.admin_status.__version__', TEST_VERSION)
|
||||
@patch(MODULE_PATH + '.admin_status.cache')
|
||||
def test_current_version_info_normal(self, mock_cache):
|
||||
# given
|
||||
mock_cache.get_or_set.return_value = GITHUB_TAGS
|
||||
# when
|
||||
result = _current_version_summary()
|
||||
# then
|
||||
self.assertTrue(result['latest_major'])
|
||||
self.assertTrue(result['latest_minor'])
|
||||
self.assertTrue(result['latest_patch'])
|
||||
self.assertEqual(result['latest_major_version'], '2.0.0')
|
||||
self.assertEqual(result['latest_minor_version'], '2.4.0')
|
||||
self.assertEqual(result['latest_patch_version'], '2.4.5')
|
||||
self.assertEqual(result['latest_beta_version'], '2.4.6a1')
|
||||
|
||||
@patch(MODULE_PATH + '.admin_status.__version__', TEST_VERSION)
|
||||
@requests_mock.mock()
|
||||
def test_current_version_info_failed(self, requests_mocker):
|
||||
# given
|
||||
url = (
|
||||
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth'
|
||||
'/repository/tags'
|
||||
)
|
||||
requests_mocker.get(url, status_code=500)
|
||||
# when
|
||||
result = _current_version_summary()
|
||||
# then
|
||||
self.assertEqual(result, {})
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_fetch_tags_from_gitlab(self, requests_mocker):
|
||||
# given
|
||||
url = (
|
||||
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth'
|
||||
'/repository/tags'
|
||||
)
|
||||
requests_mocker.get(url, json=GITHUB_TAGS)
|
||||
# when
|
||||
result = _current_version_summary()
|
||||
# then
|
||||
self.assertTrue(result)
|
||||
|
||||
@patch(MODULE_PATH + '.admin_status.__version__', TEST_VERSION)
|
||||
@patch(MODULE_PATH + '.admin_status.cache')
|
||||
def test_current_version_info_return_no_data(self, mock_cache):
|
||||
# given
|
||||
mock_cache.get_or_set.return_value = None
|
||||
# when
|
||||
result = _current_version_summary()
|
||||
# then
|
||||
self.assertEqual(result, {})
|
||||
|
||||
|
||||
class TestLatestsVersion(TestCase):
|
||||
|
||||
def test_all_version_types_defined(self):
|
||||
|
||||
tags = create_tags_list(
|
||||
['2.1.1', '2.1.0', '2.0.0', '2.1.1a1', '1.1.1', '1.1.0', '1.0.0']
|
||||
)
|
||||
major, minor, patch, beta = _latests_versions(tags)
|
||||
self.assertEqual(major, Pep440Version('2.0.0'))
|
||||
self.assertEqual(minor, Pep440Version('2.1.0'))
|
||||
self.assertEqual(patch, Pep440Version('2.1.1'))
|
||||
self.assertEqual(beta, Pep440Version('2.1.1a1'))
|
||||
|
||||
def test_major_and_minor_not_defined_with_zero(self):
|
||||
|
||||
tags = create_tags_list(
|
||||
['2.1.2', '2.1.1', '2.0.1', '2.1.1a1', '1.1.1', '1.1.0', '1.0.0']
|
||||
)
|
||||
major, minor, patch, beta = _latests_versions(tags)
|
||||
self.assertEqual(major, Pep440Version('2.0.1'))
|
||||
self.assertEqual(minor, Pep440Version('2.1.1'))
|
||||
self.assertEqual(patch, Pep440Version('2.1.2'))
|
||||
self.assertEqual(beta, Pep440Version('2.1.1a1'))
|
||||
|
||||
def test_can_ignore_invalid_versions(self):
|
||||
|
||||
tags = create_tags_list(
|
||||
['2.1.1', '2.1.0', '2.0.0', '2.1.1a1', 'invalid']
|
||||
)
|
||||
major, minor, patch, beta = _latests_versions(tags)
|
||||
self.assertEqual(major, Pep440Version('2.0.0'))
|
||||
self.assertEqual(minor, Pep440Version('2.1.0'))
|
||||
self.assertEqual(patch, Pep440Version('2.1.1'))
|
||||
self.assertEqual(beta, Pep440Version('2.1.1a1'))
|
||||
|
||||
|
||||
class TestFetchListFromGitlab(TestCase):
|
||||
|
||||
page_size = 2
|
||||
|
||||
def setUp(self):
|
||||
self.url = (
|
||||
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth'
|
||||
'/repository/tags'
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def my_callback(cls, request, context):
|
||||
page = int(request.qs['page'][0])
|
||||
start = (page - 1) * cls.page_size
|
||||
end = start + cls.page_size
|
||||
return GITHUB_TAGS[start:end]
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_can_fetch_one_page_with_header(self, requests_mocker):
|
||||
headers = {
|
||||
'x-total-pages': '1'
|
||||
}
|
||||
requests_mocker.get(self.url, json=GITHUB_TAGS, headers=headers)
|
||||
result = _fetch_list_from_gitlab(self.url)
|
||||
self.assertEqual(result, GITHUB_TAGS)
|
||||
self.assertEqual(requests_mocker.call_count, 1)
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_can_fetch_one_page_wo_header(self, requests_mocker):
|
||||
requests_mocker.get(self.url, json=GITHUB_TAGS)
|
||||
result = _fetch_list_from_gitlab(self.url)
|
||||
self.assertEqual(result, GITHUB_TAGS)
|
||||
self.assertEqual(requests_mocker.call_count, 1)
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_can_fetch_one_page_and_ignore_invalid_header(self, requests_mocker):
|
||||
headers = {
|
||||
'x-total-pages': 'invalid'
|
||||
}
|
||||
requests_mocker.get(self.url, json=GITHUB_TAGS, headers=headers)
|
||||
result = _fetch_list_from_gitlab(self.url)
|
||||
self.assertEqual(result, GITHUB_TAGS)
|
||||
self.assertEqual(requests_mocker.call_count, 1)
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_can_fetch_multiple_pages(self, requests_mocker):
|
||||
total_pages = ceil(len(GITHUB_TAGS) / self.page_size)
|
||||
headers = {
|
||||
'x-total-pages': str(total_pages)
|
||||
}
|
||||
requests_mocker.get(self.url, json=self.my_callback, headers=headers)
|
||||
result = _fetch_list_from_gitlab(self.url)
|
||||
self.assertEqual(result, GITHUB_TAGS)
|
||||
self.assertEqual(requests_mocker.call_count, total_pages)
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_can_fetch_given_number_of_pages_only(self, requests_mocker):
|
||||
total_pages = ceil(len(GITHUB_TAGS) / self.page_size)
|
||||
headers = {
|
||||
'x-total-pages': str(total_pages)
|
||||
}
|
||||
requests_mocker.get(self.url, json=self.my_callback, headers=headers)
|
||||
max_pages = 2
|
||||
result = _fetch_list_from_gitlab(self.url, max_pages=max_pages)
|
||||
self.assertEqual(result, GITHUB_TAGS[:4])
|
||||
self.assertEqual(requests_mocker.call_count, max_pages)
|
||||
@@ -23,12 +23,5 @@ urlpatterns = [
|
||||
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'),
|
||||
]
|
||||
|
||||
@@ -6,20 +6,21 @@ from django.contrib.auth import login, authenticate
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.models import User
|
||||
from django.core import signing
|
||||
from django.urls import reverse
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import redirect, render
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from allianceauth.eveonline.models import EveCharacter
|
||||
from esi.decorators import token_required
|
||||
from esi.models import Token
|
||||
|
||||
from registration.backends.hmac.views import (
|
||||
from django_registration.backends.activation.views import (
|
||||
RegistrationView as BaseRegistrationView,
|
||||
ActivationView as BaseActivationView,
|
||||
REGISTRATION_SALT
|
||||
)
|
||||
from registration.signals import user_registered
|
||||
from django_registration.signals import user_registered
|
||||
|
||||
from .models import CharacterOwnership
|
||||
from .forms import RegistrationForm
|
||||
@@ -134,11 +135,14 @@ def sso_login(request, token):
|
||||
# Step 2
|
||||
class RegistrationView(BaseRegistrationView):
|
||||
form_class = RegistrationForm
|
||||
success_url = 'authentication:dashboard'
|
||||
template_name = "public/register.html"
|
||||
email_body_template = "registration/activation_email.txt"
|
||||
email_subject_template = "registration/activation_email_subject.txt"
|
||||
success_url = reverse_lazy('registration_complete')
|
||||
|
||||
def get_success_url(self, user):
|
||||
if not getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
|
||||
return 'authentication:dashboard', (), {}
|
||||
return reverse_lazy('authentication:dashboard')
|
||||
return super().get_success_url(user)
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
@@ -176,6 +180,9 @@ class RegistrationView(BaseRegistrationView):
|
||||
|
||||
# Step 3
|
||||
class ActivationView(BaseActivationView):
|
||||
template_name = "registration/activate.html"
|
||||
success_url = reverse_lazy('registration_activation_complete')
|
||||
|
||||
def validate_key(self, activation_key):
|
||||
try:
|
||||
dump = signing.loads(activation_key, salt=REGISTRATION_SALT,
|
||||
@@ -207,5 +214,5 @@ def activation_complete(request):
|
||||
|
||||
|
||||
def registration_closed(request):
|
||||
messages.error(request, _('Registraion of new accounts it not allowed at this time.'))
|
||||
messages.error(request, _('Registration of new accounts is not allowed at this time.'))
|
||||
return redirect('authentication:login')
|
||||
|
||||
@@ -6,11 +6,13 @@ from allianceauth.corputils import urls
|
||||
|
||||
class CorpStats(MenuItemHook):
|
||||
def __init__(self):
|
||||
MenuItemHook.__init__(self,
|
||||
MenuItemHook.__init__(
|
||||
self,
|
||||
_('Corporation Stats'),
|
||||
'fa fa-share-alt fa-fw',
|
||||
'fas fa-share-alt fa-fw',
|
||||
'corputils:view',
|
||||
navactive=['corputils:'])
|
||||
navactive=['corputils:']
|
||||
)
|
||||
|
||||
def render(self, request):
|
||||
if request.user.has_perm('corputils.view_corp_corpstats') or request.user.has_perm(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-12-14 21:36
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-12-14 21:48
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-03-22 23:35
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-03-26 20:13
|
||||
from __future__ import unicode_literals
|
||||
|
||||
@@ -13,8 +12,7 @@ def convert_json_to_members(apps, schema_editor):
|
||||
for cs in CorpStats.objects.all():
|
||||
members = json.loads(cs._members)
|
||||
CorpMember.objects.bulk_create(
|
||||
[CorpMember(corpstats=cs, character_id=member_id, character_name=member_name) for member_id, member_name in
|
||||
members.items()]
|
||||
[CorpMember(corpstats=cs, character_id=member_id, character_name=member_name) for member_id, member_name in members.items()]
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.2 on 2017-06-10 15:34
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ from bravado.exception import HTTPForbidden
|
||||
from django.db import models
|
||||
from esi.errors import TokenError
|
||||
from esi.models import Token
|
||||
from allianceauth.eveonline.models import EveCorporationInfo, EveCharacter,\
|
||||
EveAllianceInfo
|
||||
from allianceauth.eveonline.models import EveCorporationInfo, EveCharacter, EveAllianceInfo
|
||||
from allianceauth.notifications import notify
|
||||
|
||||
from allianceauth.corputils.managers import CorpStatsManager
|
||||
@@ -49,8 +48,7 @@ class CorpStats(models.Model):
|
||||
def update(self):
|
||||
try:
|
||||
c = self.token.get_esi_client(spec_file=SWAGGER_SPEC_PATH)
|
||||
assert c.Character.get_characters_character_id(character_id=self.token.character_id).result()[
|
||||
'corporation_id'] == int(self.corp.corporation_id)
|
||||
assert c.Character.get_characters_character_id(character_id=self.token.character_id).result()['corporation_id'] == int(self.corp.corporation_id)
|
||||
member_ids = c.Corporation.get_corporations_corporation_id_members(
|
||||
corporation_id=self.corp.corporation_id).result()
|
||||
|
||||
@@ -58,18 +56,15 @@ class CorpStats(models.Model):
|
||||
# the swagger spec doesn't have a maxItems count
|
||||
# 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_name_chunks = [c.Universe.post_universe_names(ids=id_chunk).result() for id_chunk in
|
||||
member_id_chunks]
|
||||
member_name_chunks = [c.Universe.post_universe_names(ids=id_chunk).result() for id_chunk in member_id_chunks]
|
||||
member_list = {}
|
||||
for name_chunk in member_name_chunks:
|
||||
member_list.update({m['id']: m['name'] for m in name_chunk})
|
||||
|
||||
# bulk create new member models
|
||||
missing_members = [m_id for m_id in member_ids if
|
||||
not CorpMember.objects.filter(corpstats=self, character_id=m_id).exists()]
|
||||
missing_members = [m_id for m_id in member_ids if not CorpMember.objects.filter(corpstats=self, character_id=m_id).exists()]
|
||||
CorpMember.objects.bulk_create(
|
||||
[CorpMember(character_id=m_id, character_name=member_list[m_id], corpstats=self) for m_id in
|
||||
missing_members])
|
||||
[CorpMember(character_id=m_id, character_name=member_list[m_id], corpstats=self) for m_id in missing_members])
|
||||
|
||||
# purge old members
|
||||
self.members.exclude(character_id__in=member_ids).delete()
|
||||
@@ -80,20 +75,21 @@ class CorpStats(models.Model):
|
||||
except TokenError as e:
|
||||
logger.warning("%s failed to update: %s" % (self, e))
|
||||
if self.token.user:
|
||||
notify(self.token.user, "%s failed to update with your ESI token." % self,
|
||||
notify(
|
||||
self.token.user, "%s failed to update with your ESI token." % self,
|
||||
message="Your token has expired or is no longer valid. Please add a new one to create a new CorpStats.",
|
||||
level="error")
|
||||
self.delete()
|
||||
except HTTPForbidden as e:
|
||||
logger.warning("%s failed to update: %s" % (self, e))
|
||||
if self.token.user:
|
||||
notify(self.token.user, "%s failed to update with your ESI token." % self,
|
||||
message="%s: %s" % (e.status_code, e.message), level="error")
|
||||
notify(self.token.user, "%s failed to update with your ESI token." % self, message="%s: %s" % (e.status_code, e.message), level="error")
|
||||
self.delete()
|
||||
except AssertionError:
|
||||
logger.warning("%s token character no longer in corp." % self)
|
||||
if self.token.user:
|
||||
notify(self.token.user, "%s cannot update with your ESI token." % self,
|
||||
notify(
|
||||
self.token.user, "%s cannot update with your ESI token." % self,
|
||||
message="%s cannot update with your ESI token as you have left corp." % self, level="error")
|
||||
self.delete()
|
||||
|
||||
@@ -127,9 +123,7 @@ class CorpStats(models.Model):
|
||||
|
||||
@property
|
||||
def mains(self):
|
||||
return self.members.filter(pk__in=[m.pk for m in self.members.all() if
|
||||
m.main_character and int(m.main_character.character_id) == int(
|
||||
m.character_id)])
|
||||
return self.members.filter(pk__in=[m.pk for m in self.members.all() if m.main_character and int(m.main_character.character_id) == int(m.character_id)])
|
||||
|
||||
def visible_to(self, user):
|
||||
return CorpStats.objects.filter(pk=self.pk).visible_to(user).exists()
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -58,8 +58,7 @@
|
||||
{% for id, main in mains.items %}
|
||||
<tr>
|
||||
<td class="text-center" style="vertical-align:middle">
|
||||
<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="{{ main.main.portrait_url_64 }}" class="img-circle">
|
||||
<div class="caption text-center">
|
||||
{{ main.main }}
|
||||
@@ -88,8 +87,7 @@
|
||||
<td class="text-center" style="width:30%">{{ alt.corporation_name }}</td>
|
||||
<td class="text-center" style="width:30%">{{ alt.alliance_name }}</td>
|
||||
<td class="text-center" style="width:5%">
|
||||
<a href="https://zkillboard.com/character/{{ alt.character_id }}/"
|
||||
class="label label-danger" target="_blank">
|
||||
<a href="https://zkillboard.com/character/{{ alt.character_id }}/" class="label label-danger" target="_blank">
|
||||
{% trans "Killboard" %}
|
||||
</a>
|
||||
</td>
|
||||
@@ -123,10 +121,9 @@
|
||||
<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">
|
||||
<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>
|
||||
@@ -136,10 +133,9 @@
|
||||
<tr class="danger">
|
||||
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
|
||||
<td class="text-center">{{ member.character_name }}</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">
|
||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">{% trans "Killboard" %}</a>
|
||||
</td>
|
||||
<td class="text-center"></td>
|
||||
<td class="text-center"></td>
|
||||
<td class="text-center"></td>
|
||||
@@ -167,9 +163,7 @@
|
||||
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
|
||||
<td class="text-center">{{ member.character_name }}</td>
|
||||
<td class="text-center">
|
||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/"
|
||||
class="label label-danger"
|
||||
target="_blank">
|
||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">
|
||||
{% trans "Killboard" %}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@@ -17,9 +17,9 @@ class CorpStatsManagerTestCase(TestCase):
|
||||
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.token = Token.objects.create(user=cls.user, access_token='a', character_id='1', character_name='test character', character_owner_hash='z')
|
||||
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.token = Token.objects.create(user=cls.user, access_token='a', character_id=1, character_name='test character', character_owner_hash='z')
|
||||
cls.corpstats = CorpStats.objects.create(corp=cls.corp, token=cls.token)
|
||||
cls.view_corp_permission = Permission.objects.get_by_natural_key('view_corp_corpstats', 'corputils', 'corpstats')
|
||||
cls.view_alliance_permission = Permission.objects.get_by_natural_key('view_alliance_corpstats', 'corputils', 'corpstats')
|
||||
@@ -66,9 +66,9 @@ class CorpStatsUpdateTestCase(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.token = Token.objects.create(user=cls.user, access_token='a', character_id='1', character_name='test character', character_owner_hash='z')
|
||||
cls.corp = EveCorporationInfo.objects.create(corporation_id='2', corporation_name='test corp', corporation_ticker='TEST', member_count=1)
|
||||
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.token = Token.objects.create(user=cls.user, access_token='a', character_id=1, character_name='test character', character_owner_hash='z')
|
||||
cls.corp = EveCorporationInfo.objects.create(corporation_id=2, corporation_name='test corp', corporation_ticker='TEST', member_count=1)
|
||||
|
||||
def setUp(self):
|
||||
self.corpstats = CorpStats.objects.get_or_create(token=self.token, corp=self.corp)[0]
|
||||
@@ -88,11 +88,11 @@ class CorpStatsUpdateTestCase(TestCase):
|
||||
SwaggerClient.from_spec.return_value.Corporation.get_corporations_corporation_id_members.return_value.result.return_value = [1]
|
||||
SwaggerClient.from_spec.return_value.Universe.post_universe_names.return_value.result.return_value = [{'id': 1, 'name': 'test character'}]
|
||||
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())
|
||||
|
||||
@mock.patch('esi.clients.SwaggerClient')
|
||||
def test_update_remove_member(self, SwaggerClient):
|
||||
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.Corporation.get_corporations_corporation_id_members.return_value.result.return_value = [1]
|
||||
SwaggerClient.from_spec.return_value.Universe.post_universe_names.return_value.result.return_value = [{'id': 1, 'name': 'test character'}]
|
||||
@@ -130,15 +130,15 @@ class CorpStatsPropertiesTestCase(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')
|
||||
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.token = Token.objects.create(user=cls.user, access_token='a', character_id='1', character_name='test character', character_owner_hash='z')
|
||||
cls.corp = EveCorporationInfo.objects.create(corporation_id='2', corporation_name='test corp', corporation_ticker='TEST', member_count=1)
|
||||
cls.token = Token.objects.create(user=cls.user, access_token='a', character_id=1, character_name='test character', character_owner_hash='z')
|
||||
cls.corp = EveCorporationInfo.objects.create(corporation_id=2, corporation_name='test corp', corporation_ticker='TEST', member_count=1)
|
||||
cls.corpstats = CorpStats.objects.create(token=cls.token, corp=cls.corp)
|
||||
cls.character = EveCharacter.objects.create(character_name='another test character', character_id='4', corporation_id='2', corporation_name='test corp', corporation_ticker='TEST')
|
||||
cls.character = EveCharacter.objects.create(character_name='another test character', character_id=4, corporation_id=2, corporation_name='test corp', corporation_ticker='TEST')
|
||||
|
||||
def test_member_count(self):
|
||||
member = CorpMember.objects.create(corpstats=self.corpstats, character_id='1', character_name='test character')
|
||||
member = CorpMember.objects.create(corpstats=self.corpstats, character_id=2, character_name='test character')
|
||||
self.assertEqual(self.corpstats.member_count, 1)
|
||||
member.delete()
|
||||
self.assertEqual(self.corpstats.member_count, 0)
|
||||
@@ -147,7 +147,7 @@ class CorpStatsPropertiesTestCase(TestCase):
|
||||
AuthUtils.disconnect_signals()
|
||||
co = CharacterOwnership.objects.create(character=self.character, user=self.user, owner_hash='a')
|
||||
AuthUtils.connect_signals()
|
||||
CorpMember.objects.create(corpstats=self.corpstats, character_id='4', character_name='test character')
|
||||
CorpMember.objects.create(corpstats=self.corpstats, character_id=4, character_name='test character')
|
||||
self.assertEqual(self.corpstats.user_count, 1)
|
||||
co.delete()
|
||||
self.assertEqual(self.corpstats.user_count, 0)
|
||||
@@ -156,7 +156,8 @@ class CorpStatsPropertiesTestCase(TestCase):
|
||||
AuthUtils.disconnect_signals()
|
||||
co = CharacterOwnership.objects.create(character=self.character, user=self.user, owner_hash='a')
|
||||
AuthUtils.connect_signals()
|
||||
member = CorpMember.objects.create(corpstats=self.corpstats, character_id='4', character_name='test character')
|
||||
member = CorpMember.objects.create(corpstats=self.corpstats, character_id=4, character_name='test character')
|
||||
self.corpstats.refresh_from_db()
|
||||
self.assertIn(member, self.corpstats.registered_members)
|
||||
self.assertEqual(self.corpstats.registered_member_count, 1)
|
||||
|
||||
@@ -165,7 +166,7 @@ class CorpStatsPropertiesTestCase(TestCase):
|
||||
self.assertEqual(self.corpstats.registered_member_count, 0)
|
||||
|
||||
def test_unregistered_members(self):
|
||||
member = CorpMember.objects.create(corpstats=self.corpstats, character_id='4', character_name='test character')
|
||||
member = CorpMember.objects.create(corpstats=self.corpstats, character_id=4, character_name='test character')
|
||||
self.corpstats.refresh_from_db()
|
||||
self.assertIn(member, self.corpstats.unregistered_members)
|
||||
self.assertEqual(self.corpstats.unregistered_member_count, 1)
|
||||
@@ -178,13 +179,13 @@ class CorpStatsPropertiesTestCase(TestCase):
|
||||
|
||||
def test_mains(self):
|
||||
# test when is a main
|
||||
member = CorpMember.objects.create(corpstats=self.corpstats, character_id='1', character_name='test character')
|
||||
member = CorpMember.objects.create(corpstats=self.corpstats, character_id=1, character_name='test character')
|
||||
self.assertIn(member, self.corpstats.mains)
|
||||
self.assertEqual(self.corpstats.main_count, 1)
|
||||
|
||||
# test when is an alt
|
||||
old_main = self.user.profile.main_character
|
||||
character = EveCharacter.objects.create(character_name='other character', character_id=10, corporation_name='test corp', corporation_id='2', corporation_ticker='TEST')
|
||||
character = EveCharacter.objects.create(character_name='other character', character_id=10, corporation_name='test corp', corporation_id=2, corporation_ticker='TEST')
|
||||
AuthUtils.disconnect_signals()
|
||||
co = CharacterOwnership.objects.create(character=character, user=self.user, owner_hash='b')
|
||||
self.user.profile.main_character = character
|
||||
@@ -208,7 +209,7 @@ class CorpStatsPropertiesTestCase(TestCase):
|
||||
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://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.save()
|
||||
self.assertEqual(self.corpstats.alliance_logo(size=128), 'https://images.evetech.net/alliances/3/logo?size=128')
|
||||
@@ -221,14 +222,14 @@ class CorpMemberTestCase(TestCase):
|
||||
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.token = Token.objects.create(user=cls.user, access_token='a', character_id='1', character_name='test character', character_owner_hash='a')
|
||||
cls.corp = EveCorporationInfo.objects.create(corporation_id='2', corporation_name='test corp', corporation_ticker='TEST', member_count=1)
|
||||
cls.token = Token.objects.create(user=cls.user, access_token='a', character_id=1, character_name='test character', character_owner_hash='a')
|
||||
cls.corp = EveCorporationInfo.objects.create(corporation_id=2, corporation_name='test corp', corporation_ticker='TEST', member_count=1)
|
||||
cls.corpstats = CorpStats.objects.create(token=cls.token, corp=cls.corp)
|
||||
cls.member = CorpMember.objects.create(corpstats=cls.corpstats, character_id='2', character_name='other test character')
|
||||
cls.member = CorpMember.objects.create(corpstats=cls.corpstats, character_id=2, character_name='other test character')
|
||||
|
||||
def test_character(self):
|
||||
self.assertIsNone(self.member.character)
|
||||
character = EveCharacter.objects.create(character_id='2', character_name='other test character', corporation_id='2', corporation_name='test corp', corporation_ticker='TEST')
|
||||
character = EveCharacter.objects.create(character_id=2, character_name='other test character', corporation_id=2, corporation_name='test corp', corporation_ticker='TEST')
|
||||
self.assertEqual(self.member.character, character)
|
||||
|
||||
def test_main_character(self):
|
||||
@@ -238,7 +239,7 @@ class CorpMemberTestCase(TestCase):
|
||||
self.assertIsNone(self.member.main_character)
|
||||
|
||||
# test when member.character is not None but also not a main
|
||||
character = EveCharacter.objects.create(character_id='2', character_name='other test character', corporation_id='2', corporation_name='test corp', corporation_ticker='TEST')
|
||||
character = EveCharacter.objects.create(character_id=2, character_name='other test character', corporation_id=2, corporation_name='test corp', corporation_ticker='TEST')
|
||||
CharacterOwnership.objects.create(character=character, user=self.user, owner_hash='b')
|
||||
self.member.refresh_from_db()
|
||||
self.assertNotEqual(self.member.main_character, self.member.character)
|
||||
@@ -260,14 +261,14 @@ class CorpMemberTestCase(TestCase):
|
||||
def test_alts(self):
|
||||
self.assertListEqual(self.member.alts, [])
|
||||
|
||||
character = EveCharacter.objects.create(character_id='2', character_name='other test character', corporation_id='2', corporation_name='test corp', corporation_ticker='TEST')
|
||||
character = EveCharacter.objects.create(character_id=2, character_name='other test character', corporation_id=2, corporation_name='test corp', corporation_ticker='TEST')
|
||||
CharacterOwnership.objects.create(character=character, user=self.user, owner_hash='b')
|
||||
self.assertIn(character, self.member.alts)
|
||||
|
||||
def test_registered(self):
|
||||
self.assertFalse(self.member.registered)
|
||||
AuthUtils.disconnect_signals()
|
||||
character = EveCharacter.objects.create(character_id='2', character_name='other test character', corporation_id='2', corporation_name='test corp', corporation_ticker='TEST')
|
||||
character = EveCharacter.objects.create(character_id=2, character_name='other test character', corporation_id=2, corporation_name='test corp', corporation_ticker='TEST')
|
||||
CharacterOwnership.objects.create(character=character, user=self.user, owner_hash='b')
|
||||
self.assertTrue(self.member.registered)
|
||||
AuthUtils.connect_signals()
|
||||
|
||||
@@ -96,24 +96,62 @@ class EveAllianceForm(EveEntityForm):
|
||||
|
||||
@admin.register(EveCorporationInfo)
|
||||
class EveCorporationInfoAdmin(admin.ModelAdmin):
|
||||
search_fields = ['corporation_name']
|
||||
list_display = ('corporation_name', 'alliance')
|
||||
list_select_related = ('alliance',)
|
||||
list_filter = (('alliance', admin.RelatedOnlyFieldListFilter),)
|
||||
ordering = ('corporation_name',)
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
def get_form(self, request, obj=None, **kwargs):
|
||||
if not obj or not obj.pk:
|
||||
return EveCorporationForm
|
||||
return super(EveCorporationInfoAdmin, self).get_form(request, obj=obj, **kwargs)
|
||||
return super().get_form(request, obj=obj, **kwargs)
|
||||
|
||||
|
||||
@admin.register(EveAllianceInfo)
|
||||
class EveAllianceInfoAdmin(admin.ModelAdmin):
|
||||
search_fields = ['alliance_name']
|
||||
list_display = ('alliance_name',)
|
||||
ordering = ('alliance_name',)
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
def get_form(self, request, obj=None, **kwargs):
|
||||
if not obj or not obj.pk:
|
||||
return EveAllianceForm
|
||||
return super(EveAllianceInfoAdmin, self).get_form(request, obj=obj, **kwargs)
|
||||
return super().get_form(request, obj=obj, **kwargs)
|
||||
|
||||
|
||||
@admin.register(EveCharacter)
|
||||
class EveCharacterAdmin(admin.ModelAdmin):
|
||||
search_fields = ['character_name', 'corporation_name', 'alliance_name', 'character_ownership__user__username']
|
||||
list_display = ('character_name', 'corporation_name', 'alliance_name', 'user', 'main_character')
|
||||
search_fields = [
|
||||
'character_name',
|
||||
'corporation_name',
|
||||
'alliance_name',
|
||||
'character_ownership__user__username'
|
||||
]
|
||||
list_display = (
|
||||
'character_name', 'corporation_name', 'alliance_name', 'user', 'main_character'
|
||||
)
|
||||
list_select_related = (
|
||||
'character_ownership', 'character_ownership__user__profile__main_character'
|
||||
)
|
||||
list_filter = (
|
||||
'corporation_name',
|
||||
'alliance_name',
|
||||
(
|
||||
'character_ownership__user__profile__main_character',
|
||||
admin.RelatedOnlyFieldListFilter
|
||||
),
|
||||
)
|
||||
ordering = ('character_name', )
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def user(obj):
|
||||
|
||||
@@ -39,4 +39,3 @@ class AutogroupsConfigAdmin(admin.ModelAdmin):
|
||||
admin.site.register(AutogroupsConfig, AutogroupsConfigAdmin)
|
||||
admin.site.register(ManagedCorpGroup)
|
||||
admin.site.register(ManagedAllianceGroup)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.6 on 2017-12-23 04:30
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -57,25 +57,21 @@ class AutogroupsConfig(models.Model):
|
||||
|
||||
states = models.ManyToManyField(State, related_name='autogroups')
|
||||
|
||||
corp_groups = models.BooleanField(default=False,
|
||||
help_text="Setting this to false will delete all the created groups.")
|
||||
corp_groups = models.BooleanField(default=False, help_text="Setting this to false will delete all the created groups.")
|
||||
corp_group_prefix = models.CharField(max_length=50, default='Corp ', blank=True)
|
||||
corp_name_source = models.CharField(max_length=20, choices=NAME_OPTIONS, default=OPT_NAME)
|
||||
|
||||
alliance_groups = models.BooleanField(default=False,
|
||||
help_text="Setting this to false will delete all the created groups.")
|
||||
alliance_groups = models.BooleanField(default=False, help_text="Setting this to false will delete all the created groups.")
|
||||
alliance_group_prefix = models.CharField(max_length=50, default='Alliance ', blank=True)
|
||||
alliance_name_source = models.CharField(max_length=20, choices=NAME_OPTIONS, default=OPT_NAME)
|
||||
|
||||
corp_managed_groups = models.ManyToManyField(
|
||||
Group, through='ManagedCorpGroup', related_name='corp_managed_config',
|
||||
help_text='A list of corporation groups created and maintained by this AutogroupConfig. '
|
||||
'You should not edit this list unless you know what you\'re doing.')
|
||||
help_text='A list of corporation groups created and maintained by this AutogroupConfig. You should not edit this list unless you know what you\'re doing.')
|
||||
|
||||
alliance_managed_groups = models.ManyToManyField(
|
||||
Group, through='ManagedAllianceGroup', related_name='alliance_managed_config',
|
||||
help_text='A list of alliance groups created and maintained by this AutogroupConfig. '
|
||||
'You should not edit this list unless you know what you\'re doing.')
|
||||
help_text='A list of alliance groups created and maintained by this AutogroupConfig. You should not edit this list unless you know what you\'re doing.')
|
||||
|
||||
replace_spaces = models.BooleanField(default=False)
|
||||
replace_spaces_with = models.CharField(
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
# 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"
|
||||
_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"
|
||||
|
||||
@@ -2,24 +2,30 @@
|
||||
|
||||
from urllib.parse import urljoin, quote
|
||||
|
||||
from . import *
|
||||
from . import (
|
||||
_ESI_CATEGORY_ALLIANCE,
|
||||
_ESI_CATEGORY_CORPORATION,
|
||||
_ESI_CATEGORY_REGION,
|
||||
_ESI_CATEGORY_SOLARSYSTEM
|
||||
)
|
||||
|
||||
BASE_URL = 'http://evemaps.dotlan.net'
|
||||
|
||||
_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:
|
||||
if category == _ESI_CATEGORY_ALLIANCE:
|
||||
partial = 'alliance'
|
||||
|
||||
elif category == ESI_CATEGORY_CORPORATION:
|
||||
elif category == _ESI_CATEGORY_CORPORATION:
|
||||
partial = 'corp'
|
||||
|
||||
elif category == ESI_CATEGORY_REGION:
|
||||
elif category == _ESI_CATEGORY_REGION:
|
||||
partial = 'map'
|
||||
|
||||
elif category == ESI_CATEGORY_SOLARSYSTEM:
|
||||
elif category == _ESI_CATEGORY_SOLARSYSTEM:
|
||||
partial = 'system'
|
||||
|
||||
else:
|
||||
@@ -28,7 +34,7 @@ def _build_url(category: str, name: str) -> str:
|
||||
)
|
||||
|
||||
url = urljoin(
|
||||
BASE_URL,
|
||||
_BASE_URL,
|
||||
'{}/{}'.format(partial, quote(str(name).replace(" ", "_")))
|
||||
|
||||
)
|
||||
@@ -37,16 +43,19 @@ def _build_url(category: str, name: str) -> str:
|
||||
|
||||
def alliance_url(name: str) -> str:
|
||||
"""url for page about given alliance on dotlan"""
|
||||
return _build_url(ESI_CATEGORY_ALLIANCE, name)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
return _build_url(_ESI_CATEGORY_SOLARSYSTEM, name)
|
||||
|
||||
129
allianceauth/eveonline/evelinks/eveimageserver.py
Normal file
129
allianceauth/eveonline/evelinks/eveimageserver.py
Normal file
@@ -0,0 +1,129 @@
|
||||
from . import (
|
||||
_ESI_CATEGORY_ALLIANCE,
|
||||
_ESI_CATEGORY_CHARACTER,
|
||||
_ESI_CATEGORY_CORPORATION,
|
||||
_ESI_CATEGORY_INVENTORYTYPE
|
||||
)
|
||||
|
||||
|
||||
_EVE_IMAGE_SERVER_URL = 'https://images.evetech.net'
|
||||
_DEFAULT_IMAGE_SIZE = 32
|
||||
|
||||
|
||||
def _eve_entity_image_url(
|
||||
category: str,
|
||||
entity_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, inventory_type
|
||||
|
||||
Arguments:
|
||||
- category: category of the ID, see ESI category constants
|
||||
- entity_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.
|
||||
- tenant: (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 = {
|
||||
_ESI_CATEGORY_ALLIANCE: {
|
||||
'endpoint': 'alliances',
|
||||
'variants': ['logo']
|
||||
},
|
||||
_ESI_CATEGORY_CORPORATION: {
|
||||
'endpoint': 'corporations',
|
||||
'variants': ['logo']
|
||||
},
|
||||
_ESI_CATEGORY_CHARACTER: {
|
||||
'endpoint': 'characters',
|
||||
'variants': ['portrait']
|
||||
},
|
||||
_ESI_CATEGORY_INVENTORYTYPE: {
|
||||
'endpoint': 'types',
|
||||
'variants': ['icon', 'render']
|
||||
}
|
||||
}
|
||||
tenants = ['tranquility', 'singularity']
|
||||
|
||||
if not entity_id:
|
||||
raise ValueError('Invalid entity_id: {}'.format(entity_id))
|
||||
else:
|
||||
entity_id = int(entity_id)
|
||||
|
||||
if not size or 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 tenant {}'.format(tenant))
|
||||
|
||||
# compose result URL
|
||||
result = '{}/{}/{}/{}?size={}'.format(
|
||||
_EVE_IMAGE_SERVER_URL,
|
||||
endpoint,
|
||||
entity_id,
|
||||
variant,
|
||||
size
|
||||
)
|
||||
if tenant:
|
||||
result += '&tenant={}'.format(tenant)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def alliance_logo_url(alliance_id: int, size: int = _DEFAULT_IMAGE_SIZE) -> str:
|
||||
"""image URL for the given alliance ID"""
|
||||
return _eve_entity_image_url(_ESI_CATEGORY_ALLIANCE, alliance_id, size)
|
||||
|
||||
|
||||
def corporation_logo_url(
|
||||
corporation_id: int, size: int = _DEFAULT_IMAGE_SIZE
|
||||
) -> str:
|
||||
"""image URL for the given corporation ID"""
|
||||
return _eve_entity_image_url(
|
||||
_ESI_CATEGORY_CORPORATION, corporation_id, size
|
||||
)
|
||||
|
||||
|
||||
def character_portrait_url(
|
||||
character_id: int, size: int = _DEFAULT_IMAGE_SIZE
|
||||
) -> str:
|
||||
"""image URL for the given character ID"""
|
||||
return _eve_entity_image_url(_ESI_CATEGORY_CHARACTER, character_id, size)
|
||||
|
||||
|
||||
def type_icon_url(type_id: int, size: int = _DEFAULT_IMAGE_SIZE) -> str:
|
||||
"""icon image URL for the given type ID"""
|
||||
return _eve_entity_image_url(
|
||||
_ESI_CATEGORY_INVENTORYTYPE, type_id, size, variant='icon'
|
||||
)
|
||||
|
||||
|
||||
def type_render_url(type_id: int, size: int = _DEFAULT_IMAGE_SIZE) -> str:
|
||||
"""render image URL for the given type ID"""
|
||||
return _eve_entity_image_url(
|
||||
_ESI_CATEGORY_INVENTORYTYPE, type_id, size, variant='render'
|
||||
)
|
||||
@@ -1,22 +1,27 @@
|
||||
# this module generates profile URLs for evewho
|
||||
|
||||
from urllib.parse import urljoin, quote
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from . import *
|
||||
from . import (
|
||||
_ESI_CATEGORY_ALLIANCE,
|
||||
_ESI_CATEGORY_CORPORATION,
|
||||
_ESI_CATEGORY_CHARACTER,
|
||||
)
|
||||
|
||||
BASE_URL = 'https://evewho.com'
|
||||
|
||||
_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:
|
||||
if category == _ESI_CATEGORY_ALLIANCE:
|
||||
partial = 'alliance'
|
||||
|
||||
elif category == ESI_CATEGORY_CORPORATION:
|
||||
elif category == _ESI_CATEGORY_CORPORATION:
|
||||
partial = 'corporation'
|
||||
|
||||
elif category == ESI_CATEGORY_CHARACTER:
|
||||
elif category == _ESI_CATEGORY_CHARACTER:
|
||||
partial = 'character'
|
||||
|
||||
else:
|
||||
@@ -25,7 +30,7 @@ def _build_url(category: str, eve_id: int) -> str:
|
||||
)
|
||||
|
||||
url = urljoin(
|
||||
BASE_URL,
|
||||
_BASE_URL,
|
||||
'{}/{}'.format(partial, int(eve_id))
|
||||
)
|
||||
return url
|
||||
@@ -33,12 +38,14 @@ def _build_url(category: str, eve_id: int) -> str:
|
||||
|
||||
def alliance_url(eve_id: int) -> str:
|
||||
"""url for page about given alliance on evewho"""
|
||||
return _build_url(ESI_CATEGORY_ALLIANCE, eve_id)
|
||||
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)
|
||||
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)
|
||||
return _build_url(_ESI_CATEGORY_CORPORATION, eve_id)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from ...models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||
from .. import dotlan, zkillboard, evewho
|
||||
from .. import dotlan, zkillboard, evewho, eveimageserver
|
||||
from ...templatetags import evelinks
|
||||
|
||||
|
||||
@@ -90,3 +90,115 @@ class TestZkillboard(TestCase):
|
||||
'https://zkillboard.com/system/12345678/'
|
||||
)
|
||||
|
||||
|
||||
class TestEveImageServer(TestCase):
|
||||
"""unit test for eveimageserver"""
|
||||
|
||||
def test_sizes(self):
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42),
|
||||
'https://images.evetech.net/characters/42/portrait?size=32'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=32),
|
||||
'https://images.evetech.net/characters/42/portrait?size=32'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=64),
|
||||
'https://images.evetech.net/characters/42/portrait?size=64'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=128),
|
||||
'https://images.evetech.net/characters/42/portrait?size=128'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=256),
|
||||
'https://images.evetech.net/characters/42/portrait?size=256'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=512),
|
||||
'https://images.evetech.net/characters/42/portrait?size=512'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=1024),
|
||||
'https://images.evetech.net/characters/42/portrait?size=1024'
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('corporation', 42, size=-5)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('corporation', 42, size=0)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('corporation', 42, size=31)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('corporation', 42, size=1025)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('corporation', 42, size=2048)
|
||||
|
||||
|
||||
def test_variant(self):
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, variant='portrait'),
|
||||
'https://images.evetech.net/characters/42/portrait?size=32'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('alliance', 42, variant='logo'),
|
||||
'https://images.evetech.net/alliances/42/logo?size=32'
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('character', 42, variant='logo')
|
||||
|
||||
|
||||
def test_alliance(self):
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('alliance', 42),
|
||||
'https://images.evetech.net/alliances/42/logo?size=32'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('corporation', 42),
|
||||
'https://images.evetech.net/corporations/42/logo?size=32'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42),
|
||||
'https://images.evetech.net/characters/42/portrait?size=32'
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('station', 42)
|
||||
|
||||
|
||||
def test_tenants(self):
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, tenant='tranquility'),
|
||||
'https://images.evetech.net/characters/42/portrait?size=32&tenant=tranquility'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, tenant='singularity'),
|
||||
'https://images.evetech.net/characters/42/portrait?size=32&tenant=singularity'
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('character', 42, tenant='xxx')
|
||||
|
||||
def test_alliance_logo_url(self):
|
||||
expected = 'https://images.evetech.net/alliances/42/logo?size=128'
|
||||
self.assertEqual(eveimageserver.alliance_logo_url(42, 128), expected)
|
||||
|
||||
def test_corporation_logo_url(self):
|
||||
expected = 'https://images.evetech.net/corporations/42/logo?size=128'
|
||||
self.assertEqual(eveimageserver.corporation_logo_url(42, 128), expected)
|
||||
|
||||
def test_character_portrait_url(self):
|
||||
expected = 'https://images.evetech.net/characters/42/portrait?size=128'
|
||||
self.assertEqual(
|
||||
eveimageserver.character_portrait_url(42, 128), expected
|
||||
)
|
||||
|
||||
def test_type_icon_url(self):
|
||||
expected = 'https://images.evetech.net/types/42/icon?size=128'
|
||||
self.assertEqual(eveimageserver.type_icon_url(42, 128), expected)
|
||||
|
||||
def test_type_render_url(self):
|
||||
expected = 'https://images.evetech.net/types/42/render?size=128'
|
||||
self.assertEqual(eveimageserver.type_render_url(42, 128), expected)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from ...models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||
from .. import dotlan, zkillboard, evewho
|
||||
from .. import eveimageserver, evewho, dotlan, zkillboard
|
||||
from ...templatetags import evelinks
|
||||
|
||||
|
||||
@@ -332,3 +332,28 @@ class TestTemplateTags(TestCase):
|
||||
''
|
||||
)
|
||||
|
||||
def test_type_icon_url(self):
|
||||
expected = eveimageserver.type_icon_url(123)
|
||||
self.assertEqual(evelinks.type_icon_url(123), expected)
|
||||
|
||||
expected = eveimageserver.type_icon_url(123, 128)
|
||||
self.assertEqual(evelinks.type_icon_url(123, 128), expected)
|
||||
|
||||
expected = ''
|
||||
self.assertEqual(evelinks.type_icon_url(123, 99), expected)
|
||||
|
||||
expected = ''
|
||||
self.assertEqual(evelinks.type_icon_url(None), expected)
|
||||
|
||||
def test_type_render_url(self):
|
||||
expected = eveimageserver.type_render_url(123)
|
||||
self.assertEqual(evelinks.type_render_url(123), expected)
|
||||
|
||||
expected = eveimageserver.type_render_url(123, 128)
|
||||
self.assertEqual(evelinks.type_render_url(123, 128), expected)
|
||||
|
||||
expected = ''
|
||||
self.assertEqual(evelinks.type_render_url(123, 99), expected)
|
||||
|
||||
expected = ''
|
||||
self.assertEqual(evelinks.type_render_url(None), expected)
|
||||
|
||||
@@ -1,28 +1,35 @@
|
||||
# this module generates profile URLs for zKillboard
|
||||
|
||||
from urllib.parse import urljoin, quote
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from . import *
|
||||
from . import (
|
||||
_ESI_CATEGORY_ALLIANCE,
|
||||
_ESI_CATEGORY_CORPORATION,
|
||||
_ESI_CATEGORY_CHARACTER,
|
||||
_ESI_CATEGORY_REGION,
|
||||
_ESI_CATEGORY_SOLARSYSTEM
|
||||
)
|
||||
|
||||
BASE_URL = 'https://zkillboard.com'
|
||||
|
||||
_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:
|
||||
if category == _ESI_CATEGORY_ALLIANCE:
|
||||
partial = 'alliance'
|
||||
|
||||
elif category == ESI_CATEGORY_CORPORATION:
|
||||
elif category == _ESI_CATEGORY_CORPORATION:
|
||||
partial = 'corporation'
|
||||
|
||||
elif category == ESI_CATEGORY_CHARACTER:
|
||||
elif category == _ESI_CATEGORY_CHARACTER:
|
||||
partial = 'character'
|
||||
|
||||
elif category == ESI_CATEGORY_REGION:
|
||||
elif category == _ESI_CATEGORY_REGION:
|
||||
partial = 'region'
|
||||
|
||||
elif category == ESI_CATEGORY_SOLARSYSTEM:
|
||||
elif category == _ESI_CATEGORY_SOLARSYSTEM:
|
||||
partial = 'system'
|
||||
|
||||
else:
|
||||
@@ -31,7 +38,7 @@ def _build_url(category: str, eve_id: int) -> str:
|
||||
)
|
||||
|
||||
url = urljoin(
|
||||
BASE_URL,
|
||||
_BASE_URL,
|
||||
'{}/{}/'.format(partial, int(eve_id))
|
||||
)
|
||||
return url
|
||||
@@ -39,19 +46,23 @@ def _build_url(category: str, eve_id: int) -> str:
|
||||
|
||||
def alliance_url(eve_id: int) -> str:
|
||||
"""url for page about given alliance on zKillboard"""
|
||||
return _build_url(ESI_CATEGORY_ALLIANCE, eve_id)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
return _build_url(_ESI_CATEGORY_REGION, eve_id)
|
||||
|
||||
|
||||
def solar_system_url(eve_id: int) -> str:
|
||||
return _build_url(ESI_CATEGORY_SOLARSYSTEM, eve_id)
|
||||
return _build_url(_ESI_CATEGORY_SOLARSYSTEM, eve_id)
|
||||
|
||||
@@ -32,9 +32,11 @@ class EveCharacterManager(models.Manager):
|
||||
def update_character(self, character_id):
|
||||
return self.get(character_id=character_id).update_character()
|
||||
|
||||
def get_character_by_id(self, char_id):
|
||||
if self.filter(character_id=char_id).exists():
|
||||
return self.get(character_id=char_id)
|
||||
def get_character_by_id(self, character_id: int):
|
||||
"""Return character by character ID or None if not found."""
|
||||
try:
|
||||
return self.get(character_id=character_id)
|
||||
except self.model.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
@@ -89,4 +91,6 @@ class EveCorporationManager(models.Manager):
|
||||
)
|
||||
|
||||
def update_corporation(self, corp_id):
|
||||
return self.get(corporation_id=corp_id).update_corporation(self.provider.get_corporation(corp_id))
|
||||
return self\
|
||||
.get(corporation_id=corp_id)\
|
||||
.update_corporation(self.provider.get_corporation(corp_id))
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-05 21:39
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-10 20:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.2 on 2016-10-26 01:49
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.2 on 2016-11-01 04:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-12-16 23:22
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2017-01-02 19:23
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-01-18 13:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-03-22 23:09
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.5 on 2017-09-28 02:16
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
43
allianceauth/eveonline/migrations/0011_ids_to_integers.py
Normal file
43
allianceauth/eveonline/migrations/0011_ids_to_integers.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# Generated by Django 2.2.12 on 2020-05-25 02:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('eveonline', '0010_alliance_ticker'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='eveallianceinfo',
|
||||
name='alliance_id',
|
||||
field=models.PositiveIntegerField(unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='eveallianceinfo',
|
||||
name='executor_corp_id',
|
||||
field=models.PositiveIntegerField(),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evecharacter',
|
||||
name='alliance_id',
|
||||
field=models.PositiveIntegerField(blank=True, default=None, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evecharacter',
|
||||
name='character_id',
|
||||
field=models.PositiveIntegerField(unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evecharacter',
|
||||
name='corporation_id',
|
||||
field=models.PositiveIntegerField(),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evecorporationinfo',
|
||||
name='corporation_id',
|
||||
field=models.PositiveIntegerField(unique=True),
|
||||
),
|
||||
]
|
||||
33
allianceauth/eveonline/migrations/0012_index_additions.py
Normal file
33
allianceauth/eveonline/migrations/0012_index_additions.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 2.2.12 on 2020-05-26 02:09
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('eveonline', '0011_ids_to_integers'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddIndex(
|
||||
model_name='eveallianceinfo',
|
||||
index=models.Index(fields=['executor_corp_id'], name='eveonline_e_executo_7f3280_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='evecharacter',
|
||||
index=models.Index(fields=['corporation_id'], name='eveonline_e_corpora_cb4cd9_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='evecharacter',
|
||||
index=models.Index(fields=['alliance_id'], name='eveonline_e_allianc_39ee2a_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='evecharacter',
|
||||
index=models.Index(fields=['corporation_name'], name='eveonline_e_corpora_893c60_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='evecharacter',
|
||||
index=models.Index(fields=['alliance_name'], name='eveonline_e_allianc_63fd98_idx'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.1.1 on 2021-01-05 14:11
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('eveonline', '0012_index_additions'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='evecorporationinfo',
|
||||
name='ceo_id',
|
||||
field=models.PositiveIntegerField(blank=True, default=None, null=True),
|
||||
),
|
||||
]
|
||||
17
allianceauth/eveonline/migrations/0014_auto_20210105_1413.py
Normal file
17
allianceauth/eveonline/migrations/0014_auto_20210105_1413.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 3.1.1 on 2021-01-05 14:13
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('eveonline', '0013_evecorporationinfo_ceo_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddIndex(
|
||||
model_name='evecorporationinfo',
|
||||
index=models.Index(fields=['ceo_id'], name='eveonline_e_ceo_id_eea7b8_idx'),
|
||||
),
|
||||
]
|
||||
@@ -5,109 +5,35 @@ from .managers import EveCharacterManager, EveCharacterProviderManager
|
||||
from .managers import EveCorporationManager, EveCorporationProviderManager
|
||||
from .managers import EveAllianceManager, EveAllianceProviderManager
|
||||
from . import providers
|
||||
from .evelinks import eveimageserver
|
||||
|
||||
|
||||
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
|
||||
_DEFAULT_IMAGE_SIZE = 32
|
||||
|
||||
|
||||
class EveAllianceInfo(models.Model):
|
||||
alliance_id = models.CharField(max_length=254, unique=True)
|
||||
alliance_id = models.PositiveIntegerField(unique=True)
|
||||
alliance_name = models.CharField(max_length=254, unique=True)
|
||||
alliance_ticker = models.CharField(max_length=254)
|
||||
executor_corp_id = models.CharField(max_length=254)
|
||||
executor_corp_id = models.PositiveIntegerField()
|
||||
|
||||
objects = EveAllianceManager()
|
||||
provider = EveAllianceProviderManager()
|
||||
|
||||
class Meta:
|
||||
indexes = [models.Index(fields=['executor_corp_id',])]
|
||||
|
||||
def populate_alliance(self):
|
||||
alliance = self.provider.get_alliance(self.alliance_id)
|
||||
for corp_id in alliance.corp_ids:
|
||||
if not EveCorporationInfo.objects.filter(corporation_id=corp_id).exists():
|
||||
EveCorporationInfo.objects.create_corporation(corp_id)
|
||||
EveCorporationInfo.objects.filter(corporation_id__in=alliance.corp_ids).update(alliance=self)
|
||||
EveCorporationInfo.objects.filter(alliance=self).exclude(corporation_id__in=alliance.corp_ids).update(
|
||||
alliance=None)
|
||||
EveCorporationInfo.objects.filter(
|
||||
corporation_id__in=alliance.corp_ids).update(alliance=self
|
||||
)
|
||||
EveCorporationInfo.objects\
|
||||
.filter(alliance=self)\
|
||||
.exclude(corporation_id__in=alliance.corp_ids)\
|
||||
.update(alliance=None)
|
||||
|
||||
def update_alliance(self, alliance: providers.Alliance = None):
|
||||
if alliance is None:
|
||||
@@ -120,11 +46,13 @@ class EveAllianceInfo(models.Model):
|
||||
return self.alliance_name
|
||||
|
||||
@staticmethod
|
||||
def generic_logo_url(alliance_id: int, size: int = 32) -> str:
|
||||
def generic_logo_url(
|
||||
alliance_id: int, size: int = _DEFAULT_IMAGE_SIZE
|
||||
) -> str:
|
||||
"""image URL for the given alliance ID"""
|
||||
return _eve_entity_image_url('alliance', alliance_id, size)
|
||||
return eveimageserver.alliance_logo_url(alliance_id, size)
|
||||
|
||||
def logo_url(self, size:int = 32) -> str:
|
||||
def logo_url(self, size: int = _DEFAULT_IMAGE_SIZE) -> str:
|
||||
"""image URL of this alliance"""
|
||||
return self.generic_logo_url(self.alliance_id, size)
|
||||
|
||||
@@ -150,19 +78,26 @@ class EveAllianceInfo(models.Model):
|
||||
|
||||
|
||||
class EveCorporationInfo(models.Model):
|
||||
corporation_id = models.CharField(max_length=254, unique=True)
|
||||
corporation_id = models.PositiveIntegerField(unique=True)
|
||||
corporation_name = models.CharField(max_length=254, unique=True)
|
||||
corporation_ticker = models.CharField(max_length=254)
|
||||
member_count = models.IntegerField()
|
||||
alliance = models.ForeignKey(EveAllianceInfo, blank=True, null=True, on_delete=models.SET_NULL)
|
||||
ceo_id = models.PositiveIntegerField(blank=True, null=True, default=None)
|
||||
alliance = models.ForeignKey(
|
||||
EveAllianceInfo, blank=True, null=True, on_delete=models.SET_NULL
|
||||
)
|
||||
|
||||
objects = EveCorporationManager()
|
||||
provider = EveCorporationProviderManager()
|
||||
|
||||
class Meta:
|
||||
indexes = [models.Index(fields=['ceo_id',]),]
|
||||
|
||||
def update_corporation(self, corp: providers.Corporation = None):
|
||||
if corp is None:
|
||||
corp = self.provider.get_corporation(self.corporation_id)
|
||||
self.member_count = corp.members
|
||||
self.ceo_id = corp.ceo_id
|
||||
try:
|
||||
self.alliance = EveAllianceInfo.objects.get(alliance_id=corp.alliance_id)
|
||||
except EveAllianceInfo.DoesNotExist:
|
||||
@@ -174,11 +109,13 @@ class EveCorporationInfo(models.Model):
|
||||
return self.corporation_name
|
||||
|
||||
@staticmethod
|
||||
def generic_logo_url(corporation_id: int, size: int = 32) -> str:
|
||||
def generic_logo_url(
|
||||
corporation_id: int, size: int = _DEFAULT_IMAGE_SIZE
|
||||
) -> str:
|
||||
"""image URL for the given corporation ID"""
|
||||
return _eve_entity_image_url('corporation', corporation_id, size)
|
||||
return eveimageserver.corporation_logo_url(corporation_id, size)
|
||||
|
||||
def logo_url(self, size:int = 32) -> str:
|
||||
def logo_url(self, size: int = _DEFAULT_IMAGE_SIZE) -> str:
|
||||
"""image URL for this corporation"""
|
||||
return self.generic_logo_url(self.corporation_id, size)
|
||||
|
||||
@@ -204,18 +141,26 @@ class EveCorporationInfo(models.Model):
|
||||
|
||||
|
||||
class EveCharacter(models.Model):
|
||||
character_id = models.CharField(max_length=254, unique=True)
|
||||
character_id = models.PositiveIntegerField(unique=True)
|
||||
character_name = models.CharField(max_length=254, unique=True)
|
||||
corporation_id = models.CharField(max_length=254)
|
||||
corporation_id = models.PositiveIntegerField()
|
||||
corporation_name = models.CharField(max_length=254)
|
||||
corporation_ticker = models.CharField(max_length=5)
|
||||
alliance_id = models.CharField(max_length=254, blank=True, null=True, default='')
|
||||
alliance_id = models.PositiveIntegerField(blank=True, null=True, default=None)
|
||||
alliance_name = models.CharField(max_length=254, blank=True, null=True, default='')
|
||||
alliance_ticker = models.CharField(max_length=5, blank=True, null=True, default='')
|
||||
|
||||
objects = EveCharacterManager()
|
||||
provider = EveCharacterProviderManager()
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(fields=['corporation_id',]),
|
||||
models.Index(fields=['alliance_id',]),
|
||||
models.Index(fields=['corporation_name',]),
|
||||
models.Index(fields=['alliance_name',]),
|
||||
]
|
||||
|
||||
@property
|
||||
def alliance(self) -> Union[EveAllianceInfo, None]:
|
||||
"""
|
||||
@@ -253,11 +198,13 @@ class EveCharacter(models.Model):
|
||||
return self.character_name
|
||||
|
||||
@staticmethod
|
||||
def generic_portrait_url(character_id: int, size: int = 32) -> str:
|
||||
def generic_portrait_url(
|
||||
character_id: int, size: int = _DEFAULT_IMAGE_SIZE
|
||||
) -> str:
|
||||
"""image URL for the given character ID"""
|
||||
return _eve_entity_image_url('character', character_id, size)
|
||||
return eveimageserver.character_portrait_url(character_id, size)
|
||||
|
||||
def portrait_url(self, size = 32) -> str:
|
||||
def portrait_url(self, size=_DEFAULT_IMAGE_SIZE) -> str:
|
||||
"""image URL for this character"""
|
||||
return self.generic_portrait_url(self.character_id, size)
|
||||
|
||||
@@ -281,7 +228,7 @@ class EveCharacter(models.Model):
|
||||
"""image URL for this character"""
|
||||
return self.portrait_url(256)
|
||||
|
||||
def corporation_logo_url(self, size = 32) -> str:
|
||||
def corporation_logo_url(self, size=_DEFAULT_IMAGE_SIZE) -> str:
|
||||
"""image URL for corporation of this character"""
|
||||
return EveCorporationInfo.generic_logo_url(self.corporation_id, size)
|
||||
|
||||
@@ -305,7 +252,7 @@ class EveCharacter(models.Model):
|
||||
"""image URL for corporation of this character"""
|
||||
return self.corporation_logo_url(256)
|
||||
|
||||
def alliance_logo_url(self, size = 32) -> str:
|
||||
def alliance_logo_url(self, size=_DEFAULT_IMAGE_SIZE) -> str:
|
||||
"""image URL for alliance of this character or empty string"""
|
||||
if self.alliance_id:
|
||||
return EveAllianceInfo.generic_logo_url(self.alliance_id, size)
|
||||
|
||||
@@ -7,6 +7,8 @@ from jsonschema.exceptions import RefResolutionError
|
||||
from django.conf import settings
|
||||
from esi.clients import esi_client_factory
|
||||
|
||||
from allianceauth import __version__
|
||||
|
||||
|
||||
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
||||
os.path.abspath(__file__)), 'swagger.json'
|
||||
@@ -166,7 +168,7 @@ class EveSwaggerProvider(EveProvider):
|
||||
else:
|
||||
try:
|
||||
self._client = esi_client_factory(
|
||||
token=token, spec_file=SWAGGER_SPEC_PATH
|
||||
token=token, spec_file=SWAGGER_SPEC_PATH, app_info_text=("allianceauth v" + __version__)
|
||||
)
|
||||
except (HTTPError, RefResolutionError):
|
||||
logger.exception(
|
||||
@@ -182,7 +184,7 @@ class EveSwaggerProvider(EveProvider):
|
||||
def client(self):
|
||||
if self._client is None:
|
||||
self._client = esi_client_factory(
|
||||
token=self._token, spec_file=SWAGGER_SPEC_PATH
|
||||
token=self._token, spec_file=SWAGGER_SPEC_PATH, app_info_text=("allianceauth v" + __version__)
|
||||
)
|
||||
return self._client
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user