arthexis 0.1.6__tar.gz → 0.1.8__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.
- {arthexis-0.1.6 → arthexis-0.1.8}/PKG-INFO +12 -8
- arthexis-0.1.8/README.md +22 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/arthexis.egg-info/PKG-INFO +12 -8
- {arthexis-0.1.6 → arthexis-0.1.8}/arthexis.egg-info/SOURCES.txt +20 -7
- {arthexis-0.1.6 → arthexis-0.1.8}/config/celery.py +7 -0
- arthexis-0.1.8/config/horologia_app.py +7 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/config/logging.py +8 -3
- {arthexis-0.1.6 → arthexis-0.1.8}/config/settings.py +3 -2
- {arthexis-0.1.6 → arthexis-0.1.8}/config/urls.py +9 -0
- arthexis-0.1.8/config/workgroup_app.py +7 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/admin.py +192 -17
- arthexis-0.1.8/core/admindocs.py +44 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/apps.py +2 -1
- arthexis-0.1.8/core/checks.py +29 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/entity.py +29 -7
- {arthexis-0.1.6 → arthexis-0.1.8}/core/models.py +124 -14
- {arthexis-0.1.6 → arthexis-0.1.8}/core/release.py +29 -141
- {arthexis-0.1.6 → arthexis-0.1.8}/core/system.py +2 -2
- arthexis-0.1.8/core/test_system_info.py +21 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/tests.py +292 -1
- {arthexis-0.1.6 → arthexis-0.1.8}/core/views.py +153 -134
- arthexis-0.1.8/core/workgroup_urls.py +13 -0
- arthexis-0.1.8/core/workgroup_views.py +57 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/nodes/admin.py +211 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/nodes/apps.py +1 -1
- {arthexis-0.1.6 → arthexis-0.1.8}/nodes/models.py +103 -7
- {arthexis-0.1.6 → arthexis-0.1.8}/nodes/tests.py +27 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/apps.py +4 -3
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/models.py +1 -1
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/simulator.py +4 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/tests.py +5 -1
- {arthexis-0.1.6 → arthexis-0.1.8}/pages/admin.py +8 -3
- {arthexis-0.1.6 → arthexis-0.1.8}/pages/apps.py +1 -1
- {arthexis-0.1.6 → arthexis-0.1.8}/pages/tests.py +23 -4
- {arthexis-0.1.6 → arthexis-0.1.8}/pages/views.py +22 -3
- {arthexis-0.1.6 → arthexis-0.1.8}/pyproject.toml +1 -1
- arthexis-0.1.8/tests/test_admin_doc_commands.py +32 -0
- arthexis-0.1.8/tests/test_business_admin_group.py +27 -0
- arthexis-0.1.8/tests/test_celery_no_debug.py +19 -0
- arthexis-0.1.8/tests/test_chat_profile_admin.py +38 -0
- arthexis-0.1.8/tests/test_chat_profile_api.py +31 -0
- arthexis-0.1.8/tests/test_dist_cleanup.py +32 -0
- arthexis-0.1.8/tests/test_env_refresh_clean.py +19 -0
- arthexis-0.1.8/tests/test_experience_admin_group.py +27 -0
- arthexis-0.1.8/tests/test_fixture_check.py +62 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_install_script.py +21 -1
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_reference_qr_code.py +1 -1
- arthexis-0.1.8/tests/test_release_progress.py +80 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_rfid_background_reader.py +15 -5
- arthexis-0.1.8/tests/test_show_leads_command.py +63 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_sigil_resolution.py +28 -0
- arthexis-0.1.8/tests/test_update_fixtures_command.py +28 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_user_datum_admin.py +0 -1
- arthexis-0.1.8/tests/test_version_file.py +23 -0
- arthexis-0.1.6/tests/test_post_office_admin_group.py → arthexis-0.1.8/tests/test_workgroup_admin_group.py +18 -3
- arthexis-0.1.6/README.md +0 -18
- arthexis-0.1.6/tests/test_env_refresh_clean.py +0 -30
- arthexis-0.1.6/tests/test_footer_admin_link.py +0 -91
- arthexis-0.1.6/tests/test_github_token.py +0 -29
- arthexis-0.1.6/tests/test_package_release_admin_actions.py +0 -199
- arthexis-0.1.6/tests/test_release_fixture_cleanup.py +0 -37
- arthexis-0.1.6/tests/test_release_mapping.py +0 -24
- arthexis-0.1.6/tests/test_release_progress.py +0 -165
- {arthexis-0.1.6 → arthexis-0.1.8}/LICENSE +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/arthexis.egg-info/dependency_links.txt +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/arthexis.egg-info/requires.txt +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/arthexis.egg-info/top_level.txt +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/config/__init__.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/config/active_app.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/config/asgi.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/config/auth_app.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/config/context_processors.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/config/loadenv.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/config/middleware.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/config/offline.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/config/wsgi.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/__init__.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/backends.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/environment.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/fields.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/lcd_screen.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/middleware.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/notifications.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/tasks.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/urls.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/core/user_data.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/nodes/__init__.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/nodes/actions.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/nodes/lcd.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/nodes/tasks.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/nodes/urls.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/nodes/utils.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/nodes/views.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/__init__.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/admin.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/consumers.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/evcs.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/routing.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/store.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/tasks.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/test_export_import.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/test_rfid.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/transactions_io.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/urls.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/ocpp/views.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/pages/__init__.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/pages/checks.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/pages/context_processors.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/pages/models.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/pages/urls.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/pages/utils.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/setup.cfg +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_acronym_capitalization.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_admin_history.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_admin_index_actions.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_awg_admin.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_csrf_failure.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_csrf_origin_subnet.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_email_collector.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_email_inbox.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_email_inbox_admin.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_email_inbox_search_action.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_env_refresh_unlink.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_footer_no_references.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_footer_presence.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_footer_render.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_language_switch.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_lcd_smbus2.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_localhost_admin_backend.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_model_verbose_name_capitalization.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_notifications_fallback.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_notify_command.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_odoo_profile.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_odoo_profile_admin.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_offline.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_pypi_token.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_readme_language.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_reference_transaction_uuid.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_register_site_apps_command.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_release_logs.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_release_tasks.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_rfid_admin_reference_clear.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_rfid_admin_scan_csrf.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_save_as_copy.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_seed_data.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/tests/test_urls_autodiscover.py +0 -0
- {arthexis-0.1.6 → arthexis-0.1.8}/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.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: Django-based MESH system
|
|
5
5
|
Author-email: "Rafael J. Guillén-Osorio" <tecnologia@gelectriic.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -109,18 +109,22 @@ Dynamic: license-file
|
|
|
109
109
|
# Arthexis Constellation
|
|
110
110
|
|
|
111
111
|
## Purpose
|
|
112
|
-
|
|
112
|
+
|
|
113
|
+
Arthexis Constellation is a [narrative-driven](https://en.wikipedia.org/wiki/Narrative) [Django](https://www.djangoproject.com/)-based [software suite](https://en.wikipedia.org/wiki/Software_suite) that centralizes tools for managing [electric vehicle charging infrastructure](https://en.wikipedia.org/wiki/Charging_station) and orchestrating [energy](https://en.wikipedia.org/wiki/Energy)-related [products](https://en.wikipedia.org/wiki/Product_(business)) and [services](https://en.wikipedia.org/wiki/Service_(economics)).
|
|
113
114
|
|
|
114
115
|
## Features
|
|
115
|
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
116
|
+
|
|
117
|
+
- Compatible with the [Open Charge Point Protocol (OCPP) 1.6](https://www.openchargealliance.org/protocols/ocpp-16/)
|
|
118
|
+
- [API](https://en.wikipedia.org/wiki/API) integration with [Odoo](https://www.odoo.com/) 1.6
|
|
119
|
+
- Runs on [Windows 11](https://www.microsoft.com/windows/windows-11) and [Ubuntu 22.04 LTS](https://releases.ubuntu.com/22.04/)
|
|
120
|
+
- Tested for the [Raspberry Pi 4 Model B](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/)
|
|
119
121
|
|
|
120
122
|
## Support
|
|
121
|
-
|
|
123
|
+
|
|
124
|
+
Contact us at [tecnologia@gelectriic.com](mailto:tecnologia@gelectriic.com) or visit our [web page](https://www.gelectriic.com/) for [professional services](https://en.wikipedia.org/wiki/Professional_services) and [commercial support](https://en.wikipedia.org/wiki/Technical_support).
|
|
122
125
|
|
|
123
126
|
## About Me
|
|
124
|
-
|
|
127
|
+
|
|
128
|
+
> "What, you want to know about me too? Well, I enjoy [developing software](https://en.wikipedia.org/wiki/Software_development), [role-playing games](https://en.wikipedia.org/wiki/Role-playing_game), long walks on the [beach](https://en.wikipedia.org/wiki/Beach) and a fourth secret thing."
|
|
125
129
|
> --Arthexis
|
|
126
130
|
|
arthexis-0.1.8/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Arthexis Constellation
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Arthexis Constellation is a [narrative-driven](https://en.wikipedia.org/wiki/Narrative) [Django](https://www.djangoproject.com/)-based [software suite](https://en.wikipedia.org/wiki/Software_suite) that centralizes tools for managing [electric vehicle charging infrastructure](https://en.wikipedia.org/wiki/Charging_station) and orchestrating [energy](https://en.wikipedia.org/wiki/Energy)-related [products](https://en.wikipedia.org/wiki/Product_(business)) and [services](https://en.wikipedia.org/wiki/Service_(economics)).
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Compatible with the [Open Charge Point Protocol (OCPP) 1.6](https://www.openchargealliance.org/protocols/ocpp-16/)
|
|
10
|
+
- [API](https://en.wikipedia.org/wiki/API) integration with [Odoo](https://www.odoo.com/) 1.6
|
|
11
|
+
- Runs on [Windows 11](https://www.microsoft.com/windows/windows-11) and [Ubuntu 22.04 LTS](https://releases.ubuntu.com/22.04/)
|
|
12
|
+
- Tested for the [Raspberry Pi 4 Model B](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/)
|
|
13
|
+
|
|
14
|
+
## Support
|
|
15
|
+
|
|
16
|
+
Contact us at [tecnologia@gelectriic.com](mailto:tecnologia@gelectriic.com) or visit our [web page](https://www.gelectriic.com/) for [professional services](https://en.wikipedia.org/wiki/Professional_services) and [commercial support](https://en.wikipedia.org/wiki/Technical_support).
|
|
17
|
+
|
|
18
|
+
## About Me
|
|
19
|
+
|
|
20
|
+
> "What, you want to know about me too? Well, I enjoy [developing software](https://en.wikipedia.org/wiki/Software_development), [role-playing games](https://en.wikipedia.org/wiki/Role-playing_game), long walks on the [beach](https://en.wikipedia.org/wiki/Beach) and a fourth secret thing."
|
|
21
|
+
> --Arthexis
|
|
22
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arthexis
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: Django-based MESH system
|
|
5
5
|
Author-email: "Rafael J. Guillén-Osorio" <tecnologia@gelectriic.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -109,18 +109,22 @@ Dynamic: license-file
|
|
|
109
109
|
# Arthexis Constellation
|
|
110
110
|
|
|
111
111
|
## Purpose
|
|
112
|
-
|
|
112
|
+
|
|
113
|
+
Arthexis Constellation is a [narrative-driven](https://en.wikipedia.org/wiki/Narrative) [Django](https://www.djangoproject.com/)-based [software suite](https://en.wikipedia.org/wiki/Software_suite) that centralizes tools for managing [electric vehicle charging infrastructure](https://en.wikipedia.org/wiki/Charging_station) and orchestrating [energy](https://en.wikipedia.org/wiki/Energy)-related [products](https://en.wikipedia.org/wiki/Product_(business)) and [services](https://en.wikipedia.org/wiki/Service_(economics)).
|
|
113
114
|
|
|
114
115
|
## Features
|
|
115
|
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
116
|
+
|
|
117
|
+
- Compatible with the [Open Charge Point Protocol (OCPP) 1.6](https://www.openchargealliance.org/protocols/ocpp-16/)
|
|
118
|
+
- [API](https://en.wikipedia.org/wiki/API) integration with [Odoo](https://www.odoo.com/) 1.6
|
|
119
|
+
- Runs on [Windows 11](https://www.microsoft.com/windows/windows-11) and [Ubuntu 22.04 LTS](https://releases.ubuntu.com/22.04/)
|
|
120
|
+
- Tested for the [Raspberry Pi 4 Model B](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/)
|
|
119
121
|
|
|
120
122
|
## Support
|
|
121
|
-
|
|
123
|
+
|
|
124
|
+
Contact us at [tecnologia@gelectriic.com](mailto:tecnologia@gelectriic.com) or visit our [web page](https://www.gelectriic.com/) for [professional services](https://en.wikipedia.org/wiki/Professional_services) and [commercial support](https://en.wikipedia.org/wiki/Technical_support).
|
|
122
125
|
|
|
123
126
|
## About Me
|
|
124
|
-
|
|
127
|
+
|
|
128
|
+
> "What, you want to know about me too? Well, I enjoy [developing software](https://en.wikipedia.org/wiki/Software_development), [role-playing games](https://en.wikipedia.org/wiki/Role-playing_game), long walks on the [beach](https://en.wikipedia.org/wiki/Beach) and a fourth secret thing."
|
|
125
129
|
> --Arthexis
|
|
126
130
|
|
|
@@ -12,17 +12,21 @@ config/asgi.py
|
|
|
12
12
|
config/auth_app.py
|
|
13
13
|
config/celery.py
|
|
14
14
|
config/context_processors.py
|
|
15
|
+
config/horologia_app.py
|
|
15
16
|
config/loadenv.py
|
|
16
17
|
config/logging.py
|
|
17
18
|
config/middleware.py
|
|
18
19
|
config/offline.py
|
|
19
20
|
config/settings.py
|
|
20
21
|
config/urls.py
|
|
22
|
+
config/workgroup_app.py
|
|
21
23
|
config/wsgi.py
|
|
22
24
|
core/__init__.py
|
|
23
25
|
core/admin.py
|
|
26
|
+
core/admindocs.py
|
|
24
27
|
core/apps.py
|
|
25
28
|
core/backends.py
|
|
29
|
+
core/checks.py
|
|
26
30
|
core/entity.py
|
|
27
31
|
core/environment.py
|
|
28
32
|
core/fields.py
|
|
@@ -33,10 +37,13 @@ core/notifications.py
|
|
|
33
37
|
core/release.py
|
|
34
38
|
core/system.py
|
|
35
39
|
core/tasks.py
|
|
40
|
+
core/test_system_info.py
|
|
36
41
|
core/tests.py
|
|
37
42
|
core/urls.py
|
|
38
43
|
core/user_data.py
|
|
39
44
|
core/views.py
|
|
45
|
+
core/workgroup_urls.py
|
|
46
|
+
core/workgroup_views.py
|
|
40
47
|
nodes/__init__.py
|
|
41
48
|
nodes/actions.py
|
|
42
49
|
nodes/admin.py
|
|
@@ -75,22 +82,28 @@ pages/urls.py
|
|
|
75
82
|
pages/utils.py
|
|
76
83
|
pages/views.py
|
|
77
84
|
tests/test_acronym_capitalization.py
|
|
85
|
+
tests/test_admin_doc_commands.py
|
|
78
86
|
tests/test_admin_history.py
|
|
79
87
|
tests/test_admin_index_actions.py
|
|
80
88
|
tests/test_awg_admin.py
|
|
89
|
+
tests/test_business_admin_group.py
|
|
90
|
+
tests/test_celery_no_debug.py
|
|
91
|
+
tests/test_chat_profile_admin.py
|
|
92
|
+
tests/test_chat_profile_api.py
|
|
81
93
|
tests/test_csrf_failure.py
|
|
82
94
|
tests/test_csrf_origin_subnet.py
|
|
95
|
+
tests/test_dist_cleanup.py
|
|
83
96
|
tests/test_email_collector.py
|
|
84
97
|
tests/test_email_inbox.py
|
|
85
98
|
tests/test_email_inbox_admin.py
|
|
86
99
|
tests/test_email_inbox_search_action.py
|
|
87
100
|
tests/test_env_refresh_clean.py
|
|
88
101
|
tests/test_env_refresh_unlink.py
|
|
89
|
-
tests/
|
|
102
|
+
tests/test_experience_admin_group.py
|
|
103
|
+
tests/test_fixture_check.py
|
|
90
104
|
tests/test_footer_no_references.py
|
|
91
105
|
tests/test_footer_presence.py
|
|
92
106
|
tests/test_footer_render.py
|
|
93
|
-
tests/test_github_token.py
|
|
94
107
|
tests/test_install_script.py
|
|
95
108
|
tests/test_language_switch.py
|
|
96
109
|
tests/test_lcd_smbus2.py
|
|
@@ -101,16 +114,12 @@ tests/test_notify_command.py
|
|
|
101
114
|
tests/test_odoo_profile.py
|
|
102
115
|
tests/test_odoo_profile_admin.py
|
|
103
116
|
tests/test_offline.py
|
|
104
|
-
tests/test_package_release_admin_actions.py
|
|
105
|
-
tests/test_post_office_admin_group.py
|
|
106
117
|
tests/test_pypi_token.py
|
|
107
118
|
tests/test_readme_language.py
|
|
108
119
|
tests/test_reference_qr_code.py
|
|
109
120
|
tests/test_reference_transaction_uuid.py
|
|
110
121
|
tests/test_register_site_apps_command.py
|
|
111
|
-
tests/test_release_fixture_cleanup.py
|
|
112
122
|
tests/test_release_logs.py
|
|
113
|
-
tests/test_release_mapping.py
|
|
114
123
|
tests/test_release_progress.py
|
|
115
124
|
tests/test_release_tasks.py
|
|
116
125
|
tests/test_rfid_admin_reference_clear.py
|
|
@@ -118,7 +127,11 @@ tests/test_rfid_admin_scan_csrf.py
|
|
|
118
127
|
tests/test_rfid_background_reader.py
|
|
119
128
|
tests/test_save_as_copy.py
|
|
120
129
|
tests/test_seed_data.py
|
|
130
|
+
tests/test_show_leads_command.py
|
|
121
131
|
tests/test_sigil_resolution.py
|
|
132
|
+
tests/test_update_fixtures_command.py
|
|
122
133
|
tests/test_urls_autodiscover.py
|
|
123
134
|
tests/test_user_datum_admin.py
|
|
124
|
-
tests/
|
|
135
|
+
tests/test_version_file.py
|
|
136
|
+
tests/test_vscode_manage.py
|
|
137
|
+
tests/test_workgroup_admin_group.py
|
|
@@ -7,6 +7,13 @@ from celery import Celery
|
|
|
7
7
|
|
|
8
8
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
|
9
9
|
|
|
10
|
+
# When running on production-oriented nodes, avoid Celery debug mode.
|
|
11
|
+
NODE_ROLE = os.environ.get("NODE_ROLE", "")
|
|
12
|
+
if NODE_ROLE in {"Constellation", "Satellite", "Virtual"}:
|
|
13
|
+
for var in ["CELERY_TRACE_APP", "CELERY_DEBUG"]:
|
|
14
|
+
os.environ.pop(var, None)
|
|
15
|
+
os.environ.setdefault("CELERY_LOG_LEVEL", "INFO")
|
|
16
|
+
|
|
10
17
|
app = Celery("config")
|
|
11
18
|
app.config_from_object("django.conf:settings", namespace="CELERY")
|
|
12
19
|
app.autodiscover_tasks()
|
|
@@ -13,14 +13,17 @@ class ActiveAppFileHandler(TimedRotatingFileHandler):
|
|
|
13
13
|
"""File handler that writes to a file named after the active app."""
|
|
14
14
|
|
|
15
15
|
def _current_file(self) -> Path:
|
|
16
|
+
log_dir = Path(settings.LOG_DIR)
|
|
17
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
16
18
|
if "test" in sys.argv:
|
|
17
|
-
return
|
|
18
|
-
return
|
|
19
|
+
return log_dir / "tests.log"
|
|
20
|
+
return log_dir / f"{get_active_app()}.log"
|
|
19
21
|
|
|
20
22
|
def emit(self, record: logging.LogRecord) -> None:
|
|
21
23
|
current = str(self._current_file())
|
|
22
24
|
if self.baseFilename != current:
|
|
23
25
|
self.baseFilename = current
|
|
26
|
+
Path(self.baseFilename).parent.mkdir(parents=True, exist_ok=True)
|
|
24
27
|
if self.stream:
|
|
25
28
|
self.stream.close()
|
|
26
29
|
self.stream = self._open()
|
|
@@ -29,7 +32,9 @@ class ActiveAppFileHandler(TimedRotatingFileHandler):
|
|
|
29
32
|
def rotation_filename(self, default_name: str) -> str:
|
|
30
33
|
"""Place rotated logs inside the old log directory."""
|
|
31
34
|
default_path = Path(default_name)
|
|
32
|
-
|
|
35
|
+
old_log_dir = Path(settings.OLD_LOG_DIR)
|
|
36
|
+
old_log_dir.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
return str(old_log_dir / default_path.name)
|
|
33
38
|
|
|
34
39
|
def getFilesToDelete(self):
|
|
35
40
|
"""Return files to delete in the old log directory respecting backupCount."""
|
|
@@ -85,6 +85,7 @@ ALLOWED_HOSTS = [
|
|
|
85
85
|
"10.42.0.0/16",
|
|
86
86
|
"192.168.0.0/16",
|
|
87
87
|
"arthexis.com",
|
|
88
|
+
"www.arthexis.com",
|
|
88
89
|
]
|
|
89
90
|
|
|
90
91
|
|
|
@@ -148,8 +149,8 @@ INSTALLED_APPS = [
|
|
|
148
149
|
"django_object_actions",
|
|
149
150
|
"django.contrib.sites",
|
|
150
151
|
"channels",
|
|
151
|
-
"
|
|
152
|
-
"
|
|
152
|
+
"config.workgroup_app.WorkgroupConfig",
|
|
153
|
+
"config.horologia_app.HorologiaConfig",
|
|
153
154
|
] + LOCAL_APPS
|
|
154
155
|
|
|
155
156
|
if DEBUG:
|
|
@@ -13,12 +13,15 @@ from django.apps import apps
|
|
|
13
13
|
from django.conf import settings
|
|
14
14
|
from django.conf.urls.static import static
|
|
15
15
|
from django.contrib import admin
|
|
16
|
+
from django.contrib.admin import autodiscover
|
|
16
17
|
from django.urls import include, path
|
|
17
18
|
from django.views.decorators.csrf import csrf_exempt
|
|
18
19
|
from django.views.i18n import set_language
|
|
19
20
|
from django.utils.translation import gettext_lazy as _
|
|
20
21
|
from core import views as core_views
|
|
22
|
+
from core.admindocs import CommandsView
|
|
21
23
|
|
|
24
|
+
autodiscover()
|
|
22
25
|
admin.site.site_header = _("Constellation")
|
|
23
26
|
admin.site.site_title = _("Constellation")
|
|
24
27
|
|
|
@@ -61,6 +64,11 @@ def autodiscovered_urlpatterns():
|
|
|
61
64
|
|
|
62
65
|
|
|
63
66
|
urlpatterns = [
|
|
67
|
+
path(
|
|
68
|
+
"admin/doc/commands/",
|
|
69
|
+
CommandsView.as_view(),
|
|
70
|
+
name="django-admindocs-commands",
|
|
71
|
+
),
|
|
64
72
|
path("admin/doc/", include("django.contrib.admindocs.urls")),
|
|
65
73
|
path(
|
|
66
74
|
"admin/core/releases/<int:pk>/<str:action>/",
|
|
@@ -69,6 +77,7 @@ urlpatterns = [
|
|
|
69
77
|
),
|
|
70
78
|
path("admin/", admin.site.urls),
|
|
71
79
|
path("i18n/setlang/", csrf_exempt(set_language), name="set_language"),
|
|
80
|
+
path("api/", include("core.workgroup_urls")),
|
|
72
81
|
path("", include("pages.urls")),
|
|
73
82
|
]
|
|
74
83
|
|
|
@@ -3,7 +3,7 @@ from django.contrib import admin
|
|
|
3
3
|
from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
|
|
4
4
|
from django.urls import path, reverse
|
|
5
5
|
from django.shortcuts import redirect, render
|
|
6
|
-
from django.http import JsonResponse, HttpResponseBase
|
|
6
|
+
from django.http import JsonResponse, HttpResponseBase, HttpResponseRedirect
|
|
7
7
|
from django.template.response import TemplateResponse
|
|
8
8
|
from django.views.decorators.csrf import csrf_exempt
|
|
9
9
|
from django.core.exceptions import ValidationError
|
|
@@ -21,6 +21,7 @@ from django.templatetags.static import static
|
|
|
21
21
|
from django.utils.html import format_html
|
|
22
22
|
import json
|
|
23
23
|
import uuid
|
|
24
|
+
import requests
|
|
24
25
|
from django_object_actions import DjangoObjectActions
|
|
25
26
|
from .user_data import UserDatumAdminMixin
|
|
26
27
|
from .models import (
|
|
@@ -43,6 +44,8 @@ from .models import (
|
|
|
43
44
|
PackageRelease,
|
|
44
45
|
ReleaseManager,
|
|
45
46
|
SecurityGroup,
|
|
47
|
+
InviteLead,
|
|
48
|
+
ChatProfile,
|
|
46
49
|
)
|
|
47
50
|
from .user_data import UserDatumAdminMixin
|
|
48
51
|
|
|
@@ -50,6 +53,30 @@ from .user_data import UserDatumAdminMixin
|
|
|
50
53
|
admin.site.unregister(Group)
|
|
51
54
|
|
|
52
55
|
|
|
56
|
+
class WorkgroupReleaseManager(ReleaseManager):
|
|
57
|
+
class Meta:
|
|
58
|
+
proxy = True
|
|
59
|
+
app_label = "post_office"
|
|
60
|
+
verbose_name = ReleaseManager._meta.verbose_name
|
|
61
|
+
verbose_name_plural = ReleaseManager._meta.verbose_name_plural
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class WorkgroupSecurityGroup(SecurityGroup):
|
|
65
|
+
class Meta:
|
|
66
|
+
proxy = True
|
|
67
|
+
app_label = "post_office"
|
|
68
|
+
verbose_name = SecurityGroup._meta.verbose_name
|
|
69
|
+
verbose_name_plural = SecurityGroup._meta.verbose_name_plural
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ExperienceReference(Reference):
|
|
73
|
+
class Meta:
|
|
74
|
+
proxy = True
|
|
75
|
+
app_label = "pages"
|
|
76
|
+
verbose_name = Reference._meta.verbose_name
|
|
77
|
+
verbose_name_plural = Reference._meta.verbose_name_plural
|
|
78
|
+
|
|
79
|
+
|
|
53
80
|
class SaveBeforeChangeAction(DjangoObjectActions):
|
|
54
81
|
def response_change(self, request, obj):
|
|
55
82
|
action = request.POST.get("_action")
|
|
@@ -63,7 +90,7 @@ class SaveBeforeChangeAction(DjangoObjectActions):
|
|
|
63
90
|
return super().response_change(request, obj)
|
|
64
91
|
|
|
65
92
|
|
|
66
|
-
@admin.register(
|
|
93
|
+
@admin.register(ExperienceReference)
|
|
67
94
|
class ReferenceAdmin(admin.ModelAdmin):
|
|
68
95
|
list_display = (
|
|
69
96
|
"alt_text",
|
|
@@ -139,14 +166,20 @@ class ReferenceAdmin(admin.ModelAdmin):
|
|
|
139
166
|
qr_code.short_description = "QR Code"
|
|
140
167
|
|
|
141
168
|
|
|
142
|
-
@admin.register(
|
|
169
|
+
@admin.register(WorkgroupReleaseManager)
|
|
143
170
|
class ReleaseManagerAdmin(admin.ModelAdmin):
|
|
144
171
|
list_display = ("user", "pypi_username", "pypi_url")
|
|
145
172
|
|
|
146
173
|
|
|
147
174
|
@admin.register(Package)
|
|
148
175
|
class PackageAdmin(SaveBeforeChangeAction, admin.ModelAdmin):
|
|
149
|
-
list_display = (
|
|
176
|
+
list_display = (
|
|
177
|
+
"name",
|
|
178
|
+
"description",
|
|
179
|
+
"homepage_url",
|
|
180
|
+
"release_manager",
|
|
181
|
+
"is_active",
|
|
182
|
+
)
|
|
150
183
|
actions = ["prepare_next_release"]
|
|
151
184
|
change_actions = ["prepare_next_release_action"]
|
|
152
185
|
|
|
@@ -175,6 +208,24 @@ class PackageAdmin(SaveBeforeChangeAction, admin.ModelAdmin):
|
|
|
175
208
|
reverse("admin:core_packagerelease_change", args=[release.pk])
|
|
176
209
|
)
|
|
177
210
|
|
|
211
|
+
def get_urls(self):
|
|
212
|
+
urls = super().get_urls()
|
|
213
|
+
custom = [
|
|
214
|
+
path(
|
|
215
|
+
"prepare-next-release/",
|
|
216
|
+
self.admin_site.admin_view(self.prepare_next_release_active),
|
|
217
|
+
name="core_package_prepare_next_release",
|
|
218
|
+
)
|
|
219
|
+
]
|
|
220
|
+
return custom + urls
|
|
221
|
+
|
|
222
|
+
def prepare_next_release_active(self, request):
|
|
223
|
+
package = Package.objects.filter(is_active=True).first()
|
|
224
|
+
if not package:
|
|
225
|
+
self.message_user(request, "No active package", messages.ERROR)
|
|
226
|
+
return redirect("admin:core_package_changelist")
|
|
227
|
+
return self._prepare(request, package)
|
|
228
|
+
|
|
178
229
|
@admin.action(description="Prepare next Release")
|
|
179
230
|
def prepare_next_release(self, request, queryset):
|
|
180
231
|
if queryset.count() != 1:
|
|
@@ -199,7 +250,7 @@ class SecurityGroupAdminForm(forms.ModelForm):
|
|
|
199
250
|
)
|
|
200
251
|
|
|
201
252
|
class Meta:
|
|
202
|
-
model =
|
|
253
|
+
model = WorkgroupSecurityGroup
|
|
203
254
|
fields = "__all__"
|
|
204
255
|
|
|
205
256
|
def __init__(self, *args, **kwargs):
|
|
@@ -217,13 +268,27 @@ class SecurityGroupAdminForm(forms.ModelForm):
|
|
|
217
268
|
return instance
|
|
218
269
|
|
|
219
270
|
|
|
220
|
-
@admin.register(
|
|
271
|
+
@admin.register(WorkgroupSecurityGroup)
|
|
221
272
|
class SecurityGroupAdmin(DjangoGroupAdmin):
|
|
222
273
|
form = SecurityGroupAdminForm
|
|
223
274
|
fieldsets = ((None, {"fields": ("name", "parent", "users", "permissions")}),)
|
|
224
275
|
filter_horizontal = ("permissions",)
|
|
225
276
|
|
|
226
277
|
|
|
278
|
+
@admin.register(InviteLead)
|
|
279
|
+
class InviteLeadAdmin(admin.ModelAdmin):
|
|
280
|
+
list_display = ("email", "created_on")
|
|
281
|
+
search_fields = ("email", "comment")
|
|
282
|
+
readonly_fields = (
|
|
283
|
+
"created_on",
|
|
284
|
+
"user",
|
|
285
|
+
"path",
|
|
286
|
+
"referer",
|
|
287
|
+
"user_agent",
|
|
288
|
+
"ip_address",
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
227
292
|
class EnergyAccountRFIDForm(forms.ModelForm):
|
|
228
293
|
"""Form for assigning existing RFIDs to an energy account."""
|
|
229
294
|
|
|
@@ -501,6 +566,46 @@ class EmailInboxAdmin(admin.ModelAdmin):
|
|
|
501
566
|
return TemplateResponse(request, "admin/core/emailinbox/search.html", context)
|
|
502
567
|
|
|
503
568
|
|
|
569
|
+
class WorkgroupChatProfile(ChatProfile):
|
|
570
|
+
class Meta:
|
|
571
|
+
proxy = True
|
|
572
|
+
app_label = "post_office"
|
|
573
|
+
verbose_name = ChatProfile._meta.verbose_name
|
|
574
|
+
verbose_name_plural = ChatProfile._meta.verbose_name_plural
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
@admin.register(WorkgroupChatProfile)
|
|
578
|
+
class ChatProfileAdmin(admin.ModelAdmin):
|
|
579
|
+
list_display = ("user", "created_at", "last_used_at", "is_active")
|
|
580
|
+
readonly_fields = ("user_key_hash",)
|
|
581
|
+
|
|
582
|
+
change_form_template = "admin/workgroupchatprofile_change_form.html"
|
|
583
|
+
|
|
584
|
+
def get_urls(self):
|
|
585
|
+
urls = super().get_urls()
|
|
586
|
+
custom = [
|
|
587
|
+
path(
|
|
588
|
+
"<path:object_id>/generate-key/",
|
|
589
|
+
self.admin_site.admin_view(self.generate_key),
|
|
590
|
+
name="post_office_workgroupchatprofile_generate_key",
|
|
591
|
+
),
|
|
592
|
+
]
|
|
593
|
+
return custom + urls
|
|
594
|
+
|
|
595
|
+
def generate_key(self, request, object_id, *args, **kwargs):
|
|
596
|
+
profile = self.get_object(request, object_id)
|
|
597
|
+
if profile is None:
|
|
598
|
+
return HttpResponseRedirect("../")
|
|
599
|
+
profile, key = ChatProfile.issue_key(profile.user)
|
|
600
|
+
context = {
|
|
601
|
+
**self.admin_site.each_context(request),
|
|
602
|
+
"opts": self.model._meta,
|
|
603
|
+
"original": profile,
|
|
604
|
+
"user_key": key,
|
|
605
|
+
}
|
|
606
|
+
return TemplateResponse(request, "admin/chatprofile_key.html", context)
|
|
607
|
+
|
|
608
|
+
|
|
504
609
|
class EnergyCreditInline(admin.TabularInline):
|
|
505
610
|
model = EnergyCredit
|
|
506
611
|
fields = ("amount_kw", "created_by", "created_on")
|
|
@@ -704,6 +809,7 @@ class RFIDForm(forms.ModelForm):
|
|
|
704
809
|
super().__init__(*args, **kwargs)
|
|
705
810
|
self.fields["reference"].required = False
|
|
706
811
|
rel = RFID._meta.get_field("reference").remote_field
|
|
812
|
+
rel.model = ExperienceReference
|
|
707
813
|
widget = self.fields["reference"].widget
|
|
708
814
|
self.fields["reference"].widget = RelatedFieldWidgetWrapper(
|
|
709
815
|
widget,
|
|
@@ -784,17 +890,17 @@ class RFIDAdmin(ImportExportModelAdmin):
|
|
|
784
890
|
class PackageReleaseAdmin(SaveBeforeChangeAction, admin.ModelAdmin):
|
|
785
891
|
list_display = (
|
|
786
892
|
"version",
|
|
787
|
-
"
|
|
893
|
+
"package_link",
|
|
788
894
|
"is_current",
|
|
789
895
|
"pypi_url",
|
|
790
|
-
"pr_link",
|
|
791
896
|
"revision_short",
|
|
792
897
|
"published_status",
|
|
793
898
|
)
|
|
794
899
|
list_display_links = ("version",)
|
|
795
|
-
actions = ["publish_release"]
|
|
900
|
+
actions = ["publish_release", "validate_releases"]
|
|
796
901
|
change_actions = ["publish_release_action"]
|
|
797
|
-
|
|
902
|
+
changelist_actions = ["refresh_from_pypi"]
|
|
903
|
+
readonly_fields = ("pypi_url", "is_current", "revision")
|
|
798
904
|
fields = (
|
|
799
905
|
"package",
|
|
800
906
|
"release_manager",
|
|
@@ -802,14 +908,58 @@ class PackageReleaseAdmin(SaveBeforeChangeAction, admin.ModelAdmin):
|
|
|
802
908
|
"revision",
|
|
803
909
|
"is_current",
|
|
804
910
|
"pypi_url",
|
|
805
|
-
"pr_url",
|
|
806
911
|
)
|
|
807
912
|
|
|
913
|
+
@admin.display(description="package", ordering="package")
|
|
914
|
+
def package_link(self, obj):
|
|
915
|
+
url = reverse("admin:core_package_change", args=[obj.package_id])
|
|
916
|
+
return format_html('<a href="{}">{}</a>', url, obj.package)
|
|
917
|
+
|
|
808
918
|
def revision_short(self, obj):
|
|
809
919
|
return obj.revision_short
|
|
810
920
|
|
|
811
921
|
revision_short.short_description = "revision"
|
|
812
922
|
|
|
923
|
+
def refresh_from_pypi(self, request, queryset):
|
|
924
|
+
package = Package.objects.filter(is_active=True).first()
|
|
925
|
+
if not package:
|
|
926
|
+
self.message_user(request, "No active package", messages.ERROR)
|
|
927
|
+
return
|
|
928
|
+
try:
|
|
929
|
+
resp = requests.get(
|
|
930
|
+
f"https://pypi.org/pypi/{package.name}/json", timeout=10
|
|
931
|
+
)
|
|
932
|
+
resp.raise_for_status()
|
|
933
|
+
except Exception as exc: # pragma: no cover - network failure
|
|
934
|
+
self.message_user(request, str(exc), messages.ERROR)
|
|
935
|
+
return
|
|
936
|
+
releases = resp.json().get("releases", {})
|
|
937
|
+
created = 0
|
|
938
|
+
for version in releases:
|
|
939
|
+
exists = PackageRelease.all_objects.filter(
|
|
940
|
+
package=package, version=version
|
|
941
|
+
).exists()
|
|
942
|
+
if not exists:
|
|
943
|
+
PackageRelease.objects.create(
|
|
944
|
+
package=package,
|
|
945
|
+
release_manager=package.release_manager,
|
|
946
|
+
version=version,
|
|
947
|
+
pypi_url=f"https://pypi.org/project/{package.name}/{version}/",
|
|
948
|
+
)
|
|
949
|
+
created += 1
|
|
950
|
+
if created:
|
|
951
|
+
PackageRelease.dump_fixture()
|
|
952
|
+
self.message_user(
|
|
953
|
+
request,
|
|
954
|
+
f"Created {created} release{'s' if created != 1 else ''} from PyPI",
|
|
955
|
+
messages.SUCCESS,
|
|
956
|
+
)
|
|
957
|
+
else:
|
|
958
|
+
self.message_user(request, "No new releases found", messages.INFO)
|
|
959
|
+
|
|
960
|
+
refresh_from_pypi.label = "Refresh from PyPI"
|
|
961
|
+
refresh_from_pypi.short_description = "Refresh from PyPI"
|
|
962
|
+
|
|
813
963
|
def _publish_release(self, request, release):
|
|
814
964
|
try:
|
|
815
965
|
release.full_clean()
|
|
@@ -833,6 +983,37 @@ class PackageReleaseAdmin(SaveBeforeChangeAction, admin.ModelAdmin):
|
|
|
833
983
|
publish_release_action.label = "Publish selected Release"
|
|
834
984
|
publish_release_action.short_description = "Publish this release"
|
|
835
985
|
|
|
986
|
+
@admin.action(description="Validate selected Releases")
|
|
987
|
+
def validate_releases(self, request, queryset):
|
|
988
|
+
deleted = False
|
|
989
|
+
for release in queryset:
|
|
990
|
+
if not release.pypi_url:
|
|
991
|
+
self.message_user(
|
|
992
|
+
request,
|
|
993
|
+
f"{release} has not been published yet",
|
|
994
|
+
messages.WARNING,
|
|
995
|
+
)
|
|
996
|
+
continue
|
|
997
|
+
url = (
|
|
998
|
+
f"https://pypi.org/pypi/{release.package.name}/{release.version}/json"
|
|
999
|
+
)
|
|
1000
|
+
try:
|
|
1001
|
+
resp = requests.get(url, timeout=10)
|
|
1002
|
+
except Exception as exc: # pragma: no cover - network failure
|
|
1003
|
+
self.message_user(request, f"{release}: {exc}", messages.ERROR)
|
|
1004
|
+
continue
|
|
1005
|
+
if resp.status_code == 200:
|
|
1006
|
+
continue
|
|
1007
|
+
release.delete()
|
|
1008
|
+
deleted = True
|
|
1009
|
+
self.message_user(
|
|
1010
|
+
request,
|
|
1011
|
+
f"Deleted {release} as it was not found on PyPI",
|
|
1012
|
+
messages.WARNING,
|
|
1013
|
+
)
|
|
1014
|
+
if deleted:
|
|
1015
|
+
PackageRelease.dump_fixture()
|
|
1016
|
+
|
|
836
1017
|
@staticmethod
|
|
837
1018
|
def _boolean_icon(value: bool) -> str:
|
|
838
1019
|
icon = static("admin/img/icon-yes.svg" if value else "admin/img/icon-no.svg")
|
|
@@ -847,10 +1028,4 @@ class PackageReleaseAdmin(SaveBeforeChangeAction, admin.ModelAdmin):
|
|
|
847
1028
|
def is_current(self, obj):
|
|
848
1029
|
return self._boolean_icon(obj.is_current)
|
|
849
1030
|
|
|
850
|
-
def pr_link(self, obj):
|
|
851
|
-
if obj.pr_url:
|
|
852
|
-
return format_html('<a href="{0}" target="_blank">{0}</a>', obj.pr_url)
|
|
853
|
-
return ""
|
|
854
|
-
|
|
855
|
-
pr_link.short_description = "PR URL"
|
|
856
1031
|
|