mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-04-19 00:45:22 +00:00
Review comments
This commit is contained in:
parent
56403fe053
commit
26762e5e93
@ -38,7 +38,7 @@ The configuration sources and their priorities are as follows:
|
||||
|
||||
### Runtime Config Updates
|
||||
|
||||
The EOS configuration can be updated at runtime. Note that those updates are not persistet
|
||||
The EOS configuration can be updated at runtime. Note that those updates are not persistent
|
||||
automatically. However it is possible to save the configuration to the `EOS configuration file`.
|
||||
|
||||
Use the following endpoints to change the current runtime configuration:
|
||||
|
@ -196,7 +196,7 @@ Prediction keys:
|
||||
|
||||
Configuration options:
|
||||
|
||||
- `prediction`: General prediction configuration.
|
||||
- `general`: General configuration.
|
||||
|
||||
- `latitude`: Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)"
|
||||
- `longitude`: Longitude in decimal degrees, within -180 to 180 (°)
|
||||
|
12
openapi.json
12
openapi.json
@ -106,7 +106,7 @@
|
||||
"title": "BaseBatteryParameters",
|
||||
"type": "object"
|
||||
},
|
||||
"ConfigCommonSettings-Input": {
|
||||
"GeneralSettings-Input": {
|
||||
"description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files and system location (latitude\nand longitude).\nValidators ensure each parameter is within a specified range. A computed property, `timezone`,\ndetermines the time zone based on latitude and longitude.\n\nAttributes:\n latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.\n longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.\n\nProperties:\n timezone (Optional[str]): Computed time zone string based on the specified latitude\n and longitude.\n\nValidators:\n validate_latitude (float): Ensures `latitude` is within the range -90 to 90.\n validate_longitude (float): Ensures `longitude` is within the range -180 to 180.",
|
||||
"properties": {
|
||||
"data_cache_subpath": {
|
||||
@ -185,10 +185,10 @@
|
||||
"title": "Longitude"
|
||||
}
|
||||
},
|
||||
"title": "ConfigCommonSettings",
|
||||
"title": "GeneralSettings",
|
||||
"type": "object"
|
||||
},
|
||||
"ConfigCommonSettings-Output": {
|
||||
"GeneralSettings-Output": {
|
||||
"description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files and system location (latitude\nand longitude).\nValidators ensure each parameter is within a specified range. A computed property, `timezone`,\ndetermines the time zone based on latitude and longitude.\n\nAttributes:\n latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.\n longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.\n\nProperties:\n timezone (Optional[str]): Computed time zone string based on the specified latitude\n and longitude.\n\nValidators:\n validate_latitude (float): Ensures `latitude` is within the range -90 to 90.\n validate_longitude (float): Ensures `longitude` is within the range -180 to 180.",
|
||||
"properties": {
|
||||
"config_file_path": {
|
||||
@ -343,7 +343,7 @@
|
||||
"config_folder_path",
|
||||
"config_file_path"
|
||||
],
|
||||
"title": "ConfigCommonSettings",
|
||||
"title": "GeneralSettings",
|
||||
"type": "object"
|
||||
},
|
||||
"ConfigEOS": {
|
||||
@ -359,7 +359,7 @@
|
||||
"default": {}
|
||||
},
|
||||
"general": {
|
||||
"$ref": "#/components/schemas/ConfigCommonSettings-Output",
|
||||
"$ref": "#/components/schemas/GeneralSettings-Output",
|
||||
"default": {
|
||||
"config_file_path": "/home/user/.config/net.akkudoktoreos.net/EOS.config.json",
|
||||
"config_folder_path": "/home/user/.config/net.akkudoktoreos.net",
|
||||
@ -2317,7 +2317,7 @@
|
||||
"general": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ConfigCommonSettings-Input"
|
||||
"$ref": "#/components/schemas/GeneralSettings-Input"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
|
@ -11,7 +11,7 @@ from typing import Any, Union
|
||||
from pydantic.fields import ComputedFieldInfo, FieldInfo
|
||||
from pydantic_core import PydanticUndefined
|
||||
|
||||
from akkudoktoreos.config.config import ConfigCommonSettings, ConfigEOS, get_config
|
||||
from akkudoktoreos.config.config import ConfigEOS, GeneralSettings, get_config
|
||||
from akkudoktoreos.core.logging import get_logger
|
||||
from akkudoktoreos.core.pydantic import PydanticBaseModel
|
||||
from akkudoktoreos.utils.docs import get_model_structure_from_examples
|
||||
@ -250,10 +250,10 @@ def generate_config_md(config_eos: ConfigEOS) -> str:
|
||||
str: The Markdown representation of the configuration spec.
|
||||
"""
|
||||
# Fix file path for general settings to not show local/test file path
|
||||
ConfigCommonSettings._config_file_path = Path(
|
||||
GeneralSettings._config_file_path = Path(
|
||||
"/home/user/.config/net.akkudoktoreos.net/EOS.config.json"
|
||||
)
|
||||
ConfigCommonSettings._config_folder_path = config_eos.general.config_file_path.parent
|
||||
GeneralSettings._config_folder_path = config_eos.general.config_file_path.parent
|
||||
|
||||
markdown = "# Configuration Table\n\n"
|
||||
|
||||
|
@ -27,6 +27,7 @@ from pydantic_settings.sources import ConfigFileSourceMixin
|
||||
# settings
|
||||
from akkudoktoreos.config.configabc import SettingsBaseModel
|
||||
from akkudoktoreos.core.coreabc import SingletonMixin
|
||||
from akkudoktoreos.core.decorators import classproperty
|
||||
from akkudoktoreos.core.logging import get_logger
|
||||
from akkudoktoreos.core.logsettings import LoggingCommonSettings
|
||||
from akkudoktoreos.core.pydantic import merge_models
|
||||
@ -40,7 +41,7 @@ from akkudoktoreos.prediction.pvforecast import PVForecastCommonSettings
|
||||
from akkudoktoreos.prediction.weather import WeatherCommonSettings
|
||||
from akkudoktoreos.server.server import ServerCommonSettings
|
||||
from akkudoktoreos.utils.datetimeutil import to_timezone
|
||||
from akkudoktoreos.utils.utils import UtilsCommonSettings, classproperty
|
||||
from akkudoktoreos.utils.utils import UtilsCommonSettings
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@ -63,7 +64,7 @@ def get_absolute_path(
|
||||
return None
|
||||
|
||||
|
||||
class ConfigCommonSettings(SettingsBaseModel):
|
||||
class GeneralSettings(SettingsBaseModel):
|
||||
"""Settings for common configuration.
|
||||
|
||||
General configuration to set directories of cache and output files and system location (latitude
|
||||
@ -152,7 +153,7 @@ class SettingsEOS(BaseSettings):
|
||||
Used by updating the configuration with specific settings only.
|
||||
"""
|
||||
|
||||
general: Optional[ConfigCommonSettings] = None
|
||||
general: Optional[GeneralSettings] = None
|
||||
logging: Optional[LoggingCommonSettings] = None
|
||||
devices: Optional[DevicesCommonSettings] = None
|
||||
measurement: Optional[MeasurementCommonSettings] = None
|
||||
@ -176,7 +177,7 @@ class SettingsEOSDefaults(SettingsEOS):
|
||||
Used by ConfigEOS instance to make all fields available.
|
||||
"""
|
||||
|
||||
general: ConfigCommonSettings = ConfigCommonSettings()
|
||||
general: GeneralSettings = GeneralSettings()
|
||||
logging: LoggingCommonSettings = LoggingCommonSettings()
|
||||
devices: DevicesCommonSettings = DevicesCommonSettings()
|
||||
measurement: MeasurementCommonSettings = MeasurementCommonSettings()
|
||||
@ -254,7 +255,7 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults):
|
||||
"""Customizes the order and handling of settings sources for a Pydantic BaseSettings subclass.
|
||||
|
||||
This method determines the sources for application configuration settings, including
|
||||
environment variables, dotenv files, JSON configuration files, and file secrets.
|
||||
environment variables, dotenv files and JSON configuration files.
|
||||
It ensures that a default configuration file exists and creates one if necessary.
|
||||
|
||||
Args:
|
||||
@ -262,7 +263,7 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults):
|
||||
init_settings (PydanticBaseSettingsSource): The initial settings source, typically passed at runtime.
|
||||
env_settings (PydanticBaseSettingsSource): Settings sourced from environment variables.
|
||||
dotenv_settings (PydanticBaseSettingsSource): Settings sourced from a dotenv file.
|
||||
file_secret_settings (PydanticBaseSettingsSource): Settings sourced from secret files.
|
||||
file_secret_settings (PydanticBaseSettingsSource): Unused (needed for parent class interface).
|
||||
|
||||
Returns:
|
||||
tuple[PydanticBaseSettingsSource, ...]: A tuple of settings sources in the order they should be applied.
|
||||
@ -272,8 +273,8 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults):
|
||||
2. If the configuration file does not exist, creates the directory (if needed) and attempts to copy a
|
||||
default configuration file to the location. If the copy fails, uses the default configuration file directly.
|
||||
3. Creates a `JsonConfigSettingsSource` for both the configuration file and the default configuration file.
|
||||
4. Updates class attributes `ConfigCommonSettings._config_folder_path` and
|
||||
`ConfigCommonSettings._config_file_path` to reflect the determined paths.
|
||||
4. Updates class attributes `GeneralSettings._config_folder_path` and
|
||||
`GeneralSettings._config_file_path` to reflect the determined paths.
|
||||
5. Returns a tuple containing all provided and newly created settings sources in the desired order.
|
||||
|
||||
Notes:
|
||||
@ -295,15 +296,14 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults):
|
||||
default_settings = JsonConfigSettingsSource(
|
||||
settings_cls, json_file=cls.config_default_file_path
|
||||
)
|
||||
ConfigCommonSettings._config_folder_path = config_dir
|
||||
ConfigCommonSettings._config_file_path = config_file
|
||||
GeneralSettings._config_folder_path = config_dir
|
||||
GeneralSettings._config_file_path = config_file
|
||||
|
||||
return (
|
||||
init_settings,
|
||||
env_settings,
|
||||
dotenv_settings,
|
||||
file_settings,
|
||||
file_secret_settings,
|
||||
default_settings,
|
||||
)
|
||||
|
||||
|
42
src/akkudoktoreos/core/decorators.py
Normal file
42
src/akkudoktoreos/core/decorators.py
Normal file
@ -0,0 +1,42 @@
|
||||
from typing import Any, Optional
|
||||
|
||||
from akkudoktoreos.core.logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class classproperty(property):
|
||||
"""A decorator to define a read-only property at the class level.
|
||||
|
||||
This class extends the built-in `property` to allow a method to be accessed
|
||||
as a property on the class itself, rather than an instance. This is useful
|
||||
when you want a property-like syntax for methods that depend on the class
|
||||
rather than any instance of the class.
|
||||
|
||||
Example:
|
||||
class MyClass:
|
||||
_value = 42
|
||||
|
||||
@classproperty
|
||||
def value(cls):
|
||||
return cls._value
|
||||
|
||||
print(MyClass.value) # Outputs: 42
|
||||
|
||||
Methods:
|
||||
__get__: Retrieves the value of the class property by calling the
|
||||
decorated method on the class.
|
||||
|
||||
Parameters:
|
||||
fget (Callable[[type], Any]): A method that takes the class as an
|
||||
argument and returns a value.
|
||||
|
||||
Raises:
|
||||
AssertionError: If `fget` is not defined when `__get__` is called.
|
||||
"""
|
||||
|
||||
def __get__(self, _: Any, owner_cls: Optional[type[Any]] = None) -> Any:
|
||||
if owner_cls is None:
|
||||
return self
|
||||
assert self.fget is not None
|
||||
return self.fget(owner_cls)
|
@ -39,153 +39,6 @@ class Devices(SingletonMixin, DevicesBase):
|
||||
device.post_setup()
|
||||
|
||||
|
||||
# # Devices
|
||||
# # TODO: Make devices class a container of device simulation providers.
|
||||
# # Device simulations to be used are then enabled in the configuration.
|
||||
# battery: ClassVar[Battery] = Battery(provider_id="GenericBattery")
|
||||
# ev: ClassVar[Battery] = Battery(provider_id="GenericBEV")
|
||||
# home_appliance: ClassVar[HomeAppliance] = HomeAppliance(provider_id="GenericDishWasher")
|
||||
# inverter: ClassVar[Inverter] = Inverter(
|
||||
# self_consumption_predictor=SelfConsumptionProbabilityInterpolator,
|
||||
# battery=battery,
|
||||
# provider_id="GenericInverter",
|
||||
# )
|
||||
#
|
||||
# def update_data(self) -> None:
|
||||
# """Update device simulation data."""
|
||||
# # Assure devices are set up
|
||||
# self.battery.setup()
|
||||
# self.ev.setup()
|
||||
# self.home_appliance.setup()
|
||||
# self.inverter.setup()
|
||||
#
|
||||
# # Pre-allocate arrays for the results, optimized for speed
|
||||
# self.last_wh_pro_stunde = np.full((self.total_hours), np.nan)
|
||||
# self.grid_export_wh_pro_stunde = np.full((self.total_hours), np.nan)
|
||||
# self.grid_import_wh_pro_stunde = np.full((self.total_hours), np.nan)
|
||||
# self.kosten_euro_pro_stunde = np.full((self.total_hours), np.nan)
|
||||
# self.einnahmen_euro_pro_stunde = np.full((self.total_hours), np.nan)
|
||||
# self.akku_soc_pro_stunde = np.full((self.total_hours), np.nan)
|
||||
# self.eauto_soc_pro_stunde = np.full((self.total_hours), np.nan)
|
||||
# self.verluste_wh_pro_stunde = np.full((self.total_hours), np.nan)
|
||||
# self.home_appliance_wh_per_hour = np.full((self.total_hours), np.nan)
|
||||
#
|
||||
# # Set initial state
|
||||
# simulation_step = to_duration("1 hour")
|
||||
# if self.battery:
|
||||
# self.akku_soc_pro_stunde[0] = self.battery.current_soc_percentage()
|
||||
# if self.ev:
|
||||
# self.eauto_soc_pro_stunde[0] = self.ev.current_soc_percentage()
|
||||
#
|
||||
# # Get predictions for full device simulation time range
|
||||
# # gesamtlast[stunde]
|
||||
# load_total_mean = self.prediction.key_to_array(
|
||||
# "load_total_mean",
|
||||
# start_datetime=self.start_datetime,
|
||||
# end_datetime=self.end_datetime,
|
||||
# interval=simulation_step,
|
||||
# )
|
||||
# # pv_prognose_wh[stunde]
|
||||
# pvforecast_ac_power = self.prediction.key_to_array(
|
||||
# "pvforecast_ac_power",
|
||||
# start_datetime=self.start_datetime,
|
||||
# end_datetime=self.end_datetime,
|
||||
# interval=simulation_step,
|
||||
# )
|
||||
# # strompreis_euro_pro_wh[stunde]
|
||||
# elecprice_marketprice_wh = self.prediction.key_to_array(
|
||||
# "elecprice_marketprice_wh",
|
||||
# start_datetime=self.start_datetime,
|
||||
# end_datetime=self.end_datetime,
|
||||
# interval=simulation_step,
|
||||
# )
|
||||
# # einspeiseverguetung_euro_pro_wh_arr[stunde]
|
||||
# # TODO: Create prediction for einspeiseverguetung_euro_pro_wh_arr
|
||||
# einspeiseverguetung_euro_pro_wh_arr = np.full((self.total_hours), 0.078)
|
||||
#
|
||||
# for stunde_since_now in range(0, self.total_hours):
|
||||
# hour = self.start_datetime.hour + stunde_since_now
|
||||
#
|
||||
# # Accumulate loads and PV generation
|
||||
# consumption = load_total_mean[stunde_since_now]
|
||||
# self.verluste_wh_pro_stunde[stunde_since_now] = 0.0
|
||||
#
|
||||
# # Home appliances
|
||||
# if self.home_appliance:
|
||||
# ha_load = self.home_appliance.get_load_for_hour(hour)
|
||||
# consumption += ha_load
|
||||
# self.home_appliance_wh_per_hour[stunde_since_now] = ha_load
|
||||
#
|
||||
# # E-Auto handling
|
||||
# if self.ev:
|
||||
# if self.ev_charge_hours[hour] > 0:
|
||||
# geladene_menge_eauto, verluste_eauto = self.ev.charge_energy(
|
||||
# None, hour, relative_power=self.ev_charge_hours[hour]
|
||||
# )
|
||||
# consumption += geladene_menge_eauto
|
||||
# self.verluste_wh_pro_stunde[stunde_since_now] += verluste_eauto
|
||||
# self.eauto_soc_pro_stunde[stunde_since_now] = self.ev.current_soc_percentage()
|
||||
#
|
||||
# # Process inverter logic
|
||||
# grid_export, grid_import, losses, self_consumption = (0.0, 0.0, 0.0, 0.0)
|
||||
# if self.battery:
|
||||
# self.battery.set_charge_allowed_for_hour(self.dc_charge_hours[hour], hour)
|
||||
# if self.inverter:
|
||||
# generation = pvforecast_ac_power[hour]
|
||||
# grid_export, grid_import, losses, self_consumption = self.inverter.process_energy(
|
||||
# generation, consumption, hour
|
||||
# )
|
||||
#
|
||||
# # AC PV Battery Charge
|
||||
# if self.battery and self.ac_charge_hours[hour] > 0.0:
|
||||
# self.battery.set_charge_allowed_for_hour(1, hour)
|
||||
# geladene_menge, verluste_wh = self.battery.charge_energy(
|
||||
# None, hour, relative_power=self.ac_charge_hours[hour]
|
||||
# )
|
||||
# # print(stunde, " ", geladene_menge, " ",self.ac_charge_hours[stunde]," ",self.battery.current_soc_percentage())
|
||||
# consumption += geladene_menge
|
||||
# grid_import += geladene_menge
|
||||
# self.verluste_wh_pro_stunde[stunde_since_now] += verluste_wh
|
||||
#
|
||||
# self.grid_export_wh_pro_stunde[stunde_since_now] = grid_export
|
||||
# self.grid_import_wh_pro_stunde[stunde_since_now] = grid_import
|
||||
# self.verluste_wh_pro_stunde[stunde_since_now] += losses
|
||||
# self.last_wh_pro_stunde[stunde_since_now] = consumption
|
||||
#
|
||||
# # Financial calculations
|
||||
# self.kosten_euro_pro_stunde[stunde_since_now] = (
|
||||
# grid_import * self.strompreis_euro_pro_wh[hour]
|
||||
# )
|
||||
# self.einnahmen_euro_pro_stunde[stunde_since_now] = (
|
||||
# grid_export * self.einspeiseverguetung_euro_pro_wh_arr[hour]
|
||||
# )
|
||||
#
|
||||
# # battery SOC tracking
|
||||
# if self.battery:
|
||||
# self.akku_soc_pro_stunde[stunde_since_now] = self.battery.current_soc_percentage()
|
||||
# else:
|
||||
# self.akku_soc_pro_stunde[stunde_since_now] = 0.0
|
||||
#
|
||||
# def report_dict(self) -> Dict[str, Any]:
|
||||
# """Provides devices simulation output as a dictionary."""
|
||||
# out: Dict[str, Optional[Union[np.ndarray, float]]] = {
|
||||
# "Last_Wh_pro_Stunde": self.last_wh_pro_stunde,
|
||||
# "grid_export_Wh_pro_Stunde": self.grid_export_wh_pro_stunde,
|
||||
# "grid_import_Wh_pro_Stunde": self.grid_import_wh_pro_stunde,
|
||||
# "Kosten_Euro_pro_Stunde": self.kosten_euro_pro_stunde,
|
||||
# "akku_soc_pro_stunde": self.akku_soc_pro_stunde,
|
||||
# "Einnahmen_Euro_pro_Stunde": self.einnahmen_euro_pro_stunde,
|
||||
# "Gesamtbilanz_Euro": self.total_balance_euro,
|
||||
# "EAuto_SoC_pro_Stunde": self.eauto_soc_pro_stunde,
|
||||
# "Gesamteinnahmen_Euro": self.total_revenues_euro,
|
||||
# "Gesamtkosten_Euro": self.total_costs_euro,
|
||||
# "Verluste_Pro_Stunde": self.verluste_wh_pro_stunde,
|
||||
# "Gesamt_Verluste": self.total_losses_wh,
|
||||
# "Home_appliance_wh_per_hour": self.home_appliance_wh_per_hour,
|
||||
# }
|
||||
# return out
|
||||
|
||||
|
||||
# Initialize the Devices simulation, it is a singleton.
|
||||
devices = Devices()
|
||||
|
||||
|
@ -147,14 +147,12 @@ class Measurement(SingletonMixin, DataImportMixin, DataSequence):
|
||||
"""Provides measurement key for given name and topic."""
|
||||
topic = topic.lower()
|
||||
|
||||
print(self.topics)
|
||||
if topic not in self.topics:
|
||||
return None
|
||||
|
||||
topic_keys = [
|
||||
key for key in self.config.measurement.model_fields.keys() if key.startswith(topic)
|
||||
]
|
||||
print(topic_keys)
|
||||
key = None
|
||||
if topic == "load":
|
||||
for config_key in topic_keys:
|
||||
|
@ -1,5 +1,5 @@
|
||||
import json
|
||||
from typing import Any, Optional
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
@ -9,14 +9,6 @@ from akkudoktoreos.core.logging import get_logger
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class classproperty(property):
|
||||
def __get__(self, _: Any, owner_cls: Optional[type[Any]] = None) -> Any:
|
||||
if owner_cls is None:
|
||||
return self
|
||||
assert self.fget is not None
|
||||
return self.fget(owner_cls)
|
||||
|
||||
|
||||
class UtilsCommonSettings(SettingsBaseModel):
|
||||
"""Utils Configuration."""
|
||||
|
||||
|
@ -5,7 +5,7 @@ from unittest.mock import patch
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from akkudoktoreos.config.config import ConfigCommonSettings, ConfigEOS
|
||||
from akkudoktoreos.config.config import ConfigEOS, GeneralSettings
|
||||
from akkudoktoreos.core.logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
@ -159,8 +159,8 @@ def test_config_copy(config_eos, monkeypatch):
|
||||
],
|
||||
)
|
||||
def test_config_common_settings_valid(latitude, longitude, expected_timezone):
|
||||
"""Test valid settings for ConfigCommonSettings."""
|
||||
general_settings = ConfigCommonSettings(
|
||||
"""Test valid settings for GeneralSettings."""
|
||||
general_settings = GeneralSettings(
|
||||
latitude=latitude,
|
||||
longitude=longitude,
|
||||
)
|
||||
@ -184,30 +184,30 @@ def test_config_common_settings_invalid(field_name, invalid_value, expected_erro
|
||||
"latitude": 40.7128,
|
||||
"longitude": -74.0060,
|
||||
}
|
||||
assert ConfigCommonSettings(**valid_data) is not None
|
||||
assert GeneralSettings(**valid_data) is not None
|
||||
valid_data[field_name] = invalid_value
|
||||
|
||||
with pytest.raises(ValidationError, match=expected_error):
|
||||
ConfigCommonSettings(**valid_data)
|
||||
GeneralSettings(**valid_data)
|
||||
|
||||
|
||||
def test_config_common_settings_no_location():
|
||||
"""Test that timezone is None when latitude and longitude are not provided."""
|
||||
settings = ConfigCommonSettings(latitude=None, longitude=None)
|
||||
settings = GeneralSettings(latitude=None, longitude=None)
|
||||
assert settings.timezone is None
|
||||
|
||||
|
||||
def test_config_common_settings_with_location():
|
||||
"""Test that timezone is correctly computed when latitude and longitude are provided."""
|
||||
settings = ConfigCommonSettings(latitude=34.0522, longitude=-118.2437)
|
||||
settings = GeneralSettings(latitude=34.0522, longitude=-118.2437)
|
||||
assert settings.timezone == "America/Los_Angeles"
|
||||
|
||||
|
||||
def test_config_common_settings_timezone_none_when_coordinates_missing():
|
||||
"""Test that timezone is None when latitude or longitude is missing."""
|
||||
config_no_latitude = ConfigCommonSettings(latitude=None, longitude=-74.0060)
|
||||
config_no_longitude = ConfigCommonSettings(latitude=40.7128, longitude=None)
|
||||
config_no_coords = ConfigCommonSettings(latitude=None, longitude=None)
|
||||
config_no_latitude = GeneralSettings(latitude=None, longitude=-74.0060)
|
||||
config_no_longitude = GeneralSettings(latitude=40.7128, longitude=None)
|
||||
config_no_coords = GeneralSettings(latitude=None, longitude=None)
|
||||
|
||||
assert config_no_latitude.timezone is None
|
||||
assert config_no_longitude.timezone is None
|
||||
|
Loading…
x
Reference in New Issue
Block a user