mirror of
https://git.linux-kernel.at/oliver/ivatar.git
synced 2025-11-16 13:08:03 +00:00
Compare commits
21 Commits
sast-2
...
avatar-cre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9423aa0f88 | ||
|
|
65be9ac7cd | ||
|
|
419f8fce90 | ||
|
|
575e3db1ed | ||
|
|
5eff8a61b4 | ||
|
|
08074e394e | ||
|
|
cc1191ee92 | ||
|
|
72344c811b | ||
|
|
f613b79ef9 | ||
|
|
0449e4f00a | ||
|
|
28ff2d16eb | ||
|
|
7f748cf8bf | ||
|
|
37d70b09c8 | ||
|
|
e6e712128a | ||
|
|
34b005643f | ||
|
|
f43d8e5642 | ||
|
|
74c43baca6 | ||
|
|
cddb6d4fbe | ||
|
|
0a877a76e3 | ||
|
|
dacbd21a7f | ||
|
|
8e5d730c25 |
@@ -1 +0,0 @@
|
|||||||
https://github.com/heroku/heroku-buildpack-python
|
|
||||||
6
.env
6
.env
@@ -1,11 +1,13 @@
|
|||||||
if [ ! -d .virtualenv ]; then
|
if [ ! -d .virtualenv ]; then
|
||||||
if [ ! "$(which virtualenv)" == "" ]; then
|
if [ ! "$(which virtualenv)" == "" ]; then
|
||||||
if [ -f .env ]; then
|
|
||||||
virtualenv -p python3 .virtualenv
|
virtualenv -p python3 .virtualenv
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
if [ -f .virtualenv/bin/activate ]; then
|
if [ -f .virtualenv/bin/activate ]; then
|
||||||
source .virtualenv/bin/activate
|
source .virtualenv/bin/activate
|
||||||
AUTOENV_ENABLE_LEAVE=True
|
AUTOENV_ENABLE_LEAVE=True
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -f .env.local ]; then
|
||||||
|
source .env.local
|
||||||
|
fi
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
|
default:
|
||||||
image:
|
image:
|
||||||
name: quay.io/rhn_support_ofalk/fedora35-python3
|
name: quay.io/rhn_support_ofalk/fedora35-python3
|
||||||
entrypoint:
|
entrypoint: [ '/bin/sh', '-c' ]
|
||||||
- "/bin/sh"
|
|
||||||
- "-c"
|
|
||||||
|
|
||||||
test_and_coverage:
|
|
||||||
stage: build
|
|
||||||
coverage: "/^TOTAL.*\\s+(\\d+\\%)$/"
|
|
||||||
before_script:
|
before_script:
|
||||||
- virtualenv -p python3 /tmp/.virtualenv
|
- virtualenv -p python3 /tmp/.virtualenv
|
||||||
- source /tmp/.virtualenv/bin/activate
|
- source /tmp/.virtualenv/bin/activate
|
||||||
- pip install -U pip
|
|
||||||
- pip install Pillow
|
- pip install Pillow
|
||||||
- pip install -r requirements.txt
|
- pip install -r requirements.txt
|
||||||
- pip install python-coveralls
|
- pip install python-coveralls
|
||||||
@@ -18,6 +13,9 @@ test_and_coverage:
|
|||||||
- pip install pycco
|
- pip install pycco
|
||||||
- pip install django_coverage_plugin
|
- pip install django_coverage_plugin
|
||||||
|
|
||||||
|
test_and_coverage:
|
||||||
|
stage: test
|
||||||
|
coverage: '/^TOTAL.*\s+(\d+\%)$/'
|
||||||
script:
|
script:
|
||||||
- echo 'from ivatar.settings import TEMPLATES' > config_local.py
|
- echo 'from ivatar.settings import TEMPLATES' > config_local.py
|
||||||
- echo 'TEMPLATES[0]["OPTIONS"]["debug"] = True' >> config_local.py
|
- echo 'TEMPLATES[0]["OPTIONS"]["debug"] = True' >> config_local.py
|
||||||
@@ -26,33 +24,26 @@ test_and_coverage:
|
|||||||
- echo "CACHES['default'] = CACHES['filesystem']" >> config_local.py
|
- echo "CACHES['default'] = CACHES['filesystem']" >> config_local.py
|
||||||
- python manage.py collectstatic --noinput
|
- python manage.py collectstatic --noinput
|
||||||
- coverage run --source . manage.py test -v3
|
- coverage run --source . manage.py test -v3
|
||||||
- coverage report --fail-under=70
|
- coverage report --fail-under=69
|
||||||
- coverage html
|
- coverage html
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- htmlcov/
|
- htmlcov/
|
||||||
|
|
||||||
pycco:
|
pycco:
|
||||||
stage: test
|
stage: test
|
||||||
before_script:
|
|
||||||
- virtualenv -p python3 /tmp/.virtualenv
|
|
||||||
- source /tmp/.virtualenv/bin/activate
|
|
||||||
- pip install -U pip
|
|
||||||
- pip install Pillow
|
|
||||||
- pip install -r requirements.txt
|
|
||||||
- pip install python-coveralls
|
|
||||||
- pip install coverage
|
|
||||||
- pip install pycco
|
|
||||||
- pip install django_coverage_plugin
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- "/bin/true"
|
- /bin/true
|
||||||
- find ivatar/ -type f -name "*.py"|grep -v __pycache__|grep -v __init__.py|grep
|
- find ivatar/ -type f -name "*.py"|grep -v __pycache__|grep -v __init__.py|grep -v /migrations/ | xargs pycco -p -d pycco -i -s
|
||||||
-v /migrations/ | xargs pycco -p -d pycco -i -s
|
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- pycco/
|
- pycco/
|
||||||
expire_in: 14 days
|
expire_in: 14 days
|
||||||
|
|
||||||
pages:
|
pages:
|
||||||
|
before_script:
|
||||||
|
- /bin/true
|
||||||
|
- /bin/true
|
||||||
stage: deploy
|
stage: deploy
|
||||||
dependencies:
|
dependencies:
|
||||||
- test_and_coverage
|
- test_and_coverage
|
||||||
@@ -66,16 +57,15 @@ pages:
|
|||||||
expire_in: 14 days
|
expire_in: 14 days
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
build-image:
|
build-image:
|
||||||
image: docker
|
image: docker
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- devel
|
|
||||||
services:
|
services:
|
||||||
- docker:dind
|
- docker:dind
|
||||||
before_script:
|
before_script:
|
||||||
- docker info
|
- docker info
|
||||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ls -lah
|
- ls -lah
|
||||||
- |
|
- |
|
||||||
@@ -88,23 +78,3 @@ build-image:
|
|||||||
fi
|
fi
|
||||||
- docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" .
|
- docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" .
|
||||||
- docker push "$CI_REGISTRY_IMAGE${tag}"
|
- docker push "$CI_REGISTRY_IMAGE${tag}"
|
||||||
semgrep:
|
|
||||||
extends: semgrep-sast
|
|
||||||
stage: test
|
|
||||||
allow_failure: true
|
|
||||||
image: registry.gitlab.com/gitlab-org/security-products/analyzers/semgrep:latest
|
|
||||||
variables:
|
|
||||||
CI_PROJECT_DIR: "/tmp/app"
|
|
||||||
SECURE_LOG_LEVEL: "debug"
|
|
||||||
script:
|
|
||||||
- rm -rf .virtualenv
|
|
||||||
- /analyzer run
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- gl-sast-report.json
|
|
||||||
- semgrep.sarif
|
|
||||||
|
|
||||||
include:
|
|
||||||
- template: Jobs/SAST.gitlab-ci.yml
|
|
||||||
- template: Jobs/Dependency-Scanning.gitlab-ci.yml
|
|
||||||
- template: Jobs/Secret-Detection.gitlab-ci.yml
|
|
||||||
|
|||||||
19
Dockerfile
19
Dockerfile
@@ -1,22 +1,17 @@
|
|||||||
FROM quay.io/rhn_support_ofalk/fedora37-python3
|
FROM quay.io/rhn_support_ofalk/fedora35-python3
|
||||||
LABEL maintainer Oliver Falk <oliver@linux-kernel.at>
|
LABEL maintainer Oliver Falk <oliver@linux-kernel.at>
|
||||||
EXPOSE 8081
|
EXPOSE 8081
|
||||||
|
|
||||||
|
RUN pip3 install pip --upgrade
|
||||||
|
|
||||||
ADD . /opt/ivatar-devel
|
ADD . /opt/ivatar-devel
|
||||||
|
|
||||||
WORKDIR /opt/ivatar-devel
|
WORKDIR /opt/ivatar-devel
|
||||||
|
|
||||||
RUN pip3 install pip --upgrade \
|
RUN pip3 install Pillow && pip3 install -r requirements.txt && pip3 install python-coveralls coverage pycco django_coverage_plugin
|
||||||
&& virtualenv .virtualenv \
|
|
||||||
&& source .virtualenv/bin/activate \
|
|
||||||
&& pip3 install Pillow \
|
|
||||||
&& pip3 install -r requirements.txt \
|
|
||||||
&& pip3 install python-coveralls coverage pycco django_coverage_plugin
|
|
||||||
|
|
||||||
RUN echo "DEBUG = True" >> /opt/ivatar-devel/config_local.py
|
RUN echo "DEBUG = True" >> /opt/ivatar-devel/config_local.py
|
||||||
RUN echo "EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'" >> /opt/ivatar-devel/config_local.py
|
RUN echo "EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'" >> /opt/ivatar-devel/config_local.py
|
||||||
RUN source .virtualenv/bin/activate \
|
RUN python3 manage.py migrate && python3 manage.py collectstatic --noinput
|
||||||
&& python3 manage.py migrate \
|
RUN echo "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser('admin', 'admin@local.tld', 'admin')" | python manage.py shell
|
||||||
&& python3 manage.py collectstatic --noinput \
|
ENTRYPOINT python3 ./manage.py runserver 0:8081
|
||||||
&& echo "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser('admin', 'admin@local.tld', 'admin')" | python manage.py shell
|
|
||||||
ENTRYPOINT source .virtualenv/bin/activate && python3 ./manage.py runserver 0:8081
|
|
||||||
|
|||||||
10
MANIFEST.in
10
MANIFEST.in
@@ -1,10 +0,0 @@
|
|||||||
include *.py
|
|
||||||
include *.md
|
|
||||||
include COPYING
|
|
||||||
include LICENSE
|
|
||||||
recursive-include templates *
|
|
||||||
recursive-include ivatar *
|
|
||||||
exclude .virtualenv
|
|
||||||
exclude libravatar.egg-info
|
|
||||||
global-exclude *.py[co]
|
|
||||||
global-exclude __pycache__
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
https://django-debug-toolbar.readthedocs.io/en/latest/installation.html
|
|
||||||
https://stackoverflow.com/questions/6548947/how-can-django-debug-toolbar-be-set-to-work-for-just-some-users/6549317#6549317
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import os
|
|
||||||
import django
|
|
||||||
import timeit
|
|
||||||
|
|
||||||
os.environ.setdefault(
|
|
||||||
"DJANGO_SETTINGS_MODULE", "ivatar.settings"
|
|
||||||
) # pylint: disable=wrong-import-position
|
|
||||||
django.setup() # pylint: disable=wrong-import-position
|
|
||||||
|
|
||||||
from ivatar.ivataraccount.models import ConfirmedEmail, APIKey
|
|
||||||
from simplecrypt import decrypt
|
|
||||||
from binascii import unhexlify
|
|
||||||
|
|
||||||
digest = None
|
|
||||||
digest_sha256 = None
|
|
||||||
|
|
||||||
|
|
||||||
def get_digest_sha256():
|
|
||||||
digest_sha256 = ConfirmedEmail.objects.first().encrypted_digest_sha256(
|
|
||||||
secret_key=APIKey.objects.first()
|
|
||||||
)
|
|
||||||
return digest_sha256
|
|
||||||
|
|
||||||
|
|
||||||
def get_digest():
|
|
||||||
digest = ConfirmedEmail.objects.first().encrypted_digest(
|
|
||||||
secret_key=APIKey.objects.first()
|
|
||||||
)
|
|
||||||
return digest
|
|
||||||
|
|
||||||
|
|
||||||
def decrypt_digest():
|
|
||||||
return decrypt(APIKey.objects.first().secret_key, unhexlify(digest))
|
|
||||||
|
|
||||||
|
|
||||||
def decrypt_digest_256():
|
|
||||||
return decrypt(APIKey.objects.first().secret_key, unhexlify(digest_sha256))
|
|
||||||
|
|
||||||
|
|
||||||
digest = get_digest()
|
|
||||||
digest_sha256 = get_digest_sha256()
|
|
||||||
|
|
||||||
print("Encrypt digest: %s" % timeit.timeit(get_digest, number=1))
|
|
||||||
print("Encrypt digest_sha256: %s" % timeit.timeit(get_digest_sha256, number=1))
|
|
||||||
print("Decrypt digest: %s" % timeit.timeit(decrypt_digest, number=1))
|
|
||||||
print("Decrypt digest_sha256: %s" % timeit.timeit(decrypt_digest_256, number=1))
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
DATABASES['default'] = {
|
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
|
||||||
'NAME': 'libravatar',
|
|
||||||
'USER': 'libravatar',
|
|
||||||
'PASSWORD': 'libravatar',
|
|
||||||
'HOST': 'localhost',
|
|
||||||
}
|
|
||||||
@@ -232,11 +232,6 @@ TRUSTED_DEFAULT_URLS = [
|
|||||||
"host_equals": "avatars.dicebear.com",
|
"host_equals": "avatars.dicebear.com",
|
||||||
"path_prefix": "/api/",
|
"path_prefix": "/api/",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"schemes": ["https"],
|
|
||||||
"host_equals": "api.dicebear.com",
|
|
||||||
"path_prefix": "/",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"schemes": ["https"],
|
"schemes": ["https"],
|
||||||
"host_equals": "badges.fedoraproject.org",
|
"host_equals": "badges.fedoraproject.org",
|
||||||
|
|||||||
196
ivatar/ivataraccount/avatar_creator_views.py
Normal file
196
ivatar/ivataraccount/avatar_creator_views.py
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
View classes for the avatar creator
|
||||||
|
"""
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.views.generic.base import View, TemplateView
|
||||||
|
from django.http import HttpResponseRedirect, HttpResponse
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from jinja2 import Environment, PackageLoader
|
||||||
|
import py_avataaars as pa
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
class AvatarCreatorView(TemplateView):
|
||||||
|
"""
|
||||||
|
View class responsible for handling avatar creation
|
||||||
|
"""
|
||||||
|
|
||||||
|
template_name = "avatar_creator.html"
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Handle get for create view
|
||||||
|
"""
|
||||||
|
if request.user:
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
return HttpResponseRedirect(reverse_lazy("profile"))
|
||||||
|
|
||||||
|
return super().get(self, request, args, kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Provide additional context data
|
||||||
|
"""
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["SkinColor"] = list(pa.SkinColor)
|
||||||
|
context["HairColor"] = list(pa.HairColor)
|
||||||
|
context["FacialHairType"] = list(pa.FacialHairType)
|
||||||
|
context["TopType"] = list(pa.TopType)
|
||||||
|
context["HatColor"] = list(pa.Color)
|
||||||
|
context["MouthType"] = list(pa.MouthType)
|
||||||
|
context["EyesType"] = list(pa.EyesType)
|
||||||
|
context["EyebrowType"] = list(pa.EyebrowType)
|
||||||
|
context["NoseType"] = list(pa.NoseType)
|
||||||
|
context["AccessoriesType"] = list(pa.AccessoriesType)
|
||||||
|
context["ClotheType"] = list(pa.ClotheType)
|
||||||
|
context["ClotheColor"] = list(pa.Color)
|
||||||
|
context["ClotheGraphicType"] = list(pa.ClotheGraphicType)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
class AvatarView(View):
|
||||||
|
"""
|
||||||
|
View class responsible for handling avatar view
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get(
|
||||||
|
self, request, *args, **kwargs
|
||||||
|
): # pylint: disable=too-many-locals,too-many-branches,too-many-statements,unused-argument
|
||||||
|
"""
|
||||||
|
Handle get for create view
|
||||||
|
"""
|
||||||
|
output_format = "svg+xml"
|
||||||
|
|
||||||
|
avatar_style = list(pa.AvatarStyle)[0]
|
||||||
|
skin_color = list(pa.SkinColor)[0]
|
||||||
|
hair_color = list(pa.HairColor)[0]
|
||||||
|
facial_hair_type = list(pa.FacialHairType)[0]
|
||||||
|
top_type = pa.TopType.SHORT_HAIR_SHORT_FLAT
|
||||||
|
hat_color = list(pa.Color)[0]
|
||||||
|
mouth_type = list(pa.MouthType)[0]
|
||||||
|
eyes_type = list(pa.EyesType)[0]
|
||||||
|
eyebrow_type = list(pa.EyebrowType)[0]
|
||||||
|
nose_type = list(pa.NoseType)[0]
|
||||||
|
accessories_type = list(pa.AccessoriesType)[0]
|
||||||
|
clothe_type = list(pa.ClotheType)[0]
|
||||||
|
clothe_color = list(pa.Color)[0]
|
||||||
|
clothe_graphic_type = list(pa.ClotheGraphicType)[0]
|
||||||
|
|
||||||
|
if "avatar_style" in request.GET:
|
||||||
|
avatar_style = list(pa.AvatarStyle)[int(request.GET["avatar_style"])]
|
||||||
|
if "skin_color" in request.GET:
|
||||||
|
skin_color = list(pa.SkinColor)[int(request.GET["skin_color"])]
|
||||||
|
if "hair_color" in request.GET:
|
||||||
|
hair_color = list(pa.HairColor)[int(request.GET["hair_color"])]
|
||||||
|
if "facial_hair_type" in request.GET:
|
||||||
|
facial_hair_type = list(pa.FacialHairType)[
|
||||||
|
int(request.GET["facial_hair_type"])
|
||||||
|
]
|
||||||
|
if "facial_hair_color" in request.GET:
|
||||||
|
facial_hair_color = list(pa.HairColor)[
|
||||||
|
int(request.GET["facial_hair_color"])
|
||||||
|
]
|
||||||
|
if "top_type" in request.GET:
|
||||||
|
top_type = list(pa.TopType)[int(request.GET["top_type"])]
|
||||||
|
if "hat_color" in request.GET:
|
||||||
|
hat_color = list(pa.Color)[int(request.GET["hat_color"])]
|
||||||
|
if "mouth_type" in request.GET:
|
||||||
|
mouth_type = list(pa.MouthType)[int(request.GET["mouth_type"])]
|
||||||
|
if "eyes_type" in request.GET:
|
||||||
|
eyes_type = list(pa.EyesType)[int(request.GET["eyes_type"])]
|
||||||
|
if "eyebrow_type" in request.GET:
|
||||||
|
eyebrow_type = list(pa.EyebrowType)[int(request.GET["eyebrow_type"])]
|
||||||
|
if "nose_type" in request.GET:
|
||||||
|
nose_type = list(pa.NoseType)[int(request.GET["nose_type"])]
|
||||||
|
if "accessories_type" in request.GET:
|
||||||
|
accessories_type = list(pa.AccessoriesType)[
|
||||||
|
int(request.GET["accessories_type"])
|
||||||
|
]
|
||||||
|
if "clothe_type" in request.GET:
|
||||||
|
clothe_type = list(pa.ClotheType)[int(request.GET["clothe_type"])]
|
||||||
|
if "clothe_color" in request.GET:
|
||||||
|
clothe_color = list(pa.Color)[int(request.GET["clothe_color"])]
|
||||||
|
if "clothe_graphic_type" in request.GET:
|
||||||
|
clothe_graphic_type = list(pa.ClotheGraphicType)[
|
||||||
|
int(request.GET["clothe_graphic_type"])
|
||||||
|
]
|
||||||
|
if "format" in request.GET:
|
||||||
|
if request.GET["format"] == "png":
|
||||||
|
output_format = request.GET["format"]
|
||||||
|
elif request.GET["format"] in ("svg", "svg+xml"):
|
||||||
|
output_format = "svg+xml"
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
"Format: '%s' isn't supported" % request.GET["format"]
|
||||||
|
) # pylint: disable=consider-using-f-string
|
||||||
|
|
||||||
|
avatar = pa.PyAvataaar(
|
||||||
|
style=avatar_style,
|
||||||
|
skin_color=skin_color,
|
||||||
|
hair_color=hair_color,
|
||||||
|
facial_hair_type=facial_hair_type,
|
||||||
|
facial_hair_color=facial_hair_color,
|
||||||
|
top_type=top_type,
|
||||||
|
hat_color=hat_color,
|
||||||
|
mouth_type=mouth_type,
|
||||||
|
eye_type=eyes_type,
|
||||||
|
eyebrow_type=eyebrow_type,
|
||||||
|
nose_type=nose_type,
|
||||||
|
accessories_type=accessories_type,
|
||||||
|
clothe_type=clothe_type,
|
||||||
|
clothe_color=clothe_color,
|
||||||
|
clothe_graphic_type=clothe_graphic_type,
|
||||||
|
)
|
||||||
|
if output_format == "png":
|
||||||
|
return HttpResponse(avatar.render_png(), content_type="image/png")
|
||||||
|
|
||||||
|
return HttpResponse(avatar.render_svg(), content_type="image/svg+xml")
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
class AvatarItemView(View):
|
||||||
|
"""
|
||||||
|
View class responsible for providing access to single avatar items
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs): # pylint: disable=unused-argument
|
||||||
|
"""
|
||||||
|
Handle get for create view
|
||||||
|
"""
|
||||||
|
item = request.GET["item"]
|
||||||
|
|
||||||
|
env = Environment(
|
||||||
|
loader=PackageLoader("py_avataaars", "templates"),
|
||||||
|
trim_blocks=True,
|
||||||
|
lstrip_blocks=True,
|
||||||
|
autoescape=True,
|
||||||
|
keep_trailing_newline=False,
|
||||||
|
extensions=[],
|
||||||
|
)
|
||||||
|
template = env.get_template(item)
|
||||||
|
|
||||||
|
def uni(attr): # pylint: disable=unused-argument
|
||||||
|
return None
|
||||||
|
|
||||||
|
rendered_template = template.render(
|
||||||
|
unique_id=uni,
|
||||||
|
template_path=pa.PyAvataaar._PyAvataaar__template_path, # pylint: disable=protected-access
|
||||||
|
template_name=pa.PyAvataaar._PyAvataaar__template_name, # pylint: disable=protected-access
|
||||||
|
facial_hair_type=pa.FacialHairType.DEFAULT,
|
||||||
|
hat_color=pa.Color.BLACK,
|
||||||
|
clothe_color=pa.Color.HEATHER,
|
||||||
|
hair_color=pa.HairColor.BROWN,
|
||||||
|
facial_hair_color=pa.HairColor.BROWN,
|
||||||
|
clothe_graphic_type=pa.ClotheGraphicType.BAT,
|
||||||
|
accessories_type=pa.AccessoriesType.DEFAULT,
|
||||||
|
).replace("\n", "")
|
||||||
|
rendered_template = (
|
||||||
|
'<?xml version="1.0" encoding="utf-8" ?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="280px" height="280px" viewBox="-6 0 274 280" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">'
|
||||||
|
+ rendered_template
|
||||||
|
+ "</svg>"
|
||||||
|
) # pylint: disable=line-too-long
|
||||||
|
|
||||||
|
return HttpResponse(rendered_template, content_type="image/svg+xml")
|
||||||
243
ivatar/ivataraccount/templates/avatar_creator.html
Normal file
243
ivatar/ivataraccount/templates/avatar_creator.html
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
{% load bootstrap4 %}
|
||||||
|
|
||||||
|
{% block title %}{% trans 'Avatar Creator' %}{% endblock title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.achoose {
|
||||||
|
float:left;
|
||||||
|
margin-right:2px;
|
||||||
|
width:20px;
|
||||||
|
height:20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var skin_color = 0;
|
||||||
|
var hair_color = 0;
|
||||||
|
var facial_hair_type = 0;
|
||||||
|
var facial_hair_color = 0;
|
||||||
|
var top_type = -1;
|
||||||
|
var hat_color = 0;
|
||||||
|
var mouth_type = 0;
|
||||||
|
var eyes_type = 0;
|
||||||
|
var eyebrow_type = 0;
|
||||||
|
var nose_type = 0;
|
||||||
|
var accessories_type = 0;
|
||||||
|
var clothe_type = 0;
|
||||||
|
var clothe_color = 0;
|
||||||
|
var clothe_graphic_type = 0;
|
||||||
|
var avatar_style = 0;
|
||||||
|
var size = 172;
|
||||||
|
|
||||||
|
function update_image() {
|
||||||
|
var url = "{% url 'avataaar' %}?avatar_style=" + avatar_style + "&skin_color=" + skin_color + "&hair_color=" + hair_color +
|
||||||
|
"&facial_hair_type=" + facial_hair_type + "&facial_hair_color=" + facial_hair_color +
|
||||||
|
"&top_type=" + top_type + "&hat_color=" + hat_color + "&mouth_type=" + mouth_type +
|
||||||
|
"&eyes_type=" + eyes_type + "&eyebrow_type=" + eyebrow_type + "&nose_type=" + nose_type +
|
||||||
|
"&accessories_type=" + accessories_type + "&clothe_type=" + clothe_type +
|
||||||
|
"&clothe_color=" + clothe_color + "&clothe_graphic_type=" + clothe_graphic_type;
|
||||||
|
$("#avatar_image").attr('src', url);
|
||||||
|
$("#avatar_image").attr('width', size + "px");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div id="preview_and_download" style="float:left;width:50%;" data-spy="affix" data-offset-top="0" data-offset-bottom="200">
|
||||||
|
<h3>{% trans 'Adjust your avatar' %}</h3>
|
||||||
|
<div id="avatar_image_div">
|
||||||
|
<img id="avatar_image" width="172px">
|
||||||
|
<script>update_image();</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" role="group" style="margin-top:5px;align:center;">
|
||||||
|
<button type="button" class="btn btn-info" onclick='url=$("#avatar_image").attr("src")+"&format=svg";window.open(url);'>Download SVG</button>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-info" onclick='url=$("#avatar_image").attr("src")+"&format=png";window.open(url);'>Download PNG</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="options" style="float:right;">
|
||||||
|
<!--
|
||||||
|
<div class="form-group" role="group">
|
||||||
|
<label for="preview_size" class="form-label">{% trans 'Preview size'%}</label>
|
||||||
|
<input type="range" class="form-range" id="preview_size" min="16" max="500" step="1" value="172">
|
||||||
|
<script>
|
||||||
|
var preview_size = document.getElementById("preview_size");
|
||||||
|
preview_size.addEventListener("input", function() {
|
||||||
|
size = this.value;
|
||||||
|
update_image();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div class="form-group" role="group">
|
||||||
|
<label for="skincolor" class="form-label">{% trans 'Skin color'%}</label>
|
||||||
|
<div id="skincolor">
|
||||||
|
{% for color in SkinColor %}
|
||||||
|
<a class="button achoose" style="background:{{ color.main_value }};" onclick='skin_color="{{ color.value }}"; update_image();'> </a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<div class="form-group" role="group">
|
||||||
|
<label for="haircolor" class="form-label">{% trans 'Hair color'%}</label>
|
||||||
|
<div id="haircolor">
|
||||||
|
{% for color in HairColor %}
|
||||||
|
<a class="button achoose" style="background:{{ color.main_value }};" onclick='hair_color="{{ color.value }}"; update_image();'> </a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<div class="form-group" role="group" style="float:none;">
|
||||||
|
<label for="facialhairtype" class="form-label">{% trans 'Facial hair type' %}</label>
|
||||||
|
<input type="range" class="form-range" id="facialhairtype" min="0" max="5" step="1" value="0">
|
||||||
|
<script>
|
||||||
|
var elem = document.getElementById("facialhairtype");
|
||||||
|
facialhairtype.addEventListener("input", function() {
|
||||||
|
facial_hair_type = this.value;
|
||||||
|
var thediv = document.getElementById("facialhaircolor").parentNode;
|
||||||
|
if(this.value != 0) {
|
||||||
|
thediv.display = "block";
|
||||||
|
thediv.removeAttribute("hidden");
|
||||||
|
} else {
|
||||||
|
thediv.setAttribute("hidden", "hidden");
|
||||||
|
}
|
||||||
|
update_image();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div hidden="hidden" class="form-group" style="float:none;">
|
||||||
|
<label for="facialhaircolor" class="form-label">{% trans 'Facial hair color'%}</label>
|
||||||
|
<div id="facialhaircolor">
|
||||||
|
{% for color in HairColor %}
|
||||||
|
<a class="button achoose" style="background:{{ color.main_value }};" onclick='facial_hair_color="{{ color.value }}"; update_image();'> </a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" role="group" style="float:none;">
|
||||||
|
<label for="toptype" class="form-label">{% trans 'Top type' %}</label>
|
||||||
|
<input type="range" class="form-range" id="toptype" min="0" max="34" step="1" value="0">
|
||||||
|
<script>
|
||||||
|
var elem = document.getElementById("toptype");
|
||||||
|
elem.addEventListener("input", function() {
|
||||||
|
top_type = this.value;
|
||||||
|
update_image();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" role="group" style="float:none;">
|
||||||
|
<label for="hatcolor" class="form-label">{% trans 'Hat color'%}</label>
|
||||||
|
<div id="hatcolor">
|
||||||
|
{% for color in HatColor %}
|
||||||
|
<a class="button achoose" style="background:{{ color.main_value }};" onclick='hat_color="{{ color.value }}"; update_image();'> </a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<div class="form-group" role="group" style="float:none;">
|
||||||
|
<label for="mouthtype" class="form-label">{% trans 'Mouth type' %}</label>
|
||||||
|
<input type="range" class="form-range" id="mouthtype" min="0" max="11" step="1" value="0">
|
||||||
|
<script>
|
||||||
|
var elem = document.getElementById("mouthtype");
|
||||||
|
elem.addEventListener("input", function() {
|
||||||
|
mouth_type = this.value;
|
||||||
|
update_image();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" role="group" style="float:none;">
|
||||||
|
<label for="eyestype" class="form-label">{% trans 'Eyes type' %}</label>
|
||||||
|
<input type="range" class="form-range" id="eyestype" min="0" max="11" step="1" value="0">
|
||||||
|
<script>
|
||||||
|
var elem = document.getElementById("eyestype");
|
||||||
|
elem.addEventListener("input", function() {
|
||||||
|
eyes_type = this.value;
|
||||||
|
update_image();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" role="group" style="float:none;">
|
||||||
|
<label for="eyebrowtype" class="form-label">{% trans 'Eyebrow type' %}</label>
|
||||||
|
<input type="range" class="form-range" id="eyebrowtype" min="0" max="12" step="1" value="0">
|
||||||
|
<script>
|
||||||
|
var elem = document.getElementById("eyebrowtype");
|
||||||
|
elem.addEventListener("input", function() {
|
||||||
|
eyebrow_type = this.value;
|
||||||
|
update_image();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" role="group" style="float:none;">
|
||||||
|
<label for="accessoriestype" class="form-label">{% trans 'Accessories type' %}</label>
|
||||||
|
<input type="range" class="form-range" id="accessoriestype" min="0" max="6" step="1" value="0">
|
||||||
|
<script>
|
||||||
|
var elem = document.getElementById("accessoriestype");
|
||||||
|
elem.addEventListener("input", function() {
|
||||||
|
accessories_type = this.value;
|
||||||
|
update_image();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" role="group" style="float:none;">
|
||||||
|
<label for="clothetype" class="form-label">{% trans 'Clothe type' %}</label>
|
||||||
|
<input type="range" class="form-range" id="clothetype" min="0" max="8" step="1" value="0">
|
||||||
|
<script>
|
||||||
|
var elem = document.getElementById("clothetype");
|
||||||
|
elem.addEventListener("input", function() {
|
||||||
|
clothe_type = this.value;
|
||||||
|
var thediv = document.getElementById("clothegraphictype").parentNode;
|
||||||
|
if(this.value == 3) {
|
||||||
|
thediv.display = "block";
|
||||||
|
thediv.removeAttribute("hidden");
|
||||||
|
} else {
|
||||||
|
thediv.setAttribute("hidden", "hidden");
|
||||||
|
}
|
||||||
|
update_image();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" role="group" style="float:none;">
|
||||||
|
<label for="clothegraphictype" class="form-label">{% trans 'Clothe color' %}</label>
|
||||||
|
<div id"=clothegraphictype">
|
||||||
|
{% for color in ClotheColor %}
|
||||||
|
<a class="button achoose" style="background:{{ color.main_value }};" onclick='clothe_color="{{ color.value }}"; update_image();'> </a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<div hidden="hidden" class="form-group" role="group" style="float:none;">
|
||||||
|
<label for="clothegraphictype" class="form-label">{% trans 'Clothe graphic type' %}</label>
|
||||||
|
<input type="range" class="form-range" id="clothegraphictype" min="0" max="10" step="1" value="0">
|
||||||
|
<script>
|
||||||
|
var elem = document.getElementById("clothegraphictype");
|
||||||
|
elem.addEventListener("input", function() {
|
||||||
|
clothe_graphic_type = this.value;
|
||||||
|
update_image();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
<div style="height:40px"></div>
|
||||||
|
{% endblock content %}
|
||||||
@@ -242,7 +242,8 @@
|
|||||||
{% if not max_photos %}
|
{% if not max_photos %}
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url 'upload_photo' %}" class="button">{% trans 'Upload a new photo' %}</a>
|
<a href="{% url 'upload_photo' %}" class="button">{% trans 'Upload a new photo' %}</a>
|
||||||
<a href="{% url 'import_photo' %}" class="button">{% trans 'Import photo from other services' %}</a>
|
<a href="{% url 'import_photo' %}" class="button">{% trans 'Import photo from other services' %}</a>
|
||||||
|
<a href="{% url 'avatar_creator' %}" class="button">{% trans 'Create an avatar' %}</a>
|
||||||
</p>
|
</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% trans "You've reached the maximum number of allowed images!" %}<br/>
|
{% trans "You've reached the maximum number of allowed images!" %}<br/>
|
||||||
|
|||||||
@@ -1998,61 +1998,4 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
Test if preferences page works
|
Test if preferences page works
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.login()
|
|
||||||
self.client.get(reverse("user_preference"))
|
self.client.get(reverse("user_preference"))
|
||||||
|
|
||||||
def test_delete_user(self):
|
|
||||||
"""
|
|
||||||
Test if deleting user profile works
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.login()
|
|
||||||
self.client.get(reverse("delete"))
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("delete"),
|
|
||||||
data={"password": self.password},
|
|
||||||
follow=True,
|
|
||||||
)
|
|
||||||
self.assertEqual(response.status_code, 200, "Deletion worked")
|
|
||||||
self.assertEqual(User.objects.count(), 0, "No user there any more")
|
|
||||||
|
|
||||||
def test_confirm_already_confirmed(self):
|
|
||||||
"""
|
|
||||||
Try to confirm a mail address that has been confirmed (by another user)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Add mail address (stays unconfirmed)
|
|
||||||
self.test_add_email()
|
|
||||||
|
|
||||||
# Create a second user that will conflict
|
|
||||||
user2 = User.objects.create_user(
|
|
||||||
username=self.username + "1",
|
|
||||||
password=self.password,
|
|
||||||
first_name=self.first_name,
|
|
||||||
last_name=self.last_name,
|
|
||||||
)
|
|
||||||
ConfirmedEmail.objects.create(
|
|
||||||
email=self.email,
|
|
||||||
user=user2,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Just to be sure
|
|
||||||
self.assertEqual(
|
|
||||||
self.user.unconfirmedemail_set.first().email,
|
|
||||||
user2.confirmedemail_set.first().email,
|
|
||||||
"Mail not the same?",
|
|
||||||
)
|
|
||||||
|
|
||||||
# This needs to be cought
|
|
||||||
try:
|
|
||||||
self.test_confirm_email()
|
|
||||||
except AssertionError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Request a random page, so we can access the messages
|
|
||||||
response = self.client.get(reverse("profile"))
|
|
||||||
self.assertEqual(
|
|
||||||
str(list(response.context[0]["messages"])[0]),
|
|
||||||
"This mail address has been taken already and cannot be confirmed",
|
|
||||||
"This should return an error message!",
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ from .views import ResendConfirmationMailView
|
|||||||
from .views import IvatarLoginView
|
from .views import IvatarLoginView
|
||||||
from .views import DeleteAccountView
|
from .views import DeleteAccountView
|
||||||
from .views import ExportView
|
from .views import ExportView
|
||||||
|
from .avatar_creator_views import AvatarCreatorView, AvatarView
|
||||||
|
|
||||||
|
# from .avatar_creator_views import AvatarItemView
|
||||||
|
|
||||||
# Define URL patterns, self documenting
|
# Define URL patterns, self documenting
|
||||||
# To see the fancy, colorful evaluation of these use:
|
# To see the fancy, colorful evaluation of these use:
|
||||||
@@ -80,6 +83,10 @@ urlpatterns = [ # pylint: disable=invalid-name
|
|||||||
path("add_openid/", AddOpenIDView.as_view(), name="add_openid"),
|
path("add_openid/", AddOpenIDView.as_view(), name="add_openid"),
|
||||||
path("upload_photo/", UploadPhotoView.as_view(), name="upload_photo"),
|
path("upload_photo/", UploadPhotoView.as_view(), name="upload_photo"),
|
||||||
path("password_set/", PasswordSetView.as_view(), name="password_set"),
|
path("password_set/", PasswordSetView.as_view(), name="password_set"),
|
||||||
|
path("avatar_creator/", AvatarCreatorView.as_view(), name="avatar_creator"),
|
||||||
|
path("avatar_view/", AvatarView.as_view(), name="avataaar"),
|
||||||
|
# This is for testing purpose only and shall not be used in production at all
|
||||||
|
# path("avatar_item_view/", AvatarItemView.as_view(), name="avataaar_item"),
|
||||||
re_path(
|
re_path(
|
||||||
r"remove_unconfirmed_openid/(?P<openid_id>\d+)",
|
r"remove_unconfirmed_openid/(?P<openid_id>\d+)",
|
||||||
RemoveUnconfirmedOpenIDView.as_view(),
|
RemoveUnconfirmedOpenIDView.as_view(),
|
||||||
|
|||||||
@@ -207,13 +207,6 @@ class ConfirmEmailView(SuccessMessageMixin, TemplateView):
|
|||||||
messages.error(request, _("Verification key does not exist"))
|
messages.error(request, _("Verification key does not exist"))
|
||||||
return HttpResponseRedirect(reverse_lazy("profile"))
|
return HttpResponseRedirect(reverse_lazy("profile"))
|
||||||
|
|
||||||
if ConfirmedEmail.objects.filter(email=unconfirmed.email).count() > 0:
|
|
||||||
messages.error(
|
|
||||||
request,
|
|
||||||
_("This mail address has been taken already and cannot be confirmed"),
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(reverse_lazy("profile"))
|
|
||||||
|
|
||||||
# TODO: Check for a reasonable expiration time in unconfirmed email
|
# TODO: Check for a reasonable expiration time in unconfirmed email
|
||||||
|
|
||||||
(confirmed_id, external_photos) = ConfirmedEmail.objects.create_confirmed_email(
|
(confirmed_id, external_photos) = ConfirmedEmail.objects.create_confirmed_email(
|
||||||
|
|||||||
@@ -48,109 +48,16 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
password=self.password,
|
password=self.password,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_check_mail(self):
|
def test_check(self):
|
||||||
"""
|
"""
|
||||||
Test check page
|
Test check page
|
||||||
"""
|
"""
|
||||||
self.login()
|
|
||||||
response = self.client.get(reverse("tools_check"))
|
response = self.client.get(reverse("tools_check"))
|
||||||
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
||||||
response = self.client.post(
|
|
||||||
reverse("tools_check"),
|
|
||||||
data={"mail": "test@test.com", "size": "85"},
|
|
||||||
follow=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertContains(
|
|
||||||
response,
|
|
||||||
'value="test@test.com"',
|
|
||||||
1,
|
|
||||||
200,
|
|
||||||
"Value not set again!?",
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertContains(
|
|
||||||
response,
|
|
||||||
"b642b4217b34b1e8d3bd915fc65c4452",
|
|
||||||
3,
|
|
||||||
200,
|
|
||||||
"Wrong md5 hash!?",
|
|
||||||
)
|
|
||||||
self.assertContains(
|
|
||||||
response,
|
|
||||||
"f660ab912ec121d1b1e928a0bb4bc61b15f5ad44d5efdc4e1c92a25e99b8e44a",
|
|
||||||
3,
|
|
||||||
200,
|
|
||||||
"Wrong sha256 hash!?",
|
|
||||||
)
|
|
||||||
self.assertContains(
|
|
||||||
response,
|
|
||||||
'value="85"',
|
|
||||||
1,
|
|
||||||
200,
|
|
||||||
"Size should be set based on post params!?",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_check_openid(self):
|
|
||||||
"""
|
|
||||||
Test check page
|
|
||||||
"""
|
|
||||||
self.login()
|
|
||||||
response = self.client.get(reverse("tools_check"))
|
|
||||||
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("tools_check"),
|
|
||||||
data={"openid": "https://test.com", "size": "85"},
|
|
||||||
follow=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertContains(
|
|
||||||
response,
|
|
||||||
'value="https://test.com"',
|
|
||||||
1,
|
|
||||||
200,
|
|
||||||
"Value not set again!?",
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertContains(
|
|
||||||
response,
|
|
||||||
"396936bd0bf0603d6784b65d03e96dae90566c36b62661f28d4116c516524bcc",
|
|
||||||
3,
|
|
||||||
200,
|
|
||||||
"Wrong sha256 hash!?",
|
|
||||||
)
|
|
||||||
self.assertContains(
|
|
||||||
response,
|
|
||||||
'value="85"',
|
|
||||||
1,
|
|
||||||
200,
|
|
||||||
"Size should be set based on post params!?",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_check_domain(self):
|
def test_check_domain(self):
|
||||||
"""
|
"""
|
||||||
Test check domain page
|
Test check domain page
|
||||||
"""
|
"""
|
||||||
self.login()
|
|
||||||
response = self.client.get(reverse("tools_check_domain"))
|
response = self.client.get(reverse("tools_check_domain"))
|
||||||
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
||||||
response = self.client.post(
|
|
||||||
reverse("tools_check_domain"),
|
|
||||||
data={"domain": "linux-kernel.at"},
|
|
||||||
follow=True,
|
|
||||||
)
|
|
||||||
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
|
||||||
self.assertContains(
|
|
||||||
response,
|
|
||||||
"http://avatars.linux-kernel.at",
|
|
||||||
2,
|
|
||||||
200,
|
|
||||||
"Not responing with right URL!?",
|
|
||||||
)
|
|
||||||
self.assertContains(
|
|
||||||
response,
|
|
||||||
"https://avatars.linux-kernel.at",
|
|
||||||
2,
|
|
||||||
200,
|
|
||||||
"Not responing with right URL!?",
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
[build-system]
|
|
||||||
requires = ['setuptools>=40.8.0', 'wheel']
|
|
||||||
build-backend = 'setuptools.build_meta:__legacy__'
|
|
||||||
@@ -23,6 +23,7 @@ notsetuptools
|
|||||||
Pillow
|
Pillow
|
||||||
pip
|
pip
|
||||||
psycopg2-binary
|
psycopg2-binary
|
||||||
|
py-avataaars
|
||||||
py3dns
|
py3dns
|
||||||
pydocstyle
|
pydocstyle
|
||||||
pyLibravatar
|
pyLibravatar
|
||||||
|
|||||||
33
setup.cfg
33
setup.cfg
@@ -1,33 +0,0 @@
|
|||||||
[metadata]
|
|
||||||
name = libravatar
|
|
||||||
version = 1.7.0
|
|
||||||
description = A Django application implementing libravatar.org
|
|
||||||
long_description = file: README.md
|
|
||||||
url = https://libravatar.org
|
|
||||||
author = Oliver Falk
|
|
||||||
author_email = oliver@linux-kernel.at
|
|
||||||
license = GPLv3
|
|
||||||
classifiers =
|
|
||||||
Environment :: Web Environment
|
|
||||||
Framework :: Django
|
|
||||||
Framework :: Django :: 3.2
|
|
||||||
Framework :: Django :: 4.0
|
|
||||||
Framework :: Django :: 4.1
|
|
||||||
License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
||||||
Operating System :: OS Independent
|
|
||||||
Programming Language :: Python
|
|
||||||
Programming Language :: Python :: 3
|
|
||||||
Programming Language :: Python :: 3 :: Only
|
|
||||||
Programming Language :: Python :: 3.8
|
|
||||||
Programming Language :: Python :: 3.9
|
|
||||||
Programming Language :: Python :: 3.10
|
|
||||||
Programming Language :: Python :: 3.11
|
|
||||||
Topic :: Internet :: WWW/HTTP
|
|
||||||
Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
||||||
|
|
||||||
[options]
|
|
||||||
include_package_data = true
|
|
||||||
packages = find:
|
|
||||||
python_requires = >=3.8
|
|
||||||
install_requires =
|
|
||||||
Django >= 3.2
|
|
||||||
Reference in New Issue
Block a user