mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-05 06:36: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
|
#transifex
|
||||||
.tx/
|
.tx/
|
||||||
|
|
||||||
|
#other
|
||||||
|
.flake8
|
||||||
|
.pylintrc
|
||||||
|
Makefile
|
||||||
|
|||||||
100
.gitlab-ci.yml
100
.gitlab-ci.yml
@@ -1,61 +1,107 @@
|
|||||||
stages:
|
stages:
|
||||||
- "test"
|
- pre-commit
|
||||||
|
- gitlab
|
||||||
|
- test
|
||||||
- deploy
|
- deploy
|
||||||
|
|
||||||
|
include:
|
||||||
|
- template: Dependency-Scanning.gitlab-ci.yml
|
||||||
|
- template: Security/SAST.gitlab-ci.yml
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- python -V
|
- apt-get update && apt-get install redis-server -y
|
||||||
- pip install wheel tox
|
- redis-server --daemonize yes
|
||||||
|
- python -V
|
||||||
|
- pip install wheel tox
|
||||||
|
|
||||||
test-3.5-core:
|
pre-commit-check:
|
||||||
image: python:3.5-buster
|
stage: pre-commit
|
||||||
script:
|
|
||||||
- tox -e py35-core
|
|
||||||
|
|
||||||
test-3.6-core:
|
|
||||||
image: python:3.6-buster
|
image: python:3.6-buster
|
||||||
|
variables:
|
||||||
|
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- ${PRE_COMMIT_HOME}
|
||||||
script:
|
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:
|
test-3.7-core:
|
||||||
image: python:3.7-buster
|
image: python:3.7-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py37-core
|
- tox -e py37-core
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
cobertura: coverage.xml
|
||||||
|
|
||||||
test-3.8-core:
|
test-3.8-core:
|
||||||
image: python:3.8-buster
|
image: python:3.8-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py38-core
|
- tox -e py38-core
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
cobertura: coverage.xml
|
||||||
|
|
||||||
test-3.5-all:
|
test-3.9-core:
|
||||||
image: python:3.5-buster
|
image: python:3.9-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py35-all
|
- tox -e py39-core
|
||||||
|
artifacts:
|
||||||
test-3.6-all:
|
when: always
|
||||||
image: python:3.6-buster
|
reports:
|
||||||
script:
|
cobertura: coverage.xml
|
||||||
- tox -e py36-all
|
|
||||||
|
|
||||||
test-3.7-all:
|
test-3.7-all:
|
||||||
image: python:3.7-buster
|
image: python:3.7-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py37-all
|
- tox -e py37-all
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
cobertura: coverage.xml
|
||||||
|
|
||||||
test-3.8-all:
|
test-3.8-all:
|
||||||
image: python:3.8-buster
|
image: python:3.8-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py38-all
|
- 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:
|
deploy_production:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image: python:3.6-stretch
|
image: python:3.9-bullseye
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- pip install twine
|
- pip install twine wheel
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- python setup.py sdist
|
- python setup.py sdist bdist_wheel
|
||||||
- twine upload dist/*
|
- twine upload dist/*
|
||||||
|
|
||||||
only:
|
rules:
|
||||||
- tags
|
- 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
|
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
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
Public License instead of this License.
|
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)
|
- 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).
|
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
|
# This will make sure the app is always imported when
|
||||||
# Django starts so that shared_task will use this app.
|
# Django starts so that shared_task will use this app.
|
||||||
|
|
||||||
__version__ = '2.6.3'
|
__version__ = '2.9.0b1'
|
||||||
NAME = 'Alliance Auth v%s' % __version__
|
__title__ = 'Alliance Auth'
|
||||||
|
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
|
||||||
|
NAME = '%s v%s' % (__title__, __version__)
|
||||||
default_app_config = 'allianceauth.apps.AllianceAuthConfig'
|
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 import admin
|
||||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||||
from django.contrib.auth.models import User as BaseUser, \
|
from django.contrib.auth.models import User as BaseUser, \
|
||||||
Permission as BasePermission, Group
|
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 allianceauth.services.hooks import ServicesHook
|
||||||
from django.db.models.signals import pre_save, post_save, pre_delete, \
|
from django.db.models.signals import pre_save, post_save, pre_delete, \
|
||||||
post_delete, m2m_changed
|
post_delete, m2m_changed
|
||||||
@@ -24,11 +22,6 @@ from allianceauth.eveonline.tasks import update_character
|
|||||||
from .app_settings import AUTHENTICATION_ADMIN_USERS_MAX_GROUPS, \
|
from .app_settings import AUTHENTICATION_ADMIN_USERS_MAX_GROUPS, \
|
||||||
AUTHENTICATION_ADMIN_USERS_MAX_CHARS
|
AUTHENTICATION_ADMIN_USERS_MAX_CHARS
|
||||||
|
|
||||||
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
|
|
||||||
_has_auto_groups = True
|
|
||||||
else:
|
|
||||||
_has_auto_groups = False
|
|
||||||
|
|
||||||
|
|
||||||
def make_service_hooks_update_groups_action(service):
|
def make_service_hooks_update_groups_action(service):
|
||||||
"""
|
"""
|
||||||
@@ -37,6 +30,9 @@ def make_service_hooks_update_groups_action(service):
|
|||||||
:return: fn to update services groups for the selected users
|
:return: fn to update services groups for the selected users
|
||||||
"""
|
"""
|
||||||
def update_service_groups(modeladmin, request, queryset):
|
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?
|
for user in queryset: # queryset filtering doesn't work here?
|
||||||
service.update_groups(user)
|
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
|
:return: fn to sync nickname for the selected users
|
||||||
"""
|
"""
|
||||||
def sync_nickname(modeladmin, request, queryset):
|
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?
|
for user in queryset: # queryset filtering doesn't work here?
|
||||||
service.sync_nickname(user)
|
service.sync_nickname(user)
|
||||||
|
|
||||||
@@ -86,7 +85,6 @@ class UserProfileInline(admin.StackedInline):
|
|||||||
query |= Q(userprofile__isnull=True)
|
query |= Q(userprofile__isnull=True)
|
||||||
else:
|
else:
|
||||||
query |= Q(character_ownership__user=obj)
|
query |= Q(character_ownership__user=obj)
|
||||||
qs = EveCharacter.objects.filter(query)
|
|
||||||
formset = super().get_formset(request, obj=obj, **kwargs)
|
formset = super().get_formset(request, obj=obj, **kwargs)
|
||||||
|
|
||||||
def get_kwargs(self, index):
|
def get_kwargs(self, index):
|
||||||
@@ -94,7 +92,7 @@ class UserProfileInline(admin.StackedInline):
|
|||||||
formset.get_form_kwargs = get_kwargs
|
formset.get_form_kwargs = get_kwargs
|
||||||
return formset
|
return formset
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request, obj=None):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
@@ -115,6 +113,8 @@ def user_profile_pic(obj):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
user_profile_pic.short_description = ''
|
user_profile_pic.short_description = ''
|
||||||
|
|
||||||
|
|
||||||
@@ -146,6 +146,7 @@ def user_username(obj):
|
|||||||
user_obj.username,
|
user_obj.username,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
user_username.short_description = 'user / main'
|
user_username.short_description = 'user / main'
|
||||||
user_username.admin_order_field = 'username'
|
user_username.admin_order_field = 'username'
|
||||||
|
|
||||||
@@ -162,7 +163,8 @@ def user_main_organization(obj):
|
|||||||
else:
|
else:
|
||||||
corporation = user_obj.profile.main_character.corporation_name
|
corporation = user_obj.profile.main_character.corporation_name
|
||||||
if user_obj.profile.main_character.alliance_id:
|
if user_obj.profile.main_character.alliance_id:
|
||||||
result = format_html('{}<br>{}',
|
result = format_html(
|
||||||
|
'{}<br>{}',
|
||||||
corporation,
|
corporation,
|
||||||
user_obj.profile.main_character.alliance_name
|
user_obj.profile.main_character.alliance_name
|
||||||
)
|
)
|
||||||
@@ -170,6 +172,7 @@ def user_main_organization(obj):
|
|||||||
result = corporation
|
result = corporation
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
user_main_organization.short_description = 'Corporation / Alliance (Main)'
|
user_main_organization.short_description = 'Corporation / Alliance (Main)'
|
||||||
user_main_organization.admin_order_field = \
|
user_main_organization.admin_order_field = \
|
||||||
'profile__main_character__corporation_name'
|
'profile__main_character__corporation_name'
|
||||||
@@ -199,13 +202,13 @@ class MainCorporationsFilter(admin.SimpleListFilter):
|
|||||||
return qs.all()
|
return qs.all()
|
||||||
else:
|
else:
|
||||||
if qs.model == User:
|
if qs.model == User:
|
||||||
return qs\
|
return qs.filter(
|
||||||
.filter(profile__main_character__corporation_id=\
|
profile__main_character__corporation_id=self.value()
|
||||||
self.value())
|
)
|
||||||
else:
|
else:
|
||||||
return qs\
|
return qs.filter(
|
||||||
.filter(user__profile__main_character__corporation_id=\
|
user__profile__main_character__corporation_id=self.value()
|
||||||
self.value())
|
)
|
||||||
|
|
||||||
|
|
||||||
class MainAllianceFilter(admin.SimpleListFilter):
|
class MainAllianceFilter(admin.SimpleListFilter):
|
||||||
@@ -233,12 +236,11 @@ class MainAllianceFilter(admin.SimpleListFilter):
|
|||||||
return qs.all()
|
return qs.all()
|
||||||
else:
|
else:
|
||||||
if qs.model == User:
|
if qs.model == User:
|
||||||
return qs\
|
return qs.filter(profile__main_character__alliance_id=self.value())
|
||||||
.filter(profile__main_character__alliance_id=self.value())
|
|
||||||
else:
|
else:
|
||||||
return qs\
|
return qs.filter(
|
||||||
.filter(user__profile__main_character__alliance_id=\
|
user__profile__main_character__alliance_id=self.value()
|
||||||
self.value())
|
)
|
||||||
|
|
||||||
|
|
||||||
def update_main_character_model(modeladmin, request, queryset):
|
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 from ESI started for {} characters'.format(tasks_count)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
update_main_character_model.short_description = \
|
update_main_character_model.short_description = \
|
||||||
'Update main character model from ESI'
|
'Update main character model from ESI'
|
||||||
|
|
||||||
@@ -261,7 +264,6 @@ class UserAdmin(BaseUserAdmin):
|
|||||||
"""Extending Django's UserAdmin model
|
"""Extending Django's UserAdmin model
|
||||||
|
|
||||||
Behavior of groups and characters columns can be configured via settings
|
Behavior of groups and characters columns can be configured via settings
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
@@ -269,24 +271,9 @@ class UserAdmin(BaseUserAdmin):
|
|||||||
"all": ("authentication/css/admin.css",)
|
"all": ("authentication/css/admin.css",)
|
||||||
}
|
}
|
||||||
|
|
||||||
class RealGroupsFilter(admin.SimpleListFilter):
|
def get_queryset(self, request):
|
||||||
"""Custom filter to get groups w/o Autogroups"""
|
qs = super().get_queryset(request)
|
||||||
title = 'group'
|
return qs.prefetch_related("character_ownerships__character", "groups")
|
||||||
parameter_name = 'group_id__exact'
|
|
||||||
|
|
||||||
def lookups(self, request, model_admin):
|
|
||||||
qs = Group.objects.all().order_by(Lower('name'))
|
|
||||||
if _has_auto_groups:
|
|
||||||
qs = qs\
|
|
||||||
.filter(managedalliancegroup__isnull=True)\
|
|
||||||
.filter(managedcorpgroup__isnull=True)
|
|
||||||
return tuple([(x.pk, x.name) for x in qs])
|
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
|
||||||
if self.value() is None:
|
|
||||||
return queryset.all()
|
|
||||||
else:
|
|
||||||
return queryset.filter(groups__pk=self.value())
|
|
||||||
|
|
||||||
def get_actions(self, request):
|
def get_actions(self, request):
|
||||||
actions = super(BaseUserAdmin, self).get_actions(request)
|
actions = super(BaseUserAdmin, self).get_actions(request)
|
||||||
@@ -335,11 +322,9 @@ class UserAdmin(BaseUserAdmin):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
inlines = BaseUserAdmin.inlines + [UserProfileInline]
|
inlines = BaseUserAdmin.inlines + [UserProfileInline]
|
||||||
|
|
||||||
ordering = ('username', )
|
ordering = ('username', )
|
||||||
list_select_related = True
|
list_select_related = ('profile__state', 'profile__main_character')
|
||||||
show_full_result_count = True
|
show_full_result_count = True
|
||||||
|
|
||||||
list_display = (
|
list_display = (
|
||||||
user_profile_pic,
|
user_profile_pic,
|
||||||
user_username,
|
user_username,
|
||||||
@@ -352,10 +337,9 @@ class UserAdmin(BaseUserAdmin):
|
|||||||
'_role'
|
'_role'
|
||||||
)
|
)
|
||||||
list_display_links = None
|
list_display_links = None
|
||||||
|
|
||||||
list_filter = (
|
list_filter = (
|
||||||
'profile__state',
|
'profile__state',
|
||||||
RealGroupsFilter,
|
'groups',
|
||||||
MainCorporationsFilter,
|
MainCorporationsFilter,
|
||||||
MainAllianceFilter,
|
MainAllianceFilter,
|
||||||
'is_active',
|
'is_active',
|
||||||
@@ -369,21 +353,15 @@ class UserAdmin(BaseUserAdmin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _characters(self, obj):
|
def _characters(self, obj):
|
||||||
my_characters = [
|
character_ownerships = list(obj.character_ownerships.all())
|
||||||
x.character.character_name
|
characters = [obj.character.character_name for obj in character_ownerships]
|
||||||
for x in CharacterOwnership.objects\
|
|
||||||
.filter(user=obj)\
|
|
||||||
.order_by('character__character_name')\
|
|
||||||
.select_related()
|
|
||||||
]
|
|
||||||
return self._list_2_html_w_tooltips(
|
return self._list_2_html_w_tooltips(
|
||||||
my_characters,
|
sorted(characters),
|
||||||
AUTHENTICATION_ADMIN_USERS_MAX_CHARS
|
AUTHENTICATION_ADMIN_USERS_MAX_CHARS
|
||||||
)
|
)
|
||||||
|
|
||||||
_characters.short_description = 'characters'
|
_characters.short_description = 'characters'
|
||||||
|
|
||||||
|
|
||||||
def _state(self, obj):
|
def _state(self, obj):
|
||||||
return obj.profile.state.name
|
return obj.profile.state.name
|
||||||
|
|
||||||
@@ -391,19 +369,9 @@ class UserAdmin(BaseUserAdmin):
|
|||||||
_state.admin_order_field = 'profile__state'
|
_state.admin_order_field = 'profile__state'
|
||||||
|
|
||||||
def _groups(self, obj):
|
def _groups(self, obj):
|
||||||
if not _has_auto_groups:
|
my_groups = sorted([group.name for group in list(obj.groups.all())])
|
||||||
my_groups = [x.name for x in obj.groups.order_by('name')]
|
|
||||||
else:
|
|
||||||
my_groups = [
|
|
||||||
x.name for x in obj.groups\
|
|
||||||
.filter(managedalliancegroup__isnull=True)\
|
|
||||||
.filter(managedcorpgroup__isnull=True)\
|
|
||||||
.order_by('name')
|
|
||||||
]
|
|
||||||
|
|
||||||
return self._list_2_html_w_tooltips(
|
return self._list_2_html_w_tooltips(
|
||||||
my_groups,
|
my_groups, AUTHENTICATION_ADMIN_USERS_MAX_GROUPS
|
||||||
AUTHENTICATION_ADMIN_USERS_MAX_GROUPS
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_groups.short_description = 'groups'
|
_groups.short_description = 'groups'
|
||||||
@@ -440,9 +408,14 @@ class StateAdmin(admin.ModelAdmin):
|
|||||||
list_select_related = True
|
list_select_related = True
|
||||||
list_display = ('name', 'priority', '_user_count')
|
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):
|
def _user_count(self, obj):
|
||||||
return obj.userprofile_set.all().count()
|
return obj.user_count
|
||||||
_user_count.short_description = 'Users'
|
_user_count.short_description = 'Users'
|
||||||
|
_user_count.admin_order_field = 'user_count'
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {
|
(None, {
|
||||||
@@ -475,6 +448,8 @@ class StateAdmin(admin.ModelAdmin):
|
|||||||
elif db_field.name == "member_alliances":
|
elif db_field.name == "member_alliances":
|
||||||
kwargs["queryset"] = EveAllianceInfo.objects.all()\
|
kwargs["queryset"] = EveAllianceInfo.objects.all()\
|
||||||
.order_by(Lower('alliance_name'))
|
.order_by(Lower('alliance_name'))
|
||||||
|
elif db_field.name == "permissions":
|
||||||
|
kwargs["queryset"] = Permission.objects.select_related("content_type").all()
|
||||||
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||||
|
|
||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
@@ -498,7 +473,8 @@ class BaseOwnershipAdmin(admin.ModelAdmin):
|
|||||||
"all": ("authentication/css/admin.css",)
|
"all": ("authentication/css/admin.css",)
|
||||||
}
|
}
|
||||||
|
|
||||||
list_select_related = True
|
list_select_related = (
|
||||||
|
'user__profile__state', 'user__profile__main_character', 'character')
|
||||||
list_display = (
|
list_display = (
|
||||||
user_profile_pic,
|
user_profile_pic,
|
||||||
user_username,
|
user_username,
|
||||||
@@ -536,6 +512,7 @@ class CharacterOwnershipAdmin(BaseOwnershipAdmin):
|
|||||||
class PermissionAdmin(admin.ModelAdmin):
|
class PermissionAdmin(admin.ModelAdmin):
|
||||||
actions = None
|
actions = None
|
||||||
readonly_fields = [field.name for field in BasePermission._meta.fields]
|
readonly_fields = [field.name for field in BasePermission._meta.fields]
|
||||||
|
search_fields = ('codename', )
|
||||||
list_display = ('admin_name', 'name', 'codename', 'content_type')
|
list_display = ('admin_name', 'name', 'codename', 'content_type')
|
||||||
list_filter = ('content_type__app_label',)
|
list_filter = ('content_type__app_label',)
|
||||||
|
|
||||||
@@ -543,7 +520,7 @@ class PermissionAdmin(admin.ModelAdmin):
|
|||||||
def admin_name(obj):
|
def admin_name(obj):
|
||||||
return str(obj)
|
return str(obj)
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request, obj=None):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
|
|||||||
@@ -43,4 +43,3 @@ AUTHENTICATION_ADMIN_USERS_MAX_GROUPS = \
|
|||||||
|
|
||||||
AUTHENTICATION_ADMIN_USERS_MAX_CHARS = \
|
AUTHENTICATION_ADMIN_USERS_MAX_CHARS = \
|
||||||
_clean_setting('AUTHENTICATION_ADMIN_USERS_MAX_CHARS', 5)
|
_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
|
import logging
|
||||||
|
|
||||||
|
from django.contrib.auth.backends import ModelBackend
|
||||||
|
from django.contrib.auth.models import User, Permission
|
||||||
|
|
||||||
from .models import UserProfile, CharacterOwnership, OwnershipRecord
|
from .models import UserProfile, CharacterOwnership, OwnershipRecord
|
||||||
|
|
||||||
|
|
||||||
@@ -11,9 +12,11 @@ logger = logging.getLogger(__name__)
|
|||||||
class StateBackend(ModelBackend):
|
class StateBackend(ModelBackend):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_state_permissions(user_obj):
|
def _get_state_permissions(user_obj):
|
||||||
profile_state_field = UserProfile._meta.get_field('state')
|
"""returns permissions for state of given user object"""
|
||||||
user_state_query = 'state__%s__user' % profile_state_field.related_query_name()
|
if hasattr(user_obj, "profile") and user_obj.profile:
|
||||||
return Permission.objects.filter(**{user_state_query: user_obj})
|
return Permission.objects.filter(state=user_obj.profile.state)
|
||||||
|
else:
|
||||||
|
return Permission.objects.none()
|
||||||
|
|
||||||
def get_state_permissions(self, user_obj, obj=None):
|
def get_state_permissions(self, user_obj, obj=None):
|
||||||
return self._get_permissions(user_obj, obj, 'state')
|
return self._get_permissions(user_obj, obj, 'state')
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from allianceauth.authentication.models import User
|
||||||
|
|
||||||
class RegistrationForm(forms.Form):
|
class RegistrationForm(forms.Form):
|
||||||
email = forms.EmailField(label=_('Email'), max_length=254, required=True)
|
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/$', views.RegistrationView.as_view(), name='registration_register'),
|
||||||
url(r'^register/complete/$', views.registration_complete, name='registration_complete'),
|
url(r'^register/complete/$', views.registration_complete, name='registration_complete'),
|
||||||
url(r'^register/closed/$', views.registration_closed, name='registration_disallowed'),
|
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):
|
def create_by_token(self, token):
|
||||||
if not EveCharacter.objects.filter(character_id=token.character_id).exists():
|
if not EveCharacter.objects.filter(character_id=token.character_id).exists():
|
||||||
EveCharacter.objects.create_character(token.character_id)
|
EveCharacter.objects.create_character(token.character_id)
|
||||||
return self.create(character=EveCharacter.objects.get(character_id=token.character_id), user=token.user,
|
return self.create(character=EveCharacter.objects.get(character_id=token.character_id), user=token.user, owner_hash=token.character_owner_hash)
|
||||||
owner_hash=token.character_owner_hash)
|
|
||||||
|
|
||||||
|
|
||||||
class StateQuerySet(QuerySet):
|
class StateQuerySet(QuerySet):
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2016-09-05 21:38
|
# Generated by Django 1.10.1 on 2016-09-05 21:38
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2016-09-07 19:14
|
# Generated by Django 1.10.1 on 2016-09-07 19:14
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2016-09-09 20:29
|
# Generated by Django 1.10.1 on 2016-09-09 20:29
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2016-09-09 23:19
|
# Generated by Django 1.10.1 on 2016-09-09 23:19
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2016-09-09 23:11
|
# Generated by Django 1.10.1 on 2016-09-09 23:11
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2016-09-10 05:42
|
# Generated by Django 1.10.1 on 2016-09-10 05:42
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2016-09-10 21:50
|
# Generated by Django 1.10.1 on 2016-09-10 21:50
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2016-09-12 13:04
|
# Generated by Django 1.10.1 on 2016-09-12 13:04
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.2 on 2016-10-21 02:28
|
# Generated by Django 1.10.2 on 2016-10-21 02:28
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2017-01-07 06:47
|
# Generated by Django 1.10.1 on 2017-01-07 06:47
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2017-01-07 07:11
|
# Generated by Django 1.10.1 on 2017-01-07 07:11
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.5 on 2017-01-12 00:59
|
# Generated by Django 1.10.5 on 2017-01-12 00:59
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.2 on 2016-12-11 23:14
|
# Generated by Django 1.10.2 on 2016-12-11 23:14
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2016-09-09 23:19
|
# Generated by Django 1.10.1 on 2016-09-09 23:19
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.5 on 2017-03-22 23:09
|
# Generated by Django 1.10.5 on 2017-03-22 23:09
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
@@ -171,8 +170,7 @@ def recreate_authservicesinfo(apps, schema_editor):
|
|||||||
|
|
||||||
# repopulate main characters
|
# repopulate main characters
|
||||||
for profile in UserProfile.objects.exclude(main_character__isnull=True).select_related('user', 'main_character'):
|
for profile in UserProfile.objects.exclude(main_character__isnull=True).select_related('user', 'main_character'):
|
||||||
AuthServicesInfo.objects.update_or_create(user=profile.user,
|
AuthServicesInfo.objects.update_or_create(user=profile.user, defaults={'main_char_id': profile.main_character.character_id})
|
||||||
defaults={'main_char_id': profile.main_character.character_id})
|
|
||||||
|
|
||||||
# repopulate states we understand
|
# repopulate states we understand
|
||||||
for profile in UserProfile.objects.exclude(state__name='Guest').filter(
|
for profile in UserProfile.objects.exclude(state__name='Guest').filter(
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations
|
from django.db import migrations
|
||||||
|
|||||||
@@ -14,15 +14,11 @@ logger = logging.getLogger(__name__)
|
|||||||
class State(models.Model):
|
class State(models.Model):
|
||||||
name = models.CharField(max_length=20, unique=True)
|
name = models.CharField(max_length=20, unique=True)
|
||||||
permissions = models.ManyToManyField(Permission, blank=True)
|
permissions = models.ManyToManyField(Permission, blank=True)
|
||||||
priority = models.IntegerField(unique=True,
|
priority = models.IntegerField(unique=True, help_text="Users get assigned the state with the highest priority available to them.")
|
||||||
help_text="Users get assigned the state with the highest priority available to them.")
|
|
||||||
|
|
||||||
member_characters = models.ManyToManyField(EveCharacter, blank=True,
|
member_characters = models.ManyToManyField(EveCharacter, blank=True, help_text="Characters to which this state is available.")
|
||||||
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_corporations = models.ManyToManyField(EveCorporationInfo, blank=True,
|
member_alliances = models.ManyToManyField(EveAllianceInfo, blank=True, help_text="Alliances to whose members this state is available.")
|
||||||
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.")
|
public = models.BooleanField(default=False, help_text="Make this state available to any character.")
|
||||||
|
|
||||||
objects = StateManager()
|
objects = StateManager()
|
||||||
@@ -73,11 +69,22 @@ class UserProfile(models.Model):
|
|||||||
if commit:
|
if commit:
|
||||||
logger.info('Updating {} state to {}'.format(self.user, self.state))
|
logger.info('Updating {} state to {}'.format(self.user, self.state))
|
||||||
self.save(update_fields=['state'])
|
self.save(update_fields=['state'])
|
||||||
notify(self.user, _('State Changed'),
|
notify(
|
||||||
_('Your user state has been changed to %(state)s') % ({'state': state}),
|
self.user,
|
||||||
'info')
|
_('State changed to: %s' % state),
|
||||||
|
_('Your user\'s state is now: %(state)s')
|
||||||
|
% ({'state': state}),
|
||||||
|
'info'
|
||||||
|
)
|
||||||
from allianceauth.authentication.signals import state_changed
|
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):
|
def __str__(self):
|
||||||
return str(self.user)
|
return str(self.user)
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ def trigger_state_check(state):
|
|||||||
check_states = State.objects.filter(priority__lt=state.priority)
|
check_states = State.objects.filter(priority__lt=state.priority)
|
||||||
for profile in UserProfile.objects.filter(state__in=check_states):
|
for profile in UserProfile.objects.filter(state__in=check_states):
|
||||||
if state.available_to_user(profile.user):
|
if state.available_to_user(profile.user):
|
||||||
profile.state = state
|
profile.assign_state(state)
|
||||||
profile.save(update_fields=['state'])
|
|
||||||
state_changed.send(sender=state.__class__, user=profile.user, state=state)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=State.member_characters.through)
|
@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)
|
@receiver(post_save, sender=Token)
|
||||||
def record_character_ownership(sender, instance, created, *args, **kwargs):
|
def record_character_ownership(sender, instance, created, *args, **kwargs):
|
||||||
if created:
|
if created:
|
||||||
logger.debug('New token for {0} character {1} saved. Evaluating ownership.'.format(instance.user,
|
logger.debug('New token for {0} character {1} saved. Evaluating ownership.'.format(instance.user, instance.character_name))
|
||||||
instance.character_name))
|
|
||||||
if instance.user:
|
if instance.user:
|
||||||
query = Q(owner_hash=instance.character_owner_hash) & Q(user=instance.user)
|
query = Q(owner_hash=instance.character_owner_hash) & Q(user=instance.user)
|
||||||
else:
|
else:
|
||||||
@@ -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()
|
CharacterOwnership.objects.filter(character__character_id=instance.character_id).exclude(query).delete()
|
||||||
# create character if needed
|
# create character if needed
|
||||||
if EveCharacter.objects.filter(character_id=instance.character_id).exists() is False:
|
if EveCharacter.objects.filter(character_id=instance.character_id).exists() is False:
|
||||||
logger.debug('Token is for a new character. Creating model for {0} ({1})'.format(instance.character_name,
|
logger.debug('Token is for a new character. Creating model for {0} ({1})'.format(instance.character_name, instance.character_id))
|
||||||
instance.character_id))
|
|
||||||
EveCharacter.objects.create_character(instance.character_id)
|
EveCharacter.objects.create_character(instance.character_id)
|
||||||
char = EveCharacter.objects.get(character_id=instance.character_id)
|
char = EveCharacter.objects.get(character_id=instance.character_id)
|
||||||
# check if we need to create ownership
|
# check if we need to create ownership
|
||||||
if instance.user and not CharacterOwnership.objects.filter(
|
if instance.user and not CharacterOwnership.objects.filter(
|
||||||
character__character_id=instance.character_id).exists():
|
character__character_id=instance.character_id).exists():
|
||||||
logger.debug("Character {0} is not yet owned. Assigning ownership to {1}".format(instance.character_name,
|
logger.debug("Character {0} is not yet owned. Assigning ownership to {1}".format(instance.character_name, instance.user))
|
||||||
instance.user))
|
CharacterOwnership.objects.update_or_create(character=char, defaults={'owner_hash': instance.character_owner_hash, 'user': instance.user})
|
||||||
CharacterOwnership.objects.update_or_create(character=char,
|
|
||||||
defaults={'owner_hash': instance.character_owner_hash,
|
|
||||||
'user': instance.user})
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_delete, sender=CharacterOwnership)
|
@receiver(pre_delete, sender=CharacterOwnership)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load staticfiles %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% trans "Dashboard" %}{% endblock %}
|
{% block page_title %}{% trans "Dashboard" %}{% endblock %}
|
||||||
@@ -14,7 +14,11 @@
|
|||||||
<div class="col-sm-6 text-center">
|
<div class="col-sm-6 text-center">
|
||||||
<div class="panel panel-primary" style="height:100%">
|
<div class="panel panel-primary" style="height:100%">
|
||||||
<div class="panel-heading">
|
<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>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% if request.user.profile.main_character %}
|
{% if request.user.profile.main_character %}
|
||||||
@@ -141,8 +145,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<table class="table table-aa visible-xs-block" style="width: 100%">
|
<table class="table table-aa visible-xs-block" style="width: 100%">
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for ownership in request.user.character_ownerships.all %}
|
{% for char in characters %}
|
||||||
{% with ownership.character as char %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center" style="vertical-align: middle">
|
<td class="text-center" style="vertical-align: middle">
|
||||||
<img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}">
|
<img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}">
|
||||||
@@ -153,7 +156,6 @@
|
|||||||
{{ char.alliance_name|default:"" }}
|
{{ char.alliance_name|default:"" }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endwith %}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" 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' %}
|
{% include 'allianceauth/icons.html' %}
|
||||||
|
|
||||||
<title>{% block title %}{{ SITE_NAME }}{% endblock %}</title>
|
<title>{% block title %}{{ SITE_NAME }}{% endblock %}</title>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{% load staticfiles %}
|
{% extends 'public/base.html' %}
|
||||||
|
{% load static %}
|
||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% extends 'public/base.html' %}
|
|
||||||
{% block page_title %}Registration{% endblock %}
|
{% block page_title %}Registration{% endblock %}
|
||||||
{% block extra_include %}
|
{% block extra_include %}
|
||||||
{% include 'bundles/bootstrap-css.html' %}
|
{% include 'bundles/bootstrap-css.html' %}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ def get_admin_change_view_url(obj: object) -> str:
|
|||||||
args=(obj.pk,)
|
args=(obj.pk,)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_admin_search_url(ModelClass: type) -> str:
|
def get_admin_search_url(ModelClass: type) -> str:
|
||||||
"""returns URL to search URL for model of given object"""
|
"""returns URL to search URL for model of given object"""
|
||||||
return '{}{}/'.format(
|
return '{}{}/'.format(
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
from urllib.parse import quote
|
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.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 django.test import TestCase, RequestFactory, Client
|
||||||
|
|
||||||
from allianceauth.authentication.models import (
|
from allianceauth.authentication.models import (
|
||||||
@@ -13,12 +11,12 @@ from allianceauth.authentication.models import (
|
|||||||
from allianceauth.eveonline.models import (
|
from allianceauth.eveonline.models import (
|
||||||
EveCharacter, EveCorporationInfo, EveAllianceInfo
|
EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||||
)
|
)
|
||||||
|
from allianceauth.services.hooks import ServicesHook
|
||||||
from allianceauth.tests.auth_utils import AuthUtils
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
|
|
||||||
from ..admin import (
|
from ..admin import (
|
||||||
BaseUserAdmin,
|
BaseUserAdmin,
|
||||||
CharacterOwnershipAdmin,
|
CharacterOwnershipAdmin,
|
||||||
PermissionAdmin,
|
|
||||||
StateAdmin,
|
StateAdmin,
|
||||||
MainCorporationsFilter,
|
MainCorporationsFilter,
|
||||||
MainAllianceFilter,
|
MainAllianceFilter,
|
||||||
@@ -28,15 +26,12 @@ from ..admin import (
|
|||||||
user_main_organization,
|
user_main_organization,
|
||||||
user_profile_pic,
|
user_profile_pic,
|
||||||
user_username,
|
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
|
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'
|
MODULE_PATH = 'allianceauth.authentication.admin'
|
||||||
|
|
||||||
@@ -46,50 +41,60 @@ class MockRequest(object):
|
|||||||
self.user = user
|
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
|
# groups
|
||||||
group_1 = Group.objects.create(
|
cls.group_1 = Group.objects.create(
|
||||||
name='Group 1'
|
name='Group 1'
|
||||||
)
|
)
|
||||||
group_2 = Group.objects.create(
|
cls.group_2 = Group.objects.create(
|
||||||
name='Group 2'
|
name='Group 2'
|
||||||
)
|
)
|
||||||
|
|
||||||
# user 1 - corp and alliance, normal user
|
# user 1 - corp and alliance, normal user
|
||||||
character_1 = EveCharacter.objects.create(
|
character_1 = EveCharacter.objects.create(
|
||||||
character_id='1001',
|
character_id=1001,
|
||||||
character_name='Bruce Wayne',
|
character_name='Bruce Wayne',
|
||||||
corporation_id='2001',
|
corporation_id=2001,
|
||||||
corporation_name='Wayne Technologies',
|
corporation_name='Wayne Technologies',
|
||||||
corporation_ticker='WT',
|
corporation_ticker='WT',
|
||||||
alliance_id='3001',
|
alliance_id=3001,
|
||||||
alliance_name='Wayne Enterprises',
|
alliance_name='Wayne Enterprises',
|
||||||
alliance_ticker='WE',
|
alliance_ticker='WE',
|
||||||
)
|
)
|
||||||
character_1a = EveCharacter.objects.create(
|
character_1a = EveCharacter.objects.create(
|
||||||
character_id='1002',
|
character_id=1002,
|
||||||
character_name='Batman',
|
character_name='Batman',
|
||||||
corporation_id='2001',
|
corporation_id=2001,
|
||||||
corporation_name='Wayne Technologies',
|
corporation_name='Wayne Technologies',
|
||||||
corporation_ticker='WT',
|
corporation_ticker='WT',
|
||||||
alliance_id='3001',
|
alliance_id=3001,
|
||||||
alliance_name='Wayne Enterprises',
|
alliance_name='Wayne Enterprises',
|
||||||
alliance_ticker='WE',
|
alliance_ticker='WE',
|
||||||
)
|
)
|
||||||
alliance = EveAllianceInfo.objects.create(
|
alliance = EveAllianceInfo.objects.create(
|
||||||
alliance_id='3001',
|
alliance_id=3001,
|
||||||
alliance_name='Wayne Enterprises',
|
alliance_name='Wayne Enterprises',
|
||||||
alliance_ticker='WE',
|
alliance_ticker='WE',
|
||||||
executor_corp_id='2001'
|
executor_corp_id=2001
|
||||||
)
|
)
|
||||||
EveCorporationInfo.objects.create(
|
EveCorporationInfo.objects.create(
|
||||||
corporation_id='2001',
|
corporation_id=2001,
|
||||||
corporation_name='Wayne Technologies',
|
corporation_name='Wayne Technologies',
|
||||||
corporation_ticker='WT',
|
corporation_ticker='WT',
|
||||||
member_count=42,
|
member_count=42,
|
||||||
alliance=alliance
|
alliance=alliance
|
||||||
)
|
)
|
||||||
user_1 = User.objects.create_user(
|
cls.user_1 = User.objects.create_user(
|
||||||
character_1.character_name.replace(' ', '_'),
|
character_1.character_name.replace(' ', '_'),
|
||||||
'abc@example.com',
|
'abc@example.com',
|
||||||
'password'
|
'password'
|
||||||
@@ -97,16 +102,16 @@ def create_test_data():
|
|||||||
CharacterOwnership.objects.create(
|
CharacterOwnership.objects.create(
|
||||||
character=character_1,
|
character=character_1,
|
||||||
owner_hash='x1' + character_1.character_name,
|
owner_hash='x1' + character_1.character_name,
|
||||||
user=user_1
|
user=cls.user_1
|
||||||
)
|
)
|
||||||
CharacterOwnership.objects.create(
|
CharacterOwnership.objects.create(
|
||||||
character=character_1a,
|
character=character_1a,
|
||||||
owner_hash='x1' + character_1a.character_name,
|
owner_hash='x1' + character_1a.character_name,
|
||||||
user=user_1
|
user=cls.user_1
|
||||||
)
|
)
|
||||||
user_1.profile.main_character = character_1
|
cls.user_1.profile.main_character = character_1
|
||||||
user_1.profile.save()
|
cls.user_1.profile.save()
|
||||||
user_1.groups.add(group_1)
|
cls.user_1.groups.add(cls.group_1)
|
||||||
|
|
||||||
# user 2 - corp only, staff
|
# user 2 - corp only, staff
|
||||||
character_2 = EveCharacter.objects.create(
|
character_2 = EveCharacter.objects.create(
|
||||||
@@ -124,7 +129,7 @@ def create_test_data():
|
|||||||
member_count=99,
|
member_count=99,
|
||||||
alliance=None
|
alliance=None
|
||||||
)
|
)
|
||||||
user_2 = User.objects.create_user(
|
cls.user_2 = User.objects.create_user(
|
||||||
character_2.character_name.replace(' ', '_'),
|
character_2.character_name.replace(' ', '_'),
|
||||||
'abc@example.com',
|
'abc@example.com',
|
||||||
'password'
|
'password'
|
||||||
@@ -132,13 +137,13 @@ def create_test_data():
|
|||||||
CharacterOwnership.objects.create(
|
CharacterOwnership.objects.create(
|
||||||
character=character_2,
|
character=character_2,
|
||||||
owner_hash='x1' + character_2.character_name,
|
owner_hash='x1' + character_2.character_name,
|
||||||
user=user_2
|
user=cls.user_2
|
||||||
)
|
)
|
||||||
user_2.profile.main_character = character_2
|
cls.user_2.profile.main_character = character_2
|
||||||
user_2.profile.save()
|
cls.user_2.profile.save()
|
||||||
user_2.groups.add(group_2)
|
cls.user_2.groups.add(cls.group_2)
|
||||||
user_2.is_staff = True
|
cls.user_2.is_staff = True
|
||||||
user_2.save()
|
cls.user_2.save()
|
||||||
|
|
||||||
# user 3 - no main, no group, superuser
|
# user 3 - no main, no group, superuser
|
||||||
character_3 = EveCharacter.objects.create(
|
character_3 = EveCharacter.objects.create(
|
||||||
@@ -157,12 +162,12 @@ def create_test_data():
|
|||||||
alliance=None
|
alliance=None
|
||||||
)
|
)
|
||||||
EveAllianceInfo.objects.create(
|
EveAllianceInfo.objects.create(
|
||||||
alliance_id='3101',
|
alliance_id=3101,
|
||||||
alliance_name='Lex World Domination',
|
alliance_name='Lex World Domination',
|
||||||
alliance_ticker='LWD',
|
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(' ', '_'),
|
character_3.character_name.replace(' ', '_'),
|
||||||
'abc@example.com',
|
'abc@example.com',
|
||||||
'password'
|
'password'
|
||||||
@@ -170,11 +175,10 @@ def create_test_data():
|
|||||||
CharacterOwnership.objects.create(
|
CharacterOwnership.objects.create(
|
||||||
character=character_3,
|
character=character_3,
|
||||||
owner_hash='x1' + character_3.character_name,
|
owner_hash='x1' + character_3.character_name,
|
||||||
user=user_3
|
user=cls.user_3
|
||||||
)
|
)
|
||||||
user_3.is_superuser = True
|
cls.user_3.is_superuser = True
|
||||||
user_3.save()
|
cls.user_3.save()
|
||||||
return user_1, user_2, user_3, group_1, group_2
|
|
||||||
|
|
||||||
|
|
||||||
def make_generic_search_request(ModelClass: type, search_term: str):
|
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):
|
class TestCharacterOwnershipAdmin(TestCaseWithTestData):
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
super().setUpClass()
|
|
||||||
cls.user_1, _, _, _, _ = create_test_data()
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.modeladmin = CharacterOwnershipAdmin(
|
self.modeladmin = CharacterOwnershipAdmin(
|
||||||
@@ -219,12 +218,7 @@ class TestCharacterOwnershipAdmin(TestCase):
|
|||||||
self.assertEqual(response.status_code, expected)
|
self.assertEqual(response.status_code, expected)
|
||||||
|
|
||||||
|
|
||||||
class TestOwnershipRecordAdmin(TestCase):
|
class TestOwnershipRecordAdmin(TestCaseWithTestData):
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
super().setUpClass()
|
|
||||||
cls.user_1, _, _, _, _ = create_test_data()
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.modeladmin = OwnershipRecordAdmin(
|
self.modeladmin = OwnershipRecordAdmin(
|
||||||
@@ -250,12 +244,7 @@ class TestOwnershipRecordAdmin(TestCase):
|
|||||||
self.assertEqual(response.status_code, expected)
|
self.assertEqual(response.status_code, expected)
|
||||||
|
|
||||||
|
|
||||||
class TestStateAdmin(TestCase):
|
class TestStateAdmin(TestCaseWithTestData):
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
super().setUpClass()
|
|
||||||
create_test_data()
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.modeladmin = StateAdmin(
|
self.modeladmin = StateAdmin(
|
||||||
@@ -283,13 +272,8 @@ class TestStateAdmin(TestCase):
|
|||||||
expected = 200
|
expected = 200
|
||||||
self.assertEqual(response.status_code, expected)
|
self.assertEqual(response.status_code, expected)
|
||||||
|
|
||||||
class TestUserAdmin(TestCase):
|
|
||||||
|
|
||||||
@classmethod
|
class TestUserAdmin(TestCaseWithTestData):
|
||||||
def setUpClass(cls):
|
|
||||||
super().setUpClass()
|
|
||||||
cls.user_1, cls.user_2, cls.user_3, cls.group_1, cls.group_2 = \
|
|
||||||
create_test_data()
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
@@ -298,23 +282,11 @@ class TestUserAdmin(TestCase):
|
|||||||
)
|
)
|
||||||
self.character_1 = self.user_1.character_ownerships.first().character
|
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):
|
def test_user_profile_pic_u1(self):
|
||||||
expected = ('<img src="https://images.evetech.net/characters/1001/'
|
expected = (
|
||||||
'portrait?size=32" class="img-circle">')
|
'<img src="https://images.evetech.net/characters/1001/'
|
||||||
|
'portrait?size=32" class="img-circle">'
|
||||||
|
)
|
||||||
self.assertEqual(user_profile_pic(self.user_1), expected)
|
self.assertEqual(user_profile_pic(self.user_1), expected)
|
||||||
|
|
||||||
def test_user_profile_pic_u3(self):
|
def test_user_profile_pic_u3(self):
|
||||||
@@ -362,36 +334,16 @@ class TestUserAdmin(TestCase):
|
|||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
def test_groups_u1(self):
|
def test_groups_u1(self):
|
||||||
self._create_autogroups()
|
|
||||||
expected = 'Group 1'
|
expected = 'Group 1'
|
||||||
result = self.modeladmin._groups(self.user_1)
|
result = self.modeladmin._groups(self.user_1)
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
def test_groups_u2(self):
|
def test_groups_u2(self):
|
||||||
self._create_autogroups()
|
|
||||||
expected = 'Group 2'
|
expected = 'Group 2'
|
||||||
result = self.modeladmin._groups(self.user_2)
|
result = self.modeladmin._groups(self.user_2)
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
def test_groups_u3(self):
|
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)
|
result = self.modeladmin._groups(self.user_3)
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
@@ -423,8 +375,10 @@ class TestUserAdmin(TestCase):
|
|||||||
|
|
||||||
def test_list_2_html_w_tooltips_w_cutoff(self):
|
def test_list_2_html_w_tooltips_w_cutoff(self):
|
||||||
items = ['one', 'two', 'three']
|
items = ['one', 'two', 'three']
|
||||||
expected = ('<span data-tooltip="one, two, three" '
|
expected = (
|
||||||
'class="tooltip">one, two, (...)</span>')
|
'<span data-tooltip="one, two, three" '
|
||||||
|
'class="tooltip">one, two, (...)</span>'
|
||||||
|
)
|
||||||
result = self.modeladmin._list_2_html_w_tooltips(items, 2)
|
result = self.modeladmin._list_2_html_w_tooltips(items, 2)
|
||||||
self.assertEqual(expected, result)
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
@@ -450,62 +404,6 @@ class TestUserAdmin(TestCase):
|
|||||||
|
|
||||||
# filters
|
# 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):
|
def test_filter_main_corporations(self):
|
||||||
|
|
||||||
class UserAdminTest(BaseUserAdmin):
|
class UserAdminTest(BaseUserAdmin):
|
||||||
@@ -520,8 +418,8 @@ class TestUserAdmin(TestCase):
|
|||||||
filters = changelist.get_filters(request)
|
filters = changelist.get_filters(request)
|
||||||
filterspec = filters[0][0]
|
filterspec = filters[0][0]
|
||||||
expected = [
|
expected = [
|
||||||
('2002', 'Daily Planet'),
|
(2002, 'Daily Planet'),
|
||||||
('2001', 'Wayne Technologies'),
|
(2001, 'Wayne Technologies'),
|
||||||
]
|
]
|
||||||
self.assertEqual(filterspec.lookup_choices, expected)
|
self.assertEqual(filterspec.lookup_choices, expected)
|
||||||
|
|
||||||
@@ -550,7 +448,7 @@ class TestUserAdmin(TestCase):
|
|||||||
filters = changelist.get_filters(request)
|
filters = changelist.get_filters(request)
|
||||||
filterspec = filters[0][0]
|
filterspec = filters[0][0]
|
||||||
expected = [
|
expected = [
|
||||||
('3001', 'Wayne Enterprises'),
|
(3001, 'Wayne Enterprises'),
|
||||||
]
|
]
|
||||||
self.assertEqual(filterspec.lookup_choices, expected)
|
self.assertEqual(filterspec.lookup_choices, expected)
|
||||||
|
|
||||||
@@ -579,3 +477,66 @@ class TestUserAdmin(TestCase):
|
|||||||
response = make_generic_search_request(type(obj), obj.username)
|
response = make_generic_search_request(type(obj), obj.username)
|
||||||
expected = 200
|
expected = 200
|
||||||
self.assertEqual(response.status_code, expected)
|
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 unittest import mock
|
||||||
from io import StringIO
|
|
||||||
from urllib import parse
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.models import AnonymousUser, User
|
|
||||||
from django.core.management import call_command
|
|
||||||
from django.http.response import HttpResponse
|
|
||||||
from django.shortcuts import reverse
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.client import RequestFactory
|
|
||||||
|
|
||||||
|
|
||||||
from allianceauth.authentication.decorators import main_character_required
|
|
||||||
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo,\
|
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo,\
|
||||||
EveAllianceInfo
|
EveAllianceInfo
|
||||||
from allianceauth.tests.auth_utils import AuthUtils
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
from esi.errors import IncompleteResponseError
|
from esi.errors import IncompleteResponseError
|
||||||
from esi.models import Token
|
from esi.models import Token
|
||||||
|
|
||||||
from ..backends import StateBackend
|
from ..models import CharacterOwnership, State, get_guest_state
|
||||||
from ..models import CharacterOwnership, UserProfile, State, get_guest_state,\
|
|
||||||
OwnershipRecord
|
|
||||||
from ..tasks import check_character_ownership
|
from ..tasks import check_character_ownership
|
||||||
|
|
||||||
MODULE_PATH = 'allianceauth.authentication'
|
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):
|
class CharacterOwnershipTestCase(TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
@@ -343,10 +216,10 @@ class CharacterOwnershipCheckTestCase(TestCase):
|
|||||||
cls.user = AuthUtils.create_user('test_user', disconnect_signals=True)
|
cls.user = AuthUtils.create_user('test_user', disconnect_signals=True)
|
||||||
AuthUtils.add_main_character(cls.user, 'Test Character', '1', corp_id='1', alliance_id='1',
|
AuthUtils.add_main_character(cls.user, 'Test Character', '1', corp_id='1', alliance_id='1',
|
||||||
corp_name='Test Corp', alliance_name='Test Alliance')
|
corp_name='Test Corp', alliance_name='Test Alliance')
|
||||||
cls.character = EveCharacter.objects.get(character_id='1')
|
cls.character = EveCharacter.objects.get(character_id=1)
|
||||||
cls.token = Token.objects.create(
|
cls.token = Token.objects.create(
|
||||||
user=cls.user,
|
user=cls.user,
|
||||||
character_id='1',
|
character_id=1,
|
||||||
character_name='Test',
|
character_name='Test',
|
||||||
character_owner_hash='1',
|
character_owner_hash='1',
|
||||||
)
|
)
|
||||||
@@ -378,30 +251,3 @@ class CharacterOwnershipCheckTestCase(TestCase):
|
|||||||
filter.return_value.exists.return_value = False
|
filter.return_value.exists.return_value = False
|
||||||
check_character_ownership(self.ownership)
|
check_character_ownership(self.ownership)
|
||||||
self.assertTrue(filter.return_value.delete.called)
|
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,
|
views.add_character,
|
||||||
name='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'),
|
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.decorators import login_required
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core import signing
|
from django.core import signing
|
||||||
from django.urls import reverse
|
from django.http import JsonResponse
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from allianceauth.eveonline.models import EveCharacter
|
from allianceauth.eveonline.models import EveCharacter
|
||||||
from esi.decorators import token_required
|
from esi.decorators import token_required
|
||||||
from esi.models import Token
|
from esi.models import Token
|
||||||
|
|
||||||
from registration.backends.hmac.views import (
|
from django_registration.backends.activation.views import (
|
||||||
RegistrationView as BaseRegistrationView,
|
RegistrationView as BaseRegistrationView,
|
||||||
ActivationView as BaseActivationView,
|
ActivationView as BaseActivationView,
|
||||||
REGISTRATION_SALT
|
REGISTRATION_SALT
|
||||||
)
|
)
|
||||||
from registration.signals import user_registered
|
from django_registration.signals import user_registered
|
||||||
|
|
||||||
from .models import CharacterOwnership
|
from .models import CharacterOwnership
|
||||||
from .forms import RegistrationForm
|
from .forms import RegistrationForm
|
||||||
@@ -134,11 +135,14 @@ def sso_login(request, token):
|
|||||||
# Step 2
|
# Step 2
|
||||||
class RegistrationView(BaseRegistrationView):
|
class RegistrationView(BaseRegistrationView):
|
||||||
form_class = RegistrationForm
|
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):
|
def get_success_url(self, user):
|
||||||
if not getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
|
if not getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
|
||||||
return 'authentication:dashboard', (), {}
|
return reverse_lazy('authentication:dashboard')
|
||||||
return super().get_success_url(user)
|
return super().get_success_url(user)
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
@@ -176,6 +180,9 @@ class RegistrationView(BaseRegistrationView):
|
|||||||
|
|
||||||
# Step 3
|
# Step 3
|
||||||
class ActivationView(BaseActivationView):
|
class ActivationView(BaseActivationView):
|
||||||
|
template_name = "registration/activate.html"
|
||||||
|
success_url = reverse_lazy('registration_activation_complete')
|
||||||
|
|
||||||
def validate_key(self, activation_key):
|
def validate_key(self, activation_key):
|
||||||
try:
|
try:
|
||||||
dump = signing.loads(activation_key, salt=REGISTRATION_SALT,
|
dump = signing.loads(activation_key, salt=REGISTRATION_SALT,
|
||||||
@@ -207,5 +214,5 @@ def activation_complete(request):
|
|||||||
|
|
||||||
|
|
||||||
def registration_closed(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')
|
return redirect('authentication:login')
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ from allianceauth.corputils import urls
|
|||||||
|
|
||||||
class CorpStats(MenuItemHook):
|
class CorpStats(MenuItemHook):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
MenuItemHook.__init__(self,
|
MenuItemHook.__init__(
|
||||||
|
self,
|
||||||
_('Corporation Stats'),
|
_('Corporation Stats'),
|
||||||
'fa fa-share-alt fa-fw',
|
'fas fa-share-alt fa-fw',
|
||||||
'corputils:view',
|
'corputils:view',
|
||||||
navactive=['corputils:'])
|
navactive=['corputils:']
|
||||||
|
)
|
||||||
|
|
||||||
def render(self, request):
|
def render(self, request):
|
||||||
if request.user.has_perm('corputils.view_corp_corpstats') or request.user.has_perm(
|
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
|
# Generated by Django 1.10.1 on 2016-12-14 21:36
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2016-12-14 21:48
|
# Generated by Django 1.10.1 on 2016-12-14 21:48
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.5 on 2017-03-22 23:35
|
# Generated by Django 1.10.5 on 2017-03-22 23:35
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.5 on 2017-03-26 20:13
|
# Generated by Django 1.10.5 on 2017-03-26 20:13
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
@@ -13,8 +12,7 @@ def convert_json_to_members(apps, schema_editor):
|
|||||||
for cs in CorpStats.objects.all():
|
for cs in CorpStats.objects.all():
|
||||||
members = json.loads(cs._members)
|
members = json.loads(cs._members)
|
||||||
CorpMember.objects.bulk_create(
|
CorpMember.objects.bulk_create(
|
||||||
[CorpMember(corpstats=cs, character_id=member_id, character_name=member_name) for member_id, member_name in
|
[CorpMember(corpstats=cs, character_id=member_id, character_name=member_name) for member_id, member_name in members.items()]
|
||||||
members.items()]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11.2 on 2017-06-10 15:34
|
# Generated by Django 1.11.2 on 2017-06-10 15:34
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ from bravado.exception import HTTPForbidden
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from esi.errors import TokenError
|
from esi.errors import TokenError
|
||||||
from esi.models import Token
|
from esi.models import Token
|
||||||
from allianceauth.eveonline.models import EveCorporationInfo, EveCharacter,\
|
from allianceauth.eveonline.models import EveCorporationInfo, EveCharacter, EveAllianceInfo
|
||||||
EveAllianceInfo
|
|
||||||
from allianceauth.notifications import notify
|
from allianceauth.notifications import notify
|
||||||
|
|
||||||
from allianceauth.corputils.managers import CorpStatsManager
|
from allianceauth.corputils.managers import CorpStatsManager
|
||||||
@@ -49,8 +48,7 @@ class CorpStats(models.Model):
|
|||||||
def update(self):
|
def update(self):
|
||||||
try:
|
try:
|
||||||
c = self.token.get_esi_client(spec_file=SWAGGER_SPEC_PATH)
|
c = self.token.get_esi_client(spec_file=SWAGGER_SPEC_PATH)
|
||||||
assert c.Character.get_characters_character_id(character_id=self.token.character_id).result()[
|
assert c.Character.get_characters_character_id(character_id=self.token.character_id).result()['corporation_id'] == int(self.corp.corporation_id)
|
||||||
'corporation_id'] == int(self.corp.corporation_id)
|
|
||||||
member_ids = c.Corporation.get_corporations_corporation_id_members(
|
member_ids = c.Corporation.get_corporations_corporation_id_members(
|
||||||
corporation_id=self.corp.corporation_id).result()
|
corporation_id=self.corp.corporation_id).result()
|
||||||
|
|
||||||
@@ -58,18 +56,15 @@ class CorpStats(models.Model):
|
|||||||
# the swagger spec doesn't have a maxItems count
|
# the swagger spec doesn't have a maxItems count
|
||||||
# manual testing says we can do over 350, but let's not risk it
|
# manual testing says we can do over 350, but let's not risk it
|
||||||
member_id_chunks = [member_ids[i:i + 255] for i in range(0, len(member_ids), 255)]
|
member_id_chunks = [member_ids[i:i + 255] for i in range(0, len(member_ids), 255)]
|
||||||
member_name_chunks = [c.Universe.post_universe_names(ids=id_chunk).result() for id_chunk in
|
member_name_chunks = [c.Universe.post_universe_names(ids=id_chunk).result() for id_chunk in member_id_chunks]
|
||||||
member_id_chunks]
|
|
||||||
member_list = {}
|
member_list = {}
|
||||||
for name_chunk in member_name_chunks:
|
for name_chunk in member_name_chunks:
|
||||||
member_list.update({m['id']: m['name'] for m in name_chunk})
|
member_list.update({m['id']: m['name'] for m in name_chunk})
|
||||||
|
|
||||||
# bulk create new member models
|
# bulk create new member models
|
||||||
missing_members = [m_id for m_id in member_ids if
|
missing_members = [m_id for m_id in member_ids if not CorpMember.objects.filter(corpstats=self, character_id=m_id).exists()]
|
||||||
not CorpMember.objects.filter(corpstats=self, character_id=m_id).exists()]
|
|
||||||
CorpMember.objects.bulk_create(
|
CorpMember.objects.bulk_create(
|
||||||
[CorpMember(character_id=m_id, character_name=member_list[m_id], corpstats=self) for m_id in
|
[CorpMember(character_id=m_id, character_name=member_list[m_id], corpstats=self) for m_id in missing_members])
|
||||||
missing_members])
|
|
||||||
|
|
||||||
# purge old members
|
# purge old members
|
||||||
self.members.exclude(character_id__in=member_ids).delete()
|
self.members.exclude(character_id__in=member_ids).delete()
|
||||||
@@ -80,20 +75,21 @@ class CorpStats(models.Model):
|
|||||||
except TokenError as e:
|
except TokenError as e:
|
||||||
logger.warning("%s failed to update: %s" % (self, e))
|
logger.warning("%s failed to update: %s" % (self, e))
|
||||||
if self.token.user:
|
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.",
|
message="Your token has expired or is no longer valid. Please add a new one to create a new CorpStats.",
|
||||||
level="error")
|
level="error")
|
||||||
self.delete()
|
self.delete()
|
||||||
except HTTPForbidden as e:
|
except HTTPForbidden as e:
|
||||||
logger.warning("%s failed to update: %s" % (self, e))
|
logger.warning("%s failed to update: %s" % (self, e))
|
||||||
if self.token.user:
|
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="%s: %s" % (e.status_code, e.message), level="error")
|
||||||
message="%s: %s" % (e.status_code, e.message), level="error")
|
|
||||||
self.delete()
|
self.delete()
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
logger.warning("%s token character no longer in corp." % self)
|
logger.warning("%s token character no longer in corp." % self)
|
||||||
if self.token.user:
|
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")
|
message="%s cannot update with your ESI token as you have left corp." % self, level="error")
|
||||||
self.delete()
|
self.delete()
|
||||||
|
|
||||||
@@ -127,9 +123,7 @@ class CorpStats(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def mains(self):
|
def mains(self):
|
||||||
return self.members.filter(pk__in=[m.pk for m in self.members.all() if
|
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)])
|
||||||
m.main_character and int(m.main_character.character_id) == int(
|
|
||||||
m.character_id)])
|
|
||||||
|
|
||||||
def visible_to(self, user):
|
def visible_to(self, user):
|
||||||
return CorpStats.objects.filter(pk=self.pk).visible_to(user).exists()
|
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 %}
|
{% for id, main in mains.items %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center" style="vertical-align:middle">
|
<td class="text-center" style="vertical-align:middle">
|
||||||
<div class="thumbnail"
|
<div class="thumbnail" style="border: 0 none; box-shadow: none; background: transparent;">
|
||||||
style="border: 0 none; box-shadow: none; background: transparent;">
|
|
||||||
<img src="{{ main.main.portrait_url_64 }}" class="img-circle">
|
<img src="{{ main.main.portrait_url_64 }}" class="img-circle">
|
||||||
<div class="caption text-center">
|
<div class="caption text-center">
|
||||||
{{ main.main }}
|
{{ main.main }}
|
||||||
@@ -88,8 +87,7 @@
|
|||||||
<td class="text-center" style="width:30%">{{ alt.corporation_name }}</td>
|
<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:30%">{{ alt.alliance_name }}</td>
|
||||||
<td class="text-center" style="width:5%">
|
<td class="text-center" style="width:5%">
|
||||||
<a href="https://zkillboard.com/character/{{ alt.character_id }}/"
|
<a href="https://zkillboard.com/character/{{ alt.character_id }}/" class="label label-danger" target="_blank">
|
||||||
class="label label-danger" target="_blank">
|
|
||||||
{% trans "Killboard" %}
|
{% trans "Killboard" %}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@@ -123,10 +121,9 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
|
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
|
||||||
<td class="text-center">{{ member }}</td>
|
<td class="text-center">{{ member }}</td>
|
||||||
<td class="text-center"><a
|
<td class="text-center">
|
||||||
href="https://zkillboard.com/character/{{ member.character_id }}/"
|
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">{% trans "Killboard" %}</a>
|
||||||
class="label label-danger"
|
</td>
|
||||||
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.character_name }}</td>
|
||||||
<td class="text-center">{{ member.character_ownership.user.profile.main_character.corporation_name }}</td>
|
<td class="text-center">{{ member.character_ownership.user.profile.main_character.corporation_name }}</td>
|
||||||
<td class="text-center">{{ member.character_ownership.user.profile.main_character.alliance_name }}</td>
|
<td class="text-center">{{ member.character_ownership.user.profile.main_character.alliance_name }}</td>
|
||||||
@@ -136,10 +133,9 @@
|
|||||||
<tr class="danger">
|
<tr class="danger">
|
||||||
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
|
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
|
||||||
<td class="text-center">{{ member.character_name }}</td>
|
<td class="text-center">{{ member.character_name }}</td>
|
||||||
<td class="text-center"><a
|
<td class="text-center">
|
||||||
href="https://zkillboard.com/character/{{ member.character_id }}/"
|
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">{% trans "Killboard" %}</a>
|
||||||
class="label label-danger"
|
</td>
|
||||||
target="_blank">{% trans "Killboard" %}</a></td>
|
|
||||||
<td class="text-center"></td>
|
<td class="text-center"></td>
|
||||||
<td class="text-center"></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><img src="{{ member.portrait_url }}" class="img-circle"></td>
|
||||||
<td class="text-center">{{ member.character_name }}</td>
|
<td class="text-center">{{ member.character_name }}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/"
|
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">
|
||||||
class="label label-danger"
|
|
||||||
target="_blank">
|
|
||||||
{% trans "Killboard" %}
|
{% trans "Killboard" %}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ class CorpStatsManagerTestCase(TestCase):
|
|||||||
cls.user = AuthUtils.create_user('test')
|
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.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.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.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.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.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_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')
|
cls.view_alliance_permission = Permission.objects.get_by_natural_key('view_alliance_corpstats', 'corputils', 'corpstats')
|
||||||
@@ -66,9 +66,9 @@ class CorpStatsUpdateTestCase(TestCase):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
cls.user = AuthUtils.create_user('test')
|
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.token = Token.objects.create(user=cls.user, access_token='a', character_id='1', character_name='test character', character_owner_hash='z')
|
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.corp = EveCorporationInfo.objects.create(corporation_id=2, corporation_name='test corp', corporation_ticker='TEST', member_count=1)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.corpstats = CorpStats.objects.get_or_create(token=self.token, corp=self.corp)[0]
|
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.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'}]
|
SwaggerClient.from_spec.return_value.Universe.post_universe_names.return_value.result.return_value = [{'id': 1, 'name': 'test character'}]
|
||||||
self.corpstats.update()
|
self.corpstats.update()
|
||||||
self.assertTrue(CorpMember.objects.filter(character_id='1', character_name='test character', corpstats=self.corpstats).exists())
|
self.assertTrue(CorpMember.objects.filter(character_id=1, character_name='test character', corpstats=self.corpstats).exists())
|
||||||
|
|
||||||
@mock.patch('esi.clients.SwaggerClient')
|
@mock.patch('esi.clients.SwaggerClient')
|
||||||
def test_update_remove_member(self, 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.Character.get_characters_character_id.return_value.result.return_value = {'corporation_id': 2}
|
||||||
SwaggerClient.from_spec.return_value.Corporation.get_corporations_corporation_id_members.return_value.result.return_value = [1]
|
SwaggerClient.from_spec.return_value.Corporation.get_corporations_corporation_id_members.return_value.result.return_value = [1]
|
||||||
SwaggerClient.from_spec.return_value.Universe.post_universe_names.return_value.result.return_value = [{'id': 1, 'name': 'test character'}]
|
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
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
cls.user = AuthUtils.create_user('test')
|
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.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.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.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.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):
|
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)
|
self.assertEqual(self.corpstats.member_count, 1)
|
||||||
member.delete()
|
member.delete()
|
||||||
self.assertEqual(self.corpstats.member_count, 0)
|
self.assertEqual(self.corpstats.member_count, 0)
|
||||||
@@ -147,7 +147,7 @@ class CorpStatsPropertiesTestCase(TestCase):
|
|||||||
AuthUtils.disconnect_signals()
|
AuthUtils.disconnect_signals()
|
||||||
co = CharacterOwnership.objects.create(character=self.character, user=self.user, owner_hash='a')
|
co = CharacterOwnership.objects.create(character=self.character, user=self.user, owner_hash='a')
|
||||||
AuthUtils.connect_signals()
|
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)
|
self.assertEqual(self.corpstats.user_count, 1)
|
||||||
co.delete()
|
co.delete()
|
||||||
self.assertEqual(self.corpstats.user_count, 0)
|
self.assertEqual(self.corpstats.user_count, 0)
|
||||||
@@ -156,7 +156,8 @@ class CorpStatsPropertiesTestCase(TestCase):
|
|||||||
AuthUtils.disconnect_signals()
|
AuthUtils.disconnect_signals()
|
||||||
co = CharacterOwnership.objects.create(character=self.character, user=self.user, owner_hash='a')
|
co = CharacterOwnership.objects.create(character=self.character, user=self.user, owner_hash='a')
|
||||||
AuthUtils.connect_signals()
|
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.assertIn(member, self.corpstats.registered_members)
|
||||||
self.assertEqual(self.corpstats.registered_member_count, 1)
|
self.assertEqual(self.corpstats.registered_member_count, 1)
|
||||||
|
|
||||||
@@ -165,7 +166,7 @@ class CorpStatsPropertiesTestCase(TestCase):
|
|||||||
self.assertEqual(self.corpstats.registered_member_count, 0)
|
self.assertEqual(self.corpstats.registered_member_count, 0)
|
||||||
|
|
||||||
def test_unregistered_members(self):
|
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.corpstats.refresh_from_db()
|
||||||
self.assertIn(member, self.corpstats.unregistered_members)
|
self.assertIn(member, self.corpstats.unregistered_members)
|
||||||
self.assertEqual(self.corpstats.unregistered_member_count, 1)
|
self.assertEqual(self.corpstats.unregistered_member_count, 1)
|
||||||
@@ -178,13 +179,13 @@ class CorpStatsPropertiesTestCase(TestCase):
|
|||||||
|
|
||||||
def test_mains(self):
|
def test_mains(self):
|
||||||
# test when is a main
|
# 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.assertIn(member, self.corpstats.mains)
|
||||||
self.assertEqual(self.corpstats.main_count, 1)
|
self.assertEqual(self.corpstats.main_count, 1)
|
||||||
|
|
||||||
# test when is an alt
|
# test when is an alt
|
||||||
old_main = self.user.profile.main_character
|
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()
|
AuthUtils.disconnect_signals()
|
||||||
co = CharacterOwnership.objects.create(character=character, user=self.user, owner_hash='b')
|
co = CharacterOwnership.objects.create(character=character, user=self.user, owner_hash='b')
|
||||||
self.user.profile.main_character = character
|
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.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')
|
self.assertEqual(self.corpstats.alliance_logo(size=128), 'https://images.evetech.net/alliances/1/logo?size=128')
|
||||||
|
|
||||||
alliance = EveAllianceInfo.objects.create(alliance_name='test alliance', alliance_id='3', alliance_ticker='TEST', executor_corp_id='2')
|
alliance = EveAllianceInfo.objects.create(alliance_name='test alliance', alliance_id=3, alliance_ticker='TEST', executor_corp_id=2)
|
||||||
self.corp.alliance = alliance
|
self.corp.alliance = alliance
|
||||||
self.corp.save()
|
self.corp.save()
|
||||||
self.assertEqual(self.corpstats.alliance_logo(size=128), 'https://images.evetech.net/alliances/3/logo?size=128')
|
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')
|
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.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.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.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.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):
|
def test_character(self):
|
||||||
self.assertIsNone(self.member.character)
|
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)
|
self.assertEqual(self.member.character, character)
|
||||||
|
|
||||||
def test_main_character(self):
|
def test_main_character(self):
|
||||||
@@ -238,7 +239,7 @@ class CorpMemberTestCase(TestCase):
|
|||||||
self.assertIsNone(self.member.main_character)
|
self.assertIsNone(self.member.main_character)
|
||||||
|
|
||||||
# test when member.character is not None but also not a main
|
# 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')
|
CharacterOwnership.objects.create(character=character, user=self.user, owner_hash='b')
|
||||||
self.member.refresh_from_db()
|
self.member.refresh_from_db()
|
||||||
self.assertNotEqual(self.member.main_character, self.member.character)
|
self.assertNotEqual(self.member.main_character, self.member.character)
|
||||||
@@ -260,14 +261,14 @@ class CorpMemberTestCase(TestCase):
|
|||||||
def test_alts(self):
|
def test_alts(self):
|
||||||
self.assertListEqual(self.member.alts, [])
|
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')
|
CharacterOwnership.objects.create(character=character, user=self.user, owner_hash='b')
|
||||||
self.assertIn(character, self.member.alts)
|
self.assertIn(character, self.member.alts)
|
||||||
|
|
||||||
def test_registered(self):
|
def test_registered(self):
|
||||||
self.assertFalse(self.member.registered)
|
self.assertFalse(self.member.registered)
|
||||||
AuthUtils.disconnect_signals()
|
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')
|
CharacterOwnership.objects.create(character=character, user=self.user, owner_hash='b')
|
||||||
self.assertTrue(self.member.registered)
|
self.assertTrue(self.member.registered)
|
||||||
AuthUtils.connect_signals()
|
AuthUtils.connect_signals()
|
||||||
|
|||||||
@@ -96,24 +96,62 @@ class EveAllianceForm(EveEntityForm):
|
|||||||
|
|
||||||
@admin.register(EveCorporationInfo)
|
@admin.register(EveCorporationInfo)
|
||||||
class EveCorporationInfoAdmin(admin.ModelAdmin):
|
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):
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
if not obj or not obj.pk:
|
if not obj or not obj.pk:
|
||||||
return EveCorporationForm
|
return EveCorporationForm
|
||||||
return super(EveCorporationInfoAdmin, self).get_form(request, obj=obj, **kwargs)
|
return super().get_form(request, obj=obj, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(EveAllianceInfo)
|
@admin.register(EveAllianceInfo)
|
||||||
class EveAllianceInfoAdmin(admin.ModelAdmin):
|
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):
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
if not obj or not obj.pk:
|
if not obj or not obj.pk:
|
||||||
return EveAllianceForm
|
return EveAllianceForm
|
||||||
return super(EveAllianceInfoAdmin, self).get_form(request, obj=obj, **kwargs)
|
return super().get_form(request, obj=obj, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(EveCharacter)
|
@admin.register(EveCharacter)
|
||||||
class EveCharacterAdmin(admin.ModelAdmin):
|
class EveCharacterAdmin(admin.ModelAdmin):
|
||||||
search_fields = ['character_name', 'corporation_name', 'alliance_name', 'character_ownership__user__username']
|
search_fields = [
|
||||||
list_display = ('character_name', 'corporation_name', 'alliance_name', 'user', 'main_character')
|
'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
|
@staticmethod
|
||||||
def user(obj):
|
def user(obj):
|
||||||
|
|||||||
@@ -39,4 +39,3 @@ class AutogroupsConfigAdmin(admin.ModelAdmin):
|
|||||||
admin.site.register(AutogroupsConfig, AutogroupsConfigAdmin)
|
admin.site.register(AutogroupsConfig, AutogroupsConfigAdmin)
|
||||||
admin.site.register(ManagedCorpGroup)
|
admin.site.register(ManagedCorpGroup)
|
||||||
admin.site.register(ManagedAllianceGroup)
|
admin.site.register(ManagedAllianceGroup)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11.6 on 2017-12-23 04:30
|
# Generated by Django 1.11.6 on 2017-12-23 04:30
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -57,25 +57,21 @@ class AutogroupsConfig(models.Model):
|
|||||||
|
|
||||||
states = models.ManyToManyField(State, related_name='autogroups')
|
states = models.ManyToManyField(State, related_name='autogroups')
|
||||||
|
|
||||||
corp_groups = models.BooleanField(default=False,
|
corp_groups = models.BooleanField(default=False, help_text="Setting this to false will delete all the created groups.")
|
||||||
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_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)
|
corp_name_source = models.CharField(max_length=20, choices=NAME_OPTIONS, default=OPT_NAME)
|
||||||
|
|
||||||
alliance_groups = models.BooleanField(default=False,
|
alliance_groups = models.BooleanField(default=False, help_text="Setting this to false will delete all the created groups.")
|
||||||
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_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)
|
alliance_name_source = models.CharField(max_length=20, choices=NAME_OPTIONS, default=OPT_NAME)
|
||||||
|
|
||||||
corp_managed_groups = models.ManyToManyField(
|
corp_managed_groups = models.ManyToManyField(
|
||||||
Group, through='ManagedCorpGroup', related_name='corp_managed_config',
|
Group, through='ManagedCorpGroup', related_name='corp_managed_config',
|
||||||
help_text='A list of corporation groups created and maintained by this AutogroupConfig. '
|
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.')
|
||||||
'You should not edit this list unless you know what you\'re doing.')
|
|
||||||
|
|
||||||
alliance_managed_groups = models.ManyToManyField(
|
alliance_managed_groups = models.ManyToManyField(
|
||||||
Group, through='ManagedAllianceGroup', related_name='alliance_managed_config',
|
Group, through='ManagedAllianceGroup', related_name='alliance_managed_config',
|
||||||
help_text='A list of alliance groups created and maintained by this AutogroupConfig. '
|
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.')
|
||||||
'You should not edit this list unless you know what you\'re doing.')
|
|
||||||
|
|
||||||
replace_spaces = models.BooleanField(default=False)
|
replace_spaces = models.BooleanField(default=False)
|
||||||
replace_spaces_with = models.CharField(
|
replace_spaces_with = models.CharField(
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
# It contains of modules for views and templatetags for templates
|
# It contains of modules for views and templatetags for templates
|
||||||
|
|
||||||
# list of all eve entity categories as defined in ESI
|
# list of all eve entity categories as defined in ESI
|
||||||
ESI_CATEGORY_AGENT = "agent"
|
_ESI_CATEGORY_AGENT = "agent"
|
||||||
ESI_CATEGORY_ALLIANCE = "alliance"
|
_ESI_CATEGORY_ALLIANCE = "alliance"
|
||||||
ESI_CATEGORY_CHARACTER = "character"
|
_ESI_CATEGORY_CHARACTER = "character"
|
||||||
ESI_CATEGORY_CONSTELLATION = "constellation"
|
_ESI_CATEGORY_CONSTELLATION = "constellation"
|
||||||
ESI_CATEGORY_CORPORATION = "corporation"
|
_ESI_CATEGORY_CORPORATION = "corporation"
|
||||||
ESI_CATEGORY_FACTION = "faction"
|
_ESI_CATEGORY_FACTION = "faction"
|
||||||
ESI_CATEGORY_INVENTORYTYPE = "inventory_type"
|
_ESI_CATEGORY_INVENTORYTYPE = "inventory_type"
|
||||||
ESI_CATEGORY_REGION = "region"
|
_ESI_CATEGORY_REGION = "region"
|
||||||
ESI_CATEGORY_SOLARSYSTEM = "solar_system"
|
_ESI_CATEGORY_SOLARSYSTEM = "solar_system"
|
||||||
ESI_CATEGORY_STATION = "station"
|
_ESI_CATEGORY_STATION = "station"
|
||||||
ESI_CATEGORY_WORMHOLE = "wormhole"
|
_ESI_CATEGORY_WORMHOLE = "wormhole"
|
||||||
|
|||||||
@@ -2,24 +2,30 @@
|
|||||||
|
|
||||||
from urllib.parse import urljoin, quote
|
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:
|
def _build_url(category: str, name: str) -> str:
|
||||||
"""return url to profile page for an eve entity"""
|
"""return url to profile page for an eve entity"""
|
||||||
|
|
||||||
if category == ESI_CATEGORY_ALLIANCE:
|
if category == _ESI_CATEGORY_ALLIANCE:
|
||||||
partial = 'alliance'
|
partial = 'alliance'
|
||||||
|
|
||||||
elif category == ESI_CATEGORY_CORPORATION:
|
elif category == _ESI_CATEGORY_CORPORATION:
|
||||||
partial = 'corp'
|
partial = 'corp'
|
||||||
|
|
||||||
elif category == ESI_CATEGORY_REGION:
|
elif category == _ESI_CATEGORY_REGION:
|
||||||
partial = 'map'
|
partial = 'map'
|
||||||
|
|
||||||
elif category == ESI_CATEGORY_SOLARSYSTEM:
|
elif category == _ESI_CATEGORY_SOLARSYSTEM:
|
||||||
partial = 'system'
|
partial = 'system'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -28,7 +34,7 @@ def _build_url(category: str, name: str) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
BASE_URL,
|
_BASE_URL,
|
||||||
'{}/{}'.format(partial, quote(str(name).replace(" ", "_")))
|
'{}/{}'.format(partial, quote(str(name).replace(" ", "_")))
|
||||||
|
|
||||||
)
|
)
|
||||||
@@ -37,16 +43,19 @@ def _build_url(category: str, name: str) -> str:
|
|||||||
|
|
||||||
def alliance_url(name: str) -> str:
|
def alliance_url(name: str) -> str:
|
||||||
"""url for page about given alliance on dotlan"""
|
"""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:
|
def corporation_url(name: str) -> str:
|
||||||
"""url for page about given corporation on dotlan"""
|
"""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:
|
def region_url(name: str) -> str:
|
||||||
"""url for page about given region on dotlan"""
|
"""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:
|
def solar_system_url(name: str) -> str:
|
||||||
"""url for page about given solar system on dotlan"""
|
"""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
|
# 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:
|
def _build_url(category: str, eve_id: int) -> str:
|
||||||
"""return url to profile page for an eve entity"""
|
"""return url to profile page for an eve entity"""
|
||||||
|
|
||||||
if category == ESI_CATEGORY_ALLIANCE:
|
if category == _ESI_CATEGORY_ALLIANCE:
|
||||||
partial = 'alliance'
|
partial = 'alliance'
|
||||||
|
|
||||||
elif category == ESI_CATEGORY_CORPORATION:
|
elif category == _ESI_CATEGORY_CORPORATION:
|
||||||
partial = 'corporation'
|
partial = 'corporation'
|
||||||
|
|
||||||
elif category == ESI_CATEGORY_CHARACTER:
|
elif category == _ESI_CATEGORY_CHARACTER:
|
||||||
partial = 'character'
|
partial = 'character'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -25,7 +30,7 @@ def _build_url(category: str, eve_id: int) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
BASE_URL,
|
_BASE_URL,
|
||||||
'{}/{}'.format(partial, int(eve_id))
|
'{}/{}'.format(partial, int(eve_id))
|
||||||
)
|
)
|
||||||
return url
|
return url
|
||||||
@@ -33,12 +38,14 @@ def _build_url(category: str, eve_id: int) -> str:
|
|||||||
|
|
||||||
def alliance_url(eve_id: int) -> str:
|
def alliance_url(eve_id: int) -> str:
|
||||||
"""url for page about given alliance on evewho"""
|
"""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:
|
def character_url(eve_id: int) -> str:
|
||||||
"""url for page about given character on evewho"""
|
"""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:
|
def corporation_url(eve_id: int) -> str:
|
||||||
"""url for page about given corporation on evewho"""
|
"""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 django.test import TestCase
|
||||||
|
|
||||||
from ...models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
from ...models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||||
from .. import dotlan, zkillboard, evewho
|
from .. import dotlan, zkillboard, evewho, eveimageserver
|
||||||
from ...templatetags import evelinks
|
from ...templatetags import evelinks
|
||||||
|
|
||||||
|
|
||||||
@@ -90,3 +90,115 @@ class TestZkillboard(TestCase):
|
|||||||
'https://zkillboard.com/system/12345678/'
|
'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 django.test import TestCase
|
||||||
|
|
||||||
from ...models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
from ...models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||||
from .. import dotlan, zkillboard, evewho
|
from .. import eveimageserver, evewho, dotlan, zkillboard
|
||||||
from ...templatetags import evelinks
|
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
|
# 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:
|
def _build_url(category: str, eve_id: int) -> str:
|
||||||
"""return url to profile page for an eve entity"""
|
"""return url to profile page for an eve entity"""
|
||||||
|
|
||||||
if category == ESI_CATEGORY_ALLIANCE:
|
if category == _ESI_CATEGORY_ALLIANCE:
|
||||||
partial = 'alliance'
|
partial = 'alliance'
|
||||||
|
|
||||||
elif category == ESI_CATEGORY_CORPORATION:
|
elif category == _ESI_CATEGORY_CORPORATION:
|
||||||
partial = 'corporation'
|
partial = 'corporation'
|
||||||
|
|
||||||
elif category == ESI_CATEGORY_CHARACTER:
|
elif category == _ESI_CATEGORY_CHARACTER:
|
||||||
partial = 'character'
|
partial = 'character'
|
||||||
|
|
||||||
elif category == ESI_CATEGORY_REGION:
|
elif category == _ESI_CATEGORY_REGION:
|
||||||
partial = 'region'
|
partial = 'region'
|
||||||
|
|
||||||
elif category == ESI_CATEGORY_SOLARSYSTEM:
|
elif category == _ESI_CATEGORY_SOLARSYSTEM:
|
||||||
partial = 'system'
|
partial = 'system'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -31,7 +38,7 @@ def _build_url(category: str, eve_id: int) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
BASE_URL,
|
_BASE_URL,
|
||||||
'{}/{}/'.format(partial, int(eve_id))
|
'{}/{}/'.format(partial, int(eve_id))
|
||||||
)
|
)
|
||||||
return url
|
return url
|
||||||
@@ -39,19 +46,23 @@ def _build_url(category: str, eve_id: int) -> str:
|
|||||||
|
|
||||||
def alliance_url(eve_id: int) -> str:
|
def alliance_url(eve_id: int) -> str:
|
||||||
"""url for page about given alliance on zKillboard"""
|
"""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:
|
def character_url(eve_id: int) -> str:
|
||||||
"""url for page about given character on zKillboard"""
|
"""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:
|
def corporation_url(eve_id: int) -> str:
|
||||||
"""url for page about given corporation on zKillboard"""
|
"""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:
|
def region_url(eve_id: int) -> str:
|
||||||
"""url for page about given region on zKillboard"""
|
"""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:
|
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):
|
def update_character(self, character_id):
|
||||||
return self.get(character_id=character_id).update_character()
|
return self.get(character_id=character_id).update_character()
|
||||||
|
|
||||||
def get_character_by_id(self, char_id):
|
def get_character_by_id(self, character_id: int):
|
||||||
if self.filter(character_id=char_id).exists():
|
"""Return character by character ID or None if not found."""
|
||||||
return self.get(character_id=char_id)
|
try:
|
||||||
|
return self.get(character_id=character_id)
|
||||||
|
except self.model.DoesNotExist:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -89,4 +91,6 @@ class EveCorporationManager(models.Manager):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def update_corporation(self, corp_id):
|
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
|
# Generated by Django 1.10.1 on 2016-09-05 21:39
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2016-09-10 20:20
|
# Generated by Django 1.10.1 on 2016-09-10 20:20
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.2 on 2016-10-26 01:49
|
# Generated by Django 1.10.2 on 2016-10-26 01:49
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.2 on 2016-11-01 04:20
|
# Generated by Django 1.10.2 on 2016-11-01 04:20
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2016-12-16 23:22
|
# Generated by Django 1.10.1 on 2016-12-16 23:22
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.1 on 2017-01-02 19:23
|
# Generated by Django 1.10.1 on 2017-01-02 19:23
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.5 on 2017-01-18 13:20
|
# Generated by Django 1.10.5 on 2017-01-18 13:20
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.5 on 2017-03-22 23:09
|
# Generated by Django 1.10.5 on 2017-03-22 23:09
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11.5 on 2017-09-28 02:16
|
# Generated by Django 1.11.5 on 2017-09-28 02:16
|
||||||
from __future__ import unicode_literals
|
from __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 EveCorporationManager, EveCorporationProviderManager
|
||||||
from .managers import EveAllianceManager, EveAllianceProviderManager
|
from .managers import EveAllianceManager, EveAllianceProviderManager
|
||||||
from . import providers
|
from . import providers
|
||||||
|
from .evelinks import eveimageserver
|
||||||
|
|
||||||
|
_DEFAULT_IMAGE_SIZE = 32
|
||||||
EVE_IMAGE_SERVER_URL = 'https://images.evetech.net'
|
|
||||||
|
|
||||||
|
|
||||||
def _eve_entity_image_url(
|
|
||||||
category: str,
|
|
||||||
id: int,
|
|
||||||
size: int = 32,
|
|
||||||
variant: str = None,
|
|
||||||
tenant: str = None,
|
|
||||||
) -> str:
|
|
||||||
"""returns image URL for an Eve Online ID.
|
|
||||||
Supported categories: `alliance`, `corporation`, `character`
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
- category: category of the ID
|
|
||||||
- id: Eve ID of the entity
|
|
||||||
- size: (optional) render size of the image.must be between 32 (default) and 1024
|
|
||||||
- variant: (optional) image variant for category. currently not relevant.
|
|
||||||
- tentant: (optional) Eve Server, either `tranquility`(default) or `singularity`
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
- URL string for the requested image on the Eve image server
|
|
||||||
|
|
||||||
Exceptions:
|
|
||||||
- Throws ValueError on invalid input
|
|
||||||
"""
|
|
||||||
|
|
||||||
# input validations
|
|
||||||
categories = {
|
|
||||||
'alliance': {
|
|
||||||
'endpoint': 'alliances',
|
|
||||||
'variants': [
|
|
||||||
'logo'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'corporation': {
|
|
||||||
'endpoint': 'corporations',
|
|
||||||
'variants': [
|
|
||||||
'logo'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'character': {
|
|
||||||
'endpoint': 'characters',
|
|
||||||
'variants': [
|
|
||||||
'portrait'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tenants = ['tranquility', 'singularity']
|
|
||||||
|
|
||||||
if size < 32 or size > 1024 or (size & (size - 1) != 0):
|
|
||||||
raise ValueError('Invalid size: {}'.format(size))
|
|
||||||
|
|
||||||
if category not in categories:
|
|
||||||
raise ValueError('Invalid category {}'.format(category))
|
|
||||||
else:
|
|
||||||
endpoint = categories[category]['endpoint']
|
|
||||||
|
|
||||||
if variant:
|
|
||||||
if variant not in categories[category]['variants']:
|
|
||||||
raise ValueError('Invalid variant {} for category {}'.format(
|
|
||||||
variant,
|
|
||||||
category
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
variant = categories[category]['variants'][0]
|
|
||||||
|
|
||||||
if tenant and tenant not in tenants:
|
|
||||||
raise ValueError('Invalid tentant {}'.format(tenant))
|
|
||||||
|
|
||||||
# compose result URL
|
|
||||||
result = '{}/{}/{}/{}?size={}'.format(
|
|
||||||
EVE_IMAGE_SERVER_URL,
|
|
||||||
endpoint,
|
|
||||||
id,
|
|
||||||
variant,
|
|
||||||
size
|
|
||||||
)
|
|
||||||
if tenant:
|
|
||||||
result += '&tenant={}'.format(tenant)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
class EveAllianceInfo(models.Model):
|
class EveAllianceInfo(models.Model):
|
||||||
alliance_id = models.CharField(max_length=254, unique=True)
|
alliance_id = models.PositiveIntegerField(unique=True)
|
||||||
alliance_name = models.CharField(max_length=254, unique=True)
|
alliance_name = models.CharField(max_length=254, unique=True)
|
||||||
alliance_ticker = models.CharField(max_length=254)
|
alliance_ticker = models.CharField(max_length=254)
|
||||||
executor_corp_id = models.CharField(max_length=254)
|
executor_corp_id = models.PositiveIntegerField()
|
||||||
|
|
||||||
objects = EveAllianceManager()
|
objects = EveAllianceManager()
|
||||||
provider = EveAllianceProviderManager()
|
provider = EveAllianceProviderManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
indexes = [models.Index(fields=['executor_corp_id',])]
|
||||||
|
|
||||||
def populate_alliance(self):
|
def populate_alliance(self):
|
||||||
alliance = self.provider.get_alliance(self.alliance_id)
|
alliance = self.provider.get_alliance(self.alliance_id)
|
||||||
for corp_id in alliance.corp_ids:
|
for corp_id in alliance.corp_ids:
|
||||||
if not EveCorporationInfo.objects.filter(corporation_id=corp_id).exists():
|
if not EveCorporationInfo.objects.filter(corporation_id=corp_id).exists():
|
||||||
EveCorporationInfo.objects.create_corporation(corp_id)
|
EveCorporationInfo.objects.create_corporation(corp_id)
|
||||||
EveCorporationInfo.objects.filter(corporation_id__in=alliance.corp_ids).update(alliance=self)
|
EveCorporationInfo.objects.filter(
|
||||||
EveCorporationInfo.objects.filter(alliance=self).exclude(corporation_id__in=alliance.corp_ids).update(
|
corporation_id__in=alliance.corp_ids).update(alliance=self
|
||||||
alliance=None)
|
)
|
||||||
|
EveCorporationInfo.objects\
|
||||||
|
.filter(alliance=self)\
|
||||||
|
.exclude(corporation_id__in=alliance.corp_ids)\
|
||||||
|
.update(alliance=None)
|
||||||
|
|
||||||
def update_alliance(self, alliance: providers.Alliance = None):
|
def update_alliance(self, alliance: providers.Alliance = None):
|
||||||
if alliance is None:
|
if alliance is None:
|
||||||
@@ -120,11 +46,13 @@ class EveAllianceInfo(models.Model):
|
|||||||
return self.alliance_name
|
return self.alliance_name
|
||||||
|
|
||||||
@staticmethod
|
@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"""
|
"""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"""
|
"""image URL of this alliance"""
|
||||||
return self.generic_logo_url(self.alliance_id, size)
|
return self.generic_logo_url(self.alliance_id, size)
|
||||||
|
|
||||||
@@ -150,19 +78,26 @@ class EveAllianceInfo(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class EveCorporationInfo(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_name = models.CharField(max_length=254, unique=True)
|
||||||
corporation_ticker = models.CharField(max_length=254)
|
corporation_ticker = models.CharField(max_length=254)
|
||||||
member_count = models.IntegerField()
|
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()
|
objects = EveCorporationManager()
|
||||||
provider = EveCorporationProviderManager()
|
provider = EveCorporationProviderManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
indexes = [models.Index(fields=['ceo_id',]),]
|
||||||
|
|
||||||
def update_corporation(self, corp: providers.Corporation = None):
|
def update_corporation(self, corp: providers.Corporation = None):
|
||||||
if corp is None:
|
if corp is None:
|
||||||
corp = self.provider.get_corporation(self.corporation_id)
|
corp = self.provider.get_corporation(self.corporation_id)
|
||||||
self.member_count = corp.members
|
self.member_count = corp.members
|
||||||
|
self.ceo_id = corp.ceo_id
|
||||||
try:
|
try:
|
||||||
self.alliance = EveAllianceInfo.objects.get(alliance_id=corp.alliance_id)
|
self.alliance = EveAllianceInfo.objects.get(alliance_id=corp.alliance_id)
|
||||||
except EveAllianceInfo.DoesNotExist:
|
except EveAllianceInfo.DoesNotExist:
|
||||||
@@ -174,11 +109,13 @@ class EveCorporationInfo(models.Model):
|
|||||||
return self.corporation_name
|
return self.corporation_name
|
||||||
|
|
||||||
@staticmethod
|
@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"""
|
"""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"""
|
"""image URL for this corporation"""
|
||||||
return self.generic_logo_url(self.corporation_id, size)
|
return self.generic_logo_url(self.corporation_id, size)
|
||||||
|
|
||||||
@@ -204,18 +141,26 @@ class EveCorporationInfo(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class EveCharacter(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)
|
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_name = models.CharField(max_length=254)
|
||||||
corporation_ticker = models.CharField(max_length=5)
|
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_name = models.CharField(max_length=254, blank=True, null=True, default='')
|
||||||
alliance_ticker = models.CharField(max_length=5, blank=True, null=True, default='')
|
alliance_ticker = models.CharField(max_length=5, blank=True, null=True, default='')
|
||||||
|
|
||||||
objects = EveCharacterManager()
|
objects = EveCharacterManager()
|
||||||
provider = EveCharacterProviderManager()
|
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
|
@property
|
||||||
def alliance(self) -> Union[EveAllianceInfo, None]:
|
def alliance(self) -> Union[EveAllianceInfo, None]:
|
||||||
"""
|
"""
|
||||||
@@ -253,11 +198,13 @@ class EveCharacter(models.Model):
|
|||||||
return self.character_name
|
return self.character_name
|
||||||
|
|
||||||
@staticmethod
|
@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"""
|
"""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"""
|
"""image URL for this character"""
|
||||||
return self.generic_portrait_url(self.character_id, size)
|
return self.generic_portrait_url(self.character_id, size)
|
||||||
|
|
||||||
@@ -281,7 +228,7 @@ class EveCharacter(models.Model):
|
|||||||
"""image URL for this character"""
|
"""image URL for this character"""
|
||||||
return self.portrait_url(256)
|
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"""
|
"""image URL for corporation of this character"""
|
||||||
return EveCorporationInfo.generic_logo_url(self.corporation_id, size)
|
return EveCorporationInfo.generic_logo_url(self.corporation_id, size)
|
||||||
|
|
||||||
@@ -305,7 +252,7 @@ class EveCharacter(models.Model):
|
|||||||
"""image URL for corporation of this character"""
|
"""image URL for corporation of this character"""
|
||||||
return self.corporation_logo_url(256)
|
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"""
|
"""image URL for alliance of this character or empty string"""
|
||||||
if self.alliance_id:
|
if self.alliance_id:
|
||||||
return EveAllianceInfo.generic_logo_url(self.alliance_id, size)
|
return EveAllianceInfo.generic_logo_url(self.alliance_id, size)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ from jsonschema.exceptions import RefResolutionError
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from esi.clients import esi_client_factory
|
from esi.clients import esi_client_factory
|
||||||
|
|
||||||
|
from allianceauth import __version__
|
||||||
|
|
||||||
|
|
||||||
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
||||||
os.path.abspath(__file__)), 'swagger.json'
|
os.path.abspath(__file__)), 'swagger.json'
|
||||||
@@ -166,7 +168,7 @@ class EveSwaggerProvider(EveProvider):
|
|||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
self._client = esi_client_factory(
|
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):
|
except (HTTPError, RefResolutionError):
|
||||||
logger.exception(
|
logger.exception(
|
||||||
@@ -182,7 +184,7 @@ class EveSwaggerProvider(EveProvider):
|
|||||||
def client(self):
|
def client(self):
|
||||||
if self._client is None:
|
if self._client is None:
|
||||||
self._client = esi_client_factory(
|
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
|
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