arthexis 0.1.4__tar.gz → 0.1.6__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 (126) hide show
  1. {arthexis-0.1.4 → arthexis-0.1.6}/PKG-INFO +2 -2
  2. {arthexis-0.1.4 → arthexis-0.1.6}/README.md +1 -1
  3. {arthexis-0.1.4 → arthexis-0.1.6}/arthexis.egg-info/PKG-INFO +2 -2
  4. {arthexis-0.1.4 → arthexis-0.1.6}/arthexis.egg-info/SOURCES.txt +1 -0
  5. {arthexis-0.1.4 → arthexis-0.1.6}/core/admin.py +37 -11
  6. {arthexis-0.1.4 → arthexis-0.1.6}/core/models.py +4 -3
  7. {arthexis-0.1.4 → arthexis-0.1.6}/core/views.py +4 -1
  8. {arthexis-0.1.4 → arthexis-0.1.6}/nodes/admin.py +13 -1
  9. {arthexis-0.1.4 → arthexis-0.1.6}/nodes/tests.py +0 -5
  10. {arthexis-0.1.4 → arthexis-0.1.6}/nodes/views.py +0 -1
  11. {arthexis-0.1.4 → arthexis-0.1.6}/pyproject.toml +1 -1
  12. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_email_inbox_search_action.py +5 -3
  13. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_package_release_admin_actions.py +19 -6
  14. arthexis-0.1.6/tests/test_post_office_admin_group.py +33 -0
  15. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_release_fixture_cleanup.py +5 -0
  16. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_release_progress.py +33 -1
  17. {arthexis-0.1.4 → arthexis-0.1.6}/LICENSE +0 -0
  18. {arthexis-0.1.4 → arthexis-0.1.6}/arthexis.egg-info/dependency_links.txt +0 -0
  19. {arthexis-0.1.4 → arthexis-0.1.6}/arthexis.egg-info/requires.txt +0 -0
  20. {arthexis-0.1.4 → arthexis-0.1.6}/arthexis.egg-info/top_level.txt +0 -0
  21. {arthexis-0.1.4 → arthexis-0.1.6}/config/__init__.py +0 -0
  22. {arthexis-0.1.4 → arthexis-0.1.6}/config/active_app.py +0 -0
  23. {arthexis-0.1.4 → arthexis-0.1.6}/config/asgi.py +0 -0
  24. {arthexis-0.1.4 → arthexis-0.1.6}/config/auth_app.py +0 -0
  25. {arthexis-0.1.4 → arthexis-0.1.6}/config/celery.py +0 -0
  26. {arthexis-0.1.4 → arthexis-0.1.6}/config/context_processors.py +0 -0
  27. {arthexis-0.1.4 → arthexis-0.1.6}/config/loadenv.py +0 -0
  28. {arthexis-0.1.4 → arthexis-0.1.6}/config/logging.py +0 -0
  29. {arthexis-0.1.4 → arthexis-0.1.6}/config/middleware.py +0 -0
  30. {arthexis-0.1.4 → arthexis-0.1.6}/config/offline.py +0 -0
  31. {arthexis-0.1.4 → arthexis-0.1.6}/config/settings.py +0 -0
  32. {arthexis-0.1.4 → arthexis-0.1.6}/config/urls.py +0 -0
  33. {arthexis-0.1.4 → arthexis-0.1.6}/config/wsgi.py +0 -0
  34. {arthexis-0.1.4 → arthexis-0.1.6}/core/__init__.py +0 -0
  35. {arthexis-0.1.4 → arthexis-0.1.6}/core/apps.py +0 -0
  36. {arthexis-0.1.4 → arthexis-0.1.6}/core/backends.py +0 -0
  37. {arthexis-0.1.4 → arthexis-0.1.6}/core/entity.py +0 -0
  38. {arthexis-0.1.4 → arthexis-0.1.6}/core/environment.py +0 -0
  39. {arthexis-0.1.4 → arthexis-0.1.6}/core/fields.py +0 -0
  40. {arthexis-0.1.4 → arthexis-0.1.6}/core/lcd_screen.py +0 -0
  41. {arthexis-0.1.4 → arthexis-0.1.6}/core/middleware.py +0 -0
  42. {arthexis-0.1.4 → arthexis-0.1.6}/core/notifications.py +0 -0
  43. {arthexis-0.1.4 → arthexis-0.1.6}/core/release.py +0 -0
  44. {arthexis-0.1.4 → arthexis-0.1.6}/core/system.py +0 -0
  45. {arthexis-0.1.4 → arthexis-0.1.6}/core/tasks.py +0 -0
  46. {arthexis-0.1.4 → arthexis-0.1.6}/core/tests.py +0 -0
  47. {arthexis-0.1.4 → arthexis-0.1.6}/core/urls.py +0 -0
  48. {arthexis-0.1.4 → arthexis-0.1.6}/core/user_data.py +0 -0
  49. {arthexis-0.1.4 → arthexis-0.1.6}/nodes/__init__.py +0 -0
  50. {arthexis-0.1.4 → arthexis-0.1.6}/nodes/actions.py +0 -0
  51. {arthexis-0.1.4 → arthexis-0.1.6}/nodes/apps.py +0 -0
  52. {arthexis-0.1.4 → arthexis-0.1.6}/nodes/lcd.py +0 -0
  53. {arthexis-0.1.4 → arthexis-0.1.6}/nodes/models.py +0 -0
  54. {arthexis-0.1.4 → arthexis-0.1.6}/nodes/tasks.py +0 -0
  55. {arthexis-0.1.4 → arthexis-0.1.6}/nodes/urls.py +0 -0
  56. {arthexis-0.1.4 → arthexis-0.1.6}/nodes/utils.py +0 -0
  57. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/__init__.py +0 -0
  58. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/admin.py +0 -0
  59. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/apps.py +0 -0
  60. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/consumers.py +0 -0
  61. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/evcs.py +0 -0
  62. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/models.py +0 -0
  63. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/routing.py +0 -0
  64. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/simulator.py +0 -0
  65. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/store.py +0 -0
  66. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/tasks.py +0 -0
  67. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/test_export_import.py +0 -0
  68. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/test_rfid.py +0 -0
  69. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/tests.py +0 -0
  70. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/transactions_io.py +0 -0
  71. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/urls.py +0 -0
  72. {arthexis-0.1.4 → arthexis-0.1.6}/ocpp/views.py +0 -0
  73. {arthexis-0.1.4 → arthexis-0.1.6}/pages/__init__.py +0 -0
  74. {arthexis-0.1.4 → arthexis-0.1.6}/pages/admin.py +0 -0
  75. {arthexis-0.1.4 → arthexis-0.1.6}/pages/apps.py +0 -0
  76. {arthexis-0.1.4 → arthexis-0.1.6}/pages/checks.py +0 -0
  77. {arthexis-0.1.4 → arthexis-0.1.6}/pages/context_processors.py +0 -0
  78. {arthexis-0.1.4 → arthexis-0.1.6}/pages/models.py +0 -0
  79. {arthexis-0.1.4 → arthexis-0.1.6}/pages/tests.py +0 -0
  80. {arthexis-0.1.4 → arthexis-0.1.6}/pages/urls.py +0 -0
  81. {arthexis-0.1.4 → arthexis-0.1.6}/pages/utils.py +0 -0
  82. {arthexis-0.1.4 → arthexis-0.1.6}/pages/views.py +0 -0
  83. {arthexis-0.1.4 → arthexis-0.1.6}/setup.cfg +0 -0
  84. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_acronym_capitalization.py +0 -0
  85. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_admin_history.py +0 -0
  86. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_admin_index_actions.py +0 -0
  87. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_awg_admin.py +0 -0
  88. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_csrf_failure.py +0 -0
  89. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_csrf_origin_subnet.py +0 -0
  90. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_email_collector.py +0 -0
  91. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_email_inbox.py +0 -0
  92. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_email_inbox_admin.py +0 -0
  93. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_env_refresh_clean.py +0 -0
  94. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_env_refresh_unlink.py +0 -0
  95. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_footer_admin_link.py +0 -0
  96. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_footer_no_references.py +0 -0
  97. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_footer_presence.py +0 -0
  98. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_footer_render.py +0 -0
  99. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_github_token.py +0 -0
  100. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_install_script.py +0 -0
  101. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_language_switch.py +0 -0
  102. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_lcd_smbus2.py +0 -0
  103. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_localhost_admin_backend.py +0 -0
  104. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_model_verbose_name_capitalization.py +0 -0
  105. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_notifications_fallback.py +0 -0
  106. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_notify_command.py +0 -0
  107. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_odoo_profile.py +0 -0
  108. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_odoo_profile_admin.py +0 -0
  109. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_offline.py +0 -0
  110. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_pypi_token.py +0 -0
  111. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_readme_language.py +0 -0
  112. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_reference_qr_code.py +0 -0
  113. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_reference_transaction_uuid.py +0 -0
  114. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_register_site_apps_command.py +0 -0
  115. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_release_logs.py +0 -0
  116. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_release_mapping.py +0 -0
  117. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_release_tasks.py +0 -0
  118. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_rfid_admin_reference_clear.py +0 -0
  119. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_rfid_admin_scan_csrf.py +0 -0
  120. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_rfid_background_reader.py +0 -0
  121. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_save_as_copy.py +0 -0
  122. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_seed_data.py +0 -0
  123. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_sigil_resolution.py +0 -0
  124. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_urls_autodiscover.py +0 -0
  125. {arthexis-0.1.4 → arthexis-0.1.6}/tests/test_user_datum_admin.py +0 -0
  126. {arthexis-0.1.4 → arthexis-0.1.6}/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.4
3
+ Version: 0.1.6
4
4
  Summary: Django-based MESH system
5
5
  Author-email: "Rafael J. Guillén-Osorio" <tecnologia@gelectriic.com>
6
6
  License-Expression: MIT
@@ -118,7 +118,7 @@ Arthexis Constellation is a narrative-driven Django-based suite that centralizes
118
118
  - Single codebase with special features per role
119
119
 
120
120
  ## Support
121
- You may contact us at tecnologia at gelectriic dot com or visit our web page https://www.gelectriic.com/ for professional services and commercial support.
121
+ You may contact us at tecnologia at gelectriic dot com or visit our [web page](https://www.gelectriic.com/) for professional services and commercial support.
122
122
 
123
123
  ## About Me
124
124
  > "What? You want to know about me too? Well, I enjoy developing software, roleplaying games, long walks on the beach and a fourth secret thing."
@@ -10,7 +10,7 @@ Arthexis Constellation is a narrative-driven Django-based suite that centralizes
10
10
  - Single codebase with special features per role
11
11
 
12
12
  ## Support
13
- You may contact us at tecnologia at gelectriic dot com or visit our web page https://www.gelectriic.com/ for professional services and commercial support.
13
+ You may contact us at tecnologia at gelectriic dot com or visit our [web page](https://www.gelectriic.com/) for professional services and commercial support.
14
14
 
15
15
  ## About Me
16
16
  > "What? You want to know about me too? Well, I enjoy developing software, roleplaying games, long walks on the beach and a fourth secret thing."
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arthexis
3
- Version: 0.1.4
3
+ Version: 0.1.6
4
4
  Summary: Django-based MESH system
5
5
  Author-email: "Rafael J. Guillén-Osorio" <tecnologia@gelectriic.com>
6
6
  License-Expression: MIT
@@ -118,7 +118,7 @@ Arthexis Constellation is a narrative-driven Django-based suite that centralizes
118
118
  - Single codebase with special features per role
119
119
 
120
120
  ## Support
121
- You may contact us at tecnologia at gelectriic dot com or visit our web page https://www.gelectriic.com/ for professional services and commercial support.
121
+ You may contact us at tecnologia at gelectriic dot com or visit our [web page](https://www.gelectriic.com/) for professional services and commercial support.
122
122
 
123
123
  ## About Me
124
124
  > "What? You want to know about me too? Well, I enjoy developing software, roleplaying games, long walks on the beach and a fourth secret thing."
@@ -102,6 +102,7 @@ tests/test_odoo_profile.py
102
102
  tests/test_odoo_profile_admin.py
103
103
  tests/test_offline.py
104
104
  tests/test_package_release_admin_actions.py
105
+ tests/test_post_office_admin_group.py
105
106
  tests/test_pypi_token.py
106
107
  tests/test_readme_language.py
107
108
  tests/test_reference_qr_code.py
@@ -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
6
+ from django.http import JsonResponse, HttpResponseBase
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
@@ -17,6 +17,7 @@ from import_export import resources, fields
17
17
  from import_export.admin import ImportExportModelAdmin
18
18
  from import_export.widgets import ForeignKeyWidget
19
19
  from django.contrib.auth.models import Group
20
+ from django.templatetags.static import static
20
21
  from django.utils.html import format_html
21
22
  import json
22
23
  import uuid
@@ -37,7 +38,7 @@ from .models import (
37
38
  Reference,
38
39
  OdooProfile,
39
40
  FediverseProfile,
40
- EmailInbox,
41
+ EmailInbox as CoreEmailInbox,
41
42
  Package,
42
43
  PackageRelease,
43
44
  ReleaseManager,
@@ -49,6 +50,19 @@ from .user_data import UserDatumAdminMixin
49
50
  admin.site.unregister(Group)
50
51
 
51
52
 
53
+ class SaveBeforeChangeAction(DjangoObjectActions):
54
+ def response_change(self, request, obj):
55
+ action = request.POST.get("_action")
56
+ if action:
57
+ allowed = self.get_change_actions(request, str(obj.pk), None)
58
+ if action in allowed and hasattr(self, action):
59
+ response = getattr(self, action)(request, obj)
60
+ if isinstance(response, HttpResponseBase):
61
+ return response
62
+ return redirect(request.path)
63
+ return super().response_change(request, obj)
64
+
65
+
52
66
  @admin.register(Reference)
53
67
  class ReferenceAdmin(admin.ModelAdmin):
54
68
  list_display = (
@@ -131,7 +145,7 @@ class ReleaseManagerAdmin(admin.ModelAdmin):
131
145
 
132
146
 
133
147
  @admin.register(Package)
134
- class PackageAdmin(DjangoObjectActions, admin.ModelAdmin):
148
+ class PackageAdmin(SaveBeforeChangeAction, admin.ModelAdmin):
135
149
  list_display = ("name", "description", "homepage_url", "release_manager")
136
150
  actions = ["prepare_next_release"]
137
151
  change_actions = ["prepare_next_release_action"]
@@ -366,6 +380,14 @@ class FediverseProfileAdmin(admin.ModelAdmin):
366
380
  self.message_user(request, f"{profile}: {exc}", level=messages.ERROR)
367
381
 
368
382
 
383
+ class EmailInbox(CoreEmailInbox):
384
+ class Meta:
385
+ proxy = True
386
+ app_label = "post_office"
387
+ verbose_name = CoreEmailInbox._meta.verbose_name
388
+ verbose_name_plural = CoreEmailInbox._meta.verbose_name_plural
389
+
390
+
369
391
  class EmailInboxAdminForm(forms.ModelForm):
370
392
  """Admin form for :class:`core.models.EmailInbox` with hidden password."""
371
393
 
@@ -376,7 +398,7 @@ class EmailInboxAdminForm(forms.ModelForm):
376
398
  )
377
399
 
378
400
  class Meta:
379
- model = EmailInbox
401
+ model = CoreEmailInbox
380
402
  fields = "__all__"
381
403
 
382
404
  def __init__(self, *args, **kwargs):
@@ -431,6 +453,10 @@ class EmailInboxAdmin(admin.ModelAdmin):
431
453
  ),
432
454
  )
433
455
 
456
+ def save_model(self, request, obj, form, change):
457
+ super().save_model(request, obj, form, change)
458
+ obj.__class__ = EmailInbox
459
+
434
460
  @admin.action(description="Test selected inboxes")
435
461
  def test_connection(self, request, queryset):
436
462
  for inbox in queryset:
@@ -755,7 +781,7 @@ class RFIDAdmin(ImportExportModelAdmin):
755
781
 
756
782
 
757
783
  @admin.register(PackageRelease)
758
- class PackageReleaseAdmin(DjangoObjectActions, admin.ModelAdmin):
784
+ class PackageReleaseAdmin(SaveBeforeChangeAction, admin.ModelAdmin):
759
785
  list_display = (
760
786
  "version",
761
787
  "package",
@@ -808,18 +834,18 @@ class PackageReleaseAdmin(DjangoObjectActions, admin.ModelAdmin):
808
834
  publish_release_action.short_description = "Publish this release"
809
835
 
810
836
  @staticmethod
811
- def _checkbox(value: bool) -> str:
812
- return format_html(
813
- '<input type="checkbox"{} disabled>', " checked" if value else ""
814
- )
837
+ def _boolean_icon(value: bool) -> str:
838
+ icon = static("admin/img/icon-yes.svg" if value else "admin/img/icon-no.svg")
839
+ alt = "True" if value else "False"
840
+ return format_html('<img src="{}" alt="{}">', icon, alt)
815
841
 
816
842
  @admin.display(description="Published")
817
843
  def published_status(self, obj):
818
- return self._checkbox(obj.is_published)
844
+ return self._boolean_icon(obj.is_published)
819
845
 
820
846
  @admin.display(description="Is current")
821
847
  def is_current(self, obj):
822
- return self._checkbox(obj.is_current)
848
+ return self._boolean_icon(obj.is_current)
823
849
 
824
850
  def pr_link(self, obj):
825
851
  if obj.pr_url:
@@ -10,7 +10,7 @@ from django.utils.translation import gettext_lazy as _
10
10
  from django.core.validators import RegexValidator
11
11
  from django.core.exceptions import ValidationError
12
12
  from django.apps import apps
13
- from django.db.models.signals import m2m_changed, post_delete
13
+ from django.db.models.signals import m2m_changed, post_delete, post_save
14
14
  from django.dispatch import receiver
15
15
  from datetime import timedelta
16
16
  from django.contrib.contenttypes.models import ContentType
@@ -1257,8 +1257,9 @@ class PackageRelease(Entity):
1257
1257
  return self.revision[-6:] if self.revision else ""
1258
1258
 
1259
1259
 
1260
- @receiver(post_delete, sender=PackageRelease)
1261
- def _delete_release_fixture(sender, instance, **kwargs) -> None:
1260
+ @receiver([post_save, post_delete], sender=PackageRelease)
1261
+ def _update_release_fixture(sender, instance, **kwargs) -> None:
1262
+ """Keep the release fixture in sync with the database."""
1262
1263
  PackageRelease.dump_fixture()
1263
1264
 
1264
1265
  # Ensure each RFID can only be linked to one energy account
@@ -399,7 +399,10 @@ def release_progress(request, pk: int, action: str):
399
399
  identifier = f"{release.package.name}-{release.version}"
400
400
  if release.revision:
401
401
  identifier = f"{identifier}-{release.revision[:7]}"
402
- log_name = ctx.get("log") or f"{identifier}.log"
402
+ log_name = f"{identifier}.log"
403
+ if ctx.get("log") != log_name:
404
+ ctx = {"step": 0, "log": log_name}
405
+ step_count = 0
403
406
  log_path = Path("logs") / log_name
404
407
  ctx.setdefault("log", log_name)
405
408
 
@@ -18,7 +18,7 @@ from .actions import NodeAction
18
18
 
19
19
  from .models import (
20
20
  Node,
21
- EmailOutbox,
21
+ EmailOutbox as NodeEmailOutbox,
22
22
  NodeRole,
23
23
  ContentSample,
24
24
  NodeTask,
@@ -181,10 +181,22 @@ class NodeAdmin(admin.ModelAdmin):
181
181
  return redirect(reverse("admin:nodes_node_change", args=[node_id]))
182
182
 
183
183
 
184
+ class EmailOutbox(NodeEmailOutbox):
185
+ class Meta:
186
+ proxy = True
187
+ app_label = "post_office"
188
+ verbose_name = NodeEmailOutbox._meta.verbose_name
189
+ verbose_name_plural = NodeEmailOutbox._meta.verbose_name_plural
190
+
191
+
184
192
  @admin.register(EmailOutbox)
185
193
  class EmailOutboxAdmin(admin.ModelAdmin):
186
194
  list_display = ("node", "host", "port", "username", "use_tls", "use_ssl")
187
195
 
196
+ def save_model(self, request, obj, form, change):
197
+ super().save_model(request, obj, form, change)
198
+ obj.__class__ = EmailOutbox
199
+
188
200
 
189
201
  class NodeRoleAdminForm(forms.ModelForm):
190
202
  nodes = forms.ModelMultipleChoiceField(
@@ -560,11 +560,6 @@ class NetMessageAdminTests(TransactionTestCase):
560
560
  class LastNetMessageViewTests(TestCase):
561
561
  def setUp(self):
562
562
  self.client = Client()
563
- User = get_user_model()
564
- self.user = User.objects.create_user(
565
- username="lastmsg", password="pwd"
566
- )
567
- self.client.force_login(self.user)
568
563
  NodeRole.objects.get_or_create(name="Terminal")
569
564
 
570
565
  def test_returns_latest_message(self):
@@ -252,7 +252,6 @@ def net_message(request):
252
252
  return JsonResponse({"status": "propagated", "complete": msg.complete})
253
253
 
254
254
 
255
- @api_login_required
256
255
  def last_net_message(request):
257
256
  """Return the most recent :class:`NetMessage`."""
258
257
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "arthexis"
7
- version = "0.1.4"
7
+ version = "0.1.6"
8
8
  description = "Django-based MESH system"
9
9
  requires-python = ">=3.10"
10
10
  license = "MIT"
@@ -4,7 +4,7 @@ from unittest.mock import patch
4
4
  from django.contrib.admin.sites import AdminSite
5
5
  from django.test import TestCase, RequestFactory
6
6
 
7
- from core.admin import EmailInboxAdmin
7
+ from core.admin import EmailInboxAdmin, EmailInbox as AdminEmailInbox
8
8
  from core.models import EmailInbox, User
9
9
 
10
10
 
@@ -93,11 +93,13 @@ class EmailInboxSearchTests(TestCase):
93
93
  use_ssl=True,
94
94
  )
95
95
  site = AdminSite()
96
- ma = EmailInboxAdmin(EmailInbox, site)
96
+ ma = EmailInboxAdmin(AdminEmailInbox, site)
97
97
  factory = RequestFactory()
98
98
  request = factory.post("/", {"apply": "yes", "subject": "S"})
99
99
  request.user = admin
100
- response = ma.search_inbox(request, EmailInbox.objects.filter(id=inbox.id))
100
+ response = ma.search_inbox(
101
+ request, AdminEmailInbox.objects.filter(id=inbox.id)
102
+ )
101
103
  self.assertEqual(response.status_code, 200)
102
104
  content = response.render().content.decode()
103
105
  self.assertIn("S", content)
@@ -28,12 +28,10 @@ class PackageReleaseAdminActionsTests(TestCase):
28
28
 
29
29
  def test_change_page_contains_publish_action(self):
30
30
  change_url = reverse("admin:core_packagerelease_change", args=[self.release.pk])
31
- action_url = reverse(
32
- "admin:core_packagerelease_actions",
33
- args=[self.release.pk, "publish_release_action"],
34
- )
35
31
  resp = self.client.get(change_url)
36
- self.assertContains(resp, action_url)
32
+ self.assertContains(
33
+ resp, 'name="_action" value="publish_release_action"'
34
+ )
37
35
 
38
36
  def test_publish_action_redirects(self):
39
37
  url = reverse(
@@ -45,6 +43,21 @@ class PackageReleaseAdminActionsTests(TestCase):
45
43
  resp, reverse("release-progress", args=[self.release.pk, "publish"])
46
44
  )
47
45
 
46
+ def test_publish_action_saves_changes_before_execution(self):
47
+ change_url = reverse("admin:core_packagerelease_change", args=[self.release.pk])
48
+ data = {
49
+ "package": self.package.pk,
50
+ "release_manager": "",
51
+ "version": "1.0.1",
52
+ "_action": "publish_release_action",
53
+ }
54
+ resp = self.client.post(change_url, data)
55
+ self.assertRedirects(
56
+ resp, reverse("release-progress", args=[self.release.pk, "publish"])
57
+ )
58
+ self.release.refresh_from_db()
59
+ self.assertEqual(self.release.version, "1.0.1")
60
+
48
61
  def test_change_page_pypi_url_readonly(self):
49
62
  change_url = reverse("admin:core_packagerelease_change", args=[self.release.pk])
50
63
  resp = self.client.get(change_url)
@@ -77,7 +90,7 @@ class PackageReleaseAdminActionsTests(TestCase):
77
90
  resp = self.client.get(list_url)
78
91
  content = resp.content.decode()
79
92
  self.assertIn("Is current", content)
80
- self.assertIn('<input type="checkbox" checked disabled>', content)
93
+ self.assertIn('icon-yes.svg', content)
81
94
 
82
95
  def test_release_revision_defaults_to_repo_revision(self):
83
96
  expected = revision_utils.get_revision()
@@ -0,0 +1,33 @@
1
+ from django.test import TestCase
2
+ from django.contrib.auth import get_user_model
3
+ from django.urls import reverse
4
+ from django.contrib.admin.sites import site
5
+
6
+ from core.admin import EmailInbox as PostOfficeEmailInbox
7
+ from nodes.admin import EmailOutbox as PostOfficeEmailOutbox
8
+
9
+
10
+ class PostOfficeAdminGroupTests(TestCase):
11
+ def setUp(self):
12
+ User = get_user_model()
13
+ self.admin = User.objects.create_superuser(
14
+ username="po-admin", password="pwd", email="admin@example.com"
15
+ )
16
+ self.client.force_login(self.admin)
17
+
18
+ def test_models_registered(self):
19
+ registry = site._registry
20
+ self.assertIn(PostOfficeEmailInbox, registry)
21
+ self.assertIn(PostOfficeEmailOutbox, registry)
22
+ self.assertEqual(
23
+ registry[PostOfficeEmailInbox].model._meta.app_label, "post_office"
24
+ )
25
+ self.assertEqual(
26
+ registry[PostOfficeEmailOutbox].model._meta.app_label, "post_office"
27
+ )
28
+
29
+ def test_admin_index_shows_post_office_group(self):
30
+ response = self.client.get(reverse("admin:index"))
31
+ self.assertContains(response, "Post Office")
32
+ self.assertContains(response, "Email Inboxes")
33
+ self.assertContains(response, "Email Outboxes")
@@ -30,3 +30,8 @@ class ReleaseFixtureCleanupTests(TestCase):
30
30
  data = self.fixture_path.read_text()
31
31
  self.assertNotIn("1.0.0", data)
32
32
 
33
+ def test_create_updates_fixture(self):
34
+ PackageRelease.objects.create(package=self.package, version="2.0.0")
35
+ data = self.fixture_path.read_text()
36
+ self.assertIn("2.0.0", data)
37
+
@@ -62,7 +62,12 @@ class ReleaseProgressTests(TestCase):
62
62
  self.assertContains(resp, "All steps completed")
63
63
  self.assertContains(
64
64
  resp,
65
- '<a href="http://example.com/pr/1" target="_blank">http://example.com/pr/1</a>',
65
+ '<a href="http://example.com/pr/1" target="_blank" rel="noopener">http://example.com/pr/1</a>',
66
+ html=True,
67
+ )
68
+ self.assertContains(
69
+ resp,
70
+ '<a href="https://pypi.org/project/pkg/1.0.0/" target="_blank" rel="noopener">https://pypi.org/project/pkg/1.0.0/</a>',
66
71
  html=True,
67
72
  )
68
73
  release.refresh_from_db()
@@ -94,6 +99,11 @@ class ReleaseProgressTests(TestCase):
94
99
  self.assertEqual(resp.status_code, 200)
95
100
  self.assertContains(resp, "All steps completed")
96
101
  self.assertIsNone(resp.context["pr_url"])
102
+ self.assertContains(
103
+ resp,
104
+ '<a href="https://pypi.org/project/pkg/1.1.0/" target="_blank" rel="noopener">https://pypi.org/project/pkg/1.1.0/</a>',
105
+ html=True,
106
+ )
97
107
  release.refresh_from_db()
98
108
  self.assertTrue(release.is_published)
99
109
  pub.assert_called_once()
@@ -131,3 +141,25 @@ class ReleaseProgressTests(TestCase):
131
141
  resp = self.client.get(f"{url}?step=0")
132
142
  self.assertEqual(resp.context["error"], "Version 2.0.0 is older than existing 3.0.0")
133
143
  self.assertEqual(self.version_path.read_text().strip(), "3.0.0")
144
+
145
+ def test_session_resets_when_version_changes(self):
146
+ release = PackageRelease.objects.create(package=self.package, version="1.2.0")
147
+ url = reverse("release-progress", args=[release.pk, "publish"])
148
+ self.client.get(url)
149
+ with patch("core.views.release_utils.network_available", return_value=False):
150
+ self.client.get(f"{url}?step=0")
151
+
152
+ release.version = "1.3.0"
153
+ release.save()
154
+
155
+ resp = self.client.get(url)
156
+ expected_log = f"logs/pkg-1.3.0-{release.revision[:7]}.log"
157
+ self.assertEqual(resp.context["log_path"], expected_log)
158
+ self.assertEqual(resp.context["current_step"], 0)
159
+
160
+ with patch("core.views.release_utils.network_available", return_value=False):
161
+ self.client.get(f"{url}?step=0")
162
+
163
+ log_content = Path(expected_log).read_text()
164
+ self.assertIn("Checking if version 1.3.0 exists on PyPI", log_content)
165
+ self.assertNotIn("1.2.0", log_content)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes