arthexis 0.1.8__py3-none-any.whl → 0.1.10__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 (84) hide show
  1. {arthexis-0.1.8.dist-info → arthexis-0.1.10.dist-info}/METADATA +87 -6
  2. arthexis-0.1.10.dist-info/RECORD +95 -0
  3. arthexis-0.1.10.dist-info/licenses/LICENSE +674 -0
  4. config/__init__.py +0 -1
  5. config/auth_app.py +0 -1
  6. config/celery.py +1 -2
  7. config/context_processors.py +1 -1
  8. config/offline.py +2 -0
  9. config/settings.py +352 -37
  10. config/urls.py +71 -6
  11. core/admin.py +1601 -200
  12. core/admin_history.py +50 -0
  13. core/admindocs.py +108 -1
  14. core/apps.py +161 -3
  15. core/auto_upgrade.py +57 -0
  16. core/backends.py +123 -7
  17. core/entity.py +62 -48
  18. core/fields.py +98 -0
  19. core/github_helper.py +25 -0
  20. core/github_issues.py +172 -0
  21. core/lcd_screen.py +1 -0
  22. core/liveupdate.py +25 -0
  23. core/log_paths.py +100 -0
  24. core/mailer.py +83 -0
  25. core/middleware.py +57 -0
  26. core/models.py +1279 -267
  27. core/notifications.py +11 -1
  28. core/public_wifi.py +227 -0
  29. core/reference_utils.py +97 -0
  30. core/release.py +27 -20
  31. core/sigil_builder.py +144 -0
  32. core/sigil_context.py +20 -0
  33. core/sigil_resolver.py +284 -0
  34. core/system.py +162 -29
  35. core/tasks.py +269 -27
  36. core/test_system_info.py +59 -1
  37. core/tests.py +644 -73
  38. core/tests_liveupdate.py +17 -0
  39. core/urls.py +2 -2
  40. core/user_data.py +425 -168
  41. core/views.py +627 -59
  42. core/widgets.py +51 -0
  43. core/workgroup_urls.py +7 -3
  44. core/workgroup_views.py +43 -6
  45. nodes/actions.py +0 -2
  46. nodes/admin.py +168 -285
  47. nodes/apps.py +9 -15
  48. nodes/backends.py +145 -0
  49. nodes/lcd.py +24 -10
  50. nodes/models.py +579 -179
  51. nodes/tasks.py +1 -5
  52. nodes/tests.py +894 -130
  53. nodes/utils.py +13 -2
  54. nodes/views.py +204 -28
  55. ocpp/admin.py +212 -63
  56. ocpp/apps.py +1 -1
  57. ocpp/consumers.py +642 -68
  58. ocpp/evcs.py +30 -10
  59. ocpp/models.py +452 -70
  60. ocpp/simulator.py +75 -11
  61. ocpp/store.py +288 -30
  62. ocpp/tasks.py +11 -7
  63. ocpp/test_export_import.py +8 -7
  64. ocpp/test_rfid.py +211 -16
  65. ocpp/tests.py +1576 -137
  66. ocpp/transactions_io.py +68 -22
  67. ocpp/urls.py +35 -2
  68. ocpp/views.py +701 -123
  69. pages/admin.py +173 -13
  70. pages/checks.py +0 -1
  71. pages/context_processors.py +39 -6
  72. pages/forms.py +131 -0
  73. pages/middleware.py +153 -0
  74. pages/models.py +37 -9
  75. pages/tests.py +1182 -42
  76. pages/urls.py +4 -0
  77. pages/utils.py +0 -1
  78. pages/views.py +844 -51
  79. arthexis-0.1.8.dist-info/RECORD +0 -80
  80. arthexis-0.1.8.dist-info/licenses/LICENSE +0 -21
  81. config/workgroup_app.py +0 -7
  82. core/checks.py +0 -29
  83. {arthexis-0.1.8.dist-info → arthexis-0.1.10.dist-info}/WHEEL +0 -0
  84. {arthexis-0.1.8.dist-info → arthexis-0.1.10.dist-info}/top_level.txt +0 -0
ocpp/test_rfid.py CHANGED
@@ -1,9 +1,16 @@
1
+ import io
1
2
  import os
3
+ import sys
4
+ import types
5
+ from pathlib import Path
2
6
  from unittest.mock import patch, MagicMock, call
3
7
 
8
+ import pytest
9
+
4
10
  os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
5
11
 
6
12
  import django
13
+
7
14
  django.setup()
8
15
 
9
16
  from django.test import SimpleTestCase, TestCase
@@ -16,9 +23,54 @@ from nodes.models import Node, NodeRole
16
23
 
17
24
  from core.models import RFID
18
25
  from ocpp.rfid.reader import read_rfid, enable_deep_read
26
+ from ocpp.rfid.detect import detect_scanner, main as detect_main
27
+ from ocpp.rfid import background_reader
28
+ from ocpp.rfid.constants import (
29
+ DEFAULT_IRQ_PIN,
30
+ DEFAULT_RST_PIN,
31
+ GPIO_PIN_MODE_BCM,
32
+ MODULE_WIRING,
33
+ SPI_BUS,
34
+ SPI_DEVICE,
35
+ )
36
+
19
37
 
38
+ pytestmark = [pytest.mark.feature("rfid-scanner")]
20
39
 
21
- class ScanNextViewTests(SimpleTestCase):
40
+
41
+ class BackgroundReaderConfigurationTests(SimpleTestCase):
42
+ def setUp(self):
43
+ background_reader._auto_detect_logged = False
44
+
45
+ def tearDown(self):
46
+ background_reader._auto_detect_logged = False
47
+
48
+ def test_is_configured_auto_detects_without_lock(self):
49
+ fake_lock = Path("/tmp/rfid-auto-detect.lock")
50
+ with (
51
+ patch("ocpp.rfid.background_reader._lock_path", return_value=fake_lock),
52
+ patch("ocpp.rfid.background_reader._has_spi_device", return_value=True),
53
+ patch(
54
+ "ocpp.rfid.background_reader._dependencies_available",
55
+ return_value=True,
56
+ ),
57
+ ):
58
+ self.assertTrue(background_reader.is_configured())
59
+
60
+ def test_is_configured_requires_dependencies(self):
61
+ fake_lock = Path("/tmp/rfid-auto-detect.lock")
62
+ with (
63
+ patch("ocpp.rfid.background_reader._lock_path", return_value=fake_lock),
64
+ patch("ocpp.rfid.background_reader._has_spi_device", return_value=True),
65
+ patch(
66
+ "ocpp.rfid.background_reader._dependencies_available",
67
+ return_value=False,
68
+ ),
69
+ ):
70
+ self.assertFalse(background_reader.is_configured())
71
+
72
+
73
+ class ScanNextViewTests(TestCase):
22
74
  @patch("config.middleware.Node.get_local", return_value=None)
23
75
  @patch("config.middleware.get_site")
24
76
  @patch(
@@ -64,6 +116,13 @@ class ReaderNotificationTests(TestCase):
64
116
  def MFRC522_Anticoll(self):
65
117
  return (self.MI_OK, [0xAB, 0xCD, 0x12, 0x34, 0x56])
66
118
 
119
+ def MFRC522_SelectTag(self, _uid):
120
+ self.select_called = True
121
+ return self.MI_OK
122
+
123
+ def MFRC522_StopCrypto1(self):
124
+ self.stop_called = True
125
+
67
126
  return MockReader()
68
127
 
69
128
  @patch("ocpp.rfid.reader.notify_async")
@@ -80,14 +139,15 @@ class ReaderNotificationTests(TestCase):
80
139
  )
81
140
  mock_get.return_value = (tag, False)
82
141
 
83
- result = read_rfid(mfrc=self._mock_reader(), cleanup=False)
142
+ reader = self._mock_reader()
143
+ result = read_rfid(mfrc=reader, cleanup=False)
84
144
  self.assertEqual(result["label_id"], 1)
85
145
  self.assertEqual(result["kind"], RFID.CLASSIC)
86
146
  self.assertEqual(result["reference"], "https://example.com")
87
147
  self.assertEqual(mock_notify.call_count, 1)
88
- mock_notify.assert_has_calls(
89
- [call("RFID 1 OK", f"{result['rfid']} B")]
90
- )
148
+ mock_notify.assert_has_calls([call("RFID 1 OK", f"{result['rfid']} B")])
149
+ self.assertTrue(getattr(reader, "select_called", False))
150
+ self.assertTrue(getattr(reader, "stop_called", False))
91
151
 
92
152
  @patch("ocpp.rfid.reader.notify_async")
93
153
  @patch("core.models.RFID.objects.get_or_create")
@@ -102,12 +162,13 @@ class ReaderNotificationTests(TestCase):
102
162
  )
103
163
  mock_get.return_value = (tag, False)
104
164
 
105
- result = read_rfid(mfrc=self._mock_reader(), cleanup=False)
165
+ reader = self._mock_reader()
166
+ result = read_rfid(mfrc=reader, cleanup=False)
106
167
  self.assertEqual(result["kind"], RFID.CLASSIC)
107
168
  self.assertEqual(mock_notify.call_count, 1)
108
- mock_notify.assert_has_calls(
109
- [call("RFID 2 BAD", f"{result['rfid']} B")]
110
- )
169
+ mock_notify.assert_has_calls([call("RFID 2 BAD", f"{result['rfid']} B")])
170
+ self.assertTrue(getattr(reader, "select_called", False))
171
+ self.assertTrue(getattr(reader, "stop_called", False))
111
172
 
112
173
 
113
174
  class CardTypeDetectionTests(TestCase):
@@ -125,12 +186,33 @@ class CardTypeDetectionTests(TestCase):
125
186
  [0x04, 0xD3, 0x2A, 0x1B, 0x5F, 0x23, 0x19],
126
187
  )
127
188
 
189
+ def MFRC522_SelectTag(self, _uid):
190
+ self.select_called = True
191
+ return self.MI_OK
192
+
193
+ def MFRC522_StopCrypto1(self):
194
+ self.stop_called = True
195
+
128
196
  return MockReader()
129
197
 
130
198
  @patch("ocpp.rfid.reader.notify_async")
131
- def test_detects_ntag215(self, _mock_notify):
132
- result = read_rfid(mfrc=self._mock_ntag_reader(), cleanup=False)
199
+ @patch("core.models.RFID.objects.get_or_create")
200
+ def test_detects_ntag215(self, mock_get, _mock_notify):
201
+ tag = MagicMock(
202
+ pk=1,
203
+ label_id=1,
204
+ allowed=True,
205
+ color="B",
206
+ released=False,
207
+ reference=None,
208
+ kind=RFID.NTAG215,
209
+ )
210
+ mock_get.return_value = (tag, True)
211
+ reader = self._mock_ntag_reader()
212
+ result = read_rfid(mfrc=reader, cleanup=False)
133
213
  self.assertEqual(result["kind"], RFID.NTAG215)
214
+ self.assertTrue(getattr(reader, "select_called", False))
215
+ self.assertTrue(getattr(reader, "stop_called", False))
134
216
 
135
217
 
136
218
  class RFIDLastSeenTests(TestCase):
@@ -145,15 +227,78 @@ class RFIDLastSeenTests(TestCase):
145
227
  def MFRC522_Anticoll(self):
146
228
  return (self.MI_OK, [0xAB, 0xCD, 0x12, 0x34])
147
229
 
230
+ def MFRC522_SelectTag(self, _uid):
231
+ self.select_called = True
232
+ return self.MI_OK
233
+
234
+ def MFRC522_StopCrypto1(self):
235
+ self.stop_called = True
236
+
148
237
  return MockReader()
149
238
 
150
239
  @patch("ocpp.rfid.reader.notify_async")
151
240
  def test_last_seen_updated_on_read(self, _mock_notify):
152
241
  tag = RFID.objects.create(rfid="ABCD1234")
153
- result = read_rfid(mfrc=self._mock_reader(), cleanup=False)
242
+ reader = self._mock_reader()
243
+ result = read_rfid(mfrc=reader, cleanup=False)
154
244
  tag.refresh_from_db()
155
245
  self.assertIsNotNone(tag.last_seen_on)
156
246
  self.assertEqual(result["kind"], RFID.CLASSIC)
247
+ self.assertTrue(getattr(reader, "select_called", False))
248
+ self.assertTrue(getattr(reader, "stop_called", False))
249
+
250
+
251
+ class RFIDDetectionScriptTests(SimpleTestCase):
252
+ @patch("ocpp.rfid.detect._ensure_django")
253
+ @patch(
254
+ "ocpp.rfid.irq_wiring_check.check_irq_pin",
255
+ return_value={"irq_pin": DEFAULT_IRQ_PIN},
256
+ )
257
+ def test_detect_scanner_success(self, mock_check, _mock_setup):
258
+ result = detect_scanner()
259
+ self.assertEqual(
260
+ result,
261
+ {
262
+ "detected": True,
263
+ "irq_pin": DEFAULT_IRQ_PIN,
264
+ },
265
+ )
266
+ mock_check.assert_called_once()
267
+
268
+ @patch("ocpp.rfid.detect._ensure_django")
269
+ @patch(
270
+ "ocpp.rfid.irq_wiring_check.check_irq_pin",
271
+ return_value={"error": "no scanner detected"},
272
+ )
273
+ def test_detect_scanner_failure(self, mock_check, _mock_setup):
274
+ result = detect_scanner()
275
+ self.assertFalse(result["detected"])
276
+ self.assertEqual(result["reason"], "no scanner detected")
277
+ mock_check.assert_called_once()
278
+
279
+ @patch(
280
+ "ocpp.rfid.detect.detect_scanner",
281
+ return_value={"detected": True, "irq_pin": DEFAULT_IRQ_PIN},
282
+ )
283
+ def test_detect_main_success_output(self, mock_detect):
284
+ buffer = io.StringIO()
285
+ with patch("sys.stdout", new=buffer):
286
+ exit_code = detect_main([])
287
+ self.assertEqual(exit_code, 0)
288
+ self.assertIn("IRQ pin", buffer.getvalue())
289
+ mock_detect.assert_called_once()
290
+
291
+ @patch(
292
+ "ocpp.rfid.detect.detect_scanner",
293
+ return_value={"detected": False, "reason": "missing hardware"},
294
+ )
295
+ def test_detect_main_failure_output(self, mock_detect):
296
+ buffer = io.StringIO()
297
+ with patch("sys.stdout", new=buffer):
298
+ exit_code = detect_main([])
299
+ self.assertEqual(exit_code, 1)
300
+ self.assertIn("missing hardware", buffer.getvalue())
301
+ mock_detect.assert_called_once()
157
302
 
158
303
 
159
304
  class RestartViewTests(SimpleTestCase):
@@ -201,9 +346,8 @@ class RFIDLandingTests(TestCase):
201
346
  app = Application.objects.create(name="Ocpp")
202
347
  module = Module.objects.create(node_role=role, application=app, path="/ocpp/")
203
348
  module.create_landings()
204
- self.assertTrue(
205
- module.landings.filter(path="/ocpp/rfid/").exists()
206
- )
349
+ self.assertTrue(module.landings.filter(path="/ocpp/rfid/").exists())
350
+
207
351
 
208
352
  class ScannerTemplateTests(TestCase):
209
353
  def setUp(self):
@@ -284,7 +428,10 @@ class ReaderPollingTests(SimpleTestCase):
284
428
  class DeepReadViewTests(TestCase):
285
429
  @patch("config.middleware.Node.get_local", return_value=None)
286
430
  @patch("config.middleware.get_site")
287
- @patch("ocpp.rfid.views.enable_deep_read_mode", return_value={"status": "deep", "timeout": 60})
431
+ @patch(
432
+ "ocpp.rfid.views.enable_deep_read_mode",
433
+ return_value={"status": "deep", "timeout": 60},
434
+ )
288
435
  def test_enable_deep_read(self, mock_enable, mock_site, mock_node):
289
436
  User = get_user_model()
290
437
  staff = User.objects.create_user("staff4", password="pwd", is_staff=True)
@@ -343,3 +490,51 @@ class DeepReadAuthTests(TestCase):
343
490
  self.assertEqual(reader.auth_calls[1], reader.PICC_AUTHENT1B)
344
491
 
345
492
 
493
+ class RFIDWiringConfigTests(SimpleTestCase):
494
+ def test_module_wiring_map(self):
495
+ expected = [
496
+ ("SDA", "CE0"),
497
+ ("SCK", "SCLK"),
498
+ ("MOSI", "MOSI"),
499
+ ("MISO", "MISO"),
500
+ ("IRQ", "IO4"),
501
+ ("GND", "GND"),
502
+ ("RST", "IO25"),
503
+ ("3v3", "3v3"),
504
+ ]
505
+ self.assertEqual(list(MODULE_WIRING.items()), expected)
506
+ self.assertEqual(DEFAULT_IRQ_PIN, 4)
507
+ self.assertEqual(DEFAULT_RST_PIN, 25)
508
+
509
+ def test_background_reader_uses_default_irq_pin(self):
510
+ self.assertEqual(background_reader.IRQ_PIN, DEFAULT_IRQ_PIN)
511
+
512
+ def test_reader_instantiation_uses_configured_pins(self):
513
+ class DummyReader:
514
+ init_args = None
515
+ init_kwargs = None
516
+
517
+ def __init__(self, *args, **kwargs):
518
+ DummyReader.init_args = args
519
+ DummyReader.init_kwargs = kwargs
520
+ self.MI_OK = 1
521
+ self.PICC_REQIDL = 0
522
+
523
+ fake_mfrc = types.ModuleType("mfrc522")
524
+ fake_mfrc.MFRC522 = DummyReader
525
+ fake_gpio = types.ModuleType("RPi.GPIO")
526
+ fake_rpi = types.ModuleType("RPi")
527
+ fake_rpi.GPIO = fake_gpio
528
+
529
+ with patch.dict(
530
+ "sys.modules",
531
+ {"mfrc522": fake_mfrc, "RPi": fake_rpi, "RPi.GPIO": fake_gpio},
532
+ ):
533
+ result = read_rfid(timeout=0, cleanup=False)
534
+
535
+ self.assertEqual(result, {"rfid": None, "label_id": None})
536
+ self.assertIsNotNone(DummyReader.init_kwargs)
537
+ self.assertEqual(DummyReader.init_kwargs["bus"], SPI_BUS)
538
+ self.assertEqual(DummyReader.init_kwargs["device"], SPI_DEVICE)
539
+ self.assertEqual(DummyReader.init_kwargs["pin_mode"], GPIO_PIN_MODE_BCM)
540
+ self.assertEqual(DummyReader.init_kwargs["pin_rst"], DEFAULT_RST_PIN)