arthexis 0.1.20__tar.gz → 0.1.21__tar.gz

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 (253) hide show
  1. {arthexis-0.1.20 → arthexis-0.1.21}/PKG-INFO +3 -4
  2. {arthexis-0.1.20 → arthexis-0.1.21}/arthexis.egg-info/PKG-INFO +3 -4
  3. {arthexis-0.1.20 → arthexis-0.1.21}/arthexis.egg-info/SOURCES.txt +2 -10
  4. {arthexis-0.1.20 → arthexis-0.1.21}/arthexis.egg-info/requires.txt +2 -3
  5. {arthexis-0.1.20 → arthexis-0.1.21}/config/asgi.py +1 -15
  6. {arthexis-0.1.20 → arthexis-0.1.21}/config/settings.py +0 -26
  7. {arthexis-0.1.20 → arthexis-0.1.21}/config/urls.py +0 -1
  8. {arthexis-0.1.20 → arthexis-0.1.21}/core/admin.py +1 -233
  9. {arthexis-0.1.20 → arthexis-0.1.21}/core/apps.py +0 -6
  10. {arthexis-0.1.20 → arthexis-0.1.21}/core/environment.py +29 -10
  11. {arthexis-0.1.20 → arthexis-0.1.21}/core/models.py +8 -77
  12. {arthexis-0.1.20 → arthexis-0.1.21}/core/tests.py +1 -7
  13. {arthexis-0.1.20 → arthexis-0.1.21}/core/views.py +0 -96
  14. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/admin.py +29 -6
  15. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/tests.py +49 -0
  16. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/views.py +60 -1
  17. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/admin.py +48 -6
  18. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/models.py +48 -0
  19. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/tests.py +83 -0
  20. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/views.py +85 -3
  21. {arthexis-0.1.20 → arthexis-0.1.21}/pages/context_processors.py +0 -12
  22. {arthexis-0.1.20 → arthexis-0.1.21}/pages/tests.py +0 -41
  23. {arthexis-0.1.20 → arthexis-0.1.21}/pages/urls.py +0 -1
  24. {arthexis-0.1.20 → arthexis-0.1.21}/pages/views.py +0 -5
  25. {arthexis-0.1.20 → arthexis-0.1.21}/pyproject.toml +2 -2
  26. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_email_profiles.py +0 -3
  27. arthexis-0.1.21/tests/test_environment_network_setup_form.py +39 -0
  28. arthexis-0.1.21/tests/test_fixture_presence.py +53 -0
  29. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_install_script.py +20 -11
  30. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_network_setup_interactive.py +10 -0
  31. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_profile_inline_deletion.py +2 -44
  32. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_release_progress.py +17 -112
  33. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_release_progress_pre_release_integration.py +10 -33
  34. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_rfid_admin_scan_csrf.py +4 -0
  35. arthexis-0.1.21/tests/test_settings_mcp_port.py +35 -0
  36. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_switch_role_script.py +0 -17
  37. arthexis-0.1.21/tests/test_uninstall_script.py +9 -0
  38. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_user_data_admin.py +1 -5
  39. arthexis-0.1.20/core/workgroup_urls.py +0 -17
  40. arthexis-0.1.20/core/workgroup_views.py +0 -94
  41. arthexis-0.1.20/tests/test_assistant_data_api.py +0 -32
  42. arthexis-0.1.20/tests/test_assistant_profile_admin.py +0 -168
  43. arthexis-0.1.20/tests/test_assistant_profile_api.py +0 -49
  44. arthexis-0.1.20/tests/test_fixture_presence.py +0 -21
  45. arthexis-0.1.20/tests/test_mcp_asgi.py +0 -55
  46. arthexis-0.1.20/tests/test_mcp_auto_start.py +0 -170
  47. arthexis-0.1.20/tests/test_mcp_process.py +0 -43
  48. arthexis-0.1.20/tests/test_mcp_sigil_server.py +0 -182
  49. arthexis-0.1.20/tests/test_mcp_sigil_server_command.py +0 -42
  50. arthexis-0.1.20/tests/test_uninstall_script.py +0 -20
  51. {arthexis-0.1.20 → arthexis-0.1.21}/LICENSE +0 -0
  52. {arthexis-0.1.20 → arthexis-0.1.21}/README.md +0 -0
  53. {arthexis-0.1.20 → arthexis-0.1.21}/arthexis.egg-info/dependency_links.txt +0 -0
  54. {arthexis-0.1.20 → arthexis-0.1.21}/arthexis.egg-info/top_level.txt +0 -0
  55. {arthexis-0.1.20 → arthexis-0.1.21}/config/__init__.py +0 -0
  56. {arthexis-0.1.20 → arthexis-0.1.21}/config/active_app.py +0 -0
  57. {arthexis-0.1.20 → arthexis-0.1.21}/config/auth_app.py +0 -0
  58. {arthexis-0.1.20 → arthexis-0.1.21}/config/celery.py +0 -0
  59. {arthexis-0.1.20 → arthexis-0.1.21}/config/context_processors.py +0 -0
  60. {arthexis-0.1.20 → arthexis-0.1.21}/config/horologia_app.py +0 -0
  61. {arthexis-0.1.20 → arthexis-0.1.21}/config/loadenv.py +0 -0
  62. {arthexis-0.1.20 → arthexis-0.1.21}/config/logging.py +0 -0
  63. {arthexis-0.1.20 → arthexis-0.1.21}/config/middleware.py +0 -0
  64. {arthexis-0.1.20 → arthexis-0.1.21}/config/offline.py +0 -0
  65. {arthexis-0.1.20 → arthexis-0.1.21}/config/settings_helpers.py +0 -0
  66. {arthexis-0.1.20 → arthexis-0.1.21}/config/wsgi.py +0 -0
  67. {arthexis-0.1.20 → arthexis-0.1.21}/core/__init__.py +0 -0
  68. {arthexis-0.1.20 → arthexis-0.1.21}/core/admin_history.py +0 -0
  69. {arthexis-0.1.20 → arthexis-0.1.21}/core/admindocs.py +0 -0
  70. {arthexis-0.1.20 → arthexis-0.1.21}/core/auto_upgrade.py +0 -0
  71. {arthexis-0.1.20 → arthexis-0.1.21}/core/backends.py +0 -0
  72. {arthexis-0.1.20 → arthexis-0.1.21}/core/changelog.py +0 -0
  73. {arthexis-0.1.20 → arthexis-0.1.21}/core/entity.py +0 -0
  74. {arthexis-0.1.20 → arthexis-0.1.21}/core/fields.py +0 -0
  75. {arthexis-0.1.20 → arthexis-0.1.21}/core/form_fields.py +0 -0
  76. {arthexis-0.1.20 → arthexis-0.1.21}/core/github_helper.py +0 -0
  77. {arthexis-0.1.20 → arthexis-0.1.21}/core/github_issues.py +0 -0
  78. {arthexis-0.1.20 → arthexis-0.1.21}/core/github_repos.py +0 -0
  79. {arthexis-0.1.20 → arthexis-0.1.21}/core/lcd_screen.py +0 -0
  80. {arthexis-0.1.20 → arthexis-0.1.21}/core/liveupdate.py +0 -0
  81. {arthexis-0.1.20 → arthexis-0.1.21}/core/log_paths.py +0 -0
  82. {arthexis-0.1.20 → arthexis-0.1.21}/core/mailer.py +0 -0
  83. {arthexis-0.1.20 → arthexis-0.1.21}/core/middleware.py +0 -0
  84. {arthexis-0.1.20 → arthexis-0.1.21}/core/notifications.py +0 -0
  85. {arthexis-0.1.20 → arthexis-0.1.21}/core/public_wifi.py +0 -0
  86. {arthexis-0.1.20 → arthexis-0.1.21}/core/reference_utils.py +0 -0
  87. {arthexis-0.1.20 → arthexis-0.1.21}/core/release.py +0 -0
  88. {arthexis-0.1.20 → arthexis-0.1.21}/core/rfid_import_export.py +0 -0
  89. {arthexis-0.1.20 → arthexis-0.1.21}/core/sigil_builder.py +0 -0
  90. {arthexis-0.1.20 → arthexis-0.1.21}/core/sigil_context.py +0 -0
  91. {arthexis-0.1.20 → arthexis-0.1.21}/core/sigil_resolver.py +0 -0
  92. {arthexis-0.1.20 → arthexis-0.1.21}/core/system.py +0 -0
  93. {arthexis-0.1.20 → arthexis-0.1.21}/core/tasks.py +0 -0
  94. {arthexis-0.1.20 → arthexis-0.1.21}/core/temp_passwords.py +0 -0
  95. {arthexis-0.1.20 → arthexis-0.1.21}/core/test_system_info.py +0 -0
  96. {arthexis-0.1.20 → arthexis-0.1.21}/core/tests_liveupdate.py +0 -0
  97. {arthexis-0.1.20 → arthexis-0.1.21}/core/urls.py +0 -0
  98. {arthexis-0.1.20 → arthexis-0.1.21}/core/user_data.py +0 -0
  99. {arthexis-0.1.20 → arthexis-0.1.21}/core/widgets.py +0 -0
  100. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/__init__.py +0 -0
  101. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/apps.py +0 -0
  102. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/backends.py +0 -0
  103. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/dns.py +0 -0
  104. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/feature_checks.py +0 -0
  105. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/lcd.py +0 -0
  106. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/models.py +0 -0
  107. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/reports.py +0 -0
  108. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/rfid_sync.py +0 -0
  109. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/signals.py +0 -0
  110. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/tasks.py +0 -0
  111. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/urls.py +0 -0
  112. {arthexis-0.1.20 → arthexis-0.1.21}/nodes/utils.py +0 -0
  113. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/__init__.py +0 -0
  114. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/apps.py +0 -0
  115. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/consumers.py +0 -0
  116. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/evcs.py +0 -0
  117. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/evcs_discovery.py +0 -0
  118. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/reference_utils.py +0 -0
  119. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/routing.py +0 -0
  120. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/simulator.py +0 -0
  121. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/status_display.py +0 -0
  122. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/store.py +0 -0
  123. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/tasks.py +0 -0
  124. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/test_export_import.py +0 -0
  125. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/test_rfid.py +0 -0
  126. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/transactions_io.py +0 -0
  127. {arthexis-0.1.20 → arthexis-0.1.21}/ocpp/urls.py +0 -0
  128. {arthexis-0.1.20 → arthexis-0.1.21}/pages/__init__.py +0 -0
  129. {arthexis-0.1.20 → arthexis-0.1.21}/pages/admin.py +0 -0
  130. {arthexis-0.1.20 → arthexis-0.1.21}/pages/apps.py +0 -0
  131. {arthexis-0.1.20 → arthexis-0.1.21}/pages/checks.py +0 -0
  132. {arthexis-0.1.20 → arthexis-0.1.21}/pages/defaults.py +0 -0
  133. {arthexis-0.1.20 → arthexis-0.1.21}/pages/forms.py +0 -0
  134. {arthexis-0.1.20 → arthexis-0.1.21}/pages/middleware.py +0 -0
  135. {arthexis-0.1.20 → arthexis-0.1.21}/pages/models.py +0 -0
  136. {arthexis-0.1.20 → arthexis-0.1.21}/pages/module_defaults.py +0 -0
  137. {arthexis-0.1.20 → arthexis-0.1.21}/pages/site_config.py +0 -0
  138. {arthexis-0.1.20 → arthexis-0.1.21}/pages/tasks.py +0 -0
  139. {arthexis-0.1.20 → arthexis-0.1.21}/pages/utils.py +0 -0
  140. {arthexis-0.1.20 → arthexis-0.1.21}/setup.cfg +0 -0
  141. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_acronym_capitalization.py +0 -0
  142. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_admin_client_report.py +0 -0
  143. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_admin_doc_commands.py +0 -0
  144. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_admin_doc_model_groups.py +0 -0
  145. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_admin_history.py +0 -0
  146. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_admin_index_actions.py +0 -0
  147. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_admin_model_graph.py +0 -0
  148. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_admin_object_history.py +0 -0
  149. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_admin_profile_link.py +0 -0
  150. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_admin_system_stop.py +0 -0
  151. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_allowed_hosts_hostname.py +0 -0
  152. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_api_login_required.py +0 -0
  153. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_auto_upgrade_scheduler.py +0 -0
  154. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_awg_admin.py +0 -0
  155. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_benchmark_command.py +0 -0
  156. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_build_pypi_command.py +0 -0
  157. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_celery_no_debug.py +0 -0
  158. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_changelog_builder.py +0 -0
  159. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_check_admin_command.py +0 -0
  160. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_check_migrations_script.py +0 -0
  161. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_check_pypi_command.py +0 -0
  162. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_clean_release_logs_command.py +0 -0
  163. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_client_report_form.py +0 -0
  164. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_client_report_generation.py +0 -0
  165. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_client_report_schedule.py +0 -0
  166. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_csrf_failure.py +0 -0
  167. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_csrf_origin_subnet.py +0 -0
  168. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_dist_cleanup.py +0 -0
  169. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_email_collector.py +0 -0
  170. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_email_inbox.py +0 -0
  171. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_email_inbox_admin.py +0 -0
  172. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_email_inbox_search_action.py +0 -0
  173. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_email_outbox_admin.py +0 -0
  174. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_email_transaction.py +0 -0
  175. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_env_refresh_clean.py +0 -0
  176. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_env_refresh_pip.py +0 -0
  177. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_env_refresh_unlink.py +0 -0
  178. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_experience_admin_group.py +0 -0
  179. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_footer_no_references.py +0 -0
  180. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_footer_presence.py +0 -0
  181. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_footer_render.py +0 -0
  182. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_git_checks.py +0 -0
  183. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_github_issue_reporting.py +0 -0
  184. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_invitation_login_view.py +0 -0
  185. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_language_switch.py +0 -0
  186. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_lcd_check_command.py +0 -0
  187. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_lcd_smbus2.py +0 -0
  188. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_localhost_admin_backend.py +0 -0
  189. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_log_paths.py +0 -0
  190. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_login_view_no_site.py +0 -0
  191. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_manage_debug_flag.py +0 -0
  192. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_manuals.py +0 -0
  193. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_message_command.py +0 -0
  194. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_migrations.py +0 -0
  195. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_model_verbose_name_capitalization.py +0 -0
  196. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_node_info_view.py +0 -0
  197. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_notifications_fallback.py +0 -0
  198. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_notify_command.py +0 -0
  199. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_ocpp_session_lock.py +0 -0
  200. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_odoo_product.py +0 -0
  201. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_odoo_profile.py +0 -0
  202. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_odoo_profile_admin.py +0 -0
  203. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_odoo_quote_report.py +0 -0
  204. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_offline.py +0 -0
  205. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_package_admin_next_release.py +0 -0
  206. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_power_admin_group.py +0 -0
  207. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_pypi_check.py +0 -0
  208. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_pypi_token.py +0 -0
  209. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_readme_editor.py +0 -0
  210. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_readme_language.py +0 -0
  211. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_reference_qr_code.py +0 -0
  212. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_reference_transaction_uuid.py +0 -0
  213. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_register_site_apps_command.py +0 -0
  214. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_release_build.py +0 -0
  215. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_release_build_flow.py +0 -0
  216. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_release_checklist.py +0 -0
  217. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_release_logs.py +0 -0
  218. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_release_manager_admin.py +0 -0
  219. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_release_packages.py +0 -0
  220. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_release_push.py +0 -0
  221. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_release_tasks.py +0 -0
  222. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_render_nginx_sites.py +0 -0
  223. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_request_invite.py +0 -0
  224. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_rfid_admin_print_labels.py +0 -0
  225. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_rfid_admin_reference_clear.py +0 -0
  226. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_rfid_always_on.py +0 -0
  227. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_rfid_backend.py +0 -0
  228. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_rfid_background_reader.py +0 -0
  229. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_rfid_client_report.py +0 -0
  230. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_rfid_watch_command.py +0 -0
  231. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_role_marker_filtering.py +0 -0
  232. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_seed_data.py +0 -0
  233. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_send_invite_command.py +0 -0
  234. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_settings_helpers.py +0 -0
  235. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_shell_scripts.py +0 -0
  236. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_show_leads_command.py +0 -0
  237. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_sigil_builder.py +0 -0
  238. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_sigil_resolution.py +0 -0
  239. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_sites_utils.py +0 -0
  240. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_social_profile.py +0 -0
  241. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_staff_login_net_message.py +0 -0
  242. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_staff_required_decorator.py +0 -0
  243. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_suite_gateway.py +0 -0
  244. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_system_changelog_report.py +0 -0
  245. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_temp_passwords.py +0 -0
  246. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_totp_admin.py +0 -0
  247. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_totp_backend.py +0 -0
  248. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_update_fixtures_command.py +0 -0
  249. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_upgrade_report.py +0 -0
  250. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_urls_autodiscover.py +0 -0
  251. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_version_endpoint.py +0 -0
  252. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_version_file.py +0 -0
  253. {arthexis-0.1.20 → arthexis-0.1.21}/tests/test_vscode_manage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arthexis
3
- Version: 0.1.20
3
+ Version: 0.1.21
4
4
  Summary: Power & Energy Infrastructure
5
5
  Author-email: "Rafael J. Guillén-Osorio" <tecnologia@gelectriic.com>
6
6
  License-Expression: GPL-3.0-only
@@ -59,7 +59,6 @@ Requires-Dist: kombu==5.5.4
59
59
  Requires-Dist: libipld==3.1.1
60
60
  Requires-Dist: Markdown==3.8.2
61
61
  Requires-Dist: mdx_truly_sane_lists==1.3
62
- Requires-Dist: mcp==1.18.0
63
62
  Requires-Dist: mfrc522==0.0.7; sys_platform == "linux"
64
63
  Requires-Dist: outcome==1.3.0.post0
65
64
  Requires-Dist: packaging==25.0
@@ -67,7 +66,7 @@ Requires-Dist: pillow==11.3.0
67
66
  Requires-Dist: prompt_toolkit==3.0.51
68
67
  Requires-Dist: psutil==7.1.1
69
68
  Requires-Dist: psycopg==3.2.9
70
- Requires-Dist: psycopg-binary==3.2.11
69
+ Requires-Dist: psycopg-binary==3.2.12
71
70
  Requires-Dist: pyasn1==0.6.1
72
71
  Requires-Dist: pyasn1_modules==0.4.2
73
72
  Requires-Dist: pycparser==2.22
@@ -111,7 +110,7 @@ Requires-Dist: websockets==13.1
111
110
  Requires-Dist: whitenoise==6.11.0
112
111
  Requires-Dist: plyer==2.1.0; sys_platform == "win32"
113
112
  Requires-Dist: wsproto==1.2.0
114
- Requires-Dist: zope.interface==7.2
113
+ Requires-Dist: zope.interface==8.0.1
115
114
  Dynamic: license-file
116
115
 
117
116
  # Arthexis Constellation
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arthexis
3
- Version: 0.1.20
3
+ Version: 0.1.21
4
4
  Summary: Power & Energy Infrastructure
5
5
  Author-email: "Rafael J. Guillén-Osorio" <tecnologia@gelectriic.com>
6
6
  License-Expression: GPL-3.0-only
@@ -59,7 +59,6 @@ Requires-Dist: kombu==5.5.4
59
59
  Requires-Dist: libipld==3.1.1
60
60
  Requires-Dist: Markdown==3.8.2
61
61
  Requires-Dist: mdx_truly_sane_lists==1.3
62
- Requires-Dist: mcp==1.18.0
63
62
  Requires-Dist: mfrc522==0.0.7; sys_platform == "linux"
64
63
  Requires-Dist: outcome==1.3.0.post0
65
64
  Requires-Dist: packaging==25.0
@@ -67,7 +66,7 @@ Requires-Dist: pillow==11.3.0
67
66
  Requires-Dist: prompt_toolkit==3.0.51
68
67
  Requires-Dist: psutil==7.1.1
69
68
  Requires-Dist: psycopg==3.2.9
70
- Requires-Dist: psycopg-binary==3.2.11
69
+ Requires-Dist: psycopg-binary==3.2.12
71
70
  Requires-Dist: pyasn1==0.6.1
72
71
  Requires-Dist: pyasn1_modules==0.4.2
73
72
  Requires-Dist: pycparser==2.22
@@ -111,7 +110,7 @@ Requires-Dist: websockets==13.1
111
110
  Requires-Dist: whitenoise==6.11.0
112
111
  Requires-Dist: plyer==2.1.0; sys_platform == "win32"
113
112
  Requires-Dist: wsproto==1.2.0
114
- Requires-Dist: zope.interface==7.2
113
+ Requires-Dist: zope.interface==8.0.1
115
114
  Dynamic: license-file
116
115
 
117
116
  # Arthexis Constellation
@@ -60,8 +60,6 @@ core/urls.py
60
60
  core/user_data.py
61
61
  core/views.py
62
62
  core/widgets.py
63
- core/workgroup_urls.py
64
- core/workgroup_views.py
65
63
  nodes/__init__.py
66
64
  nodes/admin.py
67
65
  nodes/apps.py
@@ -125,9 +123,6 @@ tests/test_admin_profile_link.py
125
123
  tests/test_admin_system_stop.py
126
124
  tests/test_allowed_hosts_hostname.py
127
125
  tests/test_api_login_required.py
128
- tests/test_assistant_data_api.py
129
- tests/test_assistant_profile_admin.py
130
- tests/test_assistant_profile_api.py
131
126
  tests/test_auto_upgrade_scheduler.py
132
127
  tests/test_awg_admin.py
133
128
  tests/test_benchmark_command.py
@@ -154,6 +149,7 @@ tests/test_email_transaction.py
154
149
  tests/test_env_refresh_clean.py
155
150
  tests/test_env_refresh_pip.py
156
151
  tests/test_env_refresh_unlink.py
152
+ tests/test_environment_network_setup_form.py
157
153
  tests/test_experience_admin_group.py
158
154
  tests/test_fixture_presence.py
159
155
  tests/test_footer_no_references.py
@@ -171,11 +167,6 @@ tests/test_log_paths.py
171
167
  tests/test_login_view_no_site.py
172
168
  tests/test_manage_debug_flag.py
173
169
  tests/test_manuals.py
174
- tests/test_mcp_asgi.py
175
- tests/test_mcp_auto_start.py
176
- tests/test_mcp_process.py
177
- tests/test_mcp_sigil_server.py
178
- tests/test_mcp_sigil_server_command.py
179
170
  tests/test_message_command.py
180
171
  tests/test_migrations.py
181
172
  tests/test_model_verbose_name_capitalization.py
@@ -223,6 +214,7 @@ tests/test_role_marker_filtering.py
223
214
  tests/test_seed_data.py
224
215
  tests/test_send_invite_command.py
225
216
  tests/test_settings_helpers.py
217
+ tests/test_settings_mcp_port.py
226
218
  tests/test_shell_scripts.py
227
219
  tests/test_show_leads_command.py
228
220
  tests/test_sigil_builder.py
@@ -45,14 +45,13 @@ kombu==5.5.4
45
45
  libipld==3.1.1
46
46
  Markdown==3.8.2
47
47
  mdx_truly_sane_lists==1.3
48
- mcp==1.18.0
49
48
  outcome==1.3.0.post0
50
49
  packaging==25.0
51
50
  pillow==11.3.0
52
51
  prompt_toolkit==3.0.51
53
52
  psutil==7.1.1
54
53
  psycopg==3.2.9
55
- psycopg-binary==3.2.11
54
+ psycopg-binary==3.2.12
56
55
  pyasn1==0.6.1
57
56
  pyasn1_modules==0.4.2
58
57
  pycparser==2.22
@@ -95,7 +94,7 @@ websocket-client==1.8.0
95
94
  websockets==13.1
96
95
  whitenoise==6.11.0
97
96
  wsproto==1.2.0
98
- zope.interface==7.2
97
+ zope.interface==8.0.1
99
98
 
100
99
  [:sys_platform == "linux"]
101
100
  gpiozero==2.0.1
@@ -9,35 +9,21 @@ https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
9
9
 
10
10
  import os
11
11
  from config.loadenv import loadenv
12
- from typing import Any, Awaitable, Callable, Dict, MutableMapping
13
12
  from channels.auth import AuthMiddlewareStack
14
13
  from channels.routing import ProtocolTypeRouter, URLRouter
15
14
  from django.core.asgi import get_asgi_application
16
15
  import ocpp.routing
17
16
 
18
- from core.mcp.asgi import application as mcp_application
19
- from core.mcp.asgi import is_mcp_scope
20
-
21
17
  loadenv()
22
18
  os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
23
19
 
24
20
  django_asgi_app = get_asgi_application()
25
21
 
26
- Scope = MutableMapping[str, Any]
27
- Receive = Callable[[], Awaitable[Dict[str, Any]]]
28
- Send = Callable[[Dict[str, Any]], Awaitable[None]]
29
-
30
22
  websocket_patterns = ocpp.routing.websocket_urlpatterns
31
23
 
32
- async def http_application(scope: Scope, receive: Receive, send: Send) -> None:
33
- if is_mcp_scope(scope):
34
- await mcp_application(scope, receive, send)
35
- else:
36
- await django_asgi_app(scope, receive, send)
37
-
38
24
  application = ProtocolTypeRouter(
39
25
  {
40
- "http": http_application,
26
+ "http": django_asgi_app,
41
27
  "websocket": AuthMiddlewareStack(URLRouter(websocket_patterns)),
42
28
  }
43
29
  )
@@ -444,32 +444,6 @@ ASGI_APPLICATION = "config.asgi.application"
444
444
  CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}}
445
445
 
446
446
 
447
- # MCP sigil resolver configuration
448
- def _env_int(name: str, default: int) -> int:
449
- try:
450
- return int(os.environ.get(name, default))
451
- except (TypeError, ValueError): # pragma: no cover - defensive
452
- return default
453
-
454
-
455
- def _split_env_list(name: str) -> list[str]:
456
- raw = os.environ.get(name)
457
- if not raw:
458
- return []
459
- return [item.strip() for item in raw.split(",") if item.strip()]
460
-
461
-
462
- MCP_SIGIL_SERVER = {
463
- "host": os.environ.get("MCP_SIGIL_HOST", "127.0.0.1"),
464
- "port": _env_int("MCP_SIGIL_PORT", 8800),
465
- "api_keys": _split_env_list("MCP_SIGIL_API_KEYS"),
466
- "required_scopes": ["sigils:read"],
467
- "issuer_url": os.environ.get("MCP_SIGIL_ISSUER_URL"),
468
- "resource_server_url": os.environ.get("MCP_SIGIL_RESOURCE_URL"),
469
- "mount_path": os.environ.get("MCP_SIGIL_MOUNT_PATH", "/mcp"),
470
- }
471
-
472
-
473
447
  # Custom user model
474
448
  AUTH_USER_MODEL = "core.User"
475
449
 
@@ -156,7 +156,6 @@ urlpatterns = [
156
156
  ),
157
157
  path("admin/", admin.site.urls),
158
158
  path("i18n/setlang/", csrf_exempt(set_language), name="set_language"),
159
- path("api/", include("core.workgroup_urls")),
160
159
  path("", include("pages.urls")),
161
160
  ]
162
161
 
@@ -91,9 +91,7 @@ from .models import (
91
91
  SecurityGroup,
92
92
  InviteLead,
93
93
  PublicWifiAccess,
94
- AssistantProfile,
95
94
  Todo,
96
- hash_key,
97
95
  )
98
96
  from .user_data import (
99
97
  EntityModelAdmin,
@@ -110,8 +108,6 @@ from .rfid_import_export import (
110
108
  parse_accounts,
111
109
  serialize_accounts,
112
110
  )
113
- from .mcp import process as mcp_process
114
- from .mcp.server import resolve_base_urls
115
111
  from . import release as release_utils
116
112
 
117
113
  logger = logging.getLogger(__name__)
@@ -1304,46 +1300,6 @@ class ReleaseManagerInlineForm(ProfileFormMixin, forms.ModelForm):
1304
1300
  }
1305
1301
 
1306
1302
 
1307
- class AssistantProfileInlineForm(ProfileFormMixin, forms.ModelForm):
1308
- user_key = forms.CharField(
1309
- required=False,
1310
- widget=forms.PasswordInput(render_value=True),
1311
- help_text="Provide a plain key to create or rotate credentials.",
1312
- )
1313
- profile_fields = ("assistant_name", "user_key", "scopes", "is_active")
1314
-
1315
- class Meta:
1316
- model = AssistantProfile
1317
- fields = ("assistant_name", "scopes", "is_active")
1318
-
1319
- def __init__(self, *args, **kwargs):
1320
- super().__init__(*args, **kwargs)
1321
- if not self.instance.pk and "is_active" in self.fields:
1322
- self.fields["is_active"].initial = False
1323
-
1324
- def clean(self):
1325
- cleaned = super().clean()
1326
- if cleaned.get("DELETE"):
1327
- return cleaned
1328
- if not self.instance.pk and not cleaned.get("user_key"):
1329
- if cleaned.get("scopes") or cleaned.get("is_active"):
1330
- raise forms.ValidationError(
1331
- "Provide a user key to create an assistant profile."
1332
- )
1333
- return cleaned
1334
-
1335
- def save(self, commit=True):
1336
- instance = super().save(commit=False)
1337
- user_key = self.cleaned_data.get("user_key")
1338
- if user_key:
1339
- instance.user_key_hash = hash_key(user_key)
1340
- instance.last_used_at = None
1341
- if commit:
1342
- instance.save()
1343
- self.save_m2m()
1344
- return instance
1345
-
1346
-
1347
1303
  PROFILE_INLINE_CONFIG = {
1348
1304
  OdooProfile: {
1349
1305
  "form": OdooProfileInlineForm,
@@ -1474,12 +1430,6 @@ PROFILE_INLINE_CONFIG = {
1474
1430
  "secondary_pypi_url",
1475
1431
  ),
1476
1432
  },
1477
- AssistantProfile: {
1478
- "form": AssistantProfileInlineForm,
1479
- "fields": ("assistant_name", "user_key", "scopes", "is_active"),
1480
- "readonly_fields": ("user_key_hash", "created_at", "last_used_at"),
1481
- "template": "admin/edit_inline/profile_stacked.html",
1482
- },
1483
1433
  }
1484
1434
 
1485
1435
 
@@ -1526,7 +1476,6 @@ PROFILE_MODELS = (
1526
1476
  EmailOutbox,
1527
1477
  SocialProfile,
1528
1478
  ReleaseManager,
1529
- AssistantProfile,
1530
1479
  )
1531
1480
  USER_PROFILE_INLINES = [
1532
1481
  _build_profile_inline(model, "user") for model in PROFILE_MODELS
@@ -2013,188 +1962,6 @@ class EmailInboxAdmin(ProfileAdminMixin, SaveBeforeChangeAction, EntityModelAdmi
2013
1962
  return TemplateResponse(request, "admin/core/emailinbox/search.html", context)
2014
1963
 
2015
1964
 
2016
- @admin.register(AssistantProfile)
2017
- class AssistantProfileAdmin(
2018
- ProfileAdminMixin, SaveBeforeChangeAction, EntityModelAdmin
2019
- ):
2020
- list_display = ("assistant_name", "owner", "created_at", "last_used_at", "is_active")
2021
- readonly_fields = ("user_key_hash", "created_at", "last_used_at")
2022
-
2023
- change_form_template = "admin/workgroupassistantprofile_change_form.html"
2024
- change_list_template = "admin/assistantprofile_change_list.html"
2025
- change_actions = ["my_profile_action"]
2026
- changelist_actions = ["my_profile"]
2027
- fieldsets = (
2028
- ("Owner", {"fields": ("user", "group")}),
2029
- ("Credentials", {"fields": ("user_key_hash",)}),
2030
- (
2031
- "Configuration",
2032
- {
2033
- "fields": (
2034
- "assistant_name",
2035
- "scopes",
2036
- "is_active",
2037
- "created_at",
2038
- "last_used_at",
2039
- )
2040
- },
2041
- ),
2042
- )
2043
-
2044
- def owner(self, obj):
2045
- return obj.owner_display()
2046
-
2047
- owner.short_description = "Owner"
2048
-
2049
- def get_urls(self):
2050
- urls = super().get_urls()
2051
- opts = self.model._meta
2052
- app_label = opts.app_label
2053
- model_name = opts.model_name
2054
- custom = [
2055
- path(
2056
- "<path:object_id>/generate-key/",
2057
- self.admin_site.admin_view(self.generate_key),
2058
- name=f"{app_label}_{model_name}_generate_key",
2059
- ),
2060
- path(
2061
- "server/start/",
2062
- self.admin_site.admin_view(self.start_server),
2063
- name=f"{app_label}_{model_name}_start_server",
2064
- ),
2065
- path(
2066
- "server/stop/",
2067
- self.admin_site.admin_view(self.stop_server),
2068
- name=f"{app_label}_{model_name}_stop_server",
2069
- ),
2070
- path(
2071
- "server/status/",
2072
- self.admin_site.admin_view(self.server_status),
2073
- name=f"{app_label}_{model_name}_status",
2074
- ),
2075
- ]
2076
- return custom + urls
2077
-
2078
- def changelist_view(self, request, extra_context=None):
2079
- extra_context = extra_context or {}
2080
- status = mcp_process.get_status()
2081
- opts = self.model._meta
2082
- app_label = opts.app_label
2083
- model_name = opts.model_name
2084
- extra_context.update(
2085
- {
2086
- "mcp_status": status,
2087
- "mcp_server_actions": {
2088
- "start": reverse(f"admin:{app_label}_{model_name}_start_server"),
2089
- "stop": reverse(f"admin:{app_label}_{model_name}_stop_server"),
2090
- "status": reverse(f"admin:{app_label}_{model_name}_status"),
2091
- },
2092
- }
2093
- )
2094
- return super().changelist_view(request, extra_context=extra_context)
2095
-
2096
- def _redirect_to_changelist(self):
2097
- opts = self.model._meta
2098
- return HttpResponseRedirect(
2099
- reverse(f"admin:{opts.app_label}_{opts.model_name}_changelist")
2100
- )
2101
-
2102
- def generate_key(self, request, object_id, *args, **kwargs):
2103
- profile = self.get_object(request, object_id)
2104
- if profile is None:
2105
- return HttpResponseRedirect("../")
2106
- if profile.user is None:
2107
- self.message_user(
2108
- request,
2109
- "Assign a user before generating a key.",
2110
- level=messages.ERROR,
2111
- )
2112
- return HttpResponseRedirect("../")
2113
- profile, key = AssistantProfile.issue_key(profile.user)
2114
- context = {
2115
- **self.admin_site.each_context(request),
2116
- "opts": self.model._meta,
2117
- "original": profile,
2118
- "user_key": key,
2119
- }
2120
- return TemplateResponse(request, "admin/assistantprofile_key.html", context)
2121
-
2122
- def render_change_form(
2123
- self, request, context, add=False, change=False, form_url="", obj=None
2124
- ):
2125
- response = super().render_change_form(
2126
- request, context, add=add, change=change, form_url=form_url, obj=obj
2127
- )
2128
- config = dict(getattr(settings, "MCP_SIGIL_SERVER", {}))
2129
- host = config.get("host") or "127.0.0.1"
2130
- port = config.get("port", 8800)
2131
- base_url, issuer_url = resolve_base_urls(config)
2132
- mount_path = config.get("mount_path") or "/"
2133
- display_base_url = base_url or f"http://{host}:{port}"
2134
- display_issuer_url = issuer_url or display_base_url
2135
- chat_endpoint = f"{display_base_url.rstrip('/')}/api/chat/"
2136
- if isinstance(response, dict):
2137
- response.setdefault("mcp_server_host", host)
2138
- response.setdefault("mcp_server_port", port)
2139
- response.setdefault("mcp_server_base_url", display_base_url)
2140
- response.setdefault("mcp_server_issuer_url", display_issuer_url)
2141
- response.setdefault("mcp_server_mount_path", mount_path)
2142
- response.setdefault("mcp_server_chat_endpoint", chat_endpoint)
2143
- else:
2144
- context_data = getattr(response, "context_data", None)
2145
- if context_data is not None:
2146
- context_data.setdefault("mcp_server_host", host)
2147
- context_data.setdefault("mcp_server_port", port)
2148
- context_data.setdefault("mcp_server_base_url", display_base_url)
2149
- context_data.setdefault("mcp_server_issuer_url", display_issuer_url)
2150
- context_data.setdefault("mcp_server_mount_path", mount_path)
2151
- context_data.setdefault("mcp_server_chat_endpoint", chat_endpoint)
2152
- return response
2153
-
2154
- def start_server(self, request):
2155
- try:
2156
- pid = mcp_process.start_server()
2157
- except mcp_process.ServerAlreadyRunningError as exc:
2158
- self.message_user(request, str(exc), level=messages.WARNING)
2159
- except mcp_process.ServerStartError as exc:
2160
- self.message_user(request, str(exc), level=messages.ERROR)
2161
- else:
2162
- self.message_user(
2163
- request,
2164
- f"Started MCP server (PID {pid}).",
2165
- level=messages.SUCCESS,
2166
- )
2167
- return self._redirect_to_changelist()
2168
-
2169
- def stop_server(self, request):
2170
- try:
2171
- pid = mcp_process.stop_server()
2172
- except mcp_process.ServerNotRunningError as exc:
2173
- self.message_user(request, str(exc), level=messages.WARNING)
2174
- except mcp_process.ServerStopError as exc:
2175
- self.message_user(request, str(exc), level=messages.ERROR)
2176
- else:
2177
- self.message_user(
2178
- request,
2179
- f"Stopped MCP server (PID {pid}).",
2180
- level=messages.SUCCESS,
2181
- )
2182
- return self._redirect_to_changelist()
2183
-
2184
- def server_status(self, request):
2185
- status = mcp_process.get_status()
2186
- if status["running"]:
2187
- msg = f"MCP server is running (PID {status['pid']})."
2188
- level = messages.INFO
2189
- else:
2190
- msg = "MCP server is not running."
2191
- level = messages.WARNING
2192
- if status.get("last_error"):
2193
- msg = f"{msg} {status['last_error']}"
2194
- self.message_user(request, msg, level=level)
2195
- return self._redirect_to_changelist()
2196
-
2197
-
2198
1965
  class EnergyCreditInline(admin.TabularInline):
2199
1966
  model = EnergyCredit
2200
1967
  fields = ("amount_kw", "created_by", "created_on")
@@ -3690,6 +3457,7 @@ class RFIDAdmin(EntityModelAdmin, ImportExportModelAdmin):
3690
3457
  "toggle_url": toggle_url,
3691
3458
  "toggle_label": toggle_label,
3692
3459
  "public_view_url": public_view_url,
3460
+ "deep_read_url": reverse("rfid-scan-deep"),
3693
3461
  }
3694
3462
  )
3695
3463
  context["title"] = _("Scan RFIDs")
@@ -348,9 +348,3 @@ class CoreConfig(AppConfig):
348
348
  weak=False,
349
349
  )
350
350
 
351
- try:
352
- from .mcp.auto_start import schedule_auto_start
353
-
354
- schedule_auto_start(check_profiles_immediately=False)
355
- except Exception: # pragma: no cover - defensive
356
- logger.exception("Failed to schedule MCP auto-start")
@@ -71,7 +71,7 @@ class NetworkSetupForm(forms.Form):
71
71
  ethernet_subnet = forms.CharField(
72
72
  label=_("Ethernet subnet"),
73
73
  required=False,
74
- help_text=_("Provide N or N/P (prefix 16 or 24) to supply --subnet."),
74
+ help_text=_("Provide Z, Z/P (prefix 16 or 24), X.Y.Z, or X.Y.Z/P to supply --subnet."),
75
75
  )
76
76
  update_ap_password_only = forms.BooleanField(
77
77
  label=_("Update access point password only"),
@@ -84,16 +84,35 @@ class NetworkSetupForm(forms.Form):
84
84
  if not value:
85
85
  return ""
86
86
  raw = value.strip()
87
- match = re.fullmatch(r"(?P<subnet>\d{1,3})(?:/(?P<prefix>\d{1,2}))?", raw)
87
+ match = re.fullmatch(
88
+ r"(?P<first>\d{1,3})(?:\.(?P<second>\d{1,3})\.(?P<third>\d{1,3}))?(?:/(?P<prefix>\d{1,2}))?",
89
+ raw,
90
+ )
88
91
  if not match:
89
92
  raise forms.ValidationError(
90
- _("Enter a subnet in the form N or N/P with prefix 16 or 24."),
91
- )
92
- subnet = int(match.group("subnet"))
93
- if subnet < 0 or subnet > 254:
94
- raise forms.ValidationError(
95
- _("Subnet value must be between 0 and 254."),
93
+ _("Enter a subnet in the form Z, Z/P, X.Y.Z, or X.Y.Z/P with prefix 16 or 24."),
96
94
  )
95
+ first_octet = int(match.group("first"))
96
+ second = match.group("second")
97
+ third = match.group("third")
98
+ if second is not None and third is not None:
99
+ octets = [first_octet, int(second), int(third)]
100
+ for octet in octets:
101
+ if octet < 0 or octet > 255:
102
+ raise forms.ValidationError(
103
+ _("Subnet octets must be between 0 and 255."),
104
+ )
105
+ if octets[2] > 254:
106
+ raise forms.ValidationError(
107
+ _("The third subnet octet must be between 0 and 254."),
108
+ )
109
+ subnet_value = ".".join(str(octet) for octet in octets)
110
+ else:
111
+ if first_octet < 0 or first_octet > 254:
112
+ raise forms.ValidationError(
113
+ _("Subnet value must be between 0 and 254."),
114
+ )
115
+ subnet_value = str(first_octet)
97
116
  prefix_value = match.group("prefix")
98
117
  if prefix_value:
99
118
  prefix = int(prefix_value)
@@ -101,8 +120,8 @@ class NetworkSetupForm(forms.Form):
101
120
  raise forms.ValidationError(
102
121
  _("Subnet prefix must be 16 or 24."),
103
122
  )
104
- return f"{subnet}/{prefix}"
105
- return str(subnet)
123
+ return f"{subnet_value}/{prefix}"
124
+ return subnet_value
106
125
 
107
126
  def clean(self) -> dict:
108
127
  cleaned_data = super().clean()