Skip to content

Commit

Permalink
Added coingecko modules
Browse files Browse the repository at this point in the history
  • Loading branch information
ByteMeAsap committed Nov 25, 2024
1 parent fc49c4f commit a08b6ae
Show file tree
Hide file tree
Showing 15 changed files with 341 additions and 0 deletions.
3 changes: 3 additions & 0 deletions currency_rate_update_coingecko/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import models
17 changes: 17 additions & 0 deletions currency_rate_update_coingecko/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2023 Tecnativa - Ernesto Tejeda
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

{
"name": "Currency Rate Update: CoinGecko",
"version": "16.0.1.0.0",
"category": "Financial Management/Configuration",
"summary": "Update exchange rates using CoinGecko",
"author": "Tecnativa, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/currency",
"license": "AGPL-3",
"installable": True,
"application": False,
"depends": ["currency_rate_update_mapping", "crypto_currency"],
"data":["data/res_currency_rate_provider.xml"],
"external_dependencies": {"python": ["pycgapi"]},
}
10 changes: 10 additions & 0 deletions currency_rate_update_coingecko/data/res_currency_rate_provider.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" ?>
<!--
Copyright 2024 Onestein
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-->
<odoo noupdate="1">
<record model="res.currency.rate.provider" id="res_currency_rate_provider_coingecko">
<field name="service">CoinGecko</field>
</record>
</odoo>
2 changes: 2 additions & 0 deletions currency_rate_update_coingecko/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from . import res_currency_rate_provider_CoinGecko
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Copyright 2024 Onestein
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import logging
from datetime import date, timedelta

from pycgapi import CoinGeckoAPI

from odoo import _, fields, models

_logger = logging.getLogger(__name__)


class ResCurrencyRateProviderCoinGecko(models.Model):
_inherit = "res.currency.rate.provider"

service = fields.Selection(
selection_add=[("CoinGecko", "CoinGecko")],
ondelete={"CoinGecko": "set default"},
)

def _get_supported_currencies(self):
self.ensure_one()
if self.service != "CoinGecko":
return super()._get_supported_currencies()
# List of cryptocurrencies based on configured provider mappings
supported_currencies = (
self.env["res.currency.rate.provider.mapping"]
.search([("provider_id", "=", self.id)])
.mapped("currency_id.name")
)
return supported_currencies

def _obtain_rates(self, base_currency, currencies, date_from, date_to):
self.ensure_one()
if self.service != "CoinGecko":
return super()._obtain_rates(base_currency, currencies, date_from, date_to)
if date_from < date.today():
return self._get_historical_rate_from_coingecko(
date_from, date_to, base_currency
)
else:
return self._get_latest_rate_from_coingecko(base_currency)

def _get_latest_rate_from_coingecko(self, base_currency):
"""Get all the exchange rates for today"""
api = CoinGeckoAPI()
today = date.today()
data = {today: {}}
for (
currency
) in self.currency_ids.res_currency_rate_provider_mapping_ids.filtered(
lambda l: l.provider_id == self
):
try:
coin_data = api.coin_historical_on_date(
currency.provider_reference, today.strftime("%m-%d-%Y")
)
except Exception as e:
_logger.warning(
'Currency Rate Provider "%(name)s" failed to obtain for %(currency)s currency'
% {
"name": self.name,
"currency": currency.currency_id.name,
},
exc_info=True,
)
self.message_post(
subject=_("Currency Rate Provider Failure"),
body=_(
'Currency Rate Provider "%(name)s" failed to obtain data(check the rate provider mapping on the currency) :\n%(error)s'
)
% {
"name": self.name,
"currency": currency.currency_id.name,
"error": str(e) if e else _("N/A"),
},
)
continue
rate = (
coin_data.get("market_data")
.get("current_price")
.get(base_currency.lower(), 0)
)
if rate:
data[today].update({currency.currency_id.name: 1 / rate})
return data

def _get_historical_rate_from_coingecko(self, date_from, date_to, base_currency):
"""Get all the exchange rates from 'date_from' to 'date_to'"""
api = CoinGeckoAPI()
content = {}
current_date = date_from
while current_date <= date_to:
content[current_date] = {}
for (
currency
) in self.currency_ids.res_currency_rate_provider_mapping_ids.filtered(
lambda l: l.provider_id == self
):
try:
coin_data = api.coin_historical_on_date(
currency.provider_reference, current_date.strftime("%m-%d-%Y")
)
except Exception as e:
_logger.warning(
'Currency Rate Provider "%(name)s" failed to obtain for %(currency)s currency'
% {
"name": self.name,
"currency": currency.currency_id.name,
},
exc_info=True,
)
self.message_post(
subject=_("Currency Rate Provider Failure"),
body=_(
'Currency Rate Provider "%(name)s" failed to obtain data(check the rate provider mapping on the currency) :\n%(error)s'
)
% {
"name": self.name,
"currency": currency.currency_id.name,
"error": str(e) if e else _("N/A"),
},
)
continue
rate = (
coin_data.get("market_data")
.get("current_price")
.get(base_currency.lower(), 0)
)
if rate:
content[current_date].update({currency.currency_id.name: 1 / rate})
current_date += timedelta(days=1)
return content
3 changes: 3 additions & 0 deletions currency_rate_update_coingecko/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import test_currency_rate_update_coingecko
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright 2024 Onestein
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from dateutil.relativedelta import relativedelta

from odoo import fields
from odoo.tests import common, tagged
from odoo.tools import mute_logger


@tagged("post_install", "-at_install")
class TestResCurrencyRateProviderCoinGecko(common.TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()

cls.Company = cls.env["res.company"]
cls.CurrencyRate = cls.env["res.currency.rate"]
cls.Currency = cls.env["res.currency"]
cls.CurrencyRateProvider = cls.env["res.currency.rate.provider"]
cls.CurrencyRateProviderMapping = cls.env["res.currency.rate.provider.mapping"]

cls.today = fields.Date.today()
cls.eur_currency = cls.env.ref("base.EUR")
cls.company = cls.Company.create(
{"name": "Test company", "currency_id": cls.eur_currency.id}
)
cls.lnk_currency = cls.Currency.create({"name": "LINK", "symbol": "LNK"})
cls.coingecko_provider = cls.CurrencyRateProvider.search([("service","=","CoinGecko")],limit =1)
cls.coingecko_provider_mapping = cls.CurrencyRateProviderMapping.create(
{
"currency_id": cls.lnk_currency.id,
"provider_id": cls.coingecko_provider.id,
"provider_reference": "chainlink",
}
)
cls.coingecko_provider.write(
{
"currency_ids": [
(4, cls.lnk_currency.id),
],
}
)
cls.env.user.company_ids += cls.company
cls.env.company = cls.company
cls.CurrencyRate.search([]).unlink()

def test_supported_currencies_CoinGecko(self):
self.coingecko_provider._get_supported_currencies()

def test_cron(self):
self.coingecko_provider._scheduled_update()
rates = self.CurrencyRate.search([])
self.assertTrue(rates)
self.assertEqual(rates.currency_id, self.lnk_currency)
self.CurrencyRate.search([("company_id", "=", self.company.id)]).unlink()

def test_wizard(self):
wizard = (
self.env["res.currency.rate.update.wizard"]
.with_context(
default_provider_ids=[(6, False, self.coingecko_provider.ids)]
)
.create({})
)
wizard.action_update()
rates = self.CurrencyRate.search([])
self.assertTrue(rates)
self.assertEqual(rates.currency_id, self.lnk_currency)
self.CurrencyRate.search([("company_id", "=", self.company.id)]).unlink()

@mute_logger(
"odoo.addons.currency_rate_update_coingecko.models.res_currency_rate_provider_CoinGecko"
)
def test_error_CoinGecko(self):
self.coingecko_provider_mapping.write({"provider_reference": "test"})
self.coingecko_provider._update(self.today, self.today)
rates = self.CurrencyRate.search([])
self.assertEqual(len(rates), 0)
self.coingecko_provider._update(self.today - relativedelta(days=2), self.today)
rates = self.CurrencyRate.search([])
self.assertEqual(len(rates), 0)
3 changes: 3 additions & 0 deletions currency_rate_update_mapping/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import models
17 changes: 17 additions & 0 deletions currency_rate_update_mapping/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2024 Onestein
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Currency Rate Update Mapping",
"version": "16.0.1.0.0",
"author": "Onestein BV",
"website": "https://www.onestein.eu",
"license": "AGPL-3",
"category": "Financial Management/Configuration",
"summary": "Allows to add mappings for currency rate providers",
"depends": ["currency_rate_update"],
"data": [
"security/ir.model.access.csv",
"views/res_currency.xml",
],
"installable": True,
}
4 changes: 4 additions & 0 deletions currency_rate_update_mapping/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import res_currency
from . import res_currency_rate_provider_mapping
16 changes: 16 additions & 0 deletions currency_rate_update_mapping/models/res_currency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2024 Onestein
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from odoo import fields, models


class ResCurrency(models.Model):
_inherit = "res.currency"

res_currency_rate_provider_mapping_ids = fields.One2many(
comodel_name="res.currency.rate.provider.mapping",
inverse_name="currency_id",
string="Currency Rate Provider Mappings",
copy=False,
help="Currency mapping with the rate provider for updating rates",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2024 Onestein
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import fields, models


class ResCurrencyRateProviderMapping(models.Model):
_name = "res.currency.rate.provider.mapping"
_description = "Currency Rate Provider Mapping"

currency_id = fields.Many2one(
string="Currency",
comodel_name="res.currency",
)
provider_id = fields.Many2one(
string="Provider",
comodel_name="res.currency.rate.provider",
ondelete="restrict",
)
provider_reference = fields.Char(
required=True,
help="Defines the reference to be used when fetching rates from the provider",
)
3 changes: 3 additions & 0 deletions currency_rate_update_mapping/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_res_currency_rate_update_mapping_service_admin,res.currency.rate.provider.mapping,model_res_currency_rate_provider_mapping,base.group_system,1,1,1,1
access_res_currency_rate_update_mapping_service_manager,res.currency.rate.provider.mapping,model_res_currency_rate_provider_mapping,account.group_account_manager,1,0,0,0
25 changes: 25 additions & 0 deletions currency_rate_update_mapping/views/res_currency.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2024 Onestein
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-->
<odoo>
<record model="ir.ui.view" id="view_currency_form">
<field name="name">res.currency.form</field>
<field name="model">res.currency</field>
<field name="inherit_id" ref="base.view_currency_form"/>
<field name="arch" type="xml">
<notebook position="inside">
<page string="Rate Provider Mappings" name="rate_provider_mappings">
<field name="res_currency_rate_provider_mapping_ids" widget="one2many">
<tree editable="top" limit="25">
<field name="provider_id"/>
<field name="provider_reference"/>
</tree>
</field>
</page>
</notebook>

</field>
</record>
</odoo>
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ xlrd
python-slugify
vcrpy
dnspython==2.6.1
pycgapi

0 comments on commit a08b6ae

Please sign in to comment.