aa-structures 2.14.0__py3-none-any.whl → 2.15.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aa-structures
3
- Version: 2.14.0
3
+ Version: 2.15.0
4
4
  Summary: App for managing Eve Online structures with Alliance Auth.
5
5
  Author-email: Erik Kalkoken <kalkoken87@gmail.com>
6
6
  Requires-Python: >=3.8
@@ -1,5 +1,5 @@
1
- structures/__init__.py,sha256=OXqNqbUO5Qpf5_RjpBoWJz1oygLBj3q-5FymboOnAeQ,204
2
- structures/admin.py,sha256=cA04JFghU-zIGC168fhZ5Hj_FYAQxlF6TFVDb8sgO4A,40066
1
+ structures/__init__.py,sha256=DW_FAyoVYr5DMbDmmJQ_yaBVMMVB07QHRZP71oApKTA,204
2
+ structures/admin.py,sha256=K7wh8mHFfGHONn7oLdqXsBAxq0rmYrwbZ7S3uIOuMtg,41303
3
3
  structures/app_settings.py,sha256=aWA2bTcv_vVwIyisx-G7GBSpfM6JDyt2MWcXKtHnjK0,6651
4
4
  structures/apps.py,sha256=MNZH9l3qWCwuS7OGiKGkBVrDzKoOFlqwDdEgyEFzxVA,195
5
5
  structures/auth_hooks.py,sha256=nRbrixFkAE5gphDokB1E8xhH8FY2VtXVwu0XMmSGBAw,1013
@@ -65,7 +65,7 @@ structures/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
65
65
  structures/models/__init__.py,sha256=Da0-Z4BtsZ_mlnI6XtyvoE0UtTuOA0psGCuVhVPVKrM,916
66
66
  structures/models/eveuniverse.py,sha256=vTIfzZL9guEDuZHQvRdG6pI4zSEdMqfVE-2SkHdqbRo,2213
67
67
  structures/models/notifications.py,sha256=HwQc-ChdP0ULuJgxUym9_CBsCWCmcHT7whlk9tB2vzw,35364
68
- structures/models/owners.py,sha256=9LLjOv7QAICAodx6XUEGGuO6RxLY4h6hZs8EWWiobs0,57875
68
+ structures/models/owners.py,sha256=7CKpwHlw4Nz8lTJUgQZxI-GHon5FrlMEILxe7q7Vfgg,58391
69
69
  structures/models/structures_1.py,sha256=zk380uG2DuQ7exO7THbq5TE5Ro2jr6EyVCIxRJXHIJI,29958
70
70
  structures/models/structures_2.py,sha256=g5Pct5jNmZC-n7fnpLs5UyiP9JLrGdI1vjYucHTV_wQ,10371
71
71
  structures/static/structures/css/global.css,sha256=R4LEH9PwLoN77qkqBK7u2y_vzRC3fq8X_zKnAW7yhiA,1165
@@ -136,7 +136,7 @@ structures/static/structures/vendor/datatables/plugins/rowGroup.dataTables.min.c
136
136
  structures/templates/structures/base.html,sha256=ND0JjP2Pa537FXOrIj3cziHfDbTh0ZNzrLFJ_zpVhw8,337
137
137
  structures/templates/structures/public.html,sha256=R3kjhr1MLC5r9r8L3t-1-Nh7-ngKbRiiZNIwgpsGg70,3239
138
138
  structures/templates/structures/statistics.html,sha256=rVI_wPW4VXIO3IoWna8_GoqWCc5ra-6Jc9iekfusS78,1736
139
- structures/templates/structures/structures.html,sha256=WdtBxY3XumtpO4b23Arw8lVqGJf_gHIeD2AAxaKf7cU,8327
139
+ structures/templates/structures/structures.html,sha256=q0c7Wlaxvt2dg1B236JIEmmSBgQyX5F_QlBLokt-W0E,8329
140
140
  structures/templates/structures/modals/fitting_assets.html,sha256=0GSpDAk-wgvaZdxM9JzteUYhoPsek5y-L4TdG2q5Sbg,264
141
141
  structures/templates/structures/modals/fitting_gfx.html,sha256=hmdU1Zv09woGKigUugbBRxScKQun8l81yEPUMGrok2g,9530
142
142
  structures/templates/structures/modals/poco_details.html,sha256=uwaEM0jDvkdu_Lff2mUKwm7pn1KXePcgN9yBwFHsQO4,2705
@@ -148,9 +148,9 @@ structures/templates/structures/partials/menu.html,sha256=pxN_Tvo_vON0Fauyg_XPYl
148
148
  structures/templates/structures/partials/public/poco_list.html,sha256=tVAsGXS7rh7Qr5x2X26buyt7jwJJ-YoNLs9eMs0gDM4,1222
149
149
  structures/templates/structures/partials/statistics/structure_summary.html,sha256=ZCEBOPhfOUyih7C8TzaQi9OI2AsMzZGBb9ZGHsShzmk,1133
150
150
  structures/templates/structures/partials/structures/active_tags.html,sha256=KhYA6LbAqg-iJJ2J4BGACXdbdiZFM4wAgDEq2RQEu0s,106
151
- structures/templates/structures/partials/structures/jump_gates_list.html,sha256=1hhx21fz1YAKTRK0E5FYP8uYTlXCFnm9FAdechiopx8,1470
152
- structures/templates/structures/partials/structures/poco_list.html,sha256=V5Z53LTvE0eHHWHKlwjGdtSwEkLSy0cIMw7ZE4KOdxc,1077
153
- structures/templates/structures/partials/structures/starbase_list.html,sha256=f-BhcqRiueLURcQoOevZHMYGj2Z6dvG36tourtv7ykE,1417
151
+ structures/templates/structures/partials/structures/jump_gate_list.html,sha256=1hhx21fz1YAKTRK0E5FYP8uYTlXCFnm9FAdechiopx8,1470
152
+ structures/templates/structures/partials/structures/orbital_list.html,sha256=uWV-61WUk1hyTCXgIkMuiAhof4VIP02bnH5N5nqaCCo,1070
153
+ structures/templates/structures/partials/structures/starbase_list.html,sha256=Djl3nsTSn9TIZ6k4F0hiF-qDv_rLiVSIapnWUz0bvqU,1412
154
154
  structures/templates/structures/partials/structures/structure_list.html,sha256=2EHP1xJrmDNhwENhxGY3L6YbC53qFwhnDHcFzxkNXpI,1421
155
155
  structures/templates/structures/templatetags/detail_title.html,sha256=PWoYNZhTt1ngDEdz_B4TlyC7duO-8MxgznSUb27lUDA,117
156
156
  structures/templates/structures/templatetags/list_asset.html,sha256=6wWndrdBlWbOMIQlGaqeUtSfk-c6F3ZQMYErZJ4H7SM,375
@@ -160,7 +160,7 @@ structures/templates/structures/templatetags/list_title.html,sha256=C2NWwo27TGfG
160
160
  structures/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
161
161
  structures/templatetags/structures.py,sha256=vSt5AnfYuo11q1SxiskT4oshSMsZ9ZgM0nYpYiMl8bA,1637
162
162
  structures/tests/__init__.py,sha256=9MrJzKr8DdsQY3-79v188pTpwqT4TDQ46vKnBJAGqic,75
163
- structures/tests/test_admin.py,sha256=cb_AbOanp31k-oZhaUXCqnlkUu87EgNddq_VQKbB_Dk,26839
163
+ structures/tests/test_admin.py,sha256=aDd88Ry0ahitpYyF_wa1jG_pWJ5m5w3jsat45sBhEmo,27493
164
164
  structures/tests/test_helpers.py,sha256=BQC-4H-9-v5qW4nugqy0bkxuGlA3UO2clYY2lI2LjoY,4893
165
165
  structures/tests/test_managers_1.py,sha256=4dnlK7Le04eJUPjJc9ex5BxZWIJGAyEvdKuuYXU2gqY,34846
166
166
  structures/tests/test_managers_2.py,sha256=FISr_NT1Qnu9j1JYYTceNsUk8k9rfCTKwjKi5WUVbPs,3025
@@ -185,7 +185,7 @@ structures/tests/models/test_notifications_2.py,sha256=wBoB6lHMK4yrd63DSkZ570yXW
185
185
  structures/tests/models/test_notifications_3.py,sha256=u11s0k9EmtFgL3kyb4zDyLVliQLOps701ZgEA2T2i0Y,7257
186
186
  structures/tests/models/test_notifications_discord.py,sha256=6maDXjl2C5yH2XRgsvMtb3P-tfOdZLQDaksffiqS8OM,6312
187
187
  structures/tests/models/test_owners_1.py,sha256=o8AJasQ0-Pdb0FRFZcmNcMGjaIk8fH3OZIZw4paJiDw,24433
188
- structures/tests/models/test_owners_2.py,sha256=gkTXpSibPWs1XfZtPvyeEvVebyYZcKERD9GQSYSClYk,21911
188
+ structures/tests/models/test_owners_2.py,sha256=GZYdsFey6bQHuk9BiT1KFT_vW01mUHRtDxJRHPEmrC4,22539
189
189
  structures/tests/models/test_owners_3.py,sha256=cpDkfFkVvC1v4uuXha1vGUmWZ163huYTUDuZyed9Rdc,16947
190
190
  structures/tests/models/test_owners_4.py,sha256=_VP4QsPYGwBnn_bVN4aNX2ZVeJzMAzWObZrgCJkOPgY,19496
191
191
  structures/tests/models/test_owners_5.py,sha256=TIF_SqJqyGja8FBV2lQNAC4yff1usmJ4e0QW7e68zTg,36929
@@ -217,13 +217,13 @@ structures/views/statistics.py,sha256=7jj8b9ATsYwE7Cg6gMp-bYx29nV43GdWYun9WBggGk
217
217
  structures/views/status.py,sha256=gcahbk6dPIZDqkaNHDAsEHyDWLzicTK18Fom0A6xx3c,718
218
218
  structures/views/structures.py,sha256=Wb57jFfRa0Zxo6TxKmOCQ-jtopzPtHe0go6gnJn40CA,22584
219
219
  structures/webhooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
220
- structures/webhooks/core.py,sha256=mP25MbQG8Yv2YayDFcR6x2V30eqXhR2q5u4SRefblf4,6573
220
+ structures/webhooks/core.py,sha256=kvbMQ_kjJleQanlFFoiN52wIkBG_4u0ONfcHfCU5lgA,6604
221
221
  structures/webhooks/managers.py,sha256=L3G3AmsyDeif_lfpWshmAxQ61UGJ9w8i9lZaF2jbOtQ,1117
222
222
  structures/webhooks/models.py,sha256=kUkt9rnRQIJIrU9Bjcs34rvkb-TMbUubHdn-kny08kI,2067
223
223
  structures/webhooks/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
224
224
  structures/webhooks/tests/test_core.py,sha256=4NcEAQgK2KhQkFOxYh2ad0S-qUWh1DNGDmLo5Mo5opI,6762
225
225
  structures/webhooks/tests/test_utils.py,sha256=ekADFv0JOEtXeqdiejbeqrABO__Q1flJHzVieQ7L9e0,459
226
- aa_structures-2.14.0.dist-info/LICENSE,sha256=XZiwB_S_40_HhnvLg5xvtBb3g1oGjPrk0rpFwk8iInE,1070
227
- aa_structures-2.14.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
228
- aa_structures-2.14.0.dist-info/METADATA,sha256=X7YMjOqlJc_CkmHeFWJiYgKb8QLDPdnzPibT_bh6ReM,5972
229
- aa_structures-2.14.0.dist-info/RECORD,,
226
+ aa_structures-2.15.0.dist-info/LICENSE,sha256=XZiwB_S_40_HhnvLg5xvtBb3g1oGjPrk0rpFwk8iInE,1070
227
+ aa_structures-2.15.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
228
+ aa_structures-2.15.0.dist-info/METADATA,sha256=UZGw51TIsMgIdznKyyE9prBvlgDQQpJ3k3z4Url-DEE,5972
229
+ aa_structures-2.15.0.dist-info/RECORD,,
structures/__init__.py CHANGED
@@ -3,5 +3,5 @@
3
3
  # pylint: disable = invalid-name
4
4
  default_app_config = "structures.apps.StructuresConfig"
5
5
 
6
- __version__ = "2.14.0"
6
+ __version__ = "2.15.0"
7
7
  __title__ = "Structures"
structures/admin.py CHANGED
@@ -1,10 +1,13 @@
1
1
  """Admin site for Structures."""
2
2
 
3
+ import functools
3
4
  import statistics
4
5
  from typing import Optional
5
6
 
7
+ from django import forms
6
8
  from django.conf import settings
7
9
  from django.contrib import admin
10
+ from django.contrib.admin.widgets import FilteredSelectMultiple
8
11
  from django.db import models
9
12
  from django.db.models import Prefetch
10
13
  from django.db.models.functions import Lower
@@ -1028,8 +1031,44 @@ class StructureAdmin(admin.ModelAdmin):
1028
1031
  )
1029
1032
 
1030
1033
 
1034
+ class WebhookAdminForm(forms.ModelForm):
1035
+ owners = forms.ModelMultipleChoiceField(
1036
+ queryset=Owner.objects.order_by(Lower("corporation__corporation_name")),
1037
+ required=False,
1038
+ widget=FilteredSelectMultiple(verbose_name=_("Users"), is_stacked=False),
1039
+ )
1040
+
1041
+ def __init__(self, *args, **kwargs):
1042
+ super().__init__(*args, **kwargs)
1043
+
1044
+ if self.instance and self.instance.pk:
1045
+ self.fields["owners"].initial = self.instance.owners.all()
1046
+
1047
+ def save(self, commit=True):
1048
+ webhook: Webhook = super().save(commit=False)
1049
+
1050
+ if commit:
1051
+ webhook.save()
1052
+
1053
+ owners = self.cleaned_data["owners"]
1054
+ if webhook.pk:
1055
+ self._save_m2m_and_users(webhook, owners)
1056
+ else:
1057
+ self.save_m2m = functools.partial(
1058
+ self._save_m2m_and_users, webhook=webhook, owners=owners
1059
+ )
1060
+
1061
+ return webhook
1062
+
1063
+ def _save_m2m_and_users(self, webhook, owners):
1064
+ """Save m2m relations incl. users."""
1065
+ webhook.owners.set(owners)
1066
+ self._save_m2m()
1067
+
1068
+
1031
1069
  @admin.register(Webhook)
1032
1070
  class WebhookAdmin(admin.ModelAdmin):
1071
+ form = WebhookAdminForm
1033
1072
  list_display = (
1034
1073
  "name",
1035
1074
  "_ping_groups",
@@ -1057,6 +1096,7 @@ class WebhookAdmin(admin.ModelAdmin):
1057
1096
  "name",
1058
1097
  "url",
1059
1098
  "notes",
1099
+ "owners",
1060
1100
  "notification_types",
1061
1101
  "ping_groups",
1062
1102
  "is_active",
@@ -592,7 +592,7 @@ class Owner(models.Model):
592
592
  is_ok = True
593
593
  # fetch main list of structure for this corporation
594
594
  try:
595
- structures = (
595
+ structures: List[dict] = (
596
596
  esi.client.Corporation.get_corporations_corporation_id_structures(
597
597
  corporation_id=self.corporation.corporation_id,
598
598
  token=token.valid_access_token(),
@@ -620,9 +620,19 @@ class Owner(models.Model):
620
620
  )
621
621
  ).results()
622
622
  except OSError as ex:
623
- self._report_esi_issue(
624
- f"fetch structure #{structure['structure_id']}", ex, token
625
- )
623
+ if isinstance(ex, HTTPForbidden):
624
+ logger.error(
625
+ "Failed to fetch structure with ID #%d belonging to %s, "
626
+ "because the character '%s' is missing "
627
+ "docking rights for it.",
628
+ structure["structure_id"],
629
+ self,
630
+ token.character_name,
631
+ )
632
+ else:
633
+ self._report_esi_issue(
634
+ f"fetch structure #{structure['structure_id']}", ex, token
635
+ )
626
636
  structure["name"] = "(no data)"
627
637
  is_ok = False
628
638
  else:
@@ -5,7 +5,7 @@
5
5
  <!-- structure list -->
6
6
 
7
7
  <h3>
8
- {% translate 'Customs Offices' %}
8
+ {% translate 'Orbitals' %}
9
9
  {% include "structures/partials/structures/active_tags.html" %}
10
10
  </h3>
11
11
 
@@ -5,7 +5,7 @@
5
5
  <!-- structure list -->
6
6
 
7
7
  <h3>
8
- {% translate 'All Structures' %}
8
+ {% translate 'Starbases' %}
9
9
  {% include "structures/partials/structures/active_tags.html" %}
10
10
  </h3>
11
11
 
@@ -78,7 +78,7 @@
78
78
  </div>
79
79
 
80
80
  <div role="tabpanel" class="tab-pane" id="orbitals">
81
- {% include "structures/partials/structures/poco_list.html" %}
81
+ {% include "structures/partials/structures/orbital_list.html" %}
82
82
  </div>
83
83
 
84
84
  <div role="tabpanel" class="tab-pane" id="starbases">
@@ -87,7 +87,7 @@
87
87
 
88
88
  {% if show_jump_gates_tab %}
89
89
  <div role="tabpanel" class="tab-pane" id="jump-gates">
90
- {% include "structures/partials/structures/jump_gates_list.html" %}
90
+ {% include "structures/partials/structures/jump_gate_list.html" %}
91
91
  </div>
92
92
  {% endif %}
93
93
 
@@ -376,6 +376,20 @@ class TestUpdateStructuresEsi(NoSocketsTestCase):
376
376
  structure = Structure.objects.get(id=1000000000002)
377
377
  self.assertEqual(structure.name, "(no data)")
378
378
 
379
+ def test_update_will_not_break_on_403_error_from_structure_info(self, mock_esi):
380
+ # given
381
+ new_endpoint = EsiEndpoint(
382
+ "Universe", "get_universe_structures_structure_id", http_error_code=403
383
+ )
384
+ mock_esi.client = self.esi_client_stub.replace_endpoints([new_endpoint])
385
+ owner = OwnerFactory(user=self.user, structures_last_update_at=None)
386
+ # when
387
+ owner.update_structures_esi()
388
+ # then
389
+ self.assertFalse(owner.is_structure_sync_fresh)
390
+ structure = Structure.objects.get(id=1000000000002)
391
+ self.assertEqual(structure.name, "(no data)")
392
+
379
393
  @patch(MODULE_PATH + ".Structure.objects.update_or_create_from_dict")
380
394
  def test_update_will_not_break_on_http_error_when_creating_structures(
381
395
  self, mock_create_structure, mock_esi
@@ -719,3 +719,22 @@ class TestWebhookAdmin(TestCase):
719
719
  webhook.refresh_from_db()
720
720
  self.assertFalse(webhook.is_active)
721
721
  self.assertTrue(mock_message_user.called)
722
+
723
+ def test_can_assign_owner(self):
724
+ # given
725
+ owner = OwnerFactory()
726
+ webhook = WebhookFactory()
727
+ self.client.force_login(self.user)
728
+ data = {
729
+ "name": webhook.name,
730
+ "notification_types": NotificationType.webhook_defaults(),
731
+ "owners": [owner.pk],
732
+ "url": webhook.url,
733
+ "webhook_type": webhook.webhook_type,
734
+ }
735
+ r = self.client.post(
736
+ f"/admin/structures/webhook/{webhook.pk}/change/", data=data
737
+ )
738
+ self.assertEqual(r.status_code, 302)
739
+ webhook.refresh_from_db()
740
+ self.assertIn(owner, webhook.owners.all())
@@ -61,7 +61,7 @@ class DiscordWebhookMixin:
61
61
 
62
62
  return counter
63
63
 
64
- # pylint: disable = too-many-arguments
64
+ # pylint: disable = too-many-positional-arguments, too-many-arguments
65
65
  def send_message(
66
66
  self,
67
67
  content: Optional[str] = None,