arthexis 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of arthexis might be problematic. Click here for more details.
- arthexis-0.1.3.dist-info/METADATA +126 -0
- arthexis-0.1.3.dist-info/RECORD +73 -0
- arthexis-0.1.3.dist-info/WHEEL +5 -0
- arthexis-0.1.3.dist-info/licenses/LICENSE +21 -0
- arthexis-0.1.3.dist-info/top_level.txt +5 -0
- config/__init__.py +6 -0
- config/active_app.py +15 -0
- config/asgi.py +29 -0
- config/auth_app.py +8 -0
- config/celery.py +19 -0
- config/context_processors.py +68 -0
- config/loadenv.py +11 -0
- config/logging.py +43 -0
- config/middleware.py +25 -0
- config/offline.py +47 -0
- config/settings.py +374 -0
- config/urls.py +91 -0
- config/wsgi.py +17 -0
- core/__init__.py +0 -0
- core/admin.py +830 -0
- core/apps.py +67 -0
- core/backends.py +82 -0
- core/entity.py +97 -0
- core/environment.py +43 -0
- core/fields.py +70 -0
- core/lcd_screen.py +77 -0
- core/middleware.py +34 -0
- core/models.py +1277 -0
- core/notifications.py +95 -0
- core/release.py +451 -0
- core/system.py +111 -0
- core/tasks.py +100 -0
- core/tests.py +483 -0
- core/urls.py +11 -0
- core/user_data.py +333 -0
- core/views.py +431 -0
- nodes/__init__.py +0 -0
- nodes/actions.py +72 -0
- nodes/admin.py +347 -0
- nodes/apps.py +76 -0
- nodes/lcd.py +151 -0
- nodes/models.py +577 -0
- nodes/tasks.py +50 -0
- nodes/tests.py +1072 -0
- nodes/urls.py +13 -0
- nodes/utils.py +62 -0
- nodes/views.py +262 -0
- ocpp/__init__.py +0 -0
- ocpp/admin.py +392 -0
- ocpp/apps.py +24 -0
- ocpp/consumers.py +267 -0
- ocpp/evcs.py +911 -0
- ocpp/models.py +300 -0
- ocpp/routing.py +9 -0
- ocpp/simulator.py +357 -0
- ocpp/store.py +175 -0
- ocpp/tasks.py +27 -0
- ocpp/test_export_import.py +129 -0
- ocpp/test_rfid.py +345 -0
- ocpp/tests.py +1229 -0
- ocpp/transactions_io.py +119 -0
- ocpp/urls.py +17 -0
- ocpp/views.py +359 -0
- pages/__init__.py +0 -0
- pages/admin.py +231 -0
- pages/apps.py +10 -0
- pages/checks.py +41 -0
- pages/context_processors.py +72 -0
- pages/models.py +224 -0
- pages/tests.py +628 -0
- pages/urls.py +17 -0
- pages/utils.py +13 -0
- pages/views.py +191 -0
pages/views.py
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
from django.contrib.auth import get_user_model, login
|
|
6
|
+
from django.contrib.auth.tokens import default_token_generator
|
|
7
|
+
from django.contrib.auth.views import LoginView
|
|
8
|
+
from django import forms
|
|
9
|
+
from utils.sites import get_site
|
|
10
|
+
from django.http import HttpResponse
|
|
11
|
+
from django.shortcuts import redirect, render
|
|
12
|
+
from nodes.models import Node
|
|
13
|
+
from django.urls import reverse
|
|
14
|
+
from django.utils import translation
|
|
15
|
+
from django.utils.encoding import force_bytes, force_str
|
|
16
|
+
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
|
|
17
|
+
from django.core.mail import send_mail
|
|
18
|
+
from django.utils.translation import gettext as _
|
|
19
|
+
from django.views.decorators.csrf import ensure_csrf_cookie
|
|
20
|
+
|
|
21
|
+
import markdown
|
|
22
|
+
from pages.utils import landing
|
|
23
|
+
from .models import Module
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@landing("Home")
|
|
30
|
+
def index(request):
|
|
31
|
+
site = get_site(request)
|
|
32
|
+
if site:
|
|
33
|
+
try:
|
|
34
|
+
landing = site.badge.landing_override
|
|
35
|
+
except Exception:
|
|
36
|
+
landing = None
|
|
37
|
+
if landing:
|
|
38
|
+
return redirect(landing.path)
|
|
39
|
+
node = Node.get_local()
|
|
40
|
+
role = node.role if node else None
|
|
41
|
+
app = (
|
|
42
|
+
Module.objects.filter(node_role=role, is_default=True)
|
|
43
|
+
.select_related("application")
|
|
44
|
+
.first()
|
|
45
|
+
)
|
|
46
|
+
app_slug = app.path.strip("/") if app else ""
|
|
47
|
+
readme_base = Path(settings.BASE_DIR) / app_slug if app_slug else Path(settings.BASE_DIR)
|
|
48
|
+
lang = translation.get_language() or ""
|
|
49
|
+
readme_file = readme_base / "README.md"
|
|
50
|
+
if lang:
|
|
51
|
+
localized = readme_base / f"README.{lang}.md"
|
|
52
|
+
if not localized.exists():
|
|
53
|
+
short = lang.split("-")[0]
|
|
54
|
+
localized = readme_base / f"README.{short}.md"
|
|
55
|
+
if localized.exists():
|
|
56
|
+
readme_file = localized
|
|
57
|
+
if not readme_file.exists():
|
|
58
|
+
readme_file = Path(settings.BASE_DIR) / "README.md"
|
|
59
|
+
text = readme_file.read_text(encoding="utf-8")
|
|
60
|
+
md = markdown.Markdown(extensions=["toc", "tables"])
|
|
61
|
+
html = md.convert(text)
|
|
62
|
+
toc_html = md.toc
|
|
63
|
+
if toc_html.strip().startswith('<div class="toc">'):
|
|
64
|
+
toc_html = toc_html.strip()[len('<div class="toc">') :]
|
|
65
|
+
if toc_html.endswith("</div>"):
|
|
66
|
+
toc_html = toc_html[: -len("</div>")]
|
|
67
|
+
toc_html = toc_html.strip()
|
|
68
|
+
title = "README" if readme_file.name.startswith("README") else readme_file.stem
|
|
69
|
+
context = {"content": html, "title": title, "toc": toc_html}
|
|
70
|
+
return render(request, "pages/readme.html", context)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def sitemap(request):
|
|
74
|
+
site = get_site(request)
|
|
75
|
+
node = Node.get_local()
|
|
76
|
+
role = node.role if node else None
|
|
77
|
+
applications = Module.objects.filter(node_role=role)
|
|
78
|
+
base = request.build_absolute_uri("/").rstrip("/")
|
|
79
|
+
lines = [
|
|
80
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
81
|
+
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
|
82
|
+
]
|
|
83
|
+
seen = set()
|
|
84
|
+
for app in applications:
|
|
85
|
+
loc = f"{base}{app.path}"
|
|
86
|
+
if loc not in seen:
|
|
87
|
+
seen.add(loc)
|
|
88
|
+
lines.append(f" <url><loc>{loc}</loc></url>")
|
|
89
|
+
lines.append("</urlset>")
|
|
90
|
+
return HttpResponse("\n".join(lines), content_type="application/xml")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class CustomLoginView(LoginView):
|
|
95
|
+
"""Login view that redirects staff to the admin."""
|
|
96
|
+
|
|
97
|
+
template_name = "pages/login.html"
|
|
98
|
+
|
|
99
|
+
def dispatch(self, request, *args, **kwargs):
|
|
100
|
+
if request.user.is_authenticated:
|
|
101
|
+
return redirect(self.get_success_url())
|
|
102
|
+
return super().dispatch(request, *args, **kwargs)
|
|
103
|
+
|
|
104
|
+
def get_success_url(self):
|
|
105
|
+
redirect_url = self.get_redirect_url()
|
|
106
|
+
if redirect_url:
|
|
107
|
+
return redirect_url
|
|
108
|
+
if self.request.user.is_staff:
|
|
109
|
+
return reverse("admin:index")
|
|
110
|
+
return "/"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
login_view = CustomLoginView.as_view()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class InvitationRequestForm(forms.Form):
|
|
117
|
+
email = forms.EmailField()
|
|
118
|
+
|
|
119
|
+
@ensure_csrf_cookie
|
|
120
|
+
def request_invite(request):
|
|
121
|
+
form = InvitationRequestForm(request.POST if request.method == "POST" else None)
|
|
122
|
+
sent = False
|
|
123
|
+
if request.method == "POST" and form.is_valid():
|
|
124
|
+
email = form.cleaned_data["email"]
|
|
125
|
+
User = get_user_model()
|
|
126
|
+
for user in User.objects.filter(email__iexact=email):
|
|
127
|
+
uid = urlsafe_base64_encode(force_bytes(user.pk))
|
|
128
|
+
token = default_token_generator.make_token(user)
|
|
129
|
+
link = request.build_absolute_uri(
|
|
130
|
+
reverse("pages:invitation-login", args=[uid, token])
|
|
131
|
+
)
|
|
132
|
+
subject = _("Your invitation link")
|
|
133
|
+
body = _(
|
|
134
|
+
"Use the following link to access your account: %(link)s"
|
|
135
|
+
) % {"link": link}
|
|
136
|
+
try:
|
|
137
|
+
node = Node.get_local()
|
|
138
|
+
if node:
|
|
139
|
+
node.send_mail(subject, body, [email])
|
|
140
|
+
else:
|
|
141
|
+
send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [email])
|
|
142
|
+
except Exception:
|
|
143
|
+
logger.exception("Failed to send invitation email to %s", email)
|
|
144
|
+
sent = True
|
|
145
|
+
return render(request, "pages/request_invite.html", {"form": form, "sent": sent})
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class InvitationPasswordForm(forms.Form):
|
|
149
|
+
new_password1 = forms.CharField(
|
|
150
|
+
widget=forms.PasswordInput, required=False, label=_("New password")
|
|
151
|
+
)
|
|
152
|
+
new_password2 = forms.CharField(
|
|
153
|
+
widget=forms.PasswordInput, required=False, label=_("Confirm password")
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def clean(self):
|
|
157
|
+
cleaned = super().clean()
|
|
158
|
+
p1 = cleaned.get("new_password1")
|
|
159
|
+
p2 = cleaned.get("new_password2")
|
|
160
|
+
if p1 or p2:
|
|
161
|
+
if not p1 or not p2 or p1 != p2:
|
|
162
|
+
raise forms.ValidationError(_("Passwords do not match"))
|
|
163
|
+
return cleaned
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def invitation_login(request, uidb64, token):
|
|
167
|
+
User = get_user_model()
|
|
168
|
+
try:
|
|
169
|
+
uid = force_str(urlsafe_base64_decode(uidb64))
|
|
170
|
+
user = User.objects.get(pk=uid)
|
|
171
|
+
except Exception:
|
|
172
|
+
user = None
|
|
173
|
+
if user is None or not default_token_generator.check_token(user, token):
|
|
174
|
+
return HttpResponse(_("Invalid invitation link"), status=400)
|
|
175
|
+
form = InvitationPasswordForm(request.POST if request.method == "POST" else None)
|
|
176
|
+
if request.method == "POST" and form.is_valid():
|
|
177
|
+
password = form.cleaned_data.get("new_password1")
|
|
178
|
+
if password:
|
|
179
|
+
user.set_password(password)
|
|
180
|
+
user.is_active = True
|
|
181
|
+
user.save()
|
|
182
|
+
login(request, user, backend="core.backends.LocalhostAdminBackend")
|
|
183
|
+
return redirect(reverse("admin:index") if user.is_staff else "/")
|
|
184
|
+
return render(request, "pages/invitation_login.html", {"form": form})
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def csrf_failure(request, reason=""):
|
|
188
|
+
"""Custom CSRF failure view with a friendly message."""
|
|
189
|
+
logger.warning("CSRF failure on %s: %s", request.path, reason)
|
|
190
|
+
return render(request, "pages/csrf_failure.html", status=403)
|
|
191
|
+
|