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.

Files changed (73) hide show
  1. arthexis-0.1.3.dist-info/METADATA +126 -0
  2. arthexis-0.1.3.dist-info/RECORD +73 -0
  3. arthexis-0.1.3.dist-info/WHEEL +5 -0
  4. arthexis-0.1.3.dist-info/licenses/LICENSE +21 -0
  5. arthexis-0.1.3.dist-info/top_level.txt +5 -0
  6. config/__init__.py +6 -0
  7. config/active_app.py +15 -0
  8. config/asgi.py +29 -0
  9. config/auth_app.py +8 -0
  10. config/celery.py +19 -0
  11. config/context_processors.py +68 -0
  12. config/loadenv.py +11 -0
  13. config/logging.py +43 -0
  14. config/middleware.py +25 -0
  15. config/offline.py +47 -0
  16. config/settings.py +374 -0
  17. config/urls.py +91 -0
  18. config/wsgi.py +17 -0
  19. core/__init__.py +0 -0
  20. core/admin.py +830 -0
  21. core/apps.py +67 -0
  22. core/backends.py +82 -0
  23. core/entity.py +97 -0
  24. core/environment.py +43 -0
  25. core/fields.py +70 -0
  26. core/lcd_screen.py +77 -0
  27. core/middleware.py +34 -0
  28. core/models.py +1277 -0
  29. core/notifications.py +95 -0
  30. core/release.py +451 -0
  31. core/system.py +111 -0
  32. core/tasks.py +100 -0
  33. core/tests.py +483 -0
  34. core/urls.py +11 -0
  35. core/user_data.py +333 -0
  36. core/views.py +431 -0
  37. nodes/__init__.py +0 -0
  38. nodes/actions.py +72 -0
  39. nodes/admin.py +347 -0
  40. nodes/apps.py +76 -0
  41. nodes/lcd.py +151 -0
  42. nodes/models.py +577 -0
  43. nodes/tasks.py +50 -0
  44. nodes/tests.py +1072 -0
  45. nodes/urls.py +13 -0
  46. nodes/utils.py +62 -0
  47. nodes/views.py +262 -0
  48. ocpp/__init__.py +0 -0
  49. ocpp/admin.py +392 -0
  50. ocpp/apps.py +24 -0
  51. ocpp/consumers.py +267 -0
  52. ocpp/evcs.py +911 -0
  53. ocpp/models.py +300 -0
  54. ocpp/routing.py +9 -0
  55. ocpp/simulator.py +357 -0
  56. ocpp/store.py +175 -0
  57. ocpp/tasks.py +27 -0
  58. ocpp/test_export_import.py +129 -0
  59. ocpp/test_rfid.py +345 -0
  60. ocpp/tests.py +1229 -0
  61. ocpp/transactions_io.py +119 -0
  62. ocpp/urls.py +17 -0
  63. ocpp/views.py +359 -0
  64. pages/__init__.py +0 -0
  65. pages/admin.py +231 -0
  66. pages/apps.py +10 -0
  67. pages/checks.py +41 -0
  68. pages/context_processors.py +72 -0
  69. pages/models.py +224 -0
  70. pages/tests.py +628 -0
  71. pages/urls.py +17 -0
  72. pages/utils.py +13 -0
  73. 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
+