mirror of
https://github.com/dtomlinson91/panaetius.git
synced 2025-12-22 04:55:44 +00:00
adding squasher utility
This commit is contained in:
3
panaetius/utilities/__init__.py
Normal file
3
panaetius/utilities/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
"""General utilities."""
|
||||||
|
|
||||||
|
from panaetius.utilities import squasher
|
||||||
64
panaetius/utilities/squasher.py
Normal file
64
panaetius/utilities/squasher.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
"""Squash a json object or Python dictionary into a single level dictionary."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
import itertools
|
||||||
|
from typing import Iterator, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
class Squash:
|
||||||
|
"""Squash a json object or Python dictionary into a single level dictionary."""
|
||||||
|
|
||||||
|
def __init__(self, data: dict) -> None:
|
||||||
|
"""
|
||||||
|
Create a Squash object to squash data into a single level dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (dict): [description]
|
||||||
|
|
||||||
|
Example:
|
||||||
|
squashed_data = Squash(my_data)
|
||||||
|
|
||||||
|
squashed_data.as_dict
|
||||||
|
"""
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
@property
|
||||||
|
def as_dict(self) -> dict:
|
||||||
|
"""
|
||||||
|
Return the squashed data as a dictionary.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: The original data squashed as a dict.
|
||||||
|
"""
|
||||||
|
return self._squash()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _unpack_dict(
|
||||||
|
key: str, value: dict | list | str
|
||||||
|
) -> Iterator[Tuple[str, dict | list | str]]:
|
||||||
|
if isinstance(value, dict):
|
||||||
|
for sub_key, sub_value in value.items():
|
||||||
|
temporary_key = f"{key}_{sub_key}"
|
||||||
|
yield temporary_key, sub_value
|
||||||
|
elif isinstance(value, list):
|
||||||
|
for index, sub_value in enumerate(value):
|
||||||
|
temporary_key = f"{key}_{index}"
|
||||||
|
yield temporary_key, sub_value
|
||||||
|
else:
|
||||||
|
yield key, value
|
||||||
|
|
||||||
|
def _squash(self) -> dict:
|
||||||
|
result = deepcopy(self.data)
|
||||||
|
while True:
|
||||||
|
result = dict(
|
||||||
|
itertools.chain.from_iterable(
|
||||||
|
itertools.starmap(self._unpack_dict, result.items())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not any(
|
||||||
|
isinstance(value, dict) for value in result.values()
|
||||||
|
) and not any(isinstance(value, list) for value in result.values()):
|
||||||
|
break
|
||||||
|
return result
|
||||||
0
tests/test_utilities/__init__.py
Normal file
0
tests/test_utilities/__init__.py
Normal file
119
tests/test_utilities/test_squasher.py
Normal file
119
tests/test_utilities/test_squasher.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from panaetius import utilities
|
||||||
|
|
||||||
|
|
||||||
|
def test_squashed_data(squashed_data, squashed_data_result):
|
||||||
|
# act
|
||||||
|
squashed_data_pre_squashed = utilities.squasher.Squash(squashed_data).as_dict
|
||||||
|
|
||||||
|
# assert
|
||||||
|
assert squashed_data_pre_squashed == squashed_data_result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def squashed_data():
|
||||||
|
return {
|
||||||
|
"destination_addresses": [
|
||||||
|
"Washington, DC, USA",
|
||||||
|
"Philadelphia, PA, USA",
|
||||||
|
"Santa Barbara, CA, USA",
|
||||||
|
"Miami, FL, USA",
|
||||||
|
"Austin, TX, USA",
|
||||||
|
"Napa County, CA, USA",
|
||||||
|
],
|
||||||
|
"origin_addresses": ["New York, NY, USA"],
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"distance": {"text": "227 mi", "value": 365468},
|
||||||
|
"duration": {
|
||||||
|
"text": "3 hours 54 mins",
|
||||||
|
"value": 14064,
|
||||||
|
},
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"distance": {"text": "94.6 mi", "value": 152193},
|
||||||
|
"duration": {"text": "1 hour 44 mins", "value": 6227},
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"distance": {"text": "2,878 mi", "value": 4632197},
|
||||||
|
"duration": {
|
||||||
|
"text": "1 day 18 hours",
|
||||||
|
"value": 151772,
|
||||||
|
},
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"distance": {"text": "1,286 mi", "value": 2069031},
|
||||||
|
"duration": {
|
||||||
|
"text": "18 hours 43 mins",
|
||||||
|
"value": 67405,
|
||||||
|
},
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"distance": {"text": "1,742 mi", "value": 2802972},
|
||||||
|
"duration": {"text": "1 day 2 hours", "value": 93070},
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"distance": {"text": "2,871 mi", "value": 4620514},
|
||||||
|
"duration": {
|
||||||
|
"text": "1 day 18 hours",
|
||||||
|
"value": 152913,
|
||||||
|
},
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "OK",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def squashed_data_result():
|
||||||
|
return {
|
||||||
|
"destination_addresses_0": "Washington, DC, USA",
|
||||||
|
"destination_addresses_1": "Philadelphia, PA, USA",
|
||||||
|
"destination_addresses_2": "Santa Barbara, CA, USA",
|
||||||
|
"destination_addresses_3": "Miami, FL, USA",
|
||||||
|
"destination_addresses_4": "Austin, TX, USA",
|
||||||
|
"destination_addresses_5": "Napa County, CA, USA",
|
||||||
|
"origin_addresses_0": "New York, NY, USA",
|
||||||
|
"rows_0_elements_0_distance_text": "227 mi",
|
||||||
|
"rows_0_elements_0_distance_value": 365468,
|
||||||
|
"rows_0_elements_0_duration_text": "3 hours 54 mins",
|
||||||
|
"rows_0_elements_0_duration_value": 14064,
|
||||||
|
"rows_0_elements_0_status": "OK",
|
||||||
|
"rows_0_elements_1_distance_text": "94.6 mi",
|
||||||
|
"rows_0_elements_1_distance_value": 152193,
|
||||||
|
"rows_0_elements_1_duration_text": "1 hour 44 mins",
|
||||||
|
"rows_0_elements_1_duration_value": 6227,
|
||||||
|
"rows_0_elements_1_status": "OK",
|
||||||
|
"rows_0_elements_2_distance_text": "2,878 mi",
|
||||||
|
"rows_0_elements_2_distance_value": 4632197,
|
||||||
|
"rows_0_elements_2_duration_text": "1 day 18 hours",
|
||||||
|
"rows_0_elements_2_duration_value": 151772,
|
||||||
|
"rows_0_elements_2_status": "OK",
|
||||||
|
"rows_0_elements_3_distance_text": "1,286 mi",
|
||||||
|
"rows_0_elements_3_distance_value": 2069031,
|
||||||
|
"rows_0_elements_3_duration_text": "18 hours 43 mins",
|
||||||
|
"rows_0_elements_3_duration_value": 67405,
|
||||||
|
"rows_0_elements_3_status": "OK",
|
||||||
|
"rows_0_elements_4_distance_text": "1,742 mi",
|
||||||
|
"rows_0_elements_4_distance_value": 2802972,
|
||||||
|
"rows_0_elements_4_duration_text": "1 day 2 hours",
|
||||||
|
"rows_0_elements_4_duration_value": 93070,
|
||||||
|
"rows_0_elements_4_status": "OK",
|
||||||
|
"rows_0_elements_5_distance_text": "2,871 mi",
|
||||||
|
"rows_0_elements_5_distance_value": 4620514,
|
||||||
|
"rows_0_elements_5_duration_text": "1 day 18 hours",
|
||||||
|
"rows_0_elements_5_duration_value": 152913,
|
||||||
|
"rows_0_elements_5_status": "OK",
|
||||||
|
"status": "OK",
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user