mirror of
https://github.com/TheRealOwenRees/plantid-discord-bot.git
synced 2026-07-23 04:26:57 +00:00
parse response and generate a interaction response
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import Config
|
||||
|
||||
config :plantid_discord_bot,
|
||||
start_time: DateTime.utc_now(),
|
||||
guild_ids: [1_002_507_312_159_797_318],
|
||||
plantnet_api_key: System.get_env("PLANTNET_API_KEY"),
|
||||
plantnet_api_base_url: "https://my-api.plantnet.org/v2",
|
||||
|
||||
@@ -5,6 +5,10 @@ defmodule PlantIdDiscordBot.Application do
|
||||
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
# Set the start time for the application
|
||||
start_time = DateTime.utc_now()
|
||||
Application.put_env(:plantid_discord_bot, :start_time, start_time)
|
||||
|
||||
children = [
|
||||
PlantIdDiscordBot.Consumer,
|
||||
PlantIdDiscordBot.RateLimiter,
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
defmodule PlantIdDiscordBot.Cog.PlantNet do
|
||||
use Nostrum.Consumer
|
||||
# alias PlantIdDiscordBot.Cog.PlantNet
|
||||
alias PlantIdDiscordBot.RateLimiter
|
||||
alias PlantIdDiscordBot.PlantNet.Parser
|
||||
|
||||
@api Application.compile_env(:plantid_discord_bot, :api)
|
||||
|
||||
# TEMP: Mock data
|
||||
@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}"
|
||||
|
||||
@doc """
|
||||
ID a plant from up to 5 images of organs.
|
||||
"""
|
||||
@@ -30,9 +35,12 @@ defmodule PlantIdDiscordBot.Cog.PlantNet do
|
||||
attachment_urls = get_attachment_urls(interaction)
|
||||
original_images = get_original_images(attachment_urls)
|
||||
|
||||
# temp data
|
||||
response_message = Parser.parse(@plantnet_raw_response)
|
||||
|
||||
@api.create_interaction_response(interaction, %{
|
||||
type: 4,
|
||||
data: %{content: "This command is not yet implemented." <> "\n#{original_images}"}
|
||||
data: %{content: response_message <> "\n#{original_images}"}
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,28 @@
|
||||
defmodule PlantIdDiscordBot.PlantNet.Parser do
|
||||
@moduledoc """
|
||||
Parses the response from the PlantNet API.
|
||||
|
||||
# Examples
|
||||
iex> response = ~S({
|
||||
...> "results": [
|
||||
...> {
|
||||
...> "score": 0.87871,
|
||||
...> "species": {
|
||||
...> "scientificNameWithoutAuthor": "Prunus cerasifera",
|
||||
...> "commonNames": ["Cherry plum", "myrobalan", "Cherry Plum", "Purple-leaf Plum"]
|
||||
...> },
|
||||
...> "gbif": {"id": "3021730"},
|
||||
...> "powo": {"id": "729568-1"},
|
||||
...> "iucn": {"category": "DD"}
|
||||
...> }
|
||||
...> ]
|
||||
...> })
|
||||
iex> PlantIdDiscordBot.PlantNet.Parser.parse(response)
|
||||
|
||||
"My best guess is **Prunus cerasifera** with a confidence of **88%**. Common names include **Cherry plum, myrobalan, Cherry Plum, Purple-leaf Plum**.
|
||||
|
||||
[GBIF](<https://www.gbif.org/species/3021730>) | [PFAF](<https://pfaf.org/user/Plant.aspx?LatinName=Prunus+cerasifera>) | [POWO](<https://powo.science.kew.org/taxon/729568-1>)
|
||||
Threat status: DD"
|
||||
"""
|
||||
|
||||
@gbif_base_url "https://www.gbif.org/species"
|
||||
@@ -12,7 +34,16 @@ defmodule PlantIdDiscordBot.PlantNet.Parser do
|
||||
Parses the response from the PlantNet API into a map.
|
||||
"""
|
||||
@spec parse(String.t()) :: map()
|
||||
def parse(response), do: Jason.decode!(response)
|
||||
def parse(response) do
|
||||
response
|
||||
|> to_map()
|
||||
|> filter_by_score()
|
||||
|> add_external_urls()
|
||||
|> generate_response_message()
|
||||
end
|
||||
|
||||
@spec to_map(String.t()) :: map()
|
||||
def to_map(response), do: Jason.decode!(response)
|
||||
|
||||
@doc """
|
||||
Filters the data by score. Data is a parsed response from the PlantNet API.
|
||||
@@ -40,6 +71,24 @@ defmodule PlantIdDiscordBot.PlantNet.Parser do
|
||||
Map.put(data, "results", updated_results)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Generates a response message from the data. Data is a parsed response from the PlantNet API.
|
||||
"""
|
||||
@spec generate_response_message(map()) :: String.t()
|
||||
def generate_response_message(data) do
|
||||
best_result = hd(data["results"])
|
||||
other_results = tl(data["results"])
|
||||
best_guess_name = best_result["species"]["scientificNameWithoutAuthor"]
|
||||
score = round(best_result["score"] * 100) |> Integer.to_string()
|
||||
|
||||
"""
|
||||
My best guess is **#{best_guess_name}** with a confidence of **#{score}%**. Common names include **#{Enum.join(best_result["species"]["commonNames"], ", ")}**.
|
||||
|
||||
[GBIF](<#{best_result["gbif_url"]}>) | [PFAF](<#{best_result["pfaf_url"]}>) | [POWO](<#{best_result["powo_url"]}>)
|
||||
Threat status: #{best_result["iucn"]["category"]}
|
||||
"""
|
||||
end
|
||||
|
||||
@spec generate_gbif_url(map()) :: map()
|
||||
defp generate_gbif_url(data) do
|
||||
Enum.map(data, fn result ->
|
||||
@@ -66,4 +115,14 @@ defmodule PlantIdDiscordBot.PlantNet.Parser do
|
||||
if powo_id, do: Map.put(result, "powo_url", "#{@powo_base_url}/#{powo_id}"), else: result
|
||||
end)
|
||||
end
|
||||
|
||||
defp get_alternatives(data) do
|
||||
if length(data) === 0 do
|
||||
"No alternatives found."
|
||||
else
|
||||
alternatives =
|
||||
Enum.map(data, & &1["species"]["scientificNameWithoutAuthor"])
|
||||
|> Enum.join(", ")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
defmodule PlantIdDiscordBot.Utils do
|
||||
@start_time Application.compile_env(:plantid_discord_bot, :start_time)
|
||||
@start_time Application.get_env(:plantid_discord_bot, :start_time)
|
||||
|
||||
def get_uptime() do
|
||||
DateTime.diff(DateTime.utc_now(), @start_time)
|
||||
|
||||
@@ -4,10 +4,10 @@ defmodule PlantIdDiscordBotTest.PlantNet.Parser do
|
||||
|
||||
doctest PlantIdDiscordBot.PlantNet.Parser
|
||||
|
||||
test "parse" do
|
||||
test "to_map" do
|
||||
result =
|
||||
PlantNetFixtures.raw_response()
|
||||
|> Parser.parse()
|
||||
|> Parser.to_map()
|
||||
|
||||
assert result == PlantNetFixtures.parsed_response()
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user