-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat(phylopic): initial commit with an empty package * ci(phylopic): setup testing * dependencies(phylopic): add required packages * feat(phylopic): add a non-exported function to ping * feat(phylopic): autocomplete endpoint * test(phylopic): ping * dependencies(phylopic): add UUIDs * feat(phylopic): search images * refactor(phylopic): move functions to their own files * feat(phylopic): get the images after a search * doc(phylopic): comments in the top level file * ci: add phylopic too the top level bootstrap * license(phylopic): MIT * doc(phylopic): README * refactor(phylopic): use a more use-case inspired design * test(phylopic): add test for names * refactor(phylopic): move names to their own files * refactor(phylopic): overload some methods for easier piping * refactor(phylopic): replace images by thumbnail and vector * dependencies(phylopic): import UUIDs * doc(phylopic): update some comments in the code * doc(phylopic): update the man pages * doc(phylopic): manpage and vignette * feat: reexport phylopic from top-level package * doc(phylopic): README * doc(phylopic): correct manpage path * doc(phylopic): pick the first silhouette in the vignette * doc(phylopic): correct index path * bug: wrong module in the phylopic example * doc(phylopic): fix axis for the figure * doc(phylopic): some more context on how to plot * doc(phylopic): use of the items argument * feat(phylopic): restrict search by license terms Closes #173 * feat(phylopic): rename names to imagesof * doc(phylopic): update vignette use-case * doc(phylopic): add docstrings in the manual section * test(phylopic): name autocompletion * bug(phylopic): wrong return when using items=1 * doc(phylopic): README * feat(phylopic): twitter image * feat(phylopic): add additional twitterimage methods * refactor(phylopic): remove unused keyword arguments * refactor(phylopic): get the link function outside of the data function * feat(phylopic): get source image * dependencies(phylopic): using Markdown * feat(phylopic): attribution string * bug(phylopic): wrong call to the function to get the links * semver(phylopic): 0.0.1
- Loading branch information
Showing
22 changed files
with
448 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
The Phylopic.jl package is licensed under the MIT "Expat" License: | ||
|
||
> Copyright (c) 2023: Timothée Poisot. | ||
> | ||
> Permission is hereby granted, free of charge, to any person obtaining a copy | ||
> of this software and associated documentation files (the "Software"), to deal | ||
> in the Software without restriction, including without limitation the rights | ||
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
> copies of the Software, and to permit persons to whom the Software is | ||
> furnished to do so, subject to the following conditions: | ||
> | ||
> The above copyright notice and this permission notice shall be included in all | ||
> copies or substantial portions of the Software. | ||
> | ||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
> SOFTWARE. | ||
> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
name = "Phylopic" | ||
uuid = "c889285c-44aa-4473-b1e1-56f5d4e3ccf5" | ||
authors = ["Timothée Poisot <timothee.poisot@umontreal.ca>"] | ||
version = "0.0.1" | ||
|
||
[deps] | ||
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" | ||
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" | ||
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" | ||
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Phylopic.jl | ||
|
||
This package is a *thin* wrapper around the Phylopic API, which is currently not covering | ||
the entire API. | ||
|
||
~~~julia | ||
using Phylopic | ||
import Downloads | ||
|
||
# Get a series of UUIDs from a name | ||
org_uuid = Phylopic.imagesof("chiroptera"; items=1) | ||
|
||
# We can query the thumbnails for this UUID | ||
thumb_url = Phylopic.thumbnail(org_uuid) | ||
|
||
# We can download the thumbnail (to a temp file) | ||
thumb_file = Downloads.download(first(thumb_url)) | ||
~~~ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
main = () -> nothing | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
module Phylopic | ||
|
||
import HTTP | ||
import JSON | ||
import UUIDs | ||
using Markdown | ||
|
||
const api = "https://api.phylopic.org/" | ||
|
||
# This file contains the ping and build functions, which are only really called when the | ||
# package is initially loaded. Ping ensures that the API responds, and build gives the value | ||
# of the current build by default, as it is required for most operations. | ||
include("ping.jl") | ||
|
||
# We do a first ping to fail ASAP if the API is not responsive | ||
@assert isnothing(Phylopic.ping()) | ||
|
||
# We put the buildnumber in a const to avoid calling it multiple times -- this is a required | ||
# parameter for a large number of queries (most of the queries, in fact), so it makes sense | ||
# to get it as a const ASAP | ||
const buildnumber = Phylopic.build() | ||
# TODO: allow an environmental variable to specify a different build version for | ||
# reproducibility | ||
|
||
# The autocomplete endpoint is meant to give an overview of possible names starting from | ||
# a stem - this is not necessarilly going to give all of the names, and I am not sure why | ||
include("autocomplete.jl") | ||
|
||
include("imagesof.jl") | ||
include("images.jl") | ||
include("attribution.jl") | ||
|
||
end # module hylopic |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
""" | ||
Phylopic.attribution(uuid::UUIDs.UUID) | ||
Generates a string for the attribution of an image, as identified by its `uuid`. This string is markdown-formatted, and will include a link to the license. | ||
""" | ||
function attribution(uuid::UUIDs.UUID) | ||
data = Phylopic.images_data(uuid) | ||
contributorname = data["attribution"] | ||
nodename = data["_links"]["specificNode"]["title"] | ||
license = data["_links"]["license"]["href"] | ||
attr_string = "Image of *$(nodename)* provided by [$(contributorname)]($license)" | ||
return Markdown.parse(attr_string) | ||
end | ||
|
||
attribution(pair::Pair{String,UUIDs.UUID}; kwargs...) = attribution(pair.second; kwargs...) | ||
attribution(dict::Dict{String,UUIDs.UUID}; kwargs...) = attribution.(collect(dict); kwargs...) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
""" | ||
Phylopic.autocomplete(query::AbstractString) | ||
Performs an autocomplete query based on a string, which must be at least two characters in length. | ||
This function will return an *array* of strings, which can be empty if there are no matches. In you want to do things depending on the values returned, check them with `isempty`, not `isnothing`. | ||
The output of this function, when not empty, can be passed to either `Phylopic.nodes` or `Phylopic.images` using their `filter_name` keyword argument. Note that the `filter_name` argument accepts a *single* name, not an array of names. | ||
""" | ||
function autocomplete(query::AbstractString) | ||
if length(query) < 2 | ||
throw(ArgumentError("The query ($(query)) must be at least two characters")) | ||
end | ||
req = HTTP.get(Phylopic.api * "autocomplete?query=$(query)") | ||
if isequal(200)(req.status) | ||
matches = JSON.parse(String(req.body))["matches"] | ||
if ~isempty(matches) | ||
return convert(Vector{String}, matches) | ||
end | ||
end | ||
# WARNING: This probably should not return an empty list here, maybe a `nothing` would | ||
# make more sense | ||
return AbstractString[] | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
""" | ||
Phylopic.vector(uuid::UUIDs.UUID) | ||
Returns the URL (if it exists) to the original vector image for the silhouette. Note that the image must be identified by its UUID, not by a string. | ||
""" | ||
function vector(uuid::UUIDs.UUID) | ||
lnks = Phylopic.images_links(uuid) | ||
return lnks["vectorFile"]["href"] | ||
end | ||
|
||
""" | ||
Phylopic.twitterimage(uuid::UUIDs.UUID) | ||
Returns the twitter image for a UUID. | ||
""" | ||
function twitterimage(uuid::UUIDs.UUID) | ||
links = Phylopic.images_links(uuid) | ||
return links["twitter:image"]["href"] | ||
end | ||
|
||
""" | ||
Phylopic.source(uuid::UUIDs.UUID) | ||
Returns the source image for a UUID. | ||
""" | ||
function source(uuid::UUIDs.UUID) | ||
links = Phylopic.images_links(uuid) | ||
return links["sourceFile"]["href"] | ||
end | ||
|
||
""" | ||
Phylopic.thumbnail(uuid::UUIDs.UUID; resolution=192) | ||
Returns the URL (if it exists) to the thumbnails for the silhouette. The thumbnail `resolution` can be `64`, `128`, or `192` (the default). | ||
""" | ||
function thumbnail(uuid::UUIDs.UUID; resolution=192) | ||
@assert resolution in [64, 128, 192] | ||
lnks = Phylopic.images_links(uuid) | ||
required_size = "$(resolution)x$(resolution)" | ||
img = filter(f -> f["sizes"] == required_size, lnks["thumbnailFiles"]) | ||
return only(img)["href"] | ||
end | ||
|
||
thumbnail(pair::Pair{String,UUIDs.UUID}; kwargs...) = thumbnail(pair.second; kwargs...) | ||
thumbnail(dict::Dict{String,UUIDs.UUID}; kwargs...) = thumbnail.(collect(dict); kwargs...) | ||
vector(pair::Pair{String,UUIDs.UUID}; kwargs...) = vector(pair.second; kwargs...) | ||
vector(dict::Dict{String,UUIDs.UUID}; kwargs...) = vector.(collect(dict); kwargs...) | ||
twitterimage(pair::Pair{String,UUIDs.UUID}; kwargs...) = twitterimage(pair.second; kwargs...) | ||
twitterimage(dict::Dict{String,UUIDs.UUID}; kwargs...) = twitterimage.(collect(dict); kwargs...) | ||
source(pair::Pair{String,UUIDs.UUID}; kwargs...) = source(pair.second; kwargs...) | ||
source(dict::Dict{String,UUIDs.UUID}; kwargs...) = source.(collect(dict); kwargs...) | ||
|
||
function images_data(uuid::UUIDs.UUID) | ||
query = [ | ||
"build" => Phylopic.buildnumber, | ||
] | ||
req = HTTP.get(Phylopic.api * "images/$(string(uuid))", query=query) | ||
if isequal(200)(req.status) | ||
response = JSON.parse(String(req.body)) | ||
return response | ||
end | ||
return nothing | ||
end | ||
|
||
function images_links(uuid::UUIDs.UUID) | ||
return images_data(uuid)["_links"] | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
""" | ||
_get_uuids_at_page(query, page) | ||
This function is an internal helped function to return an array of pairs, wherein each pair maps a name to a UUID, for a given query and page. These outpurs are collected in a dictionary by `Phylopic.names`. | ||
""" | ||
function _get_uuids_at_page(query, page) | ||
page_query = push!(query, "page" => page - 1) | ||
req = HTTP.get(Phylopic.api * "images", query=page_query) | ||
if isequal(200)(req.status) | ||
response = JSON.parse(String(req.body)) | ||
return [item["title"] => UUIDs.UUID(replace(item["href"], "/images/" => "", "?build=$(Phylopic.buildnumber)" => "")) for item in response["_links"]["items"]] | ||
end | ||
return nothing | ||
end | ||
|
||
""" | ||
imagesof(name::AbstractString; items=1, attribution=false, sharealike=false, nocommercial=false) | ||
Returns a mapping between names and UUIDs of images for a given text (see also `Phylopic.autocomplete` to find relevant names). By default, the search will return images that come without BY, SA, and NC clauses (*i.e.* public domain dedication), but this can be changed using the keyword arguments. | ||
`items` | ||
: Default to 1 | ||
: Specifies the number of items to return. When a single item is returned, it is return as a pair mapping the name to the UUID; when there are more than 1, they are returned as a dictionary | ||
`attribution` | ||
: Default to `false` | ||
: Specifies whether the images returned require attribution to the creator | ||
`sharealike` | ||
: Default to `false` | ||
: Specifies whether the images returned require sharing of derived products using a license with a SA clause | ||
`nocommercial` | ||
: Default to `false` | ||
: Specifies whether the images returned are prevented from being used in commercial projects | ||
""" | ||
function imagesof(name::AbstractString; items=1, attribution=false, sharealike=false, nocommercial=false) | ||
name = lowercase(replace(name, r"\s+" => " ")) | ||
query = [ | ||
"filter_name" => name, | ||
"filter_license_by" => attribution, | ||
"filter_license_nc" => nocommercial, | ||
"filter_license_sa" => sharealike, | ||
"build" => Phylopic.buildnumber | ||
] | ||
req = HTTP.get(Phylopic.api * "images", query=query) | ||
if isequal(200)(req.status) | ||
response = JSON.parse(String(req.body)) | ||
if iszero(response["totalItems"]) | ||
return nothing | ||
end | ||
if response["totalItems"] < items | ||
@warn "Only $(response["totalItems"]) are available, you requested $(items)" | ||
end | ||
n_pages = ceil(items / response["itemsPerPage"]) | ||
uuids = vcat([_get_uuids_at_page(query, page) for page in n_pages]...) | ||
if isone(length(uuids)) | ||
return only(uuids) | ||
else | ||
toreturn = min(items, response["totalItems"]) | ||
if isone(toreturn) | ||
return first(uuids) | ||
else | ||
return Dict(uuids[1:toreturn]) | ||
end | ||
end | ||
end | ||
return nothing | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import HTTP | ||
import JSON | ||
|
||
""" | ||
Phylopic.ping() | ||
This function will perform a simple ping of the API, and return `nothing` if it is responding, and throw and `ErrorException` (containing the string `"not responding"`) if the API does not returns a `204 No Content` success status. | ||
""" | ||
function ping() | ||
req = HTTP.get(Phylopic.api * "ping") | ||
if isequal(204)(req.status) | ||
return nothing | ||
else | ||
throw(ErrorException("The API at $(Phylopic.api) is not responding")) | ||
end | ||
return nothing | ||
end | ||
|
||
""" | ||
Phylopic.build() | ||
Returns the current build to perform the queries | ||
""" | ||
function build() | ||
req = HTTP.get(Phylopic.api) | ||
if isequal(200)(req.status) | ||
infostring = JSON.parse(String(req.body)) | ||
return infostring["build"] | ||
else | ||
throw(ErrorException("The API at $(Phylopic.api) is not responding")) | ||
end | ||
return nothing | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[deps] | ||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module TestPhylopicAutocomplete | ||
|
||
using Test | ||
using Phylopic | ||
|
||
@test ~isempty(Phylopic.autocomplete("chiro")) | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module TestPhylopicNames | ||
|
||
using Phylopic | ||
using Test | ||
|
||
@test typeof(Phylopic.imagesof("chiroptera")) == Pair{String,Base.UUID} | ||
@test typeof(Phylopic.imagesof("chiroptera"; items=2)) == Dict{String,Base.UUID} | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module TestPhylopicPing | ||
|
||
using Phylopic | ||
using Test | ||
|
||
@test isnothing(Phylopic.ping()) | ||
|
||
end |
Oops, something went wrong.
12d567c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JuliaRegistrator register subdir="Phylopic"
12d567c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Registration pull request created: JuliaRegistries/General/82334
After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.
This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via: