Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Coinmarketcap parser #1151

Merged
merged 1 commit into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,30 +43,24 @@ defmodule Archethic.OracleChain.Services.UCOPrice.Providers.CoinMarketCap do
:httpc.request(:get, {query, headers}, httpc_options, []),
{:ok, document} <- Floki.parse_document(body) do
price =
case Floki.find(document, "div.priceTitle > div.priceValue > span") do
[] ->
regex = ~r/price today is (.+) with a/

Floki.find(document, "meta[name=description]")
|> Floki.attribute("content")
|> Enum.join()
|> then(&Regex.run(regex, &1, capture: :all_but_first))
|> Enum.join()
|> String.graphemes()
|> Enum.filter(&(&1 in [".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]))
|> Enum.into("")
|> String.to_float()

element ->
element
|> Floki.text()
|> String.graphemes()
|> Enum.filter(&(&1 in [".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]))
|> Enum.into("")
|> String.to_float()
end

{:ok, {pair, [price]}}
extract_methods()
|> Enum.reduce_while(nil, fn extract_fn, acc ->
try do
{:halt, extract_fn.(document)}
rescue
_ ->
{:cont, acc}
catch
_ ->
{:cont, acc}
end
end)

if is_number(price) do
{:ok, {pair, [price]}}
else
{:error, :not_a_number}
end
else
{:ok, {{_, _, status}, _, _}} ->
{:error, status}
Expand All @@ -85,4 +79,61 @@ defmodule Archethic.OracleChain.Services.UCOPrice.Providers.CoinMarketCap do

{:ok, returned_prices}
end

defp extract_methods() do
[
&extract_method1/1,
&extract_method2/1,
&extract_method3/1,
# if every other failed
&fallback_error/1
]
end

defp extract_method1(document) do
Floki.find(document, "div.priceTitle > div.priceValue > span")
|> Floki.text()
|> String.graphemes()
|> Enum.filter(&(&1 in [".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]))
|> Enum.into("")
|> String.to_float()
end

defp extract_method2(document) do
regex = ~r/price today is (.+) with a/

Floki.find(document, "meta[name=description]")
|> Floki.attribute("content")
|> Enum.join()
|> then(&Regex.run(regex, &1, capture: :all_but_first))
|> Enum.join()
|> String.graphemes()
|> Enum.filter(&(&1 in [".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]))
|> Enum.into("")
|> String.to_float()
end

defp extract_method3(document) do
document
|> Floki.find("#__NEXT_DATA__")
|> Floki.text(js: true)
|> Jason.decode!()
|> get_in(["props", "pageProps", "info", "statistics", "price"])
end

defp fallback_error(document) do
path = "/tmp/coinmarketcap.html"

case File.write(path, Floki.raw_html(document, pretty: true)) do
:ok ->
Logger.warning(
"Coinmarketcap failed to be parsed, you may find the page we receive at #{path}"
)

{:error, _posix} ->
Logger.warning("Coinmarketcap failed to be parsed")
end

throw("coinmarketcap failed to parse")
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Archethic.OracleChain.Services.UCOPrice.Providers.CoinMarketCapTest do
alias Archethic.OracleChain.Services.UCOPrice.Providers.CoinMarketCap

@tag oracle_provider: true
test "fetch/1 should get the current UCO price from CoinGecko" do
test "fetch/1 should get the current UCO price from CoinMarketCap" do
assert {:ok, %{"eur" => prices}} = CoinMarketCap.fetch(["eur"])
assert is_list(prices)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Archethic.OracleChain.Services.UCOPrice.Providers.CoinPaprikaTest do
alias Archethic.OracleChain.Services.UCOPrice.Providers.CoinPaprika

@tag oracle_provider: true
test "fetch/1 should get the current UCO price from CoinGecko" do
test "fetch/1 should get the current UCO price from CoinPaprika" do
assert {:ok, %{"eur" => prices}} = CoinPaprika.fetch(["eur"])
assert is_list(prices)
end
Expand Down