Skip to content

Commit

Permalink
fix: Improved logic for updating validity dates of aggregated bundles…
Browse files Browse the repository at this point in the history
… to ensure accuracy (#169)
  • Loading branch information
geertmeersman authored Dec 26, 2024
2 parents 210feac + fa4c00c commit 3d92b86
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 58 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.0.1
v1.0.2-beta.7
137 changes: 81 additions & 56 deletions custom_components/mobile_vikings/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,27 +235,7 @@ async def get_subscriptions(self):
return return_sub

def aggregate_bundles_by_type(self, balance):
"""Aggregate bundles by their type, summing up total, used, and remaining values.
For each unique bundle type (data, voice, sms), a single structure is created.
Args:
balance (dict): A dictionary containing the balance information with a key `bundles`,
which is a list of dictionaries. Each bundle dictionary must have the following keys:
- `type` (str): The type of the bundle (e.g., 'data', 'sms', 'voice').
- `total` (float): The total amount of the bundle (in GB for data bundles).
- `used` (float): The amount of the bundle that has been used (in GB for data bundles).
- `remaining` (float): The amount remaining in the bundle (in GB for data bundles).
- `valid_from` (str): The start date of the bundle validity (ISO 8601 format).
- `valid_until` (str): The end date of the bundle validity (ISO 8601 format).
Returns:
dict: A dictionary with keys 'data', 'voice', 'sms', each containing the aggregated bundle for that type.
Raises:
KeyError: If required keys are missing in the input bundles.
"""
"""Aggregate bundles by their type, including usage, validity, and period details in days and GB."""
# Parse the current time and make it timezone-aware
current_time = datetime.now(timezone.utc)

Expand Down Expand Up @@ -285,9 +265,64 @@ def aggregate_bundles_by_type(self, balance):
"used": 0,
"remaining": 0,
"unlimited": False, # Default to False
"periods": [], # Store individual periods for weighted calculations
}

# Check if the bundle is not "unlimited" and set the values accordingly
# Parse bundle validity dates
bundle_valid_from = datetime.strptime(
bundle["valid_from"], "%Y-%m-%dT%H:%M:%S%z"
)
bundle_valid_until = datetime.strptime(
bundle["valid_until"], "%Y-%m-%dT%H:%M:%S%z"
)

# Update valid_from and valid_until to reflect the overall range
aggregated_valid_from = datetime.strptime(
aggregated_bundles[bundle_type]["valid_from"], "%Y-%m-%dT%H:%M:%S%z"
)
aggregated_valid_until = datetime.strptime(
aggregated_bundles[bundle_type]["valid_until"], "%Y-%m-%dT%H:%M:%S%z"
)

if bundle_valid_from < aggregated_valid_from:
aggregated_bundles[bundle_type]["valid_from"] = bundle["valid_from"]

if bundle_valid_until > aggregated_valid_until:
aggregated_bundles[bundle_type]["valid_until"] = bundle["valid_until"]

# Calculate validity period in days
validity_period_seconds = (
bundle_valid_until - bundle_valid_from
).total_seconds()
validity_period_days = validity_period_seconds / 86400 # Convert to days

# Calculate elapsed time and period percentage
elapsed_time_seconds = (current_time - bundle_valid_from).total_seconds()
period_percentage = max(
0, min((elapsed_time_seconds / validity_period_seconds) * 100, 100)
)

# Calculate usage percentage for this period
usage_percentage = (
(bundle["used"] / bundle["total"]) * 100 if bundle["total"] > 0 else 0
)

# Add this period's details to the list
aggregated_bundles[bundle_type]["periods"].append(
{
"validity_period_days": round(
validity_period_days, 2
), # Validity period in days
"period_percentage": round(period_percentage, 2),
"usage_percentage": round(usage_percentage, 2),
"remaining_gb": round(
(bundle["total"] - bundle["used"]) / (1024**3), 2
), # Remaining in GB
"weight": validity_period_seconds, # Weight by period duration
}
)

# Aggregate totals, used, and remaining
if not aggregated_bundles[bundle_type]["unlimited"]:
if bundle["total"] == 0:
# Mark as unlimited only if not already set
Expand All @@ -299,7 +334,9 @@ def aggregate_bundles_by_type(self, balance):
# Sum up values for this type
aggregated_bundles[bundle_type]["total"] += bundle["total"]
aggregated_bundles[bundle_type]["used"] += bundle["used"]
aggregated_bundles[bundle_type]["remaining"] += bundle["remaining"]
aggregated_bundles[bundle_type]["remaining"] += (
bundle["total"] - bundle["used"]
)

# Calculate additional values for each aggregated bundle
for bundle in aggregated_bundles.values():
Expand All @@ -310,41 +347,29 @@ def aggregate_bundles_by_type(self, balance):

# Calculate used percentage
bundle["used_percentage"] = (used / total) * 100 if total > 0 else 0
bundle["used_percentage"] = round(
bundle["used_percentage"], 2
) # Round to two decimal places

# Only add total_gb, used_gb, and remaining_gb if the bundle type is 'data'
if bundle["type"] == "data":
bundle["total_gb"] = round(
total / (1024**3), 2
) # Convert from bytes to GB
bundle["used_gb"] = round(used / (1024**3), 2)
bundle["remaining_gb"] = round(remaining / (1024**3), 2)

try:
valid_from = datetime.strptime(
bundle["valid_from"], "%Y-%m-%dT%H:%M:%S%z"
)
valid_until = datetime.strptime(
bundle["valid_until"], "%Y-%m-%dT%H:%M:%S%z"
)
except ValueError as e:
raise ValueError(f"Invalid date format in bundle: {e}")

# Calculate validity percentage (period_percentage)
validity_period = (valid_until - valid_from).total_seconds()
elapsed_time = (current_time - valid_from).total_seconds()
period_percentage = max(
0, min((elapsed_time / validity_period) * 100, 100)
) # Clamp between 0 and 100

# Add period percentage to the bundle
bundle["period_percentage"] = round(period_percentage, 2)
bundle["used_percentage"] = round(bundle["used_percentage"], 2)

# Convert totals and remaining to GB
bundle["total_gb"] = round(
total / (1024**3), 2
) # Convert from bytes to GB
bundle["used_gb"] = round(used / (1024**3), 2)
bundle["remaining_gb"] = round(remaining / (1024**3), 2)

# Calculate weighted period progress percentage
total_weight = sum(period["weight"] for period in bundle["periods"])
combined_period_percentage = sum(
(period["period_percentage"] * period["weight"]) / total_weight
for period in bundle["periods"]
)
bundle["period_percentage"] = round(combined_period_percentage, 2)

# Calculate remaining days in the period
# Calculate remaining days
valid_until = datetime.strptime(
bundle["valid_until"], "%Y-%m-%dT%H:%M:%S%z"
)
remaining_days = (valid_until - current_time).days
bundle["remaining_days"] = max(remaining_days, 0) # Avoid negative days
bundle["remaining_days"] = max(remaining_days, 0)

# Compare used_percentage with period_percentage and add usage_alert
bundle["usage_alert"] = (
Expand Down
2 changes: 1 addition & 1 deletion custom_components/mobile_vikings/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/geertmeersman/mobile_vikings/issues",
"requirements": [],
"version": "v1.0.1"
"version": "v1.0.2-beta.7"
}

0 comments on commit 3d92b86

Please sign in to comment.