arthexis 0.1.12__py3-none-any.whl → 0.1.14__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.12.dist-info → arthexis-0.1.14.dist-info}/METADATA +222 -221
- arthexis-0.1.14.dist-info/RECORD +109 -0
- {arthexis-0.1.12.dist-info → arthexis-0.1.14.dist-info}/licenses/LICENSE +674 -674
- config/__init__.py +5 -5
- config/active_app.py +15 -15
- config/asgi.py +43 -29
- config/auth_app.py +7 -7
- config/celery.py +32 -25
- config/context_processors.py +67 -69
- config/horologia_app.py +7 -7
- config/loadenv.py +11 -11
- config/logging.py +59 -48
- config/middleware.py +25 -25
- config/offline.py +49 -49
- config/settings.py +691 -716
- config/settings_helpers.py +109 -0
- config/urls.py +171 -166
- config/wsgi.py +17 -17
- core/admin.py +3771 -2772
- core/admin_history.py +50 -50
- core/admindocs.py +151 -151
- core/apps.py +356 -272
- core/auto_upgrade.py +57 -57
- core/backends.py +265 -236
- core/changelog.py +342 -0
- core/entity.py +133 -133
- core/environment.py +61 -61
- core/fields.py +168 -168
- core/form_fields.py +75 -0
- core/github_helper.py +188 -25
- core/github_issues.py +178 -172
- core/github_repos.py +72 -0
- core/lcd_screen.py +78 -78
- core/liveupdate.py +25 -25
- core/log_paths.py +100 -100
- core/mailer.py +85 -85
- core/middleware.py +91 -91
- core/models.py +3609 -2672
- core/notifications.py +105 -105
- core/public_wifi.py +267 -227
- core/reference_utils.py +108 -108
- core/release.py +721 -350
- core/rfid_import_export.py +113 -0
- core/sigil_builder.py +149 -149
- core/sigil_context.py +20 -20
- core/sigil_resolver.py +315 -315
- core/system.py +752 -493
- core/tasks.py +408 -394
- core/temp_passwords.py +181 -181
- core/test_system_info.py +186 -139
- core/tests.py +2095 -1511
- core/tests_liveupdate.py +17 -17
- core/urls.py +11 -11
- core/user_data.py +641 -633
- core/views.py +2175 -1382
- core/widgets.py +213 -51
- core/workgroup_urls.py +17 -17
- core/workgroup_views.py +94 -94
- nodes/admin.py +1720 -898
- nodes/apps.py +87 -70
- nodes/backends.py +160 -160
- nodes/dns.py +203 -203
- nodes/feature_checks.py +133 -133
- nodes/lcd.py +165 -165
- nodes/models.py +1737 -1416
- nodes/reports.py +411 -411
- nodes/rfid_sync.py +195 -0
- nodes/signals.py +18 -0
- nodes/tasks.py +46 -46
- nodes/tests.py +3810 -2497
- nodes/urls.py +15 -13
- nodes/utils.py +121 -105
- nodes/views.py +683 -451
- ocpp/admin.py +948 -804
- ocpp/apps.py +25 -25
- ocpp/consumers.py +1565 -1342
- ocpp/evcs.py +844 -931
- ocpp/evcs_discovery.py +158 -158
- ocpp/models.py +917 -915
- ocpp/reference_utils.py +42 -42
- ocpp/routing.py +11 -9
- ocpp/simulator.py +745 -724
- ocpp/status_display.py +26 -0
- ocpp/store.py +601 -541
- ocpp/tasks.py +31 -31
- ocpp/test_export_import.py +130 -130
- ocpp/test_rfid.py +913 -702
- ocpp/tests.py +4445 -3485
- ocpp/transactions_io.py +189 -179
- ocpp/urls.py +50 -50
- ocpp/views.py +1479 -1151
- pages/admin.py +708 -536
- pages/apps.py +10 -10
- pages/checks.py +40 -40
- pages/context_processors.py +127 -119
- pages/defaults.py +13 -13
- pages/forms.py +198 -169
- pages/middleware.py +205 -153
- pages/models.py +607 -426
- pages/tests.py +2612 -2083
- pages/urls.py +25 -25
- pages/utils.py +12 -12
- pages/views.py +1165 -1120
- arthexis-0.1.12.dist-info/RECORD +0 -102
- nodes/actions.py +0 -70
- {arthexis-0.1.12.dist-info → arthexis-0.1.14.dist-info}/WHEEL +0 -0
- {arthexis-0.1.12.dist-info → arthexis-0.1.14.dist-info}/top_level.txt +0 -0
ocpp/tasks.py
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from datetime import timedelta
|
|
3
|
-
|
|
4
|
-
from celery import shared_task
|
|
5
|
-
from django.utils import timezone
|
|
6
|
-
from django.db.models import Q
|
|
7
|
-
|
|
8
|
-
from .models import MeterValue
|
|
9
|
-
|
|
10
|
-
logger = logging.getLogger(__name__)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@shared_task
|
|
14
|
-
def purge_meter_values() -> int:
|
|
15
|
-
"""Delete meter values older than 7 days.
|
|
16
|
-
|
|
17
|
-
Values tied to transactions without a recorded meter_stop are preserved so
|
|
18
|
-
that ongoing or incomplete sessions retain their energy data.
|
|
19
|
-
Returns the number of deleted rows.
|
|
20
|
-
"""
|
|
21
|
-
cutoff = timezone.now() - timedelta(days=7)
|
|
22
|
-
qs = MeterValue.objects.filter(timestamp__lt=cutoff).filter(
|
|
23
|
-
Q(transaction__isnull=True) | Q(transaction__meter_stop__isnull=False)
|
|
24
|
-
)
|
|
25
|
-
deleted, _ = qs.delete()
|
|
26
|
-
logger.info("Purged %s meter values", deleted)
|
|
27
|
-
return deleted
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# Backwards compatibility alias
|
|
31
|
-
purge_meter_readings = purge_meter_values
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import timedelta
|
|
3
|
+
|
|
4
|
+
from celery import shared_task
|
|
5
|
+
from django.utils import timezone
|
|
6
|
+
from django.db.models import Q
|
|
7
|
+
|
|
8
|
+
from .models import MeterValue
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@shared_task
|
|
14
|
+
def purge_meter_values() -> int:
|
|
15
|
+
"""Delete meter values older than 7 days.
|
|
16
|
+
|
|
17
|
+
Values tied to transactions without a recorded meter_stop are preserved so
|
|
18
|
+
that ongoing or incomplete sessions retain their energy data.
|
|
19
|
+
Returns the number of deleted rows.
|
|
20
|
+
"""
|
|
21
|
+
cutoff = timezone.now() - timedelta(days=7)
|
|
22
|
+
qs = MeterValue.objects.filter(timestamp__lt=cutoff).filter(
|
|
23
|
+
Q(transaction__isnull=True) | Q(transaction__meter_stop__isnull=False)
|
|
24
|
+
)
|
|
25
|
+
deleted, _ = qs.delete()
|
|
26
|
+
logger.info("Purged %s meter values", deleted)
|
|
27
|
+
return deleted
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Backwards compatibility alias
|
|
31
|
+
purge_meter_readings = purge_meter_values
|
ocpp/test_export_import.py
CHANGED
|
@@ -1,130 +1,130 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import os
|
|
3
|
-
import tempfile
|
|
4
|
-
from datetime import timedelta
|
|
5
|
-
import io
|
|
6
|
-
|
|
7
|
-
from django.core.management import call_command
|
|
8
|
-
from django.test import TestCase
|
|
9
|
-
from django.utils import timezone
|
|
10
|
-
from django.urls import reverse
|
|
11
|
-
from django.contrib.auth import get_user_model
|
|
12
|
-
|
|
13
|
-
from ocpp.models import Charger, Transaction, MeterValue
|
|
14
|
-
from core.models import EnergyAccount
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class TransactionExportImportTests(TestCase):
|
|
18
|
-
def setUp(self):
|
|
19
|
-
self.account = EnergyAccount.objects.create(name="ACC")
|
|
20
|
-
self.ch1 = Charger.objects.create(charger_id="C1")
|
|
21
|
-
self.ch2 = Charger.objects.create(charger_id="C2")
|
|
22
|
-
now = timezone.now()
|
|
23
|
-
self.tx_old = Transaction.objects.create(
|
|
24
|
-
charger=self.ch1,
|
|
25
|
-
account=self.account,
|
|
26
|
-
start_time=now - timedelta(days=5),
|
|
27
|
-
)
|
|
28
|
-
self.tx_new = Transaction.objects.create(
|
|
29
|
-
charger=self.ch2,
|
|
30
|
-
start_time=now,
|
|
31
|
-
)
|
|
32
|
-
MeterValue.objects.create(
|
|
33
|
-
charger=self.ch1,
|
|
34
|
-
transaction=self.tx_old,
|
|
35
|
-
timestamp=now - timedelta(days=5),
|
|
36
|
-
energy=1,
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
def test_export_filters_and_import_creates_chargers(self):
|
|
40
|
-
with tempfile.NamedTemporaryFile(delete=False) as tmp:
|
|
41
|
-
tmp_path = tmp.name
|
|
42
|
-
|
|
43
|
-
start = (timezone.now() - timedelta(days=1)).date().isoformat()
|
|
44
|
-
call_command(
|
|
45
|
-
"export_transactions",
|
|
46
|
-
tmp_path,
|
|
47
|
-
"--start",
|
|
48
|
-
start,
|
|
49
|
-
"--chargers",
|
|
50
|
-
self.ch2.charger_id,
|
|
51
|
-
)
|
|
52
|
-
with open(tmp_path, "r", encoding="utf-8") as fh:
|
|
53
|
-
data = json.load(fh)
|
|
54
|
-
self.assertEqual(len(data["transactions"]), 1)
|
|
55
|
-
self.assertEqual(data["transactions"][0]["charger"], "C2")
|
|
56
|
-
|
|
57
|
-
MeterValue.objects.all().delete()
|
|
58
|
-
Transaction.objects.all().delete()
|
|
59
|
-
Charger.objects.all().delete()
|
|
60
|
-
|
|
61
|
-
call_command("import_transactions", tmp_path)
|
|
62
|
-
os.remove(tmp_path)
|
|
63
|
-
|
|
64
|
-
self.assertTrue(Charger.objects.filter(charger_id="C2").exists())
|
|
65
|
-
self.assertEqual(Transaction.objects.count(), 1)
|
|
66
|
-
tx = Transaction.objects.first()
|
|
67
|
-
self.assertEqual(tx.charger.charger_id, "C2")
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
class TransactionAdminExportImportTests(TestCase):
|
|
71
|
-
def setUp(self):
|
|
72
|
-
self.account = EnergyAccount.objects.create(name="ACC")
|
|
73
|
-
self.ch1 = Charger.objects.create(charger_id="C1")
|
|
74
|
-
self.ch2 = Charger.objects.create(charger_id="C2")
|
|
75
|
-
now = timezone.now()
|
|
76
|
-
Transaction.objects.create(
|
|
77
|
-
charger=self.ch1,
|
|
78
|
-
account=self.account,
|
|
79
|
-
start_time=now - timedelta(days=2),
|
|
80
|
-
)
|
|
81
|
-
Transaction.objects.create(
|
|
82
|
-
charger=self.ch2,
|
|
83
|
-
start_time=now,
|
|
84
|
-
)
|
|
85
|
-
User = get_user_model()
|
|
86
|
-
self.admin = User.objects.create_superuser(
|
|
87
|
-
username="txadmin", email="txadmin@example.com", password="pwd"
|
|
88
|
-
)
|
|
89
|
-
self.client.force_login(self.admin)
|
|
90
|
-
|
|
91
|
-
def test_admin_export_filters(self):
|
|
92
|
-
url = reverse("admin:ocpp_transaction_export")
|
|
93
|
-
start = (timezone.now() - timedelta(days=1)).isoformat()
|
|
94
|
-
response = self.client.post(
|
|
95
|
-
url,
|
|
96
|
-
{"start": start, "chargers": [self.ch2.pk]},
|
|
97
|
-
)
|
|
98
|
-
self.assertEqual(response.status_code, 200)
|
|
99
|
-
data = json.loads(response.content)
|
|
100
|
-
self.assertEqual(len(data["transactions"]), 1)
|
|
101
|
-
self.assertEqual(data["transactions"][0]["charger"], "C2")
|
|
102
|
-
|
|
103
|
-
def test_admin_import_creates_charger(self):
|
|
104
|
-
url = reverse("admin:ocpp_transaction_import")
|
|
105
|
-
payload = {
|
|
106
|
-
"chargers": [
|
|
107
|
-
{"charger_id": "C9", "connector_id": 1, "require_rfid": False}
|
|
108
|
-
],
|
|
109
|
-
"transactions": [
|
|
110
|
-
{
|
|
111
|
-
"charger": "C9",
|
|
112
|
-
"account": None,
|
|
113
|
-
"rfid": "",
|
|
114
|
-
"vin": "",
|
|
115
|
-
"meter_start": 0,
|
|
116
|
-
"meter_stop": 0,
|
|
117
|
-
"start_time": timezone.now().isoformat(),
|
|
118
|
-
"stop_time": None,
|
|
119
|
-
"meter_values": [],
|
|
120
|
-
}
|
|
121
|
-
],
|
|
122
|
-
}
|
|
123
|
-
json_file = io.StringIO(json.dumps(payload))
|
|
124
|
-
json_file.name = "tx.json"
|
|
125
|
-
response = self.client.post(url, {"file": json_file})
|
|
126
|
-
self.assertEqual(response.status_code, 302)
|
|
127
|
-
self.assertTrue(Charger.objects.filter(charger_id="C9").exists())
|
|
128
|
-
self.assertEqual(
|
|
129
|
-
Transaction.objects.filter(charger__charger_id="C9").count(), 1
|
|
130
|
-
)
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
from datetime import timedelta
|
|
5
|
+
import io
|
|
6
|
+
|
|
7
|
+
from django.core.management import call_command
|
|
8
|
+
from django.test import TestCase
|
|
9
|
+
from django.utils import timezone
|
|
10
|
+
from django.urls import reverse
|
|
11
|
+
from django.contrib.auth import get_user_model
|
|
12
|
+
|
|
13
|
+
from ocpp.models import Charger, Transaction, MeterValue
|
|
14
|
+
from core.models import EnergyAccount
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TransactionExportImportTests(TestCase):
|
|
18
|
+
def setUp(self):
|
|
19
|
+
self.account = EnergyAccount.objects.create(name="ACC")
|
|
20
|
+
self.ch1 = Charger.objects.create(charger_id="C1")
|
|
21
|
+
self.ch2 = Charger.objects.create(charger_id="C2")
|
|
22
|
+
now = timezone.now()
|
|
23
|
+
self.tx_old = Transaction.objects.create(
|
|
24
|
+
charger=self.ch1,
|
|
25
|
+
account=self.account,
|
|
26
|
+
start_time=now - timedelta(days=5),
|
|
27
|
+
)
|
|
28
|
+
self.tx_new = Transaction.objects.create(
|
|
29
|
+
charger=self.ch2,
|
|
30
|
+
start_time=now,
|
|
31
|
+
)
|
|
32
|
+
MeterValue.objects.create(
|
|
33
|
+
charger=self.ch1,
|
|
34
|
+
transaction=self.tx_old,
|
|
35
|
+
timestamp=now - timedelta(days=5),
|
|
36
|
+
energy=1,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def test_export_filters_and_import_creates_chargers(self):
|
|
40
|
+
with tempfile.NamedTemporaryFile(delete=False) as tmp:
|
|
41
|
+
tmp_path = tmp.name
|
|
42
|
+
|
|
43
|
+
start = (timezone.now() - timedelta(days=1)).date().isoformat()
|
|
44
|
+
call_command(
|
|
45
|
+
"export_transactions",
|
|
46
|
+
tmp_path,
|
|
47
|
+
"--start",
|
|
48
|
+
start,
|
|
49
|
+
"--chargers",
|
|
50
|
+
self.ch2.charger_id,
|
|
51
|
+
)
|
|
52
|
+
with open(tmp_path, "r", encoding="utf-8") as fh:
|
|
53
|
+
data = json.load(fh)
|
|
54
|
+
self.assertEqual(len(data["transactions"]), 1)
|
|
55
|
+
self.assertEqual(data["transactions"][0]["charger"], "C2")
|
|
56
|
+
|
|
57
|
+
MeterValue.objects.all().delete()
|
|
58
|
+
Transaction.objects.all().delete()
|
|
59
|
+
Charger.objects.all().delete()
|
|
60
|
+
|
|
61
|
+
call_command("import_transactions", tmp_path)
|
|
62
|
+
os.remove(tmp_path)
|
|
63
|
+
|
|
64
|
+
self.assertTrue(Charger.objects.filter(charger_id="C2").exists())
|
|
65
|
+
self.assertEqual(Transaction.objects.count(), 1)
|
|
66
|
+
tx = Transaction.objects.first()
|
|
67
|
+
self.assertEqual(tx.charger.charger_id, "C2")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class TransactionAdminExportImportTests(TestCase):
|
|
71
|
+
def setUp(self):
|
|
72
|
+
self.account = EnergyAccount.objects.create(name="ACC")
|
|
73
|
+
self.ch1 = Charger.objects.create(charger_id="C1")
|
|
74
|
+
self.ch2 = Charger.objects.create(charger_id="C2")
|
|
75
|
+
now = timezone.now()
|
|
76
|
+
Transaction.objects.create(
|
|
77
|
+
charger=self.ch1,
|
|
78
|
+
account=self.account,
|
|
79
|
+
start_time=now - timedelta(days=2),
|
|
80
|
+
)
|
|
81
|
+
Transaction.objects.create(
|
|
82
|
+
charger=self.ch2,
|
|
83
|
+
start_time=now,
|
|
84
|
+
)
|
|
85
|
+
User = get_user_model()
|
|
86
|
+
self.admin = User.objects.create_superuser(
|
|
87
|
+
username="txadmin", email="txadmin@example.com", password="pwd"
|
|
88
|
+
)
|
|
89
|
+
self.client.force_login(self.admin)
|
|
90
|
+
|
|
91
|
+
def test_admin_export_filters(self):
|
|
92
|
+
url = reverse("admin:ocpp_transaction_export")
|
|
93
|
+
start = (timezone.now() - timedelta(days=1)).isoformat()
|
|
94
|
+
response = self.client.post(
|
|
95
|
+
url,
|
|
96
|
+
{"start": start, "chargers": [self.ch2.pk]},
|
|
97
|
+
)
|
|
98
|
+
self.assertEqual(response.status_code, 200)
|
|
99
|
+
data = json.loads(response.content)
|
|
100
|
+
self.assertEqual(len(data["transactions"]), 1)
|
|
101
|
+
self.assertEqual(data["transactions"][0]["charger"], "C2")
|
|
102
|
+
|
|
103
|
+
def test_admin_import_creates_charger(self):
|
|
104
|
+
url = reverse("admin:ocpp_transaction_import")
|
|
105
|
+
payload = {
|
|
106
|
+
"chargers": [
|
|
107
|
+
{"charger_id": "C9", "connector_id": 1, "require_rfid": False}
|
|
108
|
+
],
|
|
109
|
+
"transactions": [
|
|
110
|
+
{
|
|
111
|
+
"charger": "C9",
|
|
112
|
+
"account": None,
|
|
113
|
+
"rfid": "",
|
|
114
|
+
"vin": "",
|
|
115
|
+
"meter_start": 0,
|
|
116
|
+
"meter_stop": 0,
|
|
117
|
+
"start_time": timezone.now().isoformat(),
|
|
118
|
+
"stop_time": None,
|
|
119
|
+
"meter_values": [],
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
}
|
|
123
|
+
json_file = io.StringIO(json.dumps(payload))
|
|
124
|
+
json_file.name = "tx.json"
|
|
125
|
+
response = self.client.post(url, {"file": json_file})
|
|
126
|
+
self.assertEqual(response.status_code, 302)
|
|
127
|
+
self.assertTrue(Charger.objects.filter(charger_id="C9").exists())
|
|
128
|
+
self.assertEqual(
|
|
129
|
+
Transaction.objects.filter(charger__charger_id="C9").count(), 1
|
|
130
|
+
)
|