mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-21 13:23:14 +01:00
Update update_currencies.py
Retries + logging: Set timestamped errors on 5xx/429 or JSON failures. Empty-array guard: script exits non-zero if the API returns [], so won’t commit an empty file. Exit codes: any API or I/O error yields sys.exit(1), triggering “no commit on failure” logic.
This commit is contained in:
123
.github/scripts/update_currencies.py
vendored
123
.github/scripts/update_currencies.py
vendored
@@ -1,64 +1,99 @@
|
|||||||
import requests
|
#!/usr/bin/env python3
|
||||||
import json
|
import json
|
||||||
import os
|
import logging
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from requests.adapters import HTTPAdapter, Retry
|
||||||
|
|
||||||
|
API_URL = 'https://restcountries.com/v3.1/all?fields=name,common,currencies'
|
||||||
|
SAVE_PATH = Path('backend/internal/core/currencies/currencies.json')
|
||||||
|
TIMEOUT = 10 # seconds
|
||||||
|
|
||||||
|
def setup_logging():
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s %(levelname)s: %(message)s'
|
||||||
|
)
|
||||||
|
|
||||||
def fetch_currencies():
|
def fetch_currencies():
|
||||||
|
session = requests.Session()
|
||||||
|
retries = Retry(
|
||||||
|
total=3,
|
||||||
|
backoff_factor=1,
|
||||||
|
status_forcelist=[429, 500, 502, 503, 504],
|
||||||
|
allowed_methods=frozenset(['GET'])
|
||||||
|
)
|
||||||
|
session.mount('https://', HTTPAdapter(max_retries=retries))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.get('https://restcountries.com/v3.1/all?fields=name,common,currencies')
|
resp = session.get(API_URL, timeout=TIMEOUT)
|
||||||
response.raise_for_status()
|
resp.raise_for_status()
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
print(f"An error occurred while making the request: {e}")
|
logging.error("API request failed: %s", e)
|
||||||
return []
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
countries = response.json()
|
countries = resp.json()
|
||||||
except json.JSONDecodeError:
|
except ValueError as e:
|
||||||
print("Failed to decode JSON from the response.")
|
logging.error("Failed to parse JSON response: %s", e)
|
||||||
return []
|
return None
|
||||||
|
|
||||||
currencies_list = []
|
result = []
|
||||||
for country in countries:
|
for country in countries:
|
||||||
country_name = country.get('name', {}).get('common')
|
country_name = country.get('name', {}).get('common') or "Unknown"
|
||||||
country_currencies = country.get('currencies', {})
|
for code, info in country.get('currencies', {}).items():
|
||||||
for currency_code, currency_info in country_currencies.items():
|
result.append({
|
||||||
symbol = currency_info.get('symbol', '')
|
'code': code,
|
||||||
currencies_list.append({
|
'local': country_name,
|
||||||
'code': currency_code,
|
'symbol': info.get('symbol', ''),
|
||||||
'local': country_name,
|
'name': info.get('name', '')
|
||||||
'symbol': symbol,
|
|
||||||
'name': currency_info.get('name')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return currencies_list
|
# sort by country name for consistency
|
||||||
|
return sorted(result, key=lambda x: x['local'].lower())
|
||||||
|
|
||||||
def save_currencies(currencies, file_path):
|
def load_existing(path: Path):
|
||||||
# Sort the list by the "local" field
|
if not path.exists():
|
||||||
sorted_currencies = sorted(currencies, key=lambda x: x['local'].lower() if x['local'] else "")
|
return []
|
||||||
try:
|
try:
|
||||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
with path.open('r', encoding='utf-8') as f:
|
||||||
with open(file_path, 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(sorted_currencies, f, ensure_ascii=False, indent=4)
|
|
||||||
except IOError as e:
|
|
||||||
print(f"An error occurred while writing to the file: {e}")
|
|
||||||
|
|
||||||
def load_existing_currencies(file_path):
|
|
||||||
try:
|
|
||||||
with open(file_path, 'r', encoding='utf-8') as f:
|
|
||||||
return json.load(f)
|
return json.load(f)
|
||||||
except (IOError, json.JSONDecodeError):
|
except (IOError, ValueError) as e:
|
||||||
return [] # Return an empty list if file doesn't exist or is invalid
|
logging.warning("Could not load existing file (%s): %s", path, e)
|
||||||
|
return []
|
||||||
|
|
||||||
|
def save_currencies(data, path: Path):
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
try:
|
||||||
|
with path.open('w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=4)
|
||||||
|
logging.info("Wrote %d entries to %s", len(data), path)
|
||||||
|
except IOError as e:
|
||||||
|
logging.error("Failed to write file: %s", e)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
save_path = 'backend/internal/core/currencies/currencies.json'
|
setup_logging()
|
||||||
|
logging.info("Starting currency update")
|
||||||
existing_currencies = load_existing_currencies(save_path)
|
|
||||||
new_currencies = fetch_currencies()
|
|
||||||
|
|
||||||
if new_currencies == existing_currencies:
|
existing = load_existing(SAVE_PATH)
|
||||||
print("Currencies up-to-date with API, skipping commit.")
|
new = fetch_currencies()
|
||||||
else:
|
|
||||||
save_currencies(new_currencies, save_path)
|
if new is None:
|
||||||
print("Currencies updated and saved.")
|
logging.error("Aborting: failed to fetch or parse API data.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if not new:
|
||||||
|
logging.error("Aborting: API returned an empty list.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if new == existing:
|
||||||
|
logging.info("No changes detected; skipping write.")
|
||||||
|
return
|
||||||
|
|
||||||
|
save_currencies(new, SAVE_PATH)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user