Skip to content

Commit

Permalink
Capture currency exchange rates
Browse files Browse the repository at this point in the history
  • Loading branch information
sravfeyn committed Sep 12, 2024
1 parent 8bedd96 commit 7714d9a
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 0 deletions.
1 change: 1 addition & 0 deletions .env_template
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ TWILIO_SID=
TWILIO_TOKEN=
TWILIO_MESSAGING_SERVICE=
MAPBOX_TOKEN=
OPEN_EXCHANGE_RATES_API_ID=
Empty file.
6 changes: 6 additions & 0 deletions commcare_connect/accounting/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class AccountingConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "commcare_connect.accounting"
46 changes: 46 additions & 0 deletions commcare_connect/accounting/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generated by Django 4.2.5 on 2024-09-12 16:16

from decimal import Decimal
from django.db import migrations, models


def create_task(apps, schema_editor):
schedule, _ = CrontabSchedule.objects.get_or_create(
minute="00",
hour="23",
day_of_week="*",
day_of_month="*",
month_of_year="*",
)
PeriodicTask.objects.update_or_create(
crontab=schedule,
name="update_exchange_rates",
task="commcare_connect.accounting.tasks.update_exchange_rates",
)


def delete_task(apps, schema_editor):
PeriodicTask.objects.get(
name="update_exchange_rates",
task="commcare_connect.accounting.tasks.update_exchange_rates",
).delete()

class Migration(migrations.Migration):
initial = True

dependencies = []

operations = [
migrations.CreateModel(
name="Currency",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("code", models.CharField(max_length=3, unique=True)),
("name", models.CharField(db_index=True, max_length=25)),
("symbol", models.CharField(max_length=10)),
("rate_to_default", models.DecimalField(decimal_places=9, default=Decimal("1.0"), max_digits=20)),
("date_updated", models.DateField(auto_now=True)),
],
),
migrations.RunPython(create_task, delete_task)
]
Empty file.
37 changes: 37 additions & 0 deletions commcare_connect/accounting/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from decimal import Decimal

from django.db import models

from commcare_connect.cache import quickcache

DEFAULT_CURRENCY = "USD"


class Currency(models.Model):
"""
Keeps track of the current conversion rates so that we don't have to poll the free, but rate limited API
from Open Exchange Rates.
"""

code = models.CharField(max_length=3, unique=True)
name = models.CharField(max_length=25, db_index=True)
symbol = models.CharField(max_length=10)
rate_to_default = models.DecimalField(
default=Decimal("1.0"),
max_digits=20,
decimal_places=9,
)
date_updated = models.DateField(auto_now=True)

class Meta:
app_label = "accounting"

@classmethod
def get_default(cls):
default, _ = cls.objects.get_or_create(code=DEFAULT_CURRENCY)
return default

@classmethod
@quickcache(["code"], timeout=60 * 60)
def by_code(cls, code):
return cls.objects.get(code=code)
35 changes: 35 additions & 0 deletions commcare_connect/accounting/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import json
import logging
import urllib

from django.conf import settings

from config import celery_app

from .models import Currency

logger = logging.getLogger(__name__)


@celery_app.task()
def update_exchange_rates():
app_id = settings.OPEN_EXCHANGE_RATES_API_ID
if app_id:
try:
logger.info("Updating exchange rates...")
rates = json.load(
urllib.request.urlopen("https://openexchangerates.org/api/latest.json?app_id=%s" % app_id)
)["rates"]
default_rate = float(rates[Currency.get_default().code])
for code, rate in rates.items():
currency, _ = Currency.objects.get_or_create(code=code)
currency.rate_to_default = float(rate) / default_rate
currency.save()
logger.info(
"Exchange rate for {code} updated {rate:f}.".format(
code=currency.code,
rate=currency.rate_to_default,
)
)
except Exception as e:
logger.error("Error updating exchange rates: %s" % str(e))
3 changes: 3 additions & 0 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
]

LOCAL_APPS = [
"commcare_connect.accounting",
"commcare_connect.commcarehq_provider",
"commcare_connect.form_receiver",
"commcare_connect.opportunity",
Expand Down Expand Up @@ -339,3 +340,5 @@
TWILIO_AUTH_TOKEN = env("TWILIO_TOKEN", default=None)
TWILIO_MESSAGING_SERVICE = env("TWILIO_MESSAGING_SERVICE", default=None)
MAPBOX_TOKEN = env("MAPBOX_TOKEN", default=None)

OPEN_EXCHANGE_RATES_API_ID = env("OPEN_EXCHANGE_RATES_API_ID", default=None)

0 comments on commit 7714d9a

Please sign in to comment.