From 02fbaf00a563718d577d3d54555526bd0f01a6bc Mon Sep 17 00:00:00 2001 From: egregors Date: Mon, 16 May 2022 18:13:11 +0200 Subject: [PATCH 1/6] Added linting, pre-commit hooks and format --- .gitignore | 2 +- .pre-commit-config.yaml | 38 +++++++++++++ CHANGES.md | 4 +- MANIFEST.in | 2 +- Makefile | 28 ++++++++++ README.md | 12 ++-- cbrf/__init__.py | 21 ++++--- cbrf/api.py | 47 +++++++--------- cbrf/const.py | 21 ++++--- cbrf/models.py | 120 +++++++++++++++++++--------------------- cbrf/tests.py | 66 +++++++++++++--------- cbrf/utils.py | 17 ++---- setup.cfg | 34 ++++++++++++ setup.py | 29 +++++----- 14 files changed, 265 insertions(+), 176 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 Makefile create mode 100644 setup.cfg diff --git a/.gitignore b/.gitignore index b3e187a..1900893 100644 --- a/.gitignore +++ b/.gitignore @@ -132,4 +132,4 @@ crashlytics.properties crashlytics-build.properties fabric.properties -.idea/ \ No newline at end of file +.idea/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..296819a --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,38 @@ +default_stages: [commit] + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + + - repo: https://github.com/asottile/pyupgrade + rev: v2.32.1 + hooks: + - id: pyupgrade + args: [--py39-plus] + + - repo: https://github.com/psf/black + rev: 22.3.0 + hooks: + - id: black + + - repo: https://github.com/PyCQA/isort + rev: 5.10.1 + hooks: + - id: isort + + - repo: https://github.com/PyCQA/flake8 + rev: 4.0.1 + hooks: + - id: flake8 + args: ["--config=setup.cfg"] + additional_dependencies: [flake8-isort] + +# sets up .pre-commit-ci.yaml to ensure pre-commit dependencies stay up to date +ci: + autoupdate_schedule: weekly + skip: [] + submodules: false diff --git a/CHANGES.md b/CHANGES.md index 4f43e32..5a1f745 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -35,10 +35,10 @@ Changes #### ver.: 0.4.5 (15.08.2019) -* Travis repo path update +* Travis repo path update #### ver.: 0.4.6 (15.08.2019) * Update long_description_content_type #### ver.: 0.4.7 (15.08.2019) -* i hate pypi \ No newline at end of file +* i hate pypi diff --git a/MANIFEST.in b/MANIFEST.in index 4bf4483..bb3ec5f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1 @@ -include README.md \ No newline at end of file +include README.md diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..409a453 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +.DEFAULT_GOAL := help +.PHONY: help test lint + +lint: ## Run pre-commit hooks + pre-commit run -a + +test: ## Run tests + pytest cbrf/tests.py + + +## Help + +help: ## Show help message + @IFS=$$'\n' ; \ + help_lines=(`fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##/:/'`); \ + printf "%s\n\n" "Usage: make [task]"; \ + printf "%-20s %s\n" "task" "help" ; \ + printf "%-20s %s\n" "------" "----" ; \ + for help_line in $${help_lines[@]}; do \ + IFS=$$':' ; \ + help_split=($$help_line) ; \ + help_command=`echo $${help_split[0]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \ + help_info=`echo $${help_split[2]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \ + printf '\033[36m'; \ + printf "%-20s %s" $$help_command ; \ + printf '\033[0m'; \ + printf "%s\n" $$help_info; \ + done diff --git a/README.md b/README.md index e128640..0fbf139 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ _Wrapper for The Central Bank of the Russian Federation site API_ [Site](http://www.cbr.ru/) and [API](http://www.cbr.ru/scripts/Root.asp?PrtId=SXML) of The Central Bank of the Russian Federation. - + ## Installation Stable version: @@ -27,13 +27,13 @@ Dev version: ``` ## Settings -For using with your own hostname set environment variables, for example +For using with your own hostname set environment variables, for example ``` export CBRF_URL_SCHEME=http export CBRF_URL_HOST=www.my-own-cbr.ru ``` - + ## How to use ### API @@ -103,11 +103,11 @@ Decimal('28.6200') To run tests: ``` -pytest cbrf/tests.py +pytest cbrf/tests.py ``` > You should install `pytest` first ## Contributing -Bug reports, bug fixes, and new features are always welcome. -Please open issues, and submit pull requests for any new code. \ No newline at end of file +Bug reports, bug fixes, and new features are always welcome. +Please open issues, and submit pull requests for any new code. diff --git a/cbrf/__init__.py b/cbrf/__init__.py index caf64a3..840d75f 100644 --- a/cbrf/__init__.py +++ b/cbrf/__init__.py @@ -1,23 +1,22 @@ -# -*- coding: utf-8 -*- - -# ___ ___ __ __ -# / / /__ /__ ) (__/__) -# /__/ /___) / / -# ) - """ cbrf.py ~~~~~~~ Wrapper for The Central Bank of the Russian Federation site API. -CBRF site: http://www.cbr.ru/eng/ -Source: https://github.com/Egregors/cbr +CBRF site: https://www.cbr.ru/eng/ +Source: https://github.com/egregors/cbr :copyright: (c) 2017 by Vadim Iskuchekov (@egregors) :license: MIT """ -__title__ = 'cbrf' -__version__ = '0.4.7' +__title__ = "cbrf" +__version__ = "0.5.0" from .api import get_currencies_info, get_daily_rates, get_dynamic_rates + +__all__ = [ + "get_currencies_info", + "get_daily_rates", + "get_dynamic_rates", +] diff --git a/cbrf/api.py b/cbrf/api.py index 0a0f595..7a4de46 100644 --- a/cbrf/api.py +++ b/cbrf/api.py @@ -1,22 +1,9 @@ -# -*- coding: utf-8 -*- - -""" -cbrf.api -~~~~~~~~ - -This module implements the cbrf wrapper API. - -:copyright: (c) 2017 by Vadim Iskuchekov (@egregors) -:license: MIT -""" - import datetime from xml.etree.ElementTree import XML, Element import requests -from . import const -from . import utils +from . import const, utils def get_currencies_info() -> Element: @@ -27,13 +14,13 @@ def get_currencies_info() -> Element: :return: :class: `Element ` object :rtype: ElementTree.Element """ - response = requests.get(const.CBRF_API_URLS['info'], headers=const.CBRF_HEADERS) + response = requests.get(const.CBRF_API_URLS["info"], headers=const.CBRF_HEADERS) return XML(response.text) -def get_daily_rates(date_req: datetime.datetime = None, lang: str = 'rus') -> Element: - """ Getting currency for current day. +def get_daily_rates(date_req: datetime.datetime = None, lang: str = "rus") -> Element: + """Getting currency for current day. see example: http://www.cbr.ru/scripts/Root.asp?PrtId=SXML @@ -45,22 +32,25 @@ def get_daily_rates(date_req: datetime.datetime = None, lang: str = 'rus') -> El :return: :class: `Element ` object :rtype: ElementTree.Element """ - if lang not in ['rus', 'eng']: + if lang not in ["rus", "eng"]: raise ValueError('"lang" must be string. "rus" or "eng"') - base_url = const.CBRF_API_URLS['daily_rus'] if lang == 'rus' \ - else const.CBRF_API_URLS['daily_eng'] + base_url = ( + const.CBRF_API_URLS["daily_rus"] + if lang == "rus" + else const.CBRF_API_URLS["daily_eng"] + ) - url = base_url + 'date_req=' + utils.date_to_str(date_req) if date_req else base_url + url = base_url + "date_req=" + utils.date_to_str(date_req) if date_req else base_url response = requests.get(url=url, headers=const.CBRF_HEADERS) return XML(response.text) -def get_dynamic_rates(date_req1: datetime.datetime, - date_req2: datetime.datetime, - currency_id: str) -> Element: +def get_dynamic_rates( + date_req1: datetime.datetime, date_req2: datetime.datetime, currency_id: str +) -> Element: """ :param date_req1: begin date @@ -73,10 +63,11 @@ def get_dynamic_rates(date_req1: datetime.datetime, :return: :class: `Element ` object :rtype: ElementTree.Element """ - url = const.CBRF_API_URLS['dynamic'] + 'date_req1={}&date_req2={}&VAL_NM_RQ={}'.format( - utils.date_to_str(date_req1), - utils.date_to_str(date_req2), - currency_id) + url = const.CBRF_API_URLS[ + "dynamic" + ] + "date_req1={}&date_req2={}&VAL_NM_RQ={}".format( + utils.date_to_str(date_req1), utils.date_to_str(date_req2), currency_id + ) response = requests.get(url=url, headers=const.CBRF_HEADERS) diff --git a/cbrf/const.py b/cbrf/const.py index b056af4..35a8bf9 100644 --- a/cbrf/const.py +++ b/cbrf/const.py @@ -1,24 +1,23 @@ -# -*- coding: utf-8 -*- import os CURRENCY_CODES = { # http://www.cbr.ru/scripts/XML_val.asp?d=0 - 'USD': 'R01235', - 'EUR': 'R01239', + "USD": "R01235", + "EUR": "R01239", } -CBRF_URL_SCHEME = os.getenv('CBRF_URL_SCHEME', 'http') -CBRF_URL_HOST = os.getenv('CBRF_URL_HOST', 'www.cbr.ru') +CBRF_URL_SCHEME = os.getenv("CBRF_URL_SCHEME", "http") +CBRF_URL_HOST = os.getenv("CBRF_URL_HOST", "www.cbr.ru") -CBRF_URL = '{}://{}'.format(CBRF_URL_SCHEME, CBRF_URL_HOST) +CBRF_URL = f"{CBRF_URL_SCHEME}://{CBRF_URL_HOST}" CBRF_API_URLS = { - 'info': '{}/scripts/XML_valFull.asp'.format(CBRF_URL), - 'daily_rus': '{}/scripts/XML_daily.asp?'.format(CBRF_URL), - 'daily_eng': '{}/scripts/XML_daily_eng.asp?'.format(CBRF_URL), - 'dynamic': '{}/scripts/XML_dynamic.asp?'.format(CBRF_URL), + "info": f"{CBRF_URL}/scripts/XML_valFull.asp", + "daily_rus": f"{CBRF_URL}/scripts/XML_daily.asp?", + "daily_eng": f"{CBRF_URL}/scripts/XML_daily_eng.asp?", + "dynamic": f"{CBRF_URL}/scripts/XML_dynamic.asp?", } CBRF_HEADERS = { - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Firefox/68.0' + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Firefox/68.0" } diff --git a/cbrf/models.py b/cbrf/models.py index e4d550d..6d04f7b 100644 --- a/cbrf/models.py +++ b/cbrf/models.py @@ -1,25 +1,13 @@ -# -*- coding: utf-8 -*- -""" -cbrf.models -~~~~~~~~ - -This module implements models for comfortable work with cbrf wrapper API. - -:copyright: (c) 2017 by Vadim Iskuchekov (@egregors) -:license: MIT -""" -from __future__ import unicode_literals, absolute_import - import datetime from decimal import Decimal from xml.etree.ElementTree import Element -from cbrf import * +from cbrf import get_currencies_info, get_daily_rates, get_dynamic_rates from cbrf.utils import str_to_date -class Currency(object): - """ Class to deserialize response like: +class Currency: + """Class to deserialize response like: http://www.cbr.ru/scripts/XML_valFull.asp @@ -39,20 +27,20 @@ def __init__(self, elem: Element): self._parse_currency_xml(elem) def __str__(self): - return '[{}]: {}/{}'.format(self.id, self.name, self.eng_name) + return f"[{self.id}]: {self.name}/{self.eng_name}" def _parse_currency_xml(self, elem: Element): - self.id = elem.attrib['ID'] - self.name = elem.findtext('Name') - self.eng_name = elem.findtext('EngName') - self.denomination = int(elem.findtext('Nominal')) - _iso_num_code = elem.findtext('ISO_Num_Code') + self.id = elem.attrib["ID"] + self.name = elem.findtext("Name") + self.eng_name = elem.findtext("EngName") + self.denomination = int(elem.findtext("Nominal")) + _iso_num_code = elem.findtext("ISO_Num_Code") self.iso_num_code = int(_iso_num_code) if _iso_num_code else None - self.iso_char_code = elem.findtext('ISO_Char_Code') + self.iso_char_code = elem.findtext("ISO_Char_Code") -class DailyCurrencyRecord(object): - """ Class to deserialize response like: +class DailyCurrencyRecord: + """Class to deserialize response like: http://www.cbr.ru/scripts/XML_daily.asp?date_req=02/03/2002 @@ -71,26 +59,26 @@ def __init__(self, elem: Element): self._parse_daily_currency_record_xml(elem) def __str__(self): - return '[{}]: {}₽ за {} {}'.format(self.id, self.value, self.denomination, self.name) + return f"[{self.id}]: {self.value}₽ за {self.denomination} {self.name}" def _parse_daily_currency_record_xml(self, elem: Element): - self.id = elem.attrib['ID'] - self.num_code = elem.findtext('NumCode') - self.char_code = elem.findtext('CharCode') - self.denomination = int(elem.findtext('Nominal')) - self.name = elem.findtext('Name') - self.value = Decimal(elem.findtext('Value').replace(',', '.')) + self.id = elem.attrib["ID"] + self.num_code = elem.findtext("NumCode") + self.char_code = elem.findtext("CharCode") + self.denomination = int(elem.findtext("Nominal")) + self.name = elem.findtext("Name") + self.value = Decimal(elem.findtext("Value").replace(",", ".")) -class DynamicCurrencyRecord(object): - """ Class to deserialize response like: +class DynamicCurrencyRecord: + """Class to deserialize response like: - http://www.cbr.ru/scripts/XML_dynamic.asp?date_req1=02/03/2001&date_req2=14/03/2001&VAL_NM_RQ=R01235 + http://www.cbr.ru/scripts/XML_dynamic.asp?date_req1=02/03/2001&date_req2=14/03/2001&VAL_NM_RQ=R01235 - - 1 - 28,6200 - + + 1 + 28,6200 + """ @@ -99,17 +87,17 @@ def __init__(self, elem: Element): self._parse_dynamic_currency_record(elem) def __str__(self): - return '[{} | {}]: {}'.format(self.id, self.date, self.value) + return f"[{self.id} | {self.date}]: {self.value}" def _parse_dynamic_currency_record(self, elem: Element): - self.id = elem.attrib['Id'] - self.date = str_to_date(elem.attrib['Date']) - self.denomination = int(elem.findtext('Nominal')) - self.value = Decimal(elem.findtext('Value').replace(',', '.')) + self.id = elem.attrib["Id"] + self.date = str_to_date(elem.attrib["Date"]) + self.denomination = int(elem.findtext("Nominal")) + self.value = Decimal(elem.findtext("Value").replace(",", ".")) -class CurrenciesInfo(object): - """ Full set of currencies information from http://www.cbr.ru/scripts/XML_valFull.asp """ +class CurrenciesInfo: + """Full set of currencies information from http://www.cbr.ru/scripts/XML_valFull.asp""" def __init__(self): self._raw_currencies = get_currencies_info() @@ -120,10 +108,10 @@ def __init__(self): self.currencies.append(Currency(currency)) def __str__(self): - return 'Currencies Info [{}]'.format(len(self.currencies)) + return f"Currencies Info [{len(self.currencies)}]" def get_by_id(self, id_code: str) -> Currency or None: - """ Get currency by ID + """Get currency by ID :param id_code: set, like "R01305" :return: currency or None. @@ -134,12 +122,12 @@ def get_by_id(self, id_code: str) -> Currency or None: return None -class DailyCurrenciesRates(object): - """ Full set of daily rates """ +class DailyCurrenciesRates: + """Full set of daily rates""" - def __init__(self, date: datetime.datetime = None, lang: str = 'rus'): + def __init__(self, date: datetime.datetime = None, lang: str = "rus"): self._raw_daily_rates = get_daily_rates(date_req=date, lang=lang) - self.date = str_to_date(self._raw_daily_rates.attrib['Date']) + self.date = str_to_date(self._raw_daily_rates.attrib["Date"]) self.rates = list() @@ -147,7 +135,9 @@ def __init__(self, date: datetime.datetime = None, lang: str = 'rus'): self.rates.append(DailyCurrencyRecord(rate)) def __str__(self): - return '[{}] rates for {}'.format(len(self.rates), self.date.strftime("%d/%m/%Y")) + return "[{}] rates for {}".format( + len(self.rates), self.date.strftime("%d/%m/%Y") + ) def get_by_id(self, id_code: str) -> DailyCurrencyRecord or None: try: @@ -156,14 +146,18 @@ def get_by_id(self, id_code: str) -> DailyCurrencyRecord or None: return None -class DynamicCurrenciesRates(object): - """ Full set of dynamic currency rates """ +class DynamicCurrenciesRates: + """Full set of dynamic currency rates""" - def __init__(self, date_1: datetime.datetime, date_2: datetime.datetime, id_code: str): - self._raw_dynamic_rates = get_dynamic_rates(date_req1=date_1, date_req2=date_2, currency_id=id_code) - self.date_1 = str_to_date(self._raw_dynamic_rates.attrib['DateRange1']) - self.date_2 = str_to_date(self._raw_dynamic_rates.attrib['DateRange2']) - self.id = self._raw_dynamic_rates.attrib['ID'] + def __init__( + self, date_1: datetime.datetime, date_2: datetime.datetime, id_code: str + ): + self._raw_dynamic_rates = get_dynamic_rates( + date_req1=date_1, date_req2=date_2, currency_id=id_code + ) + self.date_1 = str_to_date(self._raw_dynamic_rates.attrib["DateRange1"]) + self.date_2 = str_to_date(self._raw_dynamic_rates.attrib["DateRange2"]) + self.id = self._raw_dynamic_rates.attrib["ID"] self.rates = list() @@ -171,9 +165,11 @@ def __init__(self, date_1: datetime.datetime, date_2: datetime.datetime, id_code self.rates.append(DynamicCurrencyRecord(rate)) def __str__(self): - return '[{}] from {} to {}'.format(len(self.rates), - self.date_1.strftime("%d/%m/%Y"), - self.date_2.strftime("%d/%m/%Y")) + return "[{}] from {} to {}".format( + len(self.rates), + self.date_1.strftime("%d/%m/%Y"), + self.date_2.strftime("%d/%m/%Y"), + ) def get_by_date(self, date: datetime.datetime) -> DynamicCurrencyRecord or None: try: diff --git a/cbrf/tests.py b/cbrf/tests.py index 8b1e218..9f656cf 100644 --- a/cbrf/tests.py +++ b/cbrf/tests.py @@ -1,13 +1,17 @@ -# -*- coding: utf-8 -*- - from datetime import datetime from decimal import Decimal from unittest import TestCase from xml.etree.ElementTree import Element -from cbrf import (get_currencies_info, get_daily_rates, get_dynamic_rates) -from cbrf.models import Currency, DailyCurrencyRecord, DynamicCurrencyRecord, CurrenciesInfo, DailyCurrenciesRates, \ - DynamicCurrenciesRates +from cbrf import get_currencies_info, get_daily_rates, get_dynamic_rates +from cbrf.models import ( + CurrenciesInfo, + Currency, + DailyCurrenciesRates, + DailyCurrencyRecord, + DynamicCurrenciesRates, + DynamicCurrencyRecord, +) class CbrfAPITestCase(TestCase): @@ -23,58 +27,64 @@ def test_get_daily_rate(self): aud = rates[0] - self.assertEqual(aud.attrib['ID'], 'R01010') - self.assertEqual(aud.find('Value').text, '36,4126') + self.assertEqual(aud.attrib["ID"], "R01010") + self.assertEqual(aud.find("Value").text, "36,4126") def test_get_dynamic_rates(self): date_1 = datetime(2001, 3, 2) date_2 = datetime(2001, 3, 14) - rates = get_dynamic_rates(date_req1=date_1, date_req2=date_2, currency_id='R01235') + rates = get_dynamic_rates( + date_req1=date_1, date_req2=date_2, currency_id="R01235" + ) self.assertEqual(len(rates), 8) - self.assertEqual(rates[0].find('Nominal').text, '1') - self.assertEqual(rates[0].find('Value').text, '28,6200') + self.assertEqual(rates[0].find("Nominal").text, "1") + self.assertEqual(rates[0].find("Value").text, "28,6200") class CbrfModelsTestCase(TestCase): def test_currency_model(self): c = Currency(get_currencies_info()[0]) - self.assertEqual(c.id, 'R01010') - self.assertEqual(c.name, 'Австралийский доллар') - self.assertEqual(c.eng_name, 'Australian Dollar') + self.assertEqual(c.id, "R01010") + self.assertEqual(c.name, "Австралийский доллар") + self.assertEqual(c.eng_name, "Australian Dollar") self.assertEqual(c.denomination, 1) self.assertEqual(c.iso_num_code, 36) - self.assertEqual(c.iso_char_code, 'AUD') + self.assertEqual(c.iso_char_code, "AUD") def test_daily_currency_rate(self): d = DailyCurrencyRecord(get_daily_rates()[0]) - self.assertEqual(d.id, 'R01010') - self.assertEqual(d.num_code, '036') - self.assertEqual(d.char_code, 'AUD') + self.assertEqual(d.id, "R01010") + self.assertEqual(d.num_code, "036") + self.assertEqual(d.char_code, "AUD") self.assertEqual(d.denomination, 1) - self.assertEqual(d.name, 'Австралийский доллар') + self.assertEqual(d.name, "Австралийский доллар") def test_dynamic_currency_rate(self): date_1 = datetime(2001, 3, 2) date_2 = datetime(2001, 3, 14) - d = DynamicCurrencyRecord(get_dynamic_rates(date_req1=date_1, date_req2=date_2, currency_id='R01235')[0]) + d = DynamicCurrencyRecord( + get_dynamic_rates(date_req1=date_1, date_req2=date_2, currency_id="R01235")[ + 0 + ] + ) self.assertEqual(d.denomination, 1) - self.assertEqual(d.value, Decimal('28.6200')) + self.assertEqual(d.value, Decimal("28.6200")) def test_correncies_info(self): c_info = CurrenciesInfo() self.assertEqual(len(c_info.currencies), 61) - irish_pound_id = 'R01305' + irish_pound_id = "R01305" irish_pound = c_info.get_by_id(irish_pound_id) self.assertEqual(irish_pound.id, irish_pound_id) self.assertEqual(irish_pound.iso_num_code, 372) - bad_id = 'lol' + bad_id = "lol" self.assertIsNone(c_info.get_by_id(bad_id)) def test_daily_currencies_rates(self): @@ -84,22 +94,24 @@ def test_daily_currencies_rates(self): self.assertEqual(daily_rates.date, date) self.assertEqual(len(daily_rates.rates), 18) - gbp_id = 'R01035' + gbp_id = "R01035" gbp = daily_rates.get_by_id(gbp_id) self.assertEqual(gbp.id, gbp_id) - self.assertEqual(gbp.num_code, '826') + self.assertEqual(gbp.num_code, "826") - bad_id = 'gg wp' + bad_id = "gg wp" self.assertIsNone(daily_rates.get_by_id(bad_id)) def test_dynamic_currencies_rates(self): date_1 = datetime(2001, 3, 2) date_2 = datetime(2001, 3, 14) - id_code = 'R01235' + id_code = "R01235" dynamic_rates = DynamicCurrenciesRates(date_1, date_2, id_code) self.assertEqual(len(dynamic_rates.rates), 8) - self.assertEqual(dynamic_rates.get_by_date(datetime(2001, 3, 8)).value, Decimal('28.6200')) + self.assertEqual( + dynamic_rates.get_by_date(datetime(2001, 3, 8)).value, Decimal("28.6200") + ) self.assertIsNone(dynamic_rates.get_by_date(datetime(3000, 1, 1)), None) diff --git a/cbrf/utils.py b/cbrf/utils.py index 35e948b..dd8fcca 100644 --- a/cbrf/utils.py +++ b/cbrf/utils.py @@ -1,35 +1,26 @@ -# -*- coding: utf-8 -*- - -""" -cbrf.utils -~~~~~~~~~~ - -:copyright: (c) 2017 by Vadim Iskuchekov (@egregors) -:license: MIT -""" import datetime def date_to_str(date: datetime.datetime) -> str: - """ Convert python datetime.datetime date to str for API request + """Convert python datetime.datetime date to str for API request :param date: date for format :return: date str in right format :rtype: str """ - return '{}'.format(date.strftime("%d/%m/%Y")) if date else '' + return "{}".format(date.strftime("%d/%m/%Y")) if date else "" def str_to_date(date: str) -> datetime.datetime: - """ Convert cbr.ru API date ste to python datetime + """Convert cbr.ru API date ste to python datetime :param date: date from API response :return: date like datetime :rtype: datetime """ - date = date.split('.') + date = date.split(".") date.reverse() y, m, d = date return datetime.datetime(int(y), int(m), int(d)) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..821d2dc --- /dev/null +++ b/setup.cfg @@ -0,0 +1,34 @@ +[flake8] +max-line-length = 120 +exclude = .git,venv + +[pycodestyle] +max-line-length = 120 +exclude = .git,venv + +[isort] +line_length = 88 +;known_first_party = {{cookiecutter.project_slug}},config +multi_line_output = 3 +default_section = THIRDPARTY +skip = venv/ +;skip_glob = **/migrations/*.py +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true + +;[mypy] +;python_version = 3.9 +;check_untyped_defs = True +;ignore_missing_imports = True +;warn_unused_ignores = True +;warn_redundant_casts = True +;warn_unused_configs = True +;plugins = mypy_django_plugin.main{% if cookiecutter.use_drf == "y" %}, mypy_drf_plugin.main{% endif %} + +;[mypy.plugins.django-stubs] +;django_settings_module = config.settings.test + +;[mypy-*.migrations.*] +;# Django migrations should not produce any errors: +;ignore_errors = True diff --git a/setup.py b/setup.py index 5c816bd..814528d 100644 --- a/setup.py +++ b/setup.py @@ -3,28 +3,29 @@ from setuptools import setup -_version_re = re.compile(r'__version__\s+=\s+(.*)') +_version_re = re.compile(r"__version__\s+=\s+(.*)") -with open('cbrf/__init__.py', 'rb') as f: - version = str(ast.literal_eval(_version_re.search( - f.read().decode('utf-8')).group(1))) +with open("cbrf/__init__.py", "rb") as f: + version = str( + ast.literal_eval(_version_re.search(f.read().decode("utf-8")).group(1)) + ) print(version) setup( - name='cbrf', + name="cbrf", version=version, - packages=['cbrf'], + packages=["cbrf"], install_requires=[ "requests", ], - url='https://github.com/egregors/cbrf', - license='MIT', - author='Vadim Iskuchekov (@egregors)', - author_email='egregors@yandex.ru', - description='Wrapper for The Central Bank of the Russian Federation site API', - long_description=open('README.md', 'r', encoding='utf-8').read(), - long_description_content_type='text/markdown', + url="https://github.com/egregors/cbrf", + license="MIT", + author="Vadim Iskuchekov (@egregors)", + author_email="egregors@yandex.ru", + description="Wrapper for The Central Bank of the Russian Federation site API", + long_description=open("README.md", encoding="utf-8").read(), + long_description_content_type="text/markdown", classifiers=[ "Development Status :: 4 - Beta", "Environment :: Web Environment", @@ -32,6 +33,6 @@ "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", - 'Programming Language :: Python :: 3.6' + "Programming Language :: Python :: 3.6", ], ) From c75a44098c3dcd09a4305f1f51c1f5d45b670952 Mon Sep 17 00:00:00 2001 From: egregors Date: Mon, 16 May 2022 18:20:45 +0200 Subject: [PATCH 2/6] Added CI, add pytest conf --- .github/workflows/python-package.yml | 40 ++++++++++++++++++++++++++++ Makefile | 2 +- pytest.ini | 2 ++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/python-package.yml create mode 100644 pytest.ini diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..7a9d12d --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,40 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python package + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + python -m pip install -e . + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --config=setup.cfg --count --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --config=setup.cfg --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest diff --git a/Makefile b/Makefile index 409a453..620f2e2 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ lint: ## Run pre-commit hooks pre-commit run -a test: ## Run tests - pytest cbrf/tests.py + pytest ## Help diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..3af9058 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +python_files = tests.py test_*.py From cec9209b5176a98b34de09718ed3205d3305b876 Mon Sep 17 00:00:00 2001 From: egregors Date: Mon, 16 May 2022 18:40:44 +0200 Subject: [PATCH 3/6] Cleanup --- cbrf/api.py | 36 +++++++++++++------------- cbrf/const.py | 4 +-- cbrf/models.py | 68 ++++++++++++++++++++++++-------------------------- cbrf/tests.py | 2 +- cbrf/utils.py | 12 ++++----- 5 files changed, 60 insertions(+), 62 deletions(-) diff --git a/cbrf/api.py b/cbrf/api.py index 7a4de46..28e3aac 100644 --- a/cbrf/api.py +++ b/cbrf/api.py @@ -9,10 +9,10 @@ def get_currencies_info() -> Element: """Get META information about currencies - url: http://www.cbr.ru/scripts/XML_val.asp + url: https://www.cbr.ru/scripts/XML_val.asp - :return: :class: `Element ` object - :rtype: ElementTree.Element + :return: `Element ` object + :rtype: ElementTree.Element """ response = requests.get(const.CBRF_API_URLS["info"], headers=const.CBRF_HEADERS) @@ -22,15 +22,15 @@ def get_currencies_info() -> Element: def get_daily_rates(date_req: datetime.datetime = None, lang: str = "rus") -> Element: """Getting currency for current day. - see example: http://www.cbr.ru/scripts/Root.asp?PrtId=SXML + see example: https://www.cbr.ru/scripts/Root.asp?PrtId=SXML :param date_req: - :type date_req: datetime.datetime - :param lang: language of API response ('eng' || 'rus') - :type lang: str + :type date_req: datetime.datetime + :param lang: language of API response ('eng' || 'rus') + :type lang: str - :return: :class: `Element ` object - :rtype: ElementTree.Element + :return: `Element ` object + :rtype: ElementTree.Element """ if lang not in ["rus", "eng"]: raise ValueError('"lang" must be string. "rus" or "eng"') @@ -51,17 +51,17 @@ def get_daily_rates(date_req: datetime.datetime = None, lang: str = "rus") -> El def get_dynamic_rates( date_req1: datetime.datetime, date_req2: datetime.datetime, currency_id: str ) -> Element: - """ + """Getting currency for time period. - :param date_req1: begin date - :type date_req1: datetime.datetime - :param date_req2: end date - :type date_req2: datetime.datetime - :param currency_id: currency code (http://www.cbr.ru/scripts/XML_val.asp?d=0) - :type currency_id: str + :param date_req1: begin date + :type date_req1: datetime.datetime + :param date_req2: end date + :type date_req2: datetime.datetime + :param currency_id: currency code (https://www.cbr.ru/scripts/XML_val.asp?d=0) + :type currency_id: str - :return: :class: `Element ` object - :rtype: ElementTree.Element + :return: `Element ` object + :rtype: ElementTree.Element """ url = const.CBRF_API_URLS[ "dynamic" diff --git a/cbrf/const.py b/cbrf/const.py index 35a8bf9..2ed9fae 100644 --- a/cbrf/const.py +++ b/cbrf/const.py @@ -1,12 +1,12 @@ import os CURRENCY_CODES = { - # http://www.cbr.ru/scripts/XML_val.asp?d=0 + # https://www.cbr.ru/scripts/XML_val.asp?d=0 "USD": "R01235", "EUR": "R01239", } -CBRF_URL_SCHEME = os.getenv("CBRF_URL_SCHEME", "http") +CBRF_URL_SCHEME = os.getenv("CBRF_URL_SCHEME", "https") CBRF_URL_HOST = os.getenv("CBRF_URL_HOST", "www.cbr.ru") CBRF_URL = f"{CBRF_URL_SCHEME}://{CBRF_URL_HOST}" diff --git a/cbrf/models.py b/cbrf/models.py index 6d04f7b..0972008 100644 --- a/cbrf/models.py +++ b/cbrf/models.py @@ -9,24 +9,23 @@ class Currency: """Class to deserialize response like: - http://www.cbr.ru/scripts/XML_valFull.asp - - - Молдавский лей - Moldova Lei - 10 - R01500 - 498 - MDL - - + https://www.cbr.ru/scripts/XML_valFull.asp + + + Молдавский лей + Moldova Lei + 10 + R01500 + 498 + MDL + """ def __init__(self, elem: Element): if elem: self._parse_currency_xml(elem) - def __str__(self): + def __str__(self) -> str: return f"[{self.id}]: {self.name}/{self.eng_name}" def _parse_currency_xml(self, elem: Element): @@ -42,23 +41,22 @@ def _parse_currency_xml(self, elem: Element): class DailyCurrencyRecord: """Class to deserialize response like: - http://www.cbr.ru/scripts/XML_daily.asp?date_req=02/03/2002 - - - 036 - AUD - 1 - Австралийский доллар - 16,0102 - + https://www.cbr.ru/scripts/XML_daily.asp?date_req=02/03/2002 + + 036 + AUD + 1 + Австралийский доллар + 16,0102 + """ def __init__(self, elem: Element): if elem: self._parse_daily_currency_record_xml(elem) - def __str__(self): + def __str__(self) -> str: return f"[{self.id}]: {self.value}₽ за {self.denomination} {self.name}" def _parse_daily_currency_record_xml(self, elem: Element): @@ -73,13 +71,12 @@ def _parse_daily_currency_record_xml(self, elem: Element): class DynamicCurrencyRecord: """Class to deserialize response like: - http://www.cbr.ru/scripts/XML_dynamic.asp?date_req1=02/03/2001&date_req2=14/03/2001&VAL_NM_RQ=R01235 - - - 1 - 28,6200 - + https://www.cbr.ru/scripts/XML_dynamic.asp?date_req1=02/03/2001&date_req2=14/03/2001&VAL_NM_RQ=R01235 + + 1 + 28,6200 + """ def __init__(self, elem: Element): @@ -97,24 +94,23 @@ def _parse_dynamic_currency_record(self, elem: Element): class CurrenciesInfo: - """Full set of currencies information from http://www.cbr.ru/scripts/XML_valFull.asp""" + """Full set of currencies information from https://www.cbr.ru/scripts/XML_valFull.asp""" def __init__(self): self._raw_currencies = get_currencies_info() - + # TODO: it should be a hashmap self.currencies = list() - for currency in self._raw_currencies: self.currencies.append(Currency(currency)) - def __str__(self): + def __str__(self) -> str: return f"Currencies Info [{len(self.currencies)}]" def get_by_id(self, id_code: str) -> Currency or None: """Get currency by ID - :param id_code: set, like "R01305" - :return: currency or None. + :param: id_code: str, like "R01305" + :return: currency or None. """ try: return [_ for _ in self.currencies if _.id == id_code][0] @@ -129,12 +125,13 @@ def __init__(self, date: datetime.datetime = None, lang: str = "rus"): self._raw_daily_rates = get_daily_rates(date_req=date, lang=lang) self.date = str_to_date(self._raw_daily_rates.attrib["Date"]) + # TODO: it should be a hashmap self.rates = list() for rate in self._raw_daily_rates: self.rates.append(DailyCurrencyRecord(rate)) - def __str__(self): + def __str__(self) -> str: return "[{}] rates for {}".format( len(self.rates), self.date.strftime("%d/%m/%Y") ) @@ -159,6 +156,7 @@ def __init__( self.date_2 = str_to_date(self._raw_dynamic_rates.attrib["DateRange2"]) self.id = self._raw_dynamic_rates.attrib["ID"] + # TODO: it should be a hashmap self.rates = list() for rate in self._raw_dynamic_rates: diff --git a/cbrf/tests.py b/cbrf/tests.py index 9f656cf..203c809 100644 --- a/cbrf/tests.py +++ b/cbrf/tests.py @@ -114,4 +114,4 @@ def test_dynamic_currencies_rates(self): self.assertEqual( dynamic_rates.get_by_date(datetime(2001, 3, 8)).value, Decimal("28.6200") ) - self.assertIsNone(dynamic_rates.get_by_date(datetime(3000, 1, 1)), None) + self.assertIsNone(dynamic_rates.get_by_date(datetime(3000, 1, 1))) diff --git a/cbrf/utils.py b/cbrf/utils.py index dd8fcca..3e16058 100644 --- a/cbrf/utils.py +++ b/cbrf/utils.py @@ -4,10 +4,10 @@ def date_to_str(date: datetime.datetime) -> str: """Convert python datetime.datetime date to str for API request - :param date: date for format + :param date: date for format - :return: date str in right format - :rtype: str + :return: date str in right format + :rtype: str """ return "{}".format(date.strftime("%d/%m/%Y")) if date else "" @@ -15,10 +15,10 @@ def date_to_str(date: datetime.datetime) -> str: def str_to_date(date: str) -> datetime.datetime: """Convert cbr.ru API date ste to python datetime - :param date: date from API response + :param date: date from API response - :return: date like datetime - :rtype: datetime + :return: date like datetime + :rtype: datetime """ date = date.split(".") date.reverse() From 8ed2cf57a0496ada5470980b51630ade1744de38 Mon Sep 17 00:00:00 2001 From: egregors Date: Mon, 16 May 2022 19:13:58 +0200 Subject: [PATCH 4/6] Added debug logging for all API calls, update README a bit --- Makefile | 4 +++- README.md | 23 ++++++++++++++++------- cbrf/api.py | 18 +++++++++++++++++- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 620f2e2..acf95ab 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ -.DEFAULT_GOAL := help +.DEFAULT_GOAL := check .PHONY: help test lint +check: lint test ## Run linting and tests + lint: ## Run pre-commit hooks pre-commit run -a diff --git a/README.md b/README.md index 0fbf139..b7e15b9 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,12 @@ # cbrf - -_Обертка для работы с API Центробанка_ - _Wrapper for The Central Bank of the Russian Federation site API_ [![Build Status](https://travis-ci.org/Egregors/cbrf.svg?branch=master)](https://travis-ci.org/Egregors/cbrf) [![PyPI version](https://badge.fury.io/py/cbrf.svg)](https://badge.fury.io/py/cbrf) [Site](http://www.cbr.ru/) and [API](http://www.cbr.ru/scripts/Root.asp?PrtId=SXML) - of The Central Bank of the Russian Federation. +of The Central Bank of the Russian Federation. ## Installation @@ -20,6 +17,7 @@ Stable version: ``` Dev version: + ``` git clone https://github.com/Egregors/cbrf.git cd cbrf @@ -27,9 +25,11 @@ Dev version: ``` ## Settings + For using with your own hostname set environment variables, for example ``` +export CBRF_DEBUG=True export CBRF_URL_SCHEME=http export CBRF_URL_HOST=www.my-own-cbr.ru ``` @@ -98,13 +98,22 @@ Decimal('72.0143') Decimal('28.6200') ``` -### Tests +Also, you can show `DEBUG` info, by setting logger level to DEBUG in your code: +```python +import logging + +logging.basicConfig(level=logging.DEBUG) +``` + +### Linting & Tests To run tests: +```shell +make lint +make tests ``` -pytest cbrf/tests.py -``` + > You should install `pytest` first ## Contributing diff --git a/cbrf/api.py b/cbrf/api.py index 28e3aac..bf0ad2a 100644 --- a/cbrf/api.py +++ b/cbrf/api.py @@ -1,10 +1,13 @@ import datetime +import logging from xml.etree.ElementTree import XML, Element import requests from . import const, utils +logger = logging.getLogger(__name__) + def get_currencies_info() -> Element: """Get META information about currencies @@ -14,7 +17,11 @@ def get_currencies_info() -> Element: :return: `Element ` object :rtype: ElementTree.Element """ + logger.debug("Performing get_currencies_info") + logger.debug("Request to %s", const.CBRF_API_URLS["info"]) response = requests.get(const.CBRF_API_URLS["info"], headers=const.CBRF_HEADERS) + logger.debug("Resp: %s", response.status_code) + logger.debug("Body: %s", response.text) return XML(response.text) @@ -32,6 +39,7 @@ def get_daily_rates(date_req: datetime.datetime = None, lang: str = "rus") -> El :return: `Element ` object :rtype: ElementTree.Element """ + logger.debug("Performing get_daily_rates") if lang not in ["rus", "eng"]: raise ValueError('"lang" must be string. "rus" or "eng"') @@ -42,8 +50,10 @@ def get_daily_rates(date_req: datetime.datetime = None, lang: str = "rus") -> El ) url = base_url + "date_req=" + utils.date_to_str(date_req) if date_req else base_url - + logger.debug("Request to %s | %s : %s", url, lang, date_req) response = requests.get(url=url, headers=const.CBRF_HEADERS) + logger.debug("Resp: %s", response.status_code) + logger.debug("Body: %s", response.text) return XML(response.text) @@ -63,12 +73,18 @@ def get_dynamic_rates( :return: `Element ` object :rtype: ElementTree.Element """ + logger.debug("Performing get_dynamic_rates") url = const.CBRF_API_URLS[ "dynamic" ] + "date_req1={}&date_req2={}&VAL_NM_RQ={}".format( utils.date_to_str(date_req1), utils.date_to_str(date_req2), currency_id ) + logger.debug( + "Request to %s | %s between %s and %s", url, currency_id, date_req1, date_req2 + ) response = requests.get(url=url, headers=const.CBRF_HEADERS) + logger.debug("Resp: %s", response.status_code) + logger.debug("Body: %s", response.text) return XML(response.text) From 01d9a86e256c738236db22708cb1b651e2de17ce Mon Sep 17 00:00:00 2001 From: egregors Date: Mon, 16 May 2022 20:00:14 +0200 Subject: [PATCH 5/6] Data structures optimization --- cbrf/models.py | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/cbrf/models.py b/cbrf/models.py index 0972008..1b62135 100644 --- a/cbrf/models.py +++ b/cbrf/models.py @@ -98,10 +98,10 @@ class CurrenciesInfo: def __init__(self): self._raw_currencies = get_currencies_info() - # TODO: it should be a hashmap - self.currencies = list() + self.currencies = dict() for currency in self._raw_currencies: - self.currencies.append(Currency(currency)) + cur = Currency(currency) + self.currencies[cur.id] = cur def __str__(self) -> str: return f"Currencies Info [{len(self.currencies)}]" @@ -112,10 +112,7 @@ def get_by_id(self, id_code: str) -> Currency or None: :param: id_code: str, like "R01305" :return: currency or None. """ - try: - return [_ for _ in self.currencies if _.id == id_code][0] - except IndexError: - return None + return self.currencies.get(id_code) class DailyCurrenciesRates: @@ -125,11 +122,11 @@ def __init__(self, date: datetime.datetime = None, lang: str = "rus"): self._raw_daily_rates = get_daily_rates(date_req=date, lang=lang) self.date = str_to_date(self._raw_daily_rates.attrib["Date"]) - # TODO: it should be a hashmap - self.rates = list() + self.rates = dict() for rate in self._raw_daily_rates: - self.rates.append(DailyCurrencyRecord(rate)) + record = DailyCurrencyRecord(rate) + self.rates[record.id] = record def __str__(self) -> str: return "[{}] rates for {}".format( @@ -137,10 +134,7 @@ def __str__(self) -> str: ) def get_by_id(self, id_code: str) -> DailyCurrencyRecord or None: - try: - return [_ for _ in self.rates if _.id == id_code][0] - except IndexError: - return None + return self.rates.get(id_code) class DynamicCurrenciesRates: @@ -156,11 +150,11 @@ def __init__( self.date_2 = str_to_date(self._raw_dynamic_rates.attrib["DateRange2"]) self.id = self._raw_dynamic_rates.attrib["ID"] - # TODO: it should be a hashmap - self.rates = list() + self.rates = dict() for rate in self._raw_dynamic_rates: - self.rates.append(DynamicCurrencyRecord(rate)) + record = DynamicCurrencyRecord(rate) + self.rates[record.date] = record def __str__(self): return "[{}] from {} to {}".format( @@ -170,7 +164,4 @@ def __str__(self): ) def get_by_date(self, date: datetime.datetime) -> DynamicCurrencyRecord or None: - try: - return [_ for _ in self.rates if _.date == date][0] - except IndexError: - return None + return self.rates.get(date) From 9d8d1cbf34ec3f1e9b1e3292c1f1fa149a42e2aa Mon Sep 17 00:00:00 2001 From: egregors Date: Mon, 16 May 2022 20:12:05 +0200 Subject: [PATCH 6/6] Update docs, remove Travis --- .travis.yml | 22 ---------------------- CHANGES.md | 6 ++++++ README.md | 11 +++++------ 3 files changed, 11 insertions(+), 28 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index de6acce..0000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: python -python: -- '3.5' -- '3.6' -install: -- pip install -e . -script: -- pytest cbrf/tests.py -deploy: - provider: pypi - user: egregors - password: - secure: fTqNmQ68pJ12/Sc5qk6fDcvw7NqgpRcGfjMVI6kdO5tgO3lQe0lJprylNtwRtJF4iqcfcBwdQ/RrxaIOwRzl4Jw31FSkdq3jp4lH4bLARfFPI0d1Vq/aYoiKnMjty/3U4dUeHLHviiiqukCl5ZJ+XM3WJfLp4xppibIqV0Hb96TylAfU9Zk7PL9Jm+LATX8Mw+Yyec/WmlTiKPGzuv9ukwpvBu2vBOn8SPJoXxjyJbi3gMuFvTmUXtP+3u3MKMTuaDKeq5w156mW2kn76swEHn65Ak4aeAhxHoTKBmYbQikOYq89NYlOSqYrIKtVzE8oR8mRov9aaOpwrQO7ESNEwyQIdhHPTAxyc9ByogYmfaLZdKQdDAPK2n1eUfzUZXFblPvG9DM5rO7S5VBYiax8O5rbGfOd/NN4mNyz7Et2tK5KsmXqKMsrMuzXEEqQV+wKjuly0TxknjeYCDA52k7Izt3ubjCnf3sSVyzPKIWEafYKU7XpjuBoSbCACt1eL4LxPcFL1hq70odOJLj3ltDQZ3DBjCFXjfcjm0JvSWJ8qACFCFriIXpE8b0m01U38/Xzc9cpCP8jhoTuCS7yCDWAYS3R/ge25UGGeQ0AvhW+PVbhYayb47IL8aKVN/PHxJPuzpAya6nBY9Fujidt9Ybfgu/JfRNtBp8Zhge3mjI0Hns= - on: - tags: true - repo: egregors/cbrf - all_branches: true - distributions: sdist bdist_wheel -notifications: - slack: - rooms: - secure: GU+R7cQWnAji55HIy2R+cxeMcOx7AKyUiCLGO2iLG+8Z9nZYLG0WrpmqEn19JXLtyhuyKtBeEJarA8zbMdehj8ZoITJJX2PbWVP08r9YabNpL0AOIq2w5hjMa/kdVI3HALYCC9A+I/kprMmNhWp0AnkpykHFNNJ9qzCsdhK0Q8pt7gfxGbzxYC0Sbr0TFk6fT4d0w0M67sNSeULMsiCgV75XjEz+UGeeUFLmGn1of83QqdKRDNfbhEzGoAWBo4x2z8pxBDuaI/Es4hwexXq2Tf5LSpMNwpdQ8u19PM2mr+K1mK45+VOB5GeO800pn8drPC30eq9am79SbBB2pjsc5k4u/jDQQG2UUTTZyYgSElQl4713ZUKcttYgS6IipNJb5i0FTCvzacjAJVp0sZo2gl9NScMxzvTTIPciNQoRgmO4EEvpe7eFDAnCV9/EKL6P+AewmbWcULcPpln5SI75s37fBXOWdnP20RaKay//hayMnTHMB2/9npBedJB/rkvfRvLpMU2C+tw8oGXP1yMSrd+Zaygl1IKZTbca5A9OZacAeiekSbYLUJ0kkMOH3YKIPJV6WpPEZ7PWVYd/gVLsCfdfB2Ld3cg2k8cHeQ0+QNN3rlU4eZ1m5NghjeTQxVxHDTPnZ1jtQFrW8JjXOaBiTB9qYCmSCbUWt1asKCwoXRM= diff --git a/CHANGES.md b/CHANGES.md index 5a1f745..d2ec671 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -42,3 +42,9 @@ Changes #### ver.: 0.4.7 (15.08.2019) * i hate pypi + +#### ver.: 0.5.0 (16.05.2022) +* add debug logging +* data structures optimizing +* add pre-commit hooks +* replace Travis with GitHub Actions diff --git a/README.md b/README.md index b7e15b9..e0d7d18 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ _Wrapper for The Central Bank of the Russian Federation site API_ -[![Build Status](https://travis-ci.org/Egregors/cbrf.svg?branch=master)](https://travis-ci.org/Egregors/cbrf) +[![Build Status](https://github.com/egregors/cbrf/actions/workflows/python-package.yml/badge.svg)](https://github.com/egregors/cbrf/actions) [![PyPI version](https://badge.fury.io/py/cbrf.svg)](https://badge.fury.io/py/cbrf) -[Site](http://www.cbr.ru/) and [API](http://www.cbr.ru/scripts/Root.asp?PrtId=SXML) +[Site](https://www.cbr.ru/) and [API](https://www.cbr.ru/scripts/Root.asp?PrtId=SXML) of The Central Bank of the Russian Federation. ## Installation @@ -29,8 +29,7 @@ Dev version: For using with your own hostname set environment variables, for example ``` -export CBRF_DEBUG=True -export CBRF_URL_SCHEME=http +export CBRF_URL_SCHEME=https export CBRF_URL_HOST=www.my-own-cbr.ru ``` @@ -57,7 +56,7 @@ To get raw XML answers you should use `cbrf.api` methods: ### Models -Also you can user base models for work with API (see examples in the tests). +You can use base models for work with API (see examples in the tests). `CurrenciesInfo` @@ -107,7 +106,7 @@ logging.basicConfig(level=logging.DEBUG) ### Linting & Tests -To run tests: +To run lint & tests: ```shell make lint