aa-structures 2.12.0__py3-none-any.whl → 2.14.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.
Files changed (37) hide show
  1. {aa_structures-2.12.0.dist-info → aa_structures-2.14.0.dist-info}/METADATA +1 -1
  2. {aa_structures-2.12.0.dist-info → aa_structures-2.14.0.dist-info}/RECORD +37 -34
  3. structures/__init__.py +1 -1
  4. structures/admin.py +1 -1
  5. structures/app_settings.py +4 -0
  6. structures/constants.py +1 -0
  7. structures/core/notification_embeds/main.py +58 -31
  8. structures/core/notification_embeds/orbital_embeds.py +1 -1
  9. structures/core/notification_embeds/skyhook_embeds.py +89 -0
  10. structures/core/notification_embeds/structures_embeds.py +23 -12
  11. structures/core/notification_embeds/tower_embeds.py +1 -1
  12. structures/core/notification_timers.py +53 -6
  13. structures/core/notification_types.py +58 -36
  14. structures/managers.py +22 -8
  15. structures/migrations/0007_add_notificationtypes_skyhook_metenox.py +149 -0
  16. structures/migrations/0008_add_notificationtypes_skyhook_metenox.py +149 -0
  17. structures/models/owners.py +68 -8
  18. structures/models/structures_1.py +5 -0
  19. structures/templates/structures/public.html +2 -2
  20. structures/templates/structures/structures.html +3 -3
  21. structures/tests/core/test_notification_types.py +16 -0
  22. structures/tests/core/{test_notifications_timerboard.py → test_notifications_timers.py} +6 -1
  23. structures/tests/integration/test_tasks.py +102 -1
  24. structures/tests/integration/test_views.py +1 -1
  25. structures/tests/models/test_owners_5.py +135 -1
  26. structures/tests/models/test_structures.py +49 -42
  27. structures/tests/test_managers_1.py +11 -1
  28. structures/tests/testdata/create_eveuniverse.py +6 -1
  29. structures/tests/testdata/entities.json +71 -1
  30. structures/tests/testdata/eveuniverse.json +6306 -5145
  31. structures/tests/testdata/factories.py +21 -0
  32. structures/tests/testdata/generate_notifications.py +142 -157
  33. structures/tests/testdata/helpers.py +6 -0
  34. structures/tests/views/test_structures.py +12 -4
  35. structures/views/structures.py +6 -6
  36. {aa_structures-2.12.0.dist-info → aa_structures-2.14.0.dist-info}/LICENSE +0 -0
  37. {aa_structures-2.12.0.dist-info → aa_structures-2.14.0.dist-info}/WHEEL +0 -0
@@ -37,6 +37,7 @@ from structures.app_settings import (
37
37
  STRUCTURES_DEVELOPER_MODE,
38
38
  STRUCTURES_ESI_DIRECTOR_ERROR_MAX_RETRIES,
39
39
  STRUCTURES_FEATURE_CUSTOMS_OFFICES,
40
+ STRUCTURES_FEATURE_SKYHOOKS,
40
41
  STRUCTURES_FEATURE_STARBASES,
41
42
  STRUCTURES_HOURS_UNTIL_STALE_NOTIFICATION,
42
43
  STRUCTURES_NOTIFICATION_SYNC_GRACE_MINUTES,
@@ -545,14 +546,14 @@ class Owner(models.Model):
545
546
  )
546
547
 
547
548
  def _remove_structures_not_returned_from_esi(
548
- self, structures_qs: models.QuerySet, new_structures: Iterable
549
+ self, existing_structures: models.QuerySet, new_structures: Iterable[dict]
549
550
  ):
550
551
  """Remove structures no longer returned from ESI."""
551
- ids_local = {x.id for x in structures_qs}
552
+ ids_local = {x.id for x in existing_structures}
552
553
  ids_from_esi = {x["structure_id"] for x in new_structures}
553
554
  ids_to_remove = ids_local - ids_from_esi
554
555
  if len(ids_to_remove) > 0:
555
- structures_qs.filter(id__in=ids_to_remove).delete()
556
+ existing_structures.filter(id__in=ids_to_remove).delete()
556
557
  logger.info(
557
558
  "Removed %d structures which apparently no longer exist.",
558
559
  len(ids_to_remove),
@@ -650,7 +651,7 @@ class Owner(models.Model):
650
651
  self._store_raw_data("structures", structures)
651
652
 
652
653
  self._remove_structures_not_returned_from_esi(
653
- structures_qs=self.structures.filter_upwell_structures(),
654
+ existing_structures=self.structures.filter_upwell_structures(),
654
655
  new_structures=structures,
655
656
  )
656
657
  return is_ok
@@ -701,7 +702,7 @@ class Owner(models.Model):
701
702
  return False
702
703
 
703
704
  self._remove_structures_not_returned_from_esi(
704
- structures_qs=self.structures.filter_customs_offices(),
705
+ existing_structures=self.structures.filter_customs_offices(),
705
706
  new_structures=structures.values(),
706
707
  )
707
708
  return True
@@ -868,7 +869,7 @@ class Owner(models.Model):
868
869
  return False
869
870
 
870
871
  self._remove_structures_not_returned_from_esi(
871
- structures_qs=self.structures.filter_starbases(),
872
+ existing_structures=self.structures.filter_starbases(),
872
873
  new_structures=structures,
873
874
  )
874
875
  character.reset_error_counter()
@@ -1214,15 +1215,18 @@ class Owner(models.Model):
1214
1215
  def update_asset_esi(self, user: Optional[User] = None):
1215
1216
  """Update assets from ESI."""
1216
1217
  token = self.fetch_token()
1217
- assets_data = self._fetch_structure_assets_from_esi(token)
1218
+ assets_data = self._fetch_owner_assets_from_esi(token)
1218
1219
  self._store_items_for_upwell_structures(assets_data)
1219
1220
  self._store_items_for_starbases(assets_data)
1221
+ if STRUCTURES_FEATURE_SKYHOOKS:
1222
+ self._update_skyhooks_from_assets(assets_data)
1223
+ self._resolve_skyhook_planets()
1220
1224
  if user:
1221
1225
  self._send_report_to_user(
1222
1226
  topic="assets", topic_count=self.structures.count(), user=user
1223
1227
  )
1224
1228
 
1225
- def _fetch_structure_assets_from_esi(self, token: Token) -> dict:
1229
+ def _fetch_owner_assets_from_esi(self, token: Token) -> dict:
1226
1230
  assets_raw = esi.client.Assets.get_corporations_corporation_id_assets(
1227
1231
  corporation_id=self.corporation.corporation_id,
1228
1232
  token=token.valid_access_token(),
@@ -1306,6 +1310,62 @@ class Owner(models.Model):
1306
1310
  )
1307
1311
  structure.update_items(structure_items)
1308
1312
 
1313
+ def _update_skyhooks_from_assets(self, assets_data: dict):
1314
+ skyhooks = {
1315
+ item_id: item
1316
+ for item_id, item in assets_data.items()
1317
+ if item["type_id"] == EveTypeId.ORBITAL_SKYHOOK
1318
+ and item["location_type"] == "solar_system"
1319
+ and item["location_flag"] == "AutoFit"
1320
+ and item["is_singleton"]
1321
+ and item["position"]
1322
+ }
1323
+ structures = []
1324
+ for item in skyhooks.values():
1325
+ structures.append(
1326
+ {
1327
+ "corporation_id": self.corporation.corporation_id,
1328
+ "type_id": item["type_id"],
1329
+ "position": item["position"],
1330
+ "structure_id": item["item_id"],
1331
+ "system_id": item["location_id"],
1332
+ }
1333
+ )
1334
+
1335
+ for s in structures:
1336
+ Structure.objects.update_or_create_from_dict(s, self)
1337
+
1338
+ self._remove_structures_not_returned_from_esi(
1339
+ existing_structures=self.structures.filter_skyhooks(),
1340
+ new_structures=structures,
1341
+ )
1342
+
1343
+ def _resolve_skyhook_planets(self):
1344
+ """Add planets to all unresolved Skyhooks."""
1345
+ s: Structure
1346
+ for s in self.structures.filter_skyhooks().filter(
1347
+ eve_planet__isnull=True,
1348
+ position_x__isnull=False,
1349
+ position_y__isnull=False,
1350
+ position_z__isnull=False,
1351
+ ):
1352
+ try:
1353
+ celestial = s.eve_solar_system.nearest_celestial(
1354
+ x=s.position_x,
1355
+ y=s.position_y,
1356
+ z=s.position_z,
1357
+ group_id=EveGroupId.PLANET,
1358
+ )
1359
+ except OSError:
1360
+ continue
1361
+
1362
+ if not celestial or not isinstance(celestial.eve_object, EvePlanet):
1363
+ continue
1364
+
1365
+ s.eve_planet = celestial.eve_object
1366
+ s.name = celestial.eve_type.name
1367
+ s.save()
1368
+
1309
1369
  @staticmethod
1310
1370
  def get_esi_scopes() -> List[str]:
1311
1371
  """Return all required ESI scopes."""
@@ -464,6 +464,11 @@ class Structure(models.Model): # pylint: disable = too-many-public-methods
464
464
  """Return True if this structure is a starbase, else False."""
465
465
  return starbases.is_starbase(self.eve_type)
466
466
 
467
+ @property
468
+ def is_skyhook(self) -> bool:
469
+ """Return True if this structure is a skyhook, else False."""
470
+ return self.eve_type_id == EveTypeId.ORBITAL_SKYHOOK
471
+
467
472
  @cached_property
468
473
  def is_upwell_structure(self) -> bool:
469
474
  """Return True if this structure is an upwell structure, else False."""
@@ -28,7 +28,7 @@
28
28
  <ul id="public-tabs" class="nav nav-tabs" role="tablist">
29
29
 
30
30
  <li role="presentation" class="active">
31
- <a href="#pocos" aria-controls="pocos" role="tab" data-toggle="tab">
31
+ <a href="#pocos" aria-controls="orbitals" role="tab" data-toggle="tab">
32
32
  {% translate "Customs Offices" %} <small>({{ pocos_count|default:"-" }})</small>
33
33
  </a>
34
34
  </li>
@@ -39,7 +39,7 @@
39
39
  <div class="panel-body">
40
40
  <div class="tab-content">
41
41
 
42
- <div role="tabpanel" class="tab-pane active" id="pocos">
42
+ <div role="tabpanel" class="tab-pane active" id="orbitals">
43
43
  {% include "structures/partials/public/poco_list.html" %}
44
44
  </div>
45
45
 
@@ -48,8 +48,8 @@
48
48
  </li>
49
49
 
50
50
  <li role="presentation">
51
- <a href="#pocos" aria-controls="pocos" role="tab" data-toggle="tab">
52
- {% trans "Customs Offices" %} <small>({{ pocos_count|default:"-" }})</small>
51
+ <a href="#orbitals" aria-controls="orbitals" role="tab" data-toggle="tab">
52
+ {% trans "Orbitals" %} <small>({{ orbitals_count|default:"-" }})</small>
53
53
  </a>
54
54
  </li>
55
55
 
@@ -77,7 +77,7 @@
77
77
  {% include "structures/partials/structures/structure_list.html" %}
78
78
  </div>
79
79
 
80
- <div role="tabpanel" class="tab-pane" id="pocos">
80
+ <div role="tabpanel" class="tab-pane" id="orbitals">
81
81
  {% include "structures/partials/structures/poco_list.html" %}
82
82
  </div>
83
83
 
@@ -118,6 +118,11 @@ class TestNotificationType(TestCase):
118
118
  "CorpWarInvalidatedMsg",
119
119
  "CorpWarRetractedMsg",
120
120
  "CorpWarSurrenderMsg",
121
+ "CorporationGoalClosed",
122
+ "CorporationGoalCompleted",
123
+ "CorporationGoalCreated",
124
+ "CorporationGoalNameChange",
125
+ "CorporationLeft",
121
126
  "CustomsMsg",
122
127
  "DeclareWar",
123
128
  "DistrictAttacked",
@@ -172,6 +177,7 @@ class TestNotificationType(TestCase):
172
177
  "KillRightUnavailable",
173
178
  "KillRightUnavailableOpen",
174
179
  "KillRightUsed",
180
+ "LPAutoRedeemed",
175
181
  "LocateCharMsg",
176
182
  "MadeWarMutual",
177
183
  "MercOfferRetractedMsg",
@@ -205,7 +211,14 @@ class TestNotificationType(TestCase):
205
211
  "ReimbursementMsg",
206
212
  "ResearchMissionAvailableMsg",
207
213
  "RetractsWar",
214
+ "SPAutoRedeemed",
208
215
  "SeasonalChallengeCompleted",
216
+ "SkinSequencingCompleted",
217
+ "SkyhookDeployed",
218
+ "SkyhookDestroyed",
219
+ "SkyhookLostShields",
220
+ "SkyhookOnline",
221
+ "SkyhookUnderAttack",
209
222
  "SovAllClaimAquiredMsg",
210
223
  "SovAllClaimLostMsg",
211
224
  "SovCommandNodeEventStarted",
@@ -237,7 +250,10 @@ class TestNotificationType(TestCase):
237
250
  "StructureItemsMovedToSafety",
238
251
  "StructureLostArmor",
239
252
  "StructureLostShields",
253
+ "StructureLowReagentsAlert",
254
+ "StructureNoReagentsAlert",
240
255
  "StructureOnline",
256
+ "StructurePaintPurchased",
241
257
  "StructureServicesOffline",
242
258
  "StructureUnanchoring",
243
259
  "StructureUnderAttack",
@@ -4,6 +4,7 @@ from app_utils.django import app_labels
4
4
  from app_utils.testing import NoSocketsTestCase
5
5
 
6
6
  from structures.core import notification_timers
7
+ from structures.core.notification_types import NotificationType
7
8
  from structures.models import Notification
8
9
  from structures.tests.testdata.factories import (
9
10
  GeneratedNotificationFactory,
@@ -107,7 +108,11 @@ if "timerboard" in app_labels():
107
108
  @patch(MODULE_PATH + ".STRUCTURES_MOON_EXTRACTION_TIMERS_ENABLED", True)
108
109
  def test_run_all(self):
109
110
  for obj in Notification.objects.all():
110
- obj.add_or_remove_timer()
111
+ timer_types = NotificationType.relevant_for_timerboard()
112
+ with self.subTest(notif_type=obj.notif_type):
113
+ is_timer = obj.notif_type in timer_types
114
+ is_added = obj.add_or_remove_timer()
115
+ self.assertEqual(is_timer, is_added)
111
116
 
112
117
  @patch(MODULE_PATH + ".STRUCTURES_TIMERS_ARE_CORP_RESTRICTED", False)
113
118
  def test_corp_restriction_1(self):
@@ -7,7 +7,7 @@ import yaml
7
7
 
8
8
  from django.test import TestCase, override_settings
9
9
  from django.utils.timezone import now
10
- from eveuniverse.models import EveSolarSystem
10
+ from eveuniverse.models import EvePlanet, EveSolarSystem
11
11
 
12
12
  from app_utils.django import app_labels
13
13
  from app_utils.esi import EsiStatus
@@ -22,11 +22,13 @@ from structures.tests.testdata.factories import (
22
22
  NotificationFactory,
23
23
  OwnerFactory,
24
24
  RawNotificationFactory,
25
+ SkyhookFactory,
25
26
  StarbaseFactory,
26
27
  StructureFactory,
27
28
  WebhookFactory,
28
29
  datetime_to_esi,
29
30
  )
31
+ from structures.tests.testdata.helpers import NearestCelestial
30
32
  from structures.tests.testdata.load_eveuniverse import load_eveuniverse
31
33
 
32
34
  if "structuretimers" in app_labels():
@@ -613,3 +615,102 @@ class TestTasks(TestCase):
613
615
  self.assertEqual(len(embeds), 1)
614
616
  embed = embeds[0]
615
617
  self.assertIn("Territorial Claim Unit", embed.title)
618
+
619
+ @patch(OWNERS_PATH + ".STRUCTURES_FEATURE_SKYHOOKS", True)
620
+ def test_should_fetch_new_skyhooks_from_esi(
621
+ self, mock_esi_2, mock_esi, mock_execute
622
+ ):
623
+ # given
624
+ owner = OwnerFactory()
625
+ structure = SkyhookFactory(owner=owner)
626
+ eve_planet = EvePlanet.objects.get(id=40161469)
627
+ corporation_id = owner.corporation.corporation_id
628
+ endpoints = [
629
+ EsiEndpoint(
630
+ "Assets",
631
+ "get_corporations_corporation_id_assets",
632
+ "corporation_id",
633
+ needs_token=True,
634
+ data={
635
+ str(corporation_id): [
636
+ {
637
+ "is_singleton": True,
638
+ "item_id": structure.id,
639
+ "location_flag": "AutoFit",
640
+ "location_id": 30002537,
641
+ "location_type": "solar_system",
642
+ "quantity": 1,
643
+ "type_id": 81080,
644
+ },
645
+ ]
646
+ },
647
+ ),
648
+ EsiEndpoint(
649
+ "Assets",
650
+ "post_corporations_corporation_id_assets_names",
651
+ "corporation_id",
652
+ needs_token=True,
653
+ data={str(corporation_id): []},
654
+ ),
655
+ EsiEndpoint(
656
+ "Assets",
657
+ "post_corporations_corporation_id_assets_locations",
658
+ "corporation_id",
659
+ needs_token=True,
660
+ data={
661
+ str(corporation_id): [
662
+ {"item_id": structure.id, "position": {"x": 1, "y": 2, "z": 3}}
663
+ ]
664
+ },
665
+ ),
666
+ EsiEndpoint(
667
+ "Corporation",
668
+ "get_corporations_corporation_id_starbases",
669
+ "corporation_id",
670
+ needs_token=True,
671
+ data={str(corporation_id): []},
672
+ ),
673
+ EsiEndpoint(
674
+ "Corporation",
675
+ "get_corporations_corporation_id_structures",
676
+ "corporation_id",
677
+ needs_token=True,
678
+ data={str(corporation_id): []},
679
+ ),
680
+ EsiEndpoint(
681
+ "Planetary_Interaction",
682
+ "get_corporations_corporation_id_customs_offices",
683
+ "corporation_id",
684
+ needs_token=True,
685
+ data={str(corporation_id): []},
686
+ ),
687
+ EsiEndpoint(
688
+ "Sovereignty",
689
+ "get_sovereignty_map",
690
+ needs_token=False,
691
+ data=[],
692
+ ),
693
+ EsiEndpoint(
694
+ "Universe",
695
+ "get_universe_structures_structure_id",
696
+ "structure_id",
697
+ needs_token=True,
698
+ data={},
699
+ ),
700
+ ]
701
+ mock_esi.client = mock_esi_2.client = EsiClientStub.create_from_endpoints(
702
+ endpoints
703
+ )
704
+ structure_id = structure.id
705
+ structure.delete()
706
+ # when
707
+
708
+ with patch(OWNERS_PATH + ".EveSolarSystem.nearest_celestial") as m:
709
+ m.return_value = NearestCelestial(
710
+ eve_object=eve_planet,
711
+ distance=35_000_000,
712
+ eve_type=eve_planet.eve_type,
713
+ )
714
+ tasks.update_all_structures.delay()
715
+ # then
716
+ self.assertTrue(owner.structures.filter(id=structure_id).exists())
@@ -46,7 +46,7 @@ class TestStructureListView(TestCase):
46
46
  # then
47
47
  self.assertEqual(response.status_code, 200)
48
48
  self.assertEqual(response.context["structures_count"], 2)
49
- self.assertEqual(response.context["pocos_count"], 1)
49
+ self.assertEqual(response.context["orbitals_count"], 1)
50
50
  self.assertEqual(response.context["starbases_count"], 1)
51
51
  self.assertEqual(response.context["jump_gates_count"], 1)
52
52
  self.assertIn("data_export", response.context)
@@ -7,18 +7,20 @@ from pytz import utc
7
7
  from django.test import override_settings
8
8
  from django.utils.timezone import now
9
9
  from esi.models import Token
10
+ from eveuniverse.models import EvePlanet
10
11
 
11
12
  from app_utils.esi_testing import EsiClientStub, EsiEndpoint
12
13
  from app_utils.testing import BravadoResponseStub, NoSocketsTestCase, queryset_pks
13
14
 
14
15
  from structures.core.notification_types import NotificationType
15
- from structures.models import Notification, StructureItem
16
+ from structures.models import Notification, Structure, StructureItem
16
17
  from structures.tests.testdata.factories import (
17
18
  EveCharacterFactory,
18
19
  EveCorporationInfoFactory,
19
20
  EveEntityCorporationFactory,
20
21
  JumpFuelAlertConfigFactory,
21
22
  OwnerFactory,
23
+ SkyhookFactory,
22
24
  StarbaseFactory,
23
25
  StructureFactory,
24
26
  StructureItemFactory,
@@ -27,6 +29,7 @@ from structures.tests.testdata.factories import (
27
29
  datetime_to_esi,
28
30
  )
29
31
  from structures.tests.testdata.helpers import (
32
+ NearestCelestial,
30
33
  load_eve_entities,
31
34
  load_notification_entities,
32
35
  )
@@ -711,6 +714,137 @@ class TestOwnerUpdateAssetEsi(NoSocketsTestCase):
711
714
  self.assertTrue(structure.items.filter(id=1300000003001).exists())
712
715
 
713
716
 
717
+ @patch(OWNERS_PATH + ".STRUCTURES_FEATURE_SKYHOOKS", True)
718
+ @patch(OWNERS_PATH + ".EveSolarSystem.nearest_celestial")
719
+ @patch(OWNERS_PATH + ".esi")
720
+ class TestOwnerUpdateSkyhooks(NoSocketsTestCase):
721
+ @classmethod
722
+ def setUpClass(cls):
723
+ super().setUpClass()
724
+ load_eveuniverse()
725
+ cls.corporation = EveCharacterFactory()
726
+ character = EveCharacterFactory(corporation=cls.corporation)
727
+ cls.user = UserMainDefaultOwnerFactory(main_character__character=character)
728
+ cls.planet = EvePlanet.objects.get(id=40161469)
729
+ endpoints = [
730
+ EsiEndpoint(
731
+ "Assets",
732
+ "get_corporations_corporation_id_assets",
733
+ "corporation_id",
734
+ needs_token=True,
735
+ data={
736
+ f"{cls.corporation.corporation_id}": [
737
+ {
738
+ "is_singleton": True,
739
+ "item_id": 1000000010001,
740
+ "location_flag": "AutoFit",
741
+ "location_id": 30002537,
742
+ "location_type": "solar_system",
743
+ "quantity": 1,
744
+ "type_id": 81080,
745
+ },
746
+ ],
747
+ },
748
+ ),
749
+ EsiEndpoint(
750
+ "Assets",
751
+ "post_corporations_corporation_id_assets_locations",
752
+ "corporation_id",
753
+ needs_token=True,
754
+ data={
755
+ f"{cls.corporation.corporation_id}": [
756
+ {"item_id": 1000000010001, "position": {"x": 1, "y": 2, "z": 3}}
757
+ ]
758
+ },
759
+ ),
760
+ ]
761
+ cls.esi_client_stub = EsiClientStub.create_from_endpoints(endpoints)
762
+
763
+ def test_should_create_new_skyhooks_from_scratch(
764
+ self, mock_esi, mock_nearest_celestial
765
+ ):
766
+ # given
767
+ mock_esi.client = self.esi_client_stub
768
+ mock_nearest_celestial.return_value = NearestCelestial(
769
+ eve_object=self.planet, distance=35_000_000, eve_type=self.planet.eve_type
770
+ )
771
+ owner = OwnerFactory(user=self.user, assets_last_update_at=None)
772
+ # when
773
+ owner.update_asset_esi()
774
+ # then
775
+ owner.refresh_from_db()
776
+ self.assertEqual(owner.structures.count(), 1)
777
+ obj: Structure = owner.structures.get(pk=1000000010001)
778
+ self.assertTrue(obj.is_skyhook)
779
+ self.assertEqual(obj.eve_planet, self.planet)
780
+
781
+ def test_should_remove_obsolete_skyhooks(self, mock_esi, mock_nearest_celestial):
782
+ # given
783
+ mock_esi.client = self.esi_client_stub
784
+ mock_nearest_celestial.return_value = NearestCelestial(
785
+ eve_object=self.planet, distance=35_000_000, eve_type=self.planet.eve_type
786
+ )
787
+ owner = OwnerFactory(user=self.user, assets_last_update_at=None)
788
+ SkyhookFactory.create(owner=owner)
789
+ # when
790
+ owner.update_asset_esi()
791
+ # then
792
+ owner.refresh_from_db()
793
+ self.assertEqual(owner.structures.count(), 1)
794
+ obj: Structure = owner.structures.get(pk=1000000010001)
795
+ self.assertTrue(obj.is_skyhook)
796
+ self.assertEqual(obj.eve_planet, self.planet)
797
+
798
+ def test_should_update_existing_skyhook(self, mock_esi, mock_nearest_celestial):
799
+ # given
800
+ mock_esi.client = self.esi_client_stub
801
+ mock_nearest_celestial.return_value = NearestCelestial(
802
+ eve_object=self.planet, distance=35_000_000, eve_type=self.planet.eve_type
803
+ )
804
+ owner = OwnerFactory(user=self.user, assets_last_update_at=None)
805
+ SkyhookFactory.create(owner=owner, id=1000000010001, eve_planet_name="Thera I")
806
+ # when
807
+ owner.update_asset_esi()
808
+ # then
809
+ owner.refresh_from_db()
810
+ self.assertEqual(owner.structures.count(), 1)
811
+ obj: Structure = owner.structures.get(pk=1000000010001)
812
+ self.assertTrue(obj.is_skyhook)
813
+ self.assertEqual(obj.eve_planet, self.planet)
814
+
815
+ def test_should_ignore_os_error_when_resolving_planet(
816
+ self, mock_esi, mock_nearest_celestial
817
+ ):
818
+ # given
819
+ mock_esi.client = self.esi_client_stub
820
+ mock_nearest_celestial.side_effect = OSError
821
+ owner = OwnerFactory(user=self.user, assets_last_update_at=None)
822
+ # when
823
+ owner.update_asset_esi()
824
+ # then
825
+ owner.refresh_from_db()
826
+ self.assertEqual(owner.structures.count(), 1)
827
+ obj: Structure = owner.structures.get(pk=1000000010001)
828
+ self.assertTrue(obj.is_skyhook)
829
+ self.assertIsNone(obj.eve_planet)
830
+
831
+ def test_should_ignore_no_reply_when_resolving_planet(
832
+ self, mock_esi, mock_nearest_celestial
833
+ ):
834
+ # given
835
+ mock_esi.client = self.esi_client_stub
836
+ mock_nearest_celestial.return_value = None
837
+ owner = OwnerFactory(user=self.user, assets_last_update_at=None)
838
+ # when
839
+ owner.update_asset_esi()
840
+ # then
841
+ owner.refresh_from_db()
842
+ self.assertEqual(owner.structures.count(), 1)
843
+ obj: Structure = owner.structures.get(pk=1000000010001)
844
+ self.assertTrue(obj.is_skyhook)
845
+ self.assertIsNone(obj.eve_planet)
846
+
847
+
714
848
  class TestOwnerToken(NoSocketsTestCase):
715
849
  def test_should_return_valid_token(self):
716
850
  # given
@@ -29,6 +29,7 @@ from structures.tests.testdata.factories import (
29
29
  OwnerFactory,
30
30
  PocoDetailsFactory,
31
31
  PocoFactory,
32
+ SkyhookFactory,
32
33
  StarbaseFactory,
33
34
  StructureFactory,
34
35
  StructureItemFactory,
@@ -367,48 +368,6 @@ class TestStructure(NoSocketsTestCase):
367
368
  # when/then
368
369
  self.assertEqual(obj.location_name, "?")
369
370
 
370
- def test_is_poco(self):
371
- # given
372
- structure = StructureFactory.build(owner=self.owner)
373
- poco = PocoFactory.build(owner=self.owner)
374
- starbase = StarbaseFactory.build(owner=self.owner)
375
- # then
376
- self.assertFalse(structure.is_poco)
377
- self.assertTrue(poco.is_poco)
378
- self.assertFalse(starbase.is_poco)
379
-
380
- def test_is_starbase(self):
381
- # given
382
- structure = StructureFactory.build(owner=self.owner)
383
- poco = PocoFactory.build(owner=self.owner)
384
- starbase = StarbaseFactory.build(owner=self.owner)
385
- # then
386
- self.assertFalse(structure.is_starbase)
387
- self.assertFalse(poco.is_starbase)
388
- self.assertTrue(starbase.is_starbase)
389
-
390
- def test_is_upwell_structure(self):
391
- # given
392
- structure = StructureFactory.build(owner=self.owner)
393
- poco = PocoFactory.build(owner=self.owner)
394
- starbase = StarbaseFactory.build(owner=self.owner)
395
- # then
396
- self.assertTrue(structure.is_upwell_structure)
397
- self.assertFalse(poco.is_upwell_structure)
398
- self.assertFalse(starbase.is_upwell_structure)
399
-
400
- def test_is_jump_gate(self):
401
- # given
402
- normal_structure = StructureFactory.build(owner=self.owner)
403
- poco = PocoFactory.build(owner=self.owner)
404
- starbase = StarbaseFactory.build(owner=self.owner)
405
- jump_gate = JumpGateFactory.build(owner=self.owner)
406
- # then
407
- self.assertFalse(normal_structure.is_jump_gate)
408
- self.assertFalse(poco.is_jump_gate)
409
- self.assertFalse(starbase.is_jump_gate)
410
- self.assertTrue(jump_gate.is_jump_gate)
411
-
412
371
  # TODO: activate
413
372
  # def test_is_upwell_structure_data_error(self):
414
373
  # # group without a category
@@ -490,6 +449,54 @@ class TestStructure(NoSocketsTestCase):
490
449
  self.assertFalse(obj.owner_has_sov())
491
450
 
492
451
 
452
+ class TestStructureIsX(NoSocketsTestCase):
453
+ @classmethod
454
+ def setUpClass(cls):
455
+ super().setUpClass()
456
+ load_eveuniverse()
457
+ cls.owner = OwnerFactory()
458
+ cls.jump_gate = JumpGateFactory.build(owner=cls.owner)
459
+ cls.poco = PocoFactory.build(owner=cls.owner)
460
+ cls.skyhook = SkyhookFactory.build(owner=cls.owner)
461
+ cls.starbase = StarbaseFactory.build(owner=cls.owner)
462
+ cls.upwell_structure = StructureFactory.build(owner=cls.owner)
463
+
464
+ def test_is_jump_gate(self):
465
+ self.assertFalse(self.upwell_structure.is_jump_gate)
466
+ self.assertFalse(self.poco.is_jump_gate)
467
+ self.assertFalse(self.starbase.is_jump_gate)
468
+ self.assertTrue(self.jump_gate.is_jump_gate)
469
+ self.assertFalse(self.skyhook.is_jump_gate)
470
+
471
+ def test_is_poco(self):
472
+ self.assertFalse(self.upwell_structure.is_poco)
473
+ self.assertTrue(self.poco.is_poco)
474
+ self.assertFalse(self.starbase.is_poco)
475
+ self.assertFalse(self.jump_gate.is_poco)
476
+ self.assertFalse(self.skyhook.is_poco)
477
+
478
+ def test_is_starbase(self):
479
+ self.assertFalse(self.upwell_structure.is_starbase)
480
+ self.assertFalse(self.poco.is_starbase)
481
+ self.assertTrue(self.starbase.is_starbase)
482
+ self.assertFalse(self.jump_gate.is_starbase)
483
+ self.assertFalse(self.skyhook.is_starbase)
484
+
485
+ def test_is_skyhook(self):
486
+ self.assertFalse(self.upwell_structure.is_skyhook)
487
+ self.assertFalse(self.poco.is_skyhook)
488
+ self.assertFalse(self.starbase.is_skyhook)
489
+ self.assertFalse(self.jump_gate.is_skyhook)
490
+ self.assertTrue(self.skyhook.is_skyhook)
491
+
492
+ def test_is_upwell_structure(self):
493
+ self.assertTrue(self.upwell_structure.is_upwell_structure)
494
+ self.assertFalse(self.poco.is_upwell_structure)
495
+ self.assertFalse(self.starbase.is_upwell_structure)
496
+ self.assertTrue(self.jump_gate.is_upwell_structure)
497
+ self.assertFalse(self.skyhook.is_upwell_structure)
498
+
499
+
493
500
  class TestStructureFuel(NoSocketsTestCase):
494
501
  @classmethod
495
502
  def setUpClass(cls):