diff --git a/config/dev.exs b/config/dev.exs index d185989..3dc5939 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -8,6 +8,7 @@ config :plantid_discord_bot, image_folder: "priv/static", fileserver_url: "http://localhost:4321", port: 4321, - source: "https://github.com/TheRealOwenRees/plantID_discordbot", + source: "https://github.com/TheRealOwenRees/plantid-discord-bot", invite: - "https://discord.com/api/oauth2/authorize?client_id=948227126094598204&permissions=19520&scope=bot" + "https://discord.com/api/oauth2/authorize?client_id=948227126094598204&permissions=19520&scope=bot", + api: Nostrum.Api diff --git a/config/prod.exs b/config/prod.exs index f3923df..9c045c2 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -8,6 +8,7 @@ config :plantid_discord_bot, image_folder: "priv/static", fileserver_url: "http://localhost:4321", port: 4321, - source: "https://github.com/TheRealOwenRees/plantID_discordbot", + source: "https://github.com/TheRealOwenRees/plantid-discord-bot", invite: - "https://discord.com/api/oauth2/authorize?client_id=948227126094598204&permissions=19520&scope=bot" + "https://discord.com/api/oauth2/authorize?client_id=948227126094598204&permissions=19520&scope=bot", + api: Nostrum.Api diff --git a/config/runtime.exs b/config/runtime.exs index 0853604..4643bb4 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -8,9 +8,10 @@ config :plantid_discord_bot, image_folder: "priv/static", fileserver_url: "http://localhost:4321", port: 4321, - source: "https://github.com/TheRealOwenRees/plantID_discordbot", + source: "https://github.com/TheRealOwenRees/plantid-discord-bot", invite: - "https://discord.com/api/oauth2/authorize?client_id=948227126094598204&permissions=19520&scope=bot" + "https://discord.com/api/oauth2/authorize?client_id=948227126094598204&permissions=19520&scope=bot", + api: Nostrum.Api config :nostrum, token: System.get_env("DISCORD_TOKEN"), diff --git a/config/test.exs b/config/test.exs index d185989..027988a 100644 --- a/config/test.exs +++ b/config/test.exs @@ -8,6 +8,7 @@ config :plantid_discord_bot, image_folder: "priv/static", fileserver_url: "http://localhost:4321", port: 4321, - source: "https://github.com/TheRealOwenRees/plantID_discordbot", + source: "https://github.com/TheRealOwenRees/plantid-discord-bot", invite: - "https://discord.com/api/oauth2/authorize?client_id=948227126094598204&permissions=19520&scope=bot" + "https://discord.com/api/oauth2/authorize?client_id=948227126094598204&permissions=19520&scope=bot", + api: PlantIdDiscordBotTest.Mocks.Nostrum.Api diff --git a/lib/application.ex b/lib/application.ex new file mode 100644 index 0000000..4b5bf26 --- /dev/null +++ b/lib/application.ex @@ -0,0 +1,15 @@ +defmodule PlantIdDiscordBot.Application do + use Application + + @port Application.compile_env(:plantid_discord_bot, :port) + + @impl true + def start(_type, _args) do + children = [ + PlantIdDiscordBot + ] + + opts = [strategy: :one_for_one, name: PlantIdDiscordBot.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/lib/mocks/nostrum_api.ex b/lib/mocks/nostrum_api.ex new file mode 100644 index 0000000..188561d --- /dev/null +++ b/lib/mocks/nostrum_api.ex @@ -0,0 +1,7 @@ +defmodule PlantIdDiscordBotTest.Mocks.Nostrum.Api do + @moduledoc """ + Mocks the Nostrum.Api module. + """ + + def create_interaction_response(_interaction, response), do: {:ok, response} +end diff --git a/lib/plantid_discord_bot.ex b/lib/plantid_discord_bot.ex index 63762f1..fbe870a 100644 --- a/lib/plantid_discord_bot.ex +++ b/lib/plantid_discord_bot.ex @@ -1,18 +1,37 @@ defmodule PlantIdDiscordBot do @moduledoc """ - Documentation for `PlantidDiscordBot`. + A Discord bot that identifies plants from photos of their organs. """ - @doc """ - Hello world. + use Nostrum.Consumer + alias Nostrum.Api + alias PlantIdDiscordBot.Cog - ## Examples + IO.inspect("Starting PlantIdDiscordBot") - iex> PlantidDiscordBot.hello() - :world + @global_application_commands [ + {"servers", "All servers that this bot belongs to, []"}, + {"invite", "Invite link for this bot, []"}, + {"source", "Link to the source code for this bot, []"}, + {"info", "Information about this bot, []"}, + {"stats", "Statistics about this bot, []"}, + {"help", "Help information for this bot, []"}, + {"status", "API Status, []"}, + {"id", "ID a plant from up to 5 images, []"} + ] - """ - def hello do - :world + # Starts the bot + def handle_event({:READY}) do + # mock function depending on the environment + Api.create_global_application_command(@global_application_commands) + # TODO fix, not working + Api.update_status(:online, "Guess the Plant|/help") + # TODO remove in prod + Api.create_guild_application_command(1_002_507_312_159_797_318, @global_application_commands) + end + + # Handle /source command + def handle_event({:INTERACTION_CREATE, %{data: %{name: "source"}} = interaction, _ws_state}) do + Cog.Info.source(interaction) end end diff --git a/lib/plantid_discord_bot/cogs/info.ex b/lib/plantid_discord_bot/cogs/info.ex new file mode 100644 index 0000000..117e21d --- /dev/null +++ b/lib/plantid_discord_bot/cogs/info.ex @@ -0,0 +1,13 @@ +defmodule PlantIdDiscordBot.Cog.Info do + use Nostrum.Consumer + + @api Application.compile_env(:plantid_discord_bot, :api) + @source Application.compile_env(:plantid_discord_bot, :source) + + @doc """ + Sends a link to the source code for this bot. + """ + def source(interaction) do + @api.create_interaction_response(interaction, %{type: 4, data: %{content: @source}}) + end +end diff --git a/mix.exs b/mix.exs index 2acf03d..51fc29f 100644 --- a/mix.exs +++ b/mix.exs @@ -14,7 +14,8 @@ defmodule PlantidDiscordBot.MixProject do # Run "mix help compile.app" to learn about applications. def application do [ - extra_applications: [:logger] + extra_applications: [:logger], + mod: {PlantIdDiscordBot.Application, []} ] end diff --git a/test/commands/source_test.exs b/test/commands/source_test.exs new file mode 100644 index 0000000..400c676 --- /dev/null +++ b/test/commands/source_test.exs @@ -0,0 +1,16 @@ +defmodule PlantIdDiscordBotTest.Cog.Source do + use ExUnit.Case + + @source Application.compile_env(:plantid_discord_bot, :source) + + test "source" do + interaction = %{data: %{name: "source"}} + + expected_response = %{ + type: 4, + data: %{content: @source} + } + + assert PlantIdDiscordBot.Cog.Info.source(interaction) == {:ok, expected_response} + end +end diff --git a/test/plantid_discord_bot_test.exs b/test/plantid_discord_bot_test.exs index c99204b..054f82d 100644 --- a/test/plantid_discord_bot_test.exs +++ b/test/plantid_discord_bot_test.exs @@ -1,8 +1,4 @@ defmodule PlantIdDiscordBotTest do use ExUnit.Case doctest PlantIdDiscordBot - - test "greets the world" do - assert PlantIdDiscordBot.hello() == :world - end end diff --git a/test/test_helper.exs b/test/test_helper.exs index d816db0..40bed13 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,143 +1,143 @@ -ExUnit.start() +# ExUnit.start() -defmodule PlantNetFixtures do - def raw_response, do: plantnet_raw_response - def parsed_response, do: plantnet_parsed_response -end +# defmodule PlantNetFixtures do +# def raw_response, do: plantnet_raw_response +# def parsed_response, do: plantnet_parsed_response +# end -plantnet_raw_response = - "{\"query\":{\"project\":\"all\",\"images\":[\"https://upload.wikimedia.org/wikipedia/commons/f/ff/Prunus_cerasifera_A.jpg\",\"https://le-jardin-de-pascal.com/2195113-large_default/prunus-cerasifera-atropurpurea-prunier-myrobolan-nigra.jpg\"],\"organs\":[\"auto\",\"auto\"],\"includeRelatedImages\":false,\"noReject\":false},\"language\":\"en\",\"preferedReferential\":\"k-world-flora\",\"bestMatch\":\"Prunus cerasifera Ehrh.\",\"results\":[{\"score\":0.87871,\"species\":{\"scientificNameWithoutAuthor\":\"Prunus cerasifera\",\"scientificNameAuthorship\":\"Ehrh.\",\"genus\":{\"scientificNameWithoutAuthor\":\"Prunus\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Prunus\"},\"family\":{\"scientificNameWithoutAuthor\":\"Rosaceae\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Rosaceae\"},\"commonNames\":[\"Cherry plum, myrobalan\",\"Cherry Plum\",\"Purple-leaf Plum\"],\"scientificName\":\"Prunus cerasifera Ehrh.\"},\"gbif\":{\"id\":\"3021730\"},\"powo\":{\"id\":\"729568-1\"},\"iucn\":{\"id\":\"172162\",\"category\":\"DD\"}},{\"score\":0.31668,\"species\":{\"scientificNameWithoutAuthor\":\"Prunus × cistena\",\"scientificNameAuthorship\":\"N.E.Hansen ex Koehne\",\"genus\":{\"scientificNameWithoutAuthor\":\"Prunus\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Prunus\"},\"family\":{\"scientificNameWithoutAuthor\":\"Rosaceae\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Rosaceae\"},\"commonNames\":[\"Dwarf red-leaf plum\",\"Purple-leaf sand cherry\",\"Purple-leaved sand cherry\"],\"scientificName\":\"Prunus × cistena N.E.Hansen ex Koehne\"},\"gbif\":{\"id\":\"3022465\"},\"powo\":{\"id\":\"2959315-4\"}},{\"score\":0.01801,\"species\":{\"scientificNameWithoutAuthor\":\"Prunus sargentii\",\"scientificNameAuthorship\":\"Rehder\",\"genus\":{\"scientificNameWithoutAuthor\":\"Prunus\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Prunus\"},\"family\":{\"scientificNameWithoutAuthor\":\"Rosaceae\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Rosaceae\"},\"commonNames\":[\"Sargent's cherry\",\"Northern Japanese hill cherry\",\"Sargent’s cherry\"],\"scientificName\":\"Prunus sargentii Rehder\"},\"gbif\":{\"id\":\"3020955\"},\"powo\":{\"id\":\"730239-1\"},\"iucn\":{\"id\":\"64127603\",\"category\":\"LC\"}},{\"score\":0.00896,\"species\":{\"scientificNameWithoutAuthor\":\"Prunus × yedoensis\",\"scientificNameAuthorship\":\"Matsum.\",\"genus\":{\"scientificNameWithoutAuthor\":\"Prunus\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Prunus\"},\"family\":{\"scientificNameWithoutAuthor\":\"Rosaceae\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Rosaceae\"},\"commonNames\":[\"Yoshino cherry\",\"Hybrid cherry\",\"Korean flowering cherry\"],\"scientificName\":\"Prunus × yedoensis Matsum.\"},\"gbif\":{\"id\":\"3021335\"},\"powo\":{\"id\":\"30119904-2\"}},{\"score\":0.00518,\"species\":{\"scientificNameWithoutAuthor\":\"Prunus serrulata\",\"scientificNameAuthorship\":\"Lindl.\",\"genus\":{\"scientificNameWithoutAuthor\":\"Prunus\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Prunus\"},\"family\":{\"scientificNameWithoutAuthor\":\"Rosaceae\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Rosaceae\"},\"commonNames\":[\"Japanese flowering cherry\",\"Japanese flowering cherry Kwanzan\",\"Tibetan Cherry\"],\"scientificName\":\"Prunus serrulata Lindl.\"},\"gbif\":{\"id\":\"3022609\"},\"powo\":{\"id\":\"730268-1\"},\"iucn\":{\"id\":\"217170511\",\"category\":\"LC\"}}],\"version\":\"2024-11-19 (7.3)\",\"remainingIdentificationRequests\":488}" +# plantnet_raw_response = +# "{\"query\":{\"project\":\"all\",\"images\":[\"https://upload.wikimedia.org/wikipedia/commons/f/ff/Prunus_cerasifera_A.jpg\",\"https://le-jardin-de-pascal.com/2195113-large_default/prunus-cerasifera-atropurpurea-prunier-myrobolan-nigra.jpg\"],\"organs\":[\"auto\",\"auto\"],\"includeRelatedImages\":false,\"noReject\":false},\"language\":\"en\",\"preferedReferential\":\"k-world-flora\",\"bestMatch\":\"Prunus cerasifera Ehrh.\",\"results\":[{\"score\":0.87871,\"species\":{\"scientificNameWithoutAuthor\":\"Prunus cerasifera\",\"scientificNameAuthorship\":\"Ehrh.\",\"genus\":{\"scientificNameWithoutAuthor\":\"Prunus\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Prunus\"},\"family\":{\"scientificNameWithoutAuthor\":\"Rosaceae\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Rosaceae\"},\"commonNames\":[\"Cherry plum, myrobalan\",\"Cherry Plum\",\"Purple-leaf Plum\"],\"scientificName\":\"Prunus cerasifera Ehrh.\"},\"gbif\":{\"id\":\"3021730\"},\"powo\":{\"id\":\"729568-1\"},\"iucn\":{\"id\":\"172162\",\"category\":\"DD\"}},{\"score\":0.31668,\"species\":{\"scientificNameWithoutAuthor\":\"Prunus × cistena\",\"scientificNameAuthorship\":\"N.E.Hansen ex Koehne\",\"genus\":{\"scientificNameWithoutAuthor\":\"Prunus\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Prunus\"},\"family\":{\"scientificNameWithoutAuthor\":\"Rosaceae\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Rosaceae\"},\"commonNames\":[\"Dwarf red-leaf plum\",\"Purple-leaf sand cherry\",\"Purple-leaved sand cherry\"],\"scientificName\":\"Prunus × cistena N.E.Hansen ex Koehne\"},\"gbif\":{\"id\":\"3022465\"},\"powo\":{\"id\":\"2959315-4\"}},{\"score\":0.01801,\"species\":{\"scientificNameWithoutAuthor\":\"Prunus sargentii\",\"scientificNameAuthorship\":\"Rehder\",\"genus\":{\"scientificNameWithoutAuthor\":\"Prunus\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Prunus\"},\"family\":{\"scientificNameWithoutAuthor\":\"Rosaceae\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Rosaceae\"},\"commonNames\":[\"Sargent's cherry\",\"Northern Japanese hill cherry\",\"Sargent’s cherry\"],\"scientificName\":\"Prunus sargentii Rehder\"},\"gbif\":{\"id\":\"3020955\"},\"powo\":{\"id\":\"730239-1\"},\"iucn\":{\"id\":\"64127603\",\"category\":\"LC\"}},{\"score\":0.00896,\"species\":{\"scientificNameWithoutAuthor\":\"Prunus × yedoensis\",\"scientificNameAuthorship\":\"Matsum.\",\"genus\":{\"scientificNameWithoutAuthor\":\"Prunus\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Prunus\"},\"family\":{\"scientificNameWithoutAuthor\":\"Rosaceae\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Rosaceae\"},\"commonNames\":[\"Yoshino cherry\",\"Hybrid cherry\",\"Korean flowering cherry\"],\"scientificName\":\"Prunus × yedoensis Matsum.\"},\"gbif\":{\"id\":\"3021335\"},\"powo\":{\"id\":\"30119904-2\"}},{\"score\":0.00518,\"species\":{\"scientificNameWithoutAuthor\":\"Prunus serrulata\",\"scientificNameAuthorship\":\"Lindl.\",\"genus\":{\"scientificNameWithoutAuthor\":\"Prunus\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Prunus\"},\"family\":{\"scientificNameWithoutAuthor\":\"Rosaceae\",\"scientificNameAuthorship\":\"\",\"scientificName\":\"Rosaceae\"},\"commonNames\":[\"Japanese flowering cherry\",\"Japanese flowering cherry Kwanzan\",\"Tibetan Cherry\"],\"scientificName\":\"Prunus serrulata Lindl.\"},\"gbif\":{\"id\":\"3022609\"},\"powo\":{\"id\":\"730268-1\"},\"iucn\":{\"id\":\"217170511\",\"category\":\"LC\"}}],\"version\":\"2024-11-19 (7.3)\",\"remainingIdentificationRequests\":488}" -plantnet_parsed_response = [ - %{ - "gbif" => %{"id" => "3021730"}, - "gbif_url" => "https://www.gbif.org/species/3021730", - "iucn" => %{"category" => "DD", "id" => "172162"}, - "pfaf_url" => "https://pfaf.org/user/Plant.aspx?LatinName=/Prunus+cerasifera", - "powo" => %{"id" => "729568-1"}, - "powo_url" => "https://powo.science.kew.org/taxon/729568-1", - "score" => 0.87871, - "species" => %{ - "commonNames" => ["Cherry plum, myrobalan", "Cherry Plum", "Purple-leaf Plum"], - "family" => %{ - "scientificName" => "Rosaceae", - "scientificNameAuthorship" => "", - "scientificNameWithoutAuthor" => "Rosaceae" - }, - "genus" => %{ - "scientificName" => "Prunus", - "scientificNameAuthorship" => "", - "scientificNameWithoutAuthor" => "Prunus" - }, - "scientificName" => "Prunus cerasifera Ehrh.", - "scientificNameAuthorship" => "Ehrh.", - "scientificNameWithoutAuthor" => "Prunus cerasifera" - } - }, - %{ - "gbif" => %{"id" => "3022465"}, - "gbif_url" => "https://www.gbif.org/species/3022465", - "pfaf_url" => "https://pfaf.org/user/Plant.aspx?LatinName=/Prunus+×+cistena", - "powo" => %{"id" => "2959315-4"}, - "powo_url" => "https://powo.science.kew.org/taxon/2959315-4", - "score" => 0.31668, - "species" => %{ - "commonNames" => [ - "Dwarf red-leaf plum", - "Purple-leaf sand cherry", - "Purple-leaved sand cherry" - ], - "family" => %{ - "scientificName" => "Rosaceae", - "scientificNameAuthorship" => "", - "scientificNameWithoutAuthor" => "Rosaceae" - }, - "genus" => %{ - "scientificName" => "Prunus", - "scientificNameAuthorship" => "", - "scientificNameWithoutAuthor" => "Prunus" - }, - "scientificName" => "Prunus × cistena N.E.Hansen ex Koehne", - "scientificNameAuthorship" => "N.E.Hansen ex Koehne", - "scientificNameWithoutAuthor" => "Prunus × cistena" - } - }, - %{ - "gbif" => %{"id" => "3020955"}, - "gbif_url" => "https://www.gbif.org/species/3020955", - "iucn" => %{"category" => "LC", "id" => "64127603"}, - "pfaf_url" => "https://pfaf.org/user/Plant.aspx?LatinName=/Prunus+sargentii", - "powo" => %{"id" => "730239-1"}, - "powo_url" => "https://powo.science.kew.org/taxon/730239-1", - "score" => 0.01801, - "species" => %{ - "commonNames" => ["Sargent's cherry", "Northern Japanese hill cherry", "Sargent’s cherry"], - "family" => %{ - "scientificName" => "Rosaceae", - "scientificNameAuthorship" => "", - "scientificNameWithoutAuthor" => "Rosaceae" - }, - "genus" => %{ - "scientificName" => "Prunus", - "scientificNameAuthorship" => "", - "scientificNameWithoutAuthor" => "Prunus" - }, - "scientificName" => "Prunus sargentii Rehder", - "scientificNameAuthorship" => "Rehder", - "scientificNameWithoutAuthor" => "Prunus sargentii" - } - }, - %{ - "gbif" => %{"id" => "3021335"}, - "gbif_url" => "https://www.gbif.org/species/3021335", - "pfaf_url" => "https://pfaf.org/user/Plant.aspx?LatinName=/Prunus+×+yedoensis", - "powo" => %{"id" => "30119904-2"}, - "powo_url" => "https://powo.science.kew.org/taxon/30119904-2", - "score" => 0.00896, - "species" => %{ - "commonNames" => ["Yoshino cherry", "Hybrid cherry", "Korean flowering cherry"], - "family" => %{ - "scientificName" => "Rosaceae", - "scientificNameAuthorship" => "", - "scientificNameWithoutAuthor" => "Rosaceae" - }, - "genus" => %{ - "scientificName" => "Prunus", - "scientificNameAuthorship" => "", - "scientificNameWithoutAuthor" => "Prunus" - }, - "scientificName" => "Prunus × yedoensis Matsum.", - "scientificNameAuthorship" => "Matsum.", - "scientificNameWithoutAuthor" => "Prunus × yedoensis" - } - }, - %{ - "gbif" => %{"id" => "3022609"}, - "gbif_url" => "https://www.gbif.org/species/3022609", - "iucn" => %{"category" => "LC", "id" => "217170511"}, - "pfaf_url" => "https://pfaf.org/user/Plant.aspx?LatinName=/Prunus+serrulata", - "powo" => %{"id" => "730268-1"}, - "powo_url" => "https://powo.science.kew.org/taxon/730268-1", - "score" => 0.00518, - "species" => %{ - "commonNames" => [ - "Japanese flowering cherry", - "Japanese flowering cherry Kwanzan", - "Tibetan Cherry" - ], - "family" => %{ - "scientificName" => "Rosaceae", - "scientificNameAuthorship" => "", - "scientificNameWithoutAuthor" => "Rosaceae" - }, - "genus" => %{ - "scientificName" => "Prunus", - "scientificNameAuthorship" => "", - "scientificNameWithoutAuthor" => "Prunus" - }, - "scientificName" => "Prunus serrulata Lindl.", - "scientificNameAuthorship" => "Lindl.", - "scientificNameWithoutAuthor" => "Prunus serrulata" - } - } -] +# plantnet_parsed_response = [ +# %{ +# "gbif" => %{"id" => "3021730"}, +# "gbif_url" => "https://www.gbif.org/species/3021730", +# "iucn" => %{"category" => "DD", "id" => "172162"}, +# "pfaf_url" => "https://pfaf.org/user/Plant.aspx?LatinName=/Prunus+cerasifera", +# "powo" => %{"id" => "729568-1"}, +# "powo_url" => "https://powo.science.kew.org/taxon/729568-1", +# "score" => 0.87871, +# "species" => %{ +# "commonNames" => ["Cherry plum, myrobalan", "Cherry Plum", "Purple-leaf Plum"], +# "family" => %{ +# "scientificName" => "Rosaceae", +# "scientificNameAuthorship" => "", +# "scientificNameWithoutAuthor" => "Rosaceae" +# }, +# "genus" => %{ +# "scientificName" => "Prunus", +# "scientificNameAuthorship" => "", +# "scientificNameWithoutAuthor" => "Prunus" +# }, +# "scientificName" => "Prunus cerasifera Ehrh.", +# "scientificNameAuthorship" => "Ehrh.", +# "scientificNameWithoutAuthor" => "Prunus cerasifera" +# } +# }, +# %{ +# "gbif" => %{"id" => "3022465"}, +# "gbif_url" => "https://www.gbif.org/species/3022465", +# "pfaf_url" => "https://pfaf.org/user/Plant.aspx?LatinName=/Prunus+×+cistena", +# "powo" => %{"id" => "2959315-4"}, +# "powo_url" => "https://powo.science.kew.org/taxon/2959315-4", +# "score" => 0.31668, +# "species" => %{ +# "commonNames" => [ +# "Dwarf red-leaf plum", +# "Purple-leaf sand cherry", +# "Purple-leaved sand cherry" +# ], +# "family" => %{ +# "scientificName" => "Rosaceae", +# "scientificNameAuthorship" => "", +# "scientificNameWithoutAuthor" => "Rosaceae" +# }, +# "genus" => %{ +# "scientificName" => "Prunus", +# "scientificNameAuthorship" => "", +# "scientificNameWithoutAuthor" => "Prunus" +# }, +# "scientificName" => "Prunus × cistena N.E.Hansen ex Koehne", +# "scientificNameAuthorship" => "N.E.Hansen ex Koehne", +# "scientificNameWithoutAuthor" => "Prunus × cistena" +# } +# }, +# %{ +# "gbif" => %{"id" => "3020955"}, +# "gbif_url" => "https://www.gbif.org/species/3020955", +# "iucn" => %{"category" => "LC", "id" => "64127603"}, +# "pfaf_url" => "https://pfaf.org/user/Plant.aspx?LatinName=/Prunus+sargentii", +# "powo" => %{"id" => "730239-1"}, +# "powo_url" => "https://powo.science.kew.org/taxon/730239-1", +# "score" => 0.01801, +# "species" => %{ +# "commonNames" => ["Sargent's cherry", "Northern Japanese hill cherry", "Sargent’s cherry"], +# "family" => %{ +# "scientificName" => "Rosaceae", +# "scientificNameAuthorship" => "", +# "scientificNameWithoutAuthor" => "Rosaceae" +# }, +# "genus" => %{ +# "scientificName" => "Prunus", +# "scientificNameAuthorship" => "", +# "scientificNameWithoutAuthor" => "Prunus" +# }, +# "scientificName" => "Prunus sargentii Rehder", +# "scientificNameAuthorship" => "Rehder", +# "scientificNameWithoutAuthor" => "Prunus sargentii" +# } +# }, +# %{ +# "gbif" => %{"id" => "3021335"}, +# "gbif_url" => "https://www.gbif.org/species/3021335", +# "pfaf_url" => "https://pfaf.org/user/Plant.aspx?LatinName=/Prunus+×+yedoensis", +# "powo" => %{"id" => "30119904-2"}, +# "powo_url" => "https://powo.science.kew.org/taxon/30119904-2", +# "score" => 0.00896, +# "species" => %{ +# "commonNames" => ["Yoshino cherry", "Hybrid cherry", "Korean flowering cherry"], +# "family" => %{ +# "scientificName" => "Rosaceae", +# "scientificNameAuthorship" => "", +# "scientificNameWithoutAuthor" => "Rosaceae" +# }, +# "genus" => %{ +# "scientificName" => "Prunus", +# "scientificNameAuthorship" => "", +# "scientificNameWithoutAuthor" => "Prunus" +# }, +# "scientificName" => "Prunus × yedoensis Matsum.", +# "scientificNameAuthorship" => "Matsum.", +# "scientificNameWithoutAuthor" => "Prunus × yedoensis" +# } +# }, +# %{ +# "gbif" => %{"id" => "3022609"}, +# "gbif_url" => "https://www.gbif.org/species/3022609", +# "iucn" => %{"category" => "LC", "id" => "217170511"}, +# "pfaf_url" => "https://pfaf.org/user/Plant.aspx?LatinName=/Prunus+serrulata", +# "powo" => %{"id" => "730268-1"}, +# "powo_url" => "https://powo.science.kew.org/taxon/730268-1", +# "score" => 0.00518, +# "species" => %{ +# "commonNames" => [ +# "Japanese flowering cherry", +# "Japanese flowering cherry Kwanzan", +# "Tibetan Cherry" +# ], +# "family" => %{ +# "scientificName" => "Rosaceae", +# "scientificNameAuthorship" => "", +# "scientificNameWithoutAuthor" => "Rosaceae" +# }, +# "genus" => %{ +# "scientificName" => "Prunus", +# "scientificNameAuthorship" => "", +# "scientificNameWithoutAuthor" => "Prunus" +# }, +# "scientificName" => "Prunus serrulata Lindl.", +# "scientificNameAuthorship" => "Lindl.", +# "scientificNameWithoutAuthor" => "Prunus serrulata" +# } +# } +# ]