Commit 6465a44a authored by Erik Kalkoken's avatar Erik Kalkoken
Browse files

Merge branch 'feature_nearest_celestial' into 'master'

Feature nearest celestial

See merge request !4
parents 559c54c4 a29e3a4f
Pipeline #273552055 passed with stages
in 5 minutes and 17 seconds
......@@ -11,8 +11,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added
- New model for type materials
- New model `EveTypeMaterial` for type materials
- On-demand loading of every section(=related model disabled by default) with all manager methods
- Calculate nearest celestials within solar systems with `EveSolarSystem.nearest_celestial()`
### Fixed
......
codecov:
require_ci_to_pass: yes
coverage:
precision: 2
round: down
range: "70...100"
parsers:
gcov:
branch_detection:
conditional: yes
loop: yes
method: no
macro: no
comment:
layout: "reach,diff,flags,files,footer"
behavior: default
require_changes: no
......@@ -36,6 +36,11 @@ eveskinserver
.. automodule:: eveuniverse.core.eveskinserver
:members:
fuzzwork
----------------
.. automodule:: eveuniverse.core.fuzzwork
:members:
Eve Models
==========
......
default_app_config = "eveuniverse.apps.EveuniverseConfig"
__version__ = "0.8.0a3"
__version__ = "0.8.0a4"
__title__ = "Eve Universe"
......@@ -3,3 +3,9 @@ EVE_CATEGORY_ID_SHIP = 6
EVE_CATEGORY_ID_BLUEPRINT = 9
EVE_CATEGORY_ID_STRUCTURE = 65
EVE_CATEGORY_ID_SKIN = 91
EVE_GROUP_ID_PLANET = 7
EVE_GROUP_ID_MOON = 8
EVE_GROUP_ID_ASTEROID_BELT = 9
EVE_GROUP_ID_STARGATE = 10
EVE_GROUP_ID_STATION = 15
"""Wrapper to access fuzzwork API"""
from collections import namedtuple
from urllib.parse import urlencode
from typing import Optional
import requests
from django.core.cache import cache
_CACHE_TIMEOUT = 3_600 * 12
EveItem = namedtuple("EveItem", ["id", "name", "type_id", "distance"])
def nearest_celestial(
x: int, y: int, z: int, solar_system_id: int
) -> Optional[EveItem]:
"""Fetch nearest celestial to given coordinates from API and return it
Results are cached. Returns None if nothing found nearby.
"""
cache_key_base = "EVEUNIVERSE_NEAREST_CELESTIAL"
query = urlencode(
{
"x": int(x),
"y": int(y),
"z": int(z),
"solarsystemid": int(solar_system_id),
}
)
cache_key = f"{cache_key_base}_{query}"
data = cache.get(key=cache_key)
if not data:
r = requests.get(f"https://www.fuzzwork.co.uk/api/nearestCelestial.php?{query}")
r.raise_for_status()
data = r.json()
if not data["itemName"]:
return None
cache.set(key=cache_key, value=data, timeout=_CACHE_TIMEOUT)
result = EveItem(
id=int(data["itemid"]),
name=str(data["itemName"]),
type_id=int(data["typeid"]),
distance=float(data["distance"]),
)
return result
......@@ -27,8 +27,8 @@ from .app_settings import (
EVEUNIVERSE_LOAD_TYPE_MATERIALS,
EVEUNIVERSE_USE_EVESKINSERVER,
)
from .constants import EVE_CATEGORY_ID_BLUEPRINT, EVE_CATEGORY_ID_SKIN
from .core import eveimageserver, eveskinserver
from . import constants
from .core import eveimageserver, eveskinserver, fuzzwork
from .managers import (
EveAsteroidBeltManager,
EveMarketPriceManager,
......@@ -1103,6 +1103,11 @@ class EveSolarSystem(EveUniverseEntityModel):
children = {}
load_order = 194
NearestCelestial = namedtuple(
"NearestCelestial", ["eve_type", "eve_object", "distance"]
)
NearestCelestial.__doc__ = "Container for a nearest celestial"
@property
def is_high_sec(self) -> bool:
"""returns True if this solar system is in high sec, else False"""
......@@ -1197,6 +1202,34 @@ class EveSolarSystem(EveUniverseEntityModel):
except HTTPNotFound:
return None
def nearest_celestial(self, x: int, y: int, z: int) -> Optional[NearestCelestial]:
"""Return nearest celestial to given coordinates as eveuniverse object.
Will return None if none is found.
"""
item = fuzzwork.nearest_celestial(x, y, z, solar_system_id=self.id)
if not item:
return None
eve_type, _ = EveType.objects.get_or_create_esi(id=item.type_id)
if eve_type.eve_group_id == constants.EVE_GROUP_ID_ASTEROID_BELT:
MyClass = EveAsteroidBelt
elif eve_type.eve_group_id == constants.EVE_GROUP_ID_MOON:
MyClass = EveMoon
elif eve_type.eve_group_id == constants.EVE_GROUP_ID_PLANET:
MyClass = EvePlanet
elif eve_type.eve_group_id == constants.EVE_GROUP_ID_STARGATE:
MyClass = EveStargate
elif eve_type.eve_group_id == constants.EVE_GROUP_ID_STATION:
MyClass = EveStation
else:
return None
obj, _ = MyClass.objects.get_or_create_esi(id=item.id)
return self.NearestCelestial(
eve_type=eve_type, eve_object=obj, distance=item.distance
)
@classmethod
def _children(cls, enabled_sections: Iterable[str] = None) -> dict:
enabled_sections = cls._enabled_sections_union(enabled_sections)
......@@ -1465,10 +1498,10 @@ class EveType(EveUniverseEntityModel):
if not category_id:
category_id = self.eve_group.eve_category_id
if category_id == EVE_CATEGORY_ID_BLUEPRINT:
if category_id == constants.EVE_CATEGORY_ID_BLUEPRINT:
variant = self.IconVariant.BPO
elif category_id == EVE_CATEGORY_ID_SKIN:
elif category_id == constants.EVE_CATEGORY_ID_SKIN:
variant = self.IconVariant.SKIN
if variant is self.IconVariant.BPO:
......@@ -1599,7 +1632,7 @@ class EveUnit(EveUniverseEntityModel):
# SDE models
class EveTypeMaterial(EveUniverseBaseModel):
class EveTypeMaterial(EveUniverseInlineModel):
"""Material type for an Eve online type"""
eve_type = models.ForeignKey(
......@@ -1620,6 +1653,9 @@ class EveTypeMaterial(EveUniverseBaseModel):
)
]
class EveUniverseMeta:
load_order = 137
def __str__(self) -> str:
return f"{self.eve_type}-{self.material_eve_type}"
......
from unittest.mock import patch, Mock
from bravado.exception import HTTPInternalServerError
import requests_mock
from django.core.cache import cache
from django.test import TestCase
from ..core import eveimageserver, esitools, eveskinserver
from ..core import eveimageserver, esitools, eveskinserver, fuzzwork
from .testdata.esi import EsiClientStub
from ..utils import NoSocketsTestCase
......@@ -199,3 +201,71 @@ class TestEveSkinServer(TestCase):
"""when called with invalid size, will raise exception"""
with self.assertRaises(ValueError):
eveskinserver.type_icon_url(42, size=22)
@requests_mock.Mocker()
class TestFuzzworkNearestCelestial(TestCase):
def setUp(self) -> None:
cache.clear()
def test_should_return_item_from_api(self, requests_mocker):
# given
item = {
"itemName": "Colelie VI - Asteroid Belt 1",
"typeid": 15,
"itemid": 40170698,
"distance": 701983768513.2802,
}
requests_mocker.register_uri(
"GET",
url="https://www.fuzzwork.co.uk/api/nearestCelestial.php?x=660502472160&y=-130687672800&z=-813545103840&solarsystemid=30002682",
json=item,
)
# when
result = fuzzwork.nearest_celestial(
x=660502472160, y=-130687672800, z=-813545103840, solar_system_id=30002682
)
# then
self.assertEqual(result.id, 40170698)
self.assertEqual(result.name, "Colelie VI - Asteroid Belt 1")
self.assertEqual(result.type_id, 15)
self.assertEqual(result.distance, 701983768513.2802)
self.assertEqual(requests_mocker.call_count, 1)
def test_should_return_item_from_cache(self, requests_mocker):
# given
item = {
"itemName": "Colelie VI - Asteroid Belt 1",
"typeid": 15,
"itemid": 40170698,
"distance": 701983768513.2802,
}
requests_mocker.register_uri(
"GET",
url="https://www.fuzzwork.co.uk/api/nearestCelestial.php?x=1&y=2&z=3&solarsystemid=99",
json=item,
)
fuzzwork.nearest_celestial(x=1, y=2, z=3, solar_system_id=99)
# when
result = fuzzwork.nearest_celestial(x=1, y=2, z=3, solar_system_id=99)
# then
self.assertEqual(result.id, 40170698)
self.assertEqual(requests_mocker.call_count, 1)
def test_should_return_none_if_nothing_found(self, requests_mocker):
# given
item = {
"itemName": None,
"typeid": 15,
"itemid": 40170698,
"distance": 701983768513.2802,
}
requests_mocker.register_uri(
"GET",
url="https://www.fuzzwork.co.uk/api/nearestCelestial.php?x=1&y=2&z=3&solarsystemid=30002682",
json=item,
)
# when
result = fuzzwork.nearest_celestial(x=1, y=2, z=3, solar_system_id=30002682)
# then
self.assertIsNone(result)
......@@ -35,6 +35,7 @@ from ..models import (
EveType,
EveTypeDogmaAttribute,
EveTypeDogmaEffect,
EveTypeMaterial,
EveUnit,
EveEntity,
)
......@@ -66,6 +67,7 @@ class TestEveUniverseBaseModel(NoSocketsTestCase):
EveCategory, # load_order = 130
EveGroup, # load_order = 132
EveType, # load_order = 134
EveTypeMaterial, # load_order = 135
EveDogmaAttribute, # load_order = 140
EveDogmaEffect, # load_order = 142
EveDogmaEffectModifier, # load_order = 144
......
......@@ -2,9 +2,19 @@ from unittest.mock import patch
import requests_mock
from ..core import fuzzwork
from .testdata.esi import EsiClientStub
from .testdata.sde import sde_data, type_materials_cache_content
from ..models import EvePlanet, EveType, EveTypeMaterial, EveSolarSystem
from ..models import (
EveAsteroidBelt,
EveMoon,
EveStargate,
EvePlanet,
EveType,
EveTypeMaterial,
EveSolarSystem,
EveStation,
)
from ..utils import NoSocketsTestCase
......@@ -894,3 +904,115 @@ class TestEvePlanetWithSections(NoSocketsTestCase):
)
self.assertFalse(obj.enabled_sections.asteroid_belts)
self.assertTrue(obj.enabled_sections.moons)
@patch(MODELS_PATH + ".fuzzwork")
@patch(MANAGERS_PATH + ".esi")
class TestEveSolarSystemNearestCelestial(NoSocketsTestCase):
def test_should_return_stargate(self, mock_esi, mock_fuzzwork):
# given
mock_esi.client = EsiClientStub()
mock_fuzzwork.nearest_celestial.return_value = fuzzwork.EveItem(
id=50016284, name="Stargate (Akidagi)", type_id=16, distance=1000
)
enaluri, _ = EveSolarSystem.objects.get_or_create_esi(id=30045339)
# when
result = enaluri.nearest_celestial(x=-1, y=-2, z=3)
# then
self.assertEqual(result.eve_type, EveType.objects.get_or_create_esi(id=16)[0])
self.assertEqual(
result.eve_object, EveStargate.objects.get_or_create_esi(id=50016284)[0]
)
self.assertEqual(result.distance, 1000)
def test_should_return_planet(self, mock_esi, mock_fuzzwork):
# given
mock_esi.client = EsiClientStub()
mock_fuzzwork.nearest_celestial.return_value = fuzzwork.EveItem(
id=40349471, name="Enaluri III", type_id=13, distance=1000
)
enaluri, _ = EveSolarSystem.objects.get_or_create_esi(id=30045339)
# when
result = enaluri.nearest_celestial(x=-1, y=-2, z=3)
# then
self.assertEqual(result.eve_type, EveType.objects.get_or_create_esi(id=13)[0])
self.assertEqual(
result.eve_object, EvePlanet.objects.get_or_create_esi(id=40349471)[0]
)
self.assertEqual(result.distance, 1000)
def test_should_return_station(self, mock_esi, mock_fuzzwork):
# given
mock_esi.client = EsiClientStub()
mock_fuzzwork.nearest_celestial.return_value = fuzzwork.EveItem(
id=60015068,
name="Enaluri V - State Protectorate Assembly Plant",
type_id=1529,
distance=1000,
)
enaluri, _ = EveSolarSystem.objects.get_or_create_esi(id=30045339)
# when
result = enaluri.nearest_celestial(x=-1, y=-2, z=3)
# then
self.assertEqual(result.eve_type, EveType.objects.get_or_create_esi(id=1529)[0])
self.assertEqual(
result.eve_object, EveStation.objects.get_or_create_esi(id=60015068)[0]
)
self.assertEqual(result.distance, 1000)
def test_should_return_asteroid_belt(self, mock_esi, mock_fuzzwork):
# given
mock_esi.client = EsiClientStub()
mock_fuzzwork.nearest_celestial.return_value = fuzzwork.EveItem(
id=40349487, name="Enaluri III - Asteroid Belt 1", type_id=15, distance=1000
)
enaluri, _ = EveSolarSystem.objects.get_or_create_esi(id=30045339)
# when
result = enaluri.nearest_celestial(x=-1, y=-2, z=3)
# then
self.assertEqual(
result.eve_type,
EveType.objects.get_or_create_esi(id=15)[0],
)
self.assertEqual(
result.eve_object, EveAsteroidBelt.objects.get_or_create_esi(id=40349487)[0]
)
self.assertEqual(result.distance, 1000)
def test_should_return_moon(self, mock_esi, mock_fuzzwork):
# given
mock_esi.client = EsiClientStub()
mock_fuzzwork.nearest_celestial.return_value = fuzzwork.EveItem(
id=40349472, name="Enaluri III - Moon 1", type_id=14, distance=1000
)
enaluri, _ = EveSolarSystem.objects.get_or_create_esi(id=30045339)
# when
result = enaluri.nearest_celestial(x=-1, y=-2, z=3)
# then
self.assertEqual(result.eve_type, EveType.objects.get_or_create_esi(id=14)[0])
self.assertEqual(
result.eve_object, EveMoon.objects.get_or_create_esi(id=40349472)[0]
)
self.assertEqual(result.distance, 1000)
def test_should_return_none_if_unknown_type(self, mock_esi, mock_fuzzwork):
# given
mock_esi.client = EsiClientStub()
mock_fuzzwork.nearest_celestial.return_value = fuzzwork.EveItem(
id=99, name="Merlin", type_id=603, distance=1000
)
enaluri, _ = EveSolarSystem.objects.get_or_create_esi(id=30045339)
# when
result = enaluri.nearest_celestial(x=-1, y=-2, z=3)
# then
self.assertIsNone(result)
def test_should_return_none_if_not_found(self, mock_esi, mock_fuzzwork):
# given
mock_esi.client = EsiClientStub()
mock_fuzzwork.nearest_celestial.return_value = None
enaluri, _ = EveSolarSystem.objects.get_or_create_esi(id=30045339)
# when
result = enaluri.nearest_celestial(x=-1, y=-2, z=3)
# then
self.assertIsNone(result)
......@@ -453,6 +453,24 @@
3800
]
},
"8": {
"category_id": 2,
"group_id": 8,
"name": "Moon",
"published": false,
"types": [
14
]
},
"9": {
"category_id": 2,
"group_id": 9,
"name": "Asteroid Belt",
"published": false,
"types": [
15
]
},
"18": {
"category_id": 4,
"group_id": 18,
......@@ -1011,6 +1029,35 @@
"type_id": 13,
"volume": 1
},
"14": {
"capacity": 0,
"description": "",
"graphic_id": 10,
"group_id": 8,
"icon_id": 10141,
"mass": 1e+35,
"name": "Moon",
"packaged_volume": 1,
"portion_size": 1,
"published": false,
"radius": 10000,
"type_id": 14,
"volume": 1
},
"15": {
"capacity": 0,
"description": "",
"group_id": 9,
"icon_id": 15,
"mass": 1e+35,
"name": "Asteroid Belt",
"packaged_volume": 1,
"portion_size": 1,
"published": false,
"radius": 1,
"type_id": 15,
"volume": 1
},
"16": {
"capacity": 0,
"description": "",
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment