diff --git a/.github/workflows/package-publish.yml b/.github/workflows/package-publish.yml
index d6f7bc1..496ba7c 100644
--- a/.github/workflows/package-publish.yml
+++ b/.github/workflows/package-publish.yml
@@ -32,3 +32,53 @@ jobs:
- name: 🚀 Push nuget.org
run: dotnet nuget push SpeciesDatabaseApi*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN} --skip-duplicate
+
+ publish:
+ name: Publish for ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ include:
+ - os: ubuntu-latest
+ artifact_name: SpeciesDatabaseCmd_win-x64_$tag.zip
+ asset_name: SpeciesDatabaseCmd_linux-x64_$tag
+ os_identifier: linux-x64
+
+ - os: windows-latest
+ artifact_name: SpeciesDatabaseCmd_win-x64_$tag.zip
+ asset_name: SpeciesDatabaseCmd_win-x64_$tag
+ os_identifier: win-x64
+
+ - os: macos-latest
+ artifact_name: SpeciesDatabaseCmd_macos-x64_$tag.zip
+ asset_name: SpeciesDatabaseCmd_macos-x64_$tag
+ os_identifier: macos-x64
+
+ steps:
+ - name: 🛒 Checkout
+ uses: actions/checkout@v3
+ - name: Verify commit exists in origin/master
+ run: |
+ git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/*
+ git branch --remote --contains | grep origin/master
+
+ - name: 🟣 Setup .NET 7.0
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 7.0.x
+
+ - name: 🔧 Build
+ run: |
+ dotnet publish SpeciesDatabaseCmd -o ${{ matrix.asset_name }} -c Release -r ${{ matrix.os_identifier }} -p:PublishReadyToRun=true --self-contained
+
+ - name: 📦 Zip
+ run: |
+ zip -rq ${{ matrix.asset_name }} .
+
+ - name: 🚀 Upload binaries to release
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ file: target/release/${{ matrix.artifact_name }}
+ asset_name: ${{ matrix.asset_name }}
+ tag: ${{ github.ref }}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 11b5d74..6cacbe9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 12/08/2023 - v1.0.1
+
+- Add Marine Regions (`MrClient`)
+- Add `bool ThrowExceptionIfRequestStatusCodeFails` properties to clients:
+ - Gets or sets if it should throw an exception when the request code is other than success.
+ - If false it will return a null object
+- Fix the regex for `WebsiteUrl` to better remove the api sub-domain
+
## 11/08/2023 - v1.0.0
- First release
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index 1e196a5..6e59c6f 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,17 +4,17 @@
Tiago Conceição; sn4k3
PTRTECH
Copyright 2023-$([System.DateTime]::Now.ToString(`yyyy`)) © PTRTECH
- 1.0.0
+ 1.0.1
Queries and fetch data from species, taxon and conservation database(s) to retrieve information using the provider API.
MIT
true
https://github.com/sn4k3/SpeciesDatabaseApi
https://github.com/sn4k3/SpeciesDatabaseApi
- https://github.com/sn4k3/SpeciesDatabaseApi/blob/master/CHANGELOG.md
+ https://github.com/sn4k3/SpeciesDatabaseApi/releases
README.md
icon.png
- species, taxomy, taxomony, biota, database, api, rest, world register of marine species, wrms, worms, iucn, conservation, nature
+ species, taxomy, taxomony, biota, database, api, rest, world, register, marine, wrms, worms, iucn, mr, conservation, nature, regions
Git
enable
diff --git a/README.md b/README.md
index 52da8c9..b6eb142 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
[![License](https://img.shields.io/github/license/sn4k3/SpeciesDatabaseApi?style=for-the-badge)](https://github.com/sn4k3/SpeciesDatabaseApi/blob/master/LICENSE.txt)
[![GitHub repo size](https://img.shields.io/github/repo-size/sn4k3/SpeciesDatabaseApi?style=for-the-badge)](#)
[![Code size](https://img.shields.io/github/languages/code-size/sn4k3/SpeciesDatabaseApi?style=for-the-badge)](#)
-[![Total code](https://img.shields.io/tokei/lines/github/sn4k3/SpeciesDatabaseApi?style=for-the-badge)](#)
[![Nuget](https://img.shields.io/nuget/v/SpeciesDatabaseApi?style=for-the-badge)](https://www.nuget.org/packages/SpeciesDatabaseApi)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/sn4k3?color=red&style=for-the-badge)](https://github.com/sponsors/sn4k3)
+
# Species Database Api
@@ -14,7 +14,8 @@ Queries and fetch data from species, taxon and conservation database(s) to retri
| Acronym | Name | Class | Terms of use |
| --------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| [WoRMS](https://www.marinespecies.org) | World Register of Marine Species | [WormsClient](https://github.com/sn4k3/SpeciesDatabaseApi/blob/master/SpeciesDatabaseApi/Worms/WormsClient.cs) | [Terms of use](https://www.marinespecies.org/about.php#terms) |
-| [IUCN](https://www.iucn.org) | International Union for Conservation of Nature | [IucnClient](https://github.com/sn4k3/SpeciesDatabaseApi/blob/master/SpeciesDatabaseApi/Iucn/IucnClient.cs) | [Terms of use](http://apiv3.iucnredlist.org/about) |
+| [IUCN](https://www.iucn.org) | International Union for Conservation of Nature | [IucnClient](https://github.com/sn4k3/SpeciesDatabaseApi/blob/master/SpeciesDatabaseApi/Iucn/IucnClient.cs) | [Terms of use](http://apiv3.iucnredlist.org/about) |
+| [MR](https://www.iucn.org) | Marine Regions | [MrClient](https://github.com/sn4k3/SpeciesDatabaseApi/blob/master/SpeciesDatabaseApi/Mr/MrClient.cs) | [Terms of use](https://marineregions.org/disclaimer.php) |
## Terms of use
@@ -76,7 +77,7 @@ Modified: 15/01/2008 17:27:08
## Example (IUCN)
```C#
- private static readonly IucnClient Client = new IucnClient();
+ private static readonly IucnClient Client = new IucnClient("your-api-key");
private async void Main()
{
@@ -113,8 +114,9 @@ Run the "SpeciesDatabaseCmd.exe" and follow the in-terminal instructions to call
# -?, -h, --help Show help and usage information
#
# Commands:
-# WORMS Query - World Register of Marine Species
-# IUCN Query - International Union for Conservation of Nature
+# WORMS Query - World Register of Marine Species (https://marinespecies.org)
+# IUCN Query - International Union for Conservation of Nature (http://iucnredlist.org)
+# MR Query - Marine Regions (https://marineregions.org)
SpeciesDatabaseCmd.exe IUCN SpecieCommonNames "Carcharodon carcharias"
diff --git a/SpeciesDatabaseApi/BaseClient.cs b/SpeciesDatabaseApi/BaseClient.cs
index a48625b..a9b0777 100644
--- a/SpeciesDatabaseApi/BaseClient.cs
+++ b/SpeciesDatabaseApi/BaseClient.cs
@@ -2,7 +2,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
-using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
@@ -23,12 +22,12 @@ namespace SpeciesDatabaseApi;
public abstract class BaseClient : BindableBase, IDisposable
{
- #region Constants
+ #region Constants
- private static readonly Lazy HttpClientShared = new(() =>
- {
- var httpClient = new HttpClient();
- httpClient.DefaultRequestHeaders.Add("User-Agent", AboutLibrary.SoftwareWithVersion);
+ private static readonly Lazy HttpClientShared = new(() =>
+ {
+ var httpClient = new HttpClient();
+ httpClient.DefaultRequestHeaders.Add("User-Agent", AboutLibrary.SoftwareWithVersion);
return httpClient;
});
@@ -110,7 +109,7 @@ public virtual string WebsiteUrl
get
{
var domain = ApiAddress.GetLeftPart(UriPartial.Authority);
- return Regex.Replace(domain, @"\/\/(.*api|www)[.]", "//");
+ return Regex.Replace(domain, @"\/\/(.*api([a-zA-Z0-9_-]+)?|www)[.]", "//");
}
}
@@ -159,6 +158,12 @@ public bool AutoWaitForRequestLimit
///
public ProductInfoHeaderValue? ProductInfoHeader { get; set; }
+ ///
+ /// Gets or sets if it should throw an exception when the request code is other than success.
+ /// If false it will return a null object
+ ///
+ public bool ThrowExceptionIfRequestStatusCodeFails { get; set; } = true;
+
#endregion
#region Constructors
@@ -220,7 +225,7 @@ public static string UrlEncode(string query)
///
///
///
- public string GetRawRequestUrl(string path) => $"{ApiAddress}/{path.Trim(' ', '/')}";
+ public string GetRawRequestUrl(string path) => $"{ApiAddress}/{path.TrimStart(' ', '/').TrimEnd()}";
public string GetRequestUrl(string path)
{
@@ -239,7 +244,7 @@ public string GetRequestUrl(string path)
/// Partial path without Api address
/// Parameters to send with the request
/// Absolute path for the request
- public string GetRequestUrl(string path, IEnumerable> parameters)
+ public string GetRequestUrl(string path, IEnumerable> parameters)
{
return $"{GetRawRequestUrl(path)}{GetUrlParametersString(parameters)}";
}
@@ -250,7 +255,7 @@ public string GetRequestUrl(string path, IEnumerablePartial path without Api address
/// Parameters to send with the request
/// Absolute path for the request
- public string GetRequestUrl(string path, IReadOnlyDictionary parameters) => GetRequestUrl(path, parameters.AsEnumerable());
+ public string GetRequestUrl(string path, IReadOnlyDictionary parameters) => GetRequestUrl(path, parameters.AsEnumerable());
///
/// Gets the absolute url for an request to the Api
@@ -258,7 +263,7 @@ public string GetRequestUrl(string path, IEnumerablePartial path without Api address
/// Parameter to send with the request
/// Absolute path for the request
- public string GetRequestUrl(string path, KeyValuePair parameter) => GetRequestUrl(path, new[] { parameter });
+ public string GetRequestUrl(string path, KeyValuePair parameter) => GetRequestUrl(path, new[] { parameter });
///
/// Gets the absolute url for an request to the Api
@@ -276,14 +281,42 @@ public string GetRequestUrl(string path, object classObj)
///
///
/// The formatted string
- public string GetUrlParametersString(IEnumerable> parameters)
+ public string GetUrlParametersString(IEnumerable> parameters)
{
var builder = new StringBuilder();
// Sort the parameters alphabetically to avoid http redirection.
foreach (var item in parameters.OrderBy(x => x.Key))
{
- builder.Append($"{(builder.Length == 0 ? "?" : " & ")}{UrlEncode(item.Key.ToLowerInvariant())}={UrlEncode(item.Value.ToString()?.ToLowerInvariant() ?? string.Empty)}");
+ string? value;
+ switch (item.Value)
+ {
+ case null:
+ continue;
+ case IList list:
+ if (list.Count == 0) continue;
+
+ var items = new List(list.Count);
+ foreach (var obj in list)
+ {
+ if (obj is null) continue;
+ value = obj.ToString()?.Trim().ToLowerInvariant();
+ if (string.IsNullOrWhiteSpace(value)) continue;
+ items.Add(value);
+ }
+ value = string.Join(',', items);
+ break;
+ case string str:
+ value = str.Trim().ToLowerInvariant();
+ if (string.IsNullOrWhiteSpace(value)) continue;
+ break;
+ default:
+ value = item.Value.ToString()?.Trim().ToLowerInvariant();
+ if (string.IsNullOrWhiteSpace(value)) continue;
+ break;
+ }
+
+ builder.Append($"{(builder.Length == 0 ? "?" : " & ")}{UrlEncode(item.Key.ToLowerInvariant())}={UrlEncode(value)}");
}
if (ApiToken is { CanUse: true, Placement: ApiTokenPlacement.Get })
@@ -299,14 +332,14 @@ public string GetUrlParametersString(IEnumerable> p
///
///
/// The formatted string
- public string GetUrlParametersString(IReadOnlyDictionary parameters) => GetUrlParametersString(parameters.AsEnumerable());
+ public string GetUrlParametersString(IReadOnlyDictionary parameters) => GetUrlParametersString(parameters.AsEnumerable());
///
/// Gets the parameters string from a dictionary of key and values parameters
///
///
/// The formatted string
- public string GetUrlParametersString(KeyValuePair keyValue) => GetUrlParametersString(new[]{ keyValue });
+ public string GetUrlParametersString(KeyValuePair keyValue) => GetUrlParametersString(new[]{ keyValue });
///
/// Gets the parameters string from a class (Reflection)
@@ -315,7 +348,7 @@ public string GetUrlParametersString(IEnumerable> p
/// The formatted string
public string GetUrlParametersString(object? obj)
{
- var dict = new Dictionary();
+ var dict = new Dictionary();
if (obj is not null)
{
@@ -327,23 +360,6 @@ public string GetUrlParametersString(object? obj)
if (method is null) continue;
var value = propertyInfo.GetValue(obj);
- switch (value)
- {
- case null:
- continue;
- case IList list:
- if (list.Count == 0) continue;
-
- var items = new List(list.Count);
- foreach (var item in list)
- {
- if (item is null) continue;
- items.Add(item.ToString()!);
- }
- value = string.Join(',', items);
- break;
- }
-
var attr = propertyInfo.GetCustomAttribute();
dict.Add(attr?.Name ?? method.Name, value);
}
@@ -376,7 +392,7 @@ protected HttpRequestMessage PrepareJsonHttpRequestMessage(string requestUrl, Ht
return request;
}
- public Task PostJsonAsync(string requestUrl, object postData, IEnumerable> urlParameters, CancellationToken cancellationToken = default)
+ public Task PostJsonAsync(string requestUrl, object postData, IEnumerable> urlParameters, CancellationToken cancellationToken = default)
{
using var request = PrepareJsonHttpRequestMessage(GetRequestUrl(requestUrl, urlParameters), HttpMethod.Post);
@@ -386,7 +402,7 @@ protected HttpRequestMessage PrepareJsonHttpRequestMessage(string requestUrl, Ht
return SendRequestAsync(request, cancellationToken);
}
- public Task PostJsonAsync(string requestUrl, object postData, IReadOnlyDictionary urlParameters, CancellationToken cancellationToken = default)
+ public Task PostJsonAsync(string requestUrl, object postData, IReadOnlyDictionary urlParameters, CancellationToken cancellationToken = default)
{
using var request = PrepareJsonHttpRequestMessage(GetRequestUrl(requestUrl, urlParameters), HttpMethod.Post);
@@ -396,7 +412,7 @@ protected HttpRequestMessage PrepareJsonHttpRequestMessage(string requestUrl, Ht
return SendRequestAsync(request, cancellationToken);
}
- public Task PostJsonAsync(string requestUrl, object postData, KeyValuePair urlParameter, CancellationToken cancellationToken = default)
+ public Task PostJsonAsync(string requestUrl, object postData, KeyValuePair urlParameter, CancellationToken cancellationToken = default)
{
using var request = PrepareJsonHttpRequestMessage(GetRequestUrl(requestUrl, urlParameter), HttpMethod.Post);
@@ -426,19 +442,19 @@ protected HttpRequestMessage PrepareJsonHttpRequestMessage(string requestUrl, Ht
return SendRequestAsync(request, cancellationToken);
}
- public Task GetJsonAsync(string requestUrl, IEnumerable> urlParameters, CancellationToken cancellationToken = default)
+ public Task GetJsonAsync(string requestUrl, IEnumerable> urlParameters, CancellationToken cancellationToken = default)
{
using var request = PrepareJsonHttpRequestMessage(GetRequestUrl(requestUrl, urlParameters), HttpMethod.Get);
return SendRequestAsync(request, cancellationToken);
}
- public Task GetJsonAsync(string requestUrl, IReadOnlyDictionary urlParameters, CancellationToken cancellationToken = default)
+ public Task GetJsonAsync(string requestUrl, IReadOnlyDictionary urlParameters, CancellationToken cancellationToken = default)
{
using var request = PrepareJsonHttpRequestMessage(GetRequestUrl(requestUrl, urlParameters), HttpMethod.Get);
return SendRequestAsync(request, cancellationToken);
}
- public Task GetJsonAsync(string requestUrl, KeyValuePair urlParameter, CancellationToken cancellationToken = default)
+ public Task GetJsonAsync(string requestUrl, KeyValuePair urlParameter, CancellationToken cancellationToken = default)
{
using var request = PrepareJsonHttpRequestMessage(GetRequestUrl(requestUrl, urlParameter), HttpMethod.Get);
return SendRequestAsync(request, cancellationToken);
@@ -456,19 +472,19 @@ protected HttpRequestMessage PrepareJsonHttpRequestMessage(string requestUrl, Ht
return SendRequestAsync(request, cancellationToken);
}
- public Task DeleteJsonAsync(string requestUrl, IEnumerable> urlParameters, CancellationToken cancellationToken = default)
+ public Task DeleteJsonAsync(string requestUrl, IEnumerable> urlParameters, CancellationToken cancellationToken = default)
{
using var request = PrepareJsonHttpRequestMessage(GetRequestUrl(requestUrl, urlParameters), HttpMethod.Delete);
return SendRequestAsync(request, cancellationToken);
}
- public Task DeleteJsonAsync(string requestUrl, IReadOnlyDictionary urlParameters, CancellationToken cancellationToken = default)
+ public Task DeleteJsonAsync(string requestUrl, IReadOnlyDictionary urlParameters, CancellationToken cancellationToken = default)
{
using var request = PrepareJsonHttpRequestMessage(GetRequestUrl(requestUrl, urlParameters), HttpMethod.Delete);
return SendRequestAsync(request, cancellationToken);
}
- public Task DeleteJsonAsync(string requestUrl, KeyValuePair urlParameter, CancellationToken cancellationToken = default)
+ public Task DeleteJsonAsync(string requestUrl, KeyValuePair urlParameter, CancellationToken cancellationToken = default)
{
using var request = PrepareJsonHttpRequestMessage(GetRequestUrl(requestUrl, urlParameter), HttpMethod.Delete);
return SendRequestAsync(request, cancellationToken);
@@ -520,26 +536,28 @@ protected virtual Task OnBeforeSendRequestAsync(HttpRequestMessage request, Canc
await Task.Delay(waitTime, cancellationToken).ConfigureAwait(false);
} while (RequestsHitLimit);
}
-
- using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
- Interlocked.Increment(ref _totalRequests);
- response.EnsureSuccessStatusCode();
+ using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
- switch (response.StatusCode)
- {
- case HttpStatusCode.NoContent:
- return default;
- }
+ Interlocked.Increment(ref _totalRequests);
- if (_autoWaitForRequestLimit && _maximumRequestsPerSecond > 0)
- {
- Interlocked.Increment(ref _requestsInCurrentSecond);
- _resetRequestsTimer.Start();
- }
-
- return await response.Content.ReadFromJsonAsync(DefaultJsonSerializerOptions, cancellationToken).ConfigureAwait(false);
- }
+ if (ThrowExceptionIfRequestStatusCodeFails) response.EnsureSuccessStatusCode();
+ else if (!response.IsSuccessStatusCode) return default;
+
+ switch (response.StatusCode)
+ {
+ case HttpStatusCode.NoContent:
+ return default;
+ }
+
+ if (_autoWaitForRequestLimit && _maximumRequestsPerSecond > 0)
+ {
+ Interlocked.Increment(ref _requestsInCurrentSecond);
+ _resetRequestsTimer.Start();
+ }
+
+ return await response.Content.ReadFromJsonAsync(DefaultJsonSerializerOptions, cancellationToken).ConfigureAwait(false);
+ }
#endregion
}
\ No newline at end of file
diff --git a/SpeciesDatabaseApi/Mr/Enumerations.cs b/SpeciesDatabaseApi/Mr/Enumerations.cs
new file mode 100644
index 0000000..fb4bdf3
--- /dev/null
+++ b/SpeciesDatabaseApi/Mr/Enumerations.cs
@@ -0,0 +1,19 @@
+namespace SpeciesDatabaseApi.Mr;
+
+public enum MrRelationDirection
+{
+ Upper,
+ Lower,
+ Both
+}
+
+public enum MrRelationType
+{
+ PartOf,
+ PartlyPartOf,
+ AdjacentTo,
+ SimilarTo,
+ AdministrativePartOf,
+ InfluencedBy,
+ All
+}
diff --git a/SpeciesDatabaseApi/Mr/GazetteerRecord.cs b/SpeciesDatabaseApi/Mr/GazetteerRecord.cs
new file mode 100644
index 0000000..650a8e8
--- /dev/null
+++ b/SpeciesDatabaseApi/Mr/GazetteerRecord.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Text.Json.Serialization;
+
+namespace SpeciesDatabaseApi.Mr;
+
+public class GazetteerRecord : IEquatable
+{
+ [JsonPropertyName("MRGID")]
+ public int MrgId { get; set; }
+
+ [JsonPropertyName("gazetteerSource")]
+ public string GazetteerSource { get; set; } = string.Empty;
+
+ [JsonPropertyName("placeType")]
+ public string PlaceType { get; set; } = string.Empty;
+
+ [JsonPropertyName("latitude")]
+ public decimal? Latitude { get; set; } = null;
+
+ [JsonPropertyName("longitude")]
+ public decimal? Longitude { get; set; } = null;
+
+ [JsonPropertyName("minLatitude")]
+ public decimal? MinLatitude { get; set; } = null;
+
+ [JsonPropertyName("minLongitude")]
+ public decimal? MinLongitude { get; set; } = null;
+
+ [JsonPropertyName("maxLatitude")]
+ public decimal? MaxLatitude { get; set; } = null;
+
+ [JsonPropertyName("maxLongitude")]
+ public decimal? MaxLongitude { get; set; } = null;
+
+ [JsonPropertyName("precision")]
+ public decimal? Precision { get; set; } = null;
+
+ [JsonPropertyName("preferredGazetteerName")]
+ public string PreferredGazetteerName { get; set; } = string.Empty;
+
+ [JsonPropertyName("preferredGazetteerNameLang")]
+ public string PreferredGazetteerNameLang { get; set; } = string.Empty;
+
+ [JsonPropertyName("status")]
+ public string Status { get; set; } = string.Empty;
+
+ [JsonPropertyName("accepted")]
+ public int Accepted { get; set; }
+
+
+ ///
+ public override string ToString()
+ {
+ return $"{nameof(MrgId)}: {MrgId}, {nameof(GazetteerSource)}: {GazetteerSource}, {nameof(PlaceType)}: {PlaceType}, {nameof(Latitude)}: {Latitude}, {nameof(Longitude)}: {Longitude}, {nameof(MinLatitude)}: {MinLatitude}, {nameof(MinLongitude)}: {MinLongitude}, {nameof(MaxLatitude)}: {MaxLatitude}, {nameof(MaxLongitude)}: {MaxLongitude}, {nameof(Precision)}: {Precision}, {nameof(PreferredGazetteerName)}: {PreferredGazetteerName}, {nameof(PreferredGazetteerNameLang)}: {PreferredGazetteerNameLang}, {nameof(Status)}: {Status}, {nameof(Accepted)}: {Accepted}";
+ }
+
+ ///
+ public bool Equals(GazetteerRecord? other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return MrgId == other.MrgId && GazetteerSource == other.GazetteerSource && PlaceType == other.PlaceType && Latitude == other.Latitude && Longitude == other.Longitude && MinLatitude == other.MinLatitude && MinLongitude == other.MinLongitude && MaxLatitude == other.MaxLatitude && MaxLongitude == other.MaxLongitude && Precision == other.Precision && PreferredGazetteerName == other.PreferredGazetteerName && PreferredGazetteerNameLang == other.PreferredGazetteerNameLang && Status == other.Status && Accepted == other.Accepted;
+ }
+
+ ///
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((GazetteerRecord)obj);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(MrgId);
+ hashCode.Add(GazetteerSource);
+ hashCode.Add(PlaceType);
+ hashCode.Add(Latitude);
+ hashCode.Add(Longitude);
+ hashCode.Add(MinLatitude);
+ hashCode.Add(MinLongitude);
+ hashCode.Add(MaxLatitude);
+ hashCode.Add(MaxLongitude);
+ hashCode.Add(Precision);
+ hashCode.Add(PreferredGazetteerName);
+ hashCode.Add(PreferredGazetteerNameLang);
+ hashCode.Add(Status);
+ hashCode.Add(Accepted);
+ return hashCode.ToHashCode();
+ }
+
+ public static bool operator ==(GazetteerRecord? left, GazetteerRecord? right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(GazetteerRecord? left, GazetteerRecord? right)
+ {
+ return !Equals(left, right);
+ }
+}
\ No newline at end of file
diff --git a/SpeciesDatabaseApi/Mr/GazetteerSource.cs b/SpeciesDatabaseApi/Mr/GazetteerSource.cs
new file mode 100644
index 0000000..b5e5ed2
--- /dev/null
+++ b/SpeciesDatabaseApi/Mr/GazetteerSource.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Text.Json.Serialization;
+using System.Xml.Serialization;
+
+namespace SpeciesDatabaseApi.Mr;
+
+public class GazetteerSource : IEquatable
+{
+ [JsonPropertyName("sourceId")]
+ [XmlElement("sourceId")]
+ public int SourceId { get; set; }
+
+ [JsonPropertyName("source")]
+ [XmlElement("source")]
+ public string Source { get; set; } = string.Empty;
+
+ [JsonPropertyName("sourceURL")]
+ [XmlElement("sourceURL")]
+ public Uri? SourceUrl { get; set; }
+
+ ///
+ public override string ToString()
+ {
+ return $"{nameof(SourceId)}: {SourceId}, {nameof(Source)}: {Source}, {nameof(SourceUrl)}: {SourceUrl}";
+ }
+
+ public bool Equals(GazetteerSource? other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return SourceId == other.SourceId && Source == other.Source && Equals(SourceUrl, other.SourceUrl);
+ }
+
+ ///
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((GazetteerSource)obj);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(SourceId, Source, SourceUrl);
+ }
+
+ public static bool operator ==(GazetteerSource? left, GazetteerSource? right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(GazetteerSource? left, GazetteerSource? right)
+ {
+ return !Equals(left, right);
+ }
+}
\ No newline at end of file
diff --git a/SpeciesDatabaseApi/Mr/GazetteerSourceName.cs b/SpeciesDatabaseApi/Mr/GazetteerSourceName.cs
new file mode 100644
index 0000000..0c364f1
--- /dev/null
+++ b/SpeciesDatabaseApi/Mr/GazetteerSourceName.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Text.Json.Serialization;
+using System.Xml.Serialization;
+
+namespace SpeciesDatabaseApi.Mr;
+
+public class GazetteerSourceName : IEquatable
+{
+ [JsonPropertyName("source")]
+ [XmlElement("source")]
+ public string Source { get; set; } = string.Empty;
+
+ [JsonPropertyName("url")]
+ [XmlElement("url")]
+ public Uri? Url { get; set; }
+
+ ///
+ public override string ToString()
+ {
+ return $"{nameof(Source)}: {Source}, {nameof(Url)}: {Url}";
+ }
+
+ public bool Equals(GazetteerSourceName? other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Source == other.Source && Equals(Url, other.Url);
+ }
+
+ ///
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((GazetteerSourceName)obj);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(Source, Url);
+ }
+
+ public static bool operator ==(GazetteerSourceName? left, GazetteerSourceName? right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(GazetteerSourceName? left, GazetteerSourceName? right)
+ {
+ return !Equals(left, right);
+ }
+}
\ No newline at end of file
diff --git a/SpeciesDatabaseApi/Mr/GazetteerType.cs b/SpeciesDatabaseApi/Mr/GazetteerType.cs
new file mode 100644
index 0000000..6a2e29b
--- /dev/null
+++ b/SpeciesDatabaseApi/Mr/GazetteerType.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Text.Json.Serialization;
+
+namespace SpeciesDatabaseApi.Mr;
+
+public class GazetteerType : IEquatable
+{
+ [JsonPropertyName("typeID")]
+ public int TypeId { get; set; }
+
+ [JsonPropertyName("type")]
+ public string Type { get; set; } = string.Empty;
+
+ [JsonPropertyName("description")]
+ public string? Description { get; set; }
+
+ ///
+ public override string ToString()
+ {
+ return $"{nameof(TypeId)}: {TypeId}, {nameof(Type)}: {Type}, {nameof(Description)}: {Description}";
+ }
+
+ public bool Equals(GazetteerType? other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return TypeId == other.TypeId && Type == other.Type && Description == other.Description;
+ }
+
+ ///
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((GazetteerType)obj);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(TypeId, Type, Description);
+ }
+
+ public static bool operator ==(GazetteerType? left, GazetteerType? right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(GazetteerType? left, GazetteerType? right)
+ {
+ return !Equals(left, right);
+ }
+}
\ No newline at end of file
diff --git a/SpeciesDatabaseApi/Mr/GazetteerWms.cs b/SpeciesDatabaseApi/Mr/GazetteerWms.cs
new file mode 100644
index 0000000..4a3a8b9
--- /dev/null
+++ b/SpeciesDatabaseApi/Mr/GazetteerWms.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Text.Json.Serialization;
+using System.Xml.Serialization;
+
+namespace SpeciesDatabaseApi.Mr;
+
+public class GazetteerWms : IEquatable
+{
+ [JsonPropertyName("value")]
+ [XmlElement("value")]
+ public string Value { get; set; } = string.Empty;
+
+ [JsonPropertyName("MRGID")]
+ [XmlElement("MRGID")]
+ public int MrgId { get; set; }
+
+ [JsonPropertyName("url")]
+ [XmlElement("url")]
+ public Uri? Url { get; set; }
+
+ [JsonPropertyName("namespace")]
+ [XmlElement("namespace")]
+ public string Namespace { get; set; } = string.Empty;
+
+ [JsonPropertyName("featureType")]
+ [XmlElement("featureType")]
+ public string FeatureType { get; set; } = string.Empty;
+
+ [JsonPropertyName("featureName")]
+ [XmlElement("featureName")]
+ public string FeatureName { get; set; } = string.Empty;
+
+ ///
+ public override string ToString()
+ {
+ return $"{nameof(Value)}: {Value}, {nameof(MrgId)}: {MrgId}, {nameof(Url)}: {Url}, {nameof(Namespace)}: {Namespace}, {nameof(FeatureType)}: {FeatureType}, {nameof(FeatureName)}: {FeatureName}";
+ }
+
+ public bool Equals(GazetteerWms? other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Value == other.Value && MrgId == other.MrgId && Equals(Url, other.Url) && Namespace == other.Namespace && FeatureType == other.FeatureType && FeatureName == other.FeatureName;
+ }
+
+ ///
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((GazetteerWms)obj);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(Value, MrgId, Url, Namespace, FeatureType, FeatureName);
+ }
+
+ public static bool operator ==(GazetteerWms? left, GazetteerWms? right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(GazetteerWms? left, GazetteerWms? right)
+ {
+ return !Equals(left, right);
+ }
+}
\ No newline at end of file
diff --git a/SpeciesDatabaseApi/Mr/MrClient.cs b/SpeciesDatabaseApi/Mr/MrClient.cs
new file mode 100644
index 0000000..179351c
--- /dev/null
+++ b/SpeciesDatabaseApi/Mr/MrClient.cs
@@ -0,0 +1,275 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Net.Http;
+using System.Runtime.ConstrainedExecution;
+using System.Text;
+using System.Text.Json.Nodes;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SpeciesDatabaseApi.Mr;
+
+///
+/// The client for https://marineregions.org API
+///
+public class MrClient : BaseClient
+{
+ #region Static objects
+ ///
+ /// The client full name/provider
+ ///
+ public const string FullName = "Marine Regions";
+
+ ///
+ /// The Api default address
+ ///
+ public static readonly Uri DefaultApiAddress = new("https://marineregions.org/rest");
+ #endregion
+
+ #region Properties
+ ///
+ public override int Version => 1;
+
+ ///
+ public override string ClientFullName => FullName;
+
+ #endregion
+
+ #region Constructor
+
+ public MrClient(HttpClient? httpClient = null) : base(DefaultApiAddress, httpClient)
+ {
+ }
+
+ #endregion
+
+ #region Methods
+ ///
+ /// Gets one record for the given MRGID
+ ///
+ /// The MRGID to search for
+ ///
+ ///
+ public Task GetGazetteerRecord(int mrgId, CancellationToken token = default)
+ {
+ return GetJsonAsync($"getGazetteerRecordByMRGID.json/{mrgId}/", token);
+ }
+
+ ///
+ /// Gets one record for the given MRGID
+ ///
+ /// The MRGID to search for
+ ///
+ ///
+ public Task GetFullGazetteerRecord(int mrgId, CancellationToken token = default)
+ {
+ return GetJsonAsync($"getGazetteerRecordByMRGID.jsonld/{mrgId}/", token);
+ }
+
+ ///
+ /// Gets all geometries associated with a gazetteer record
+ ///
+ /// The MRGID to search for
+ ///
+ ///
+ public Task GetGazetteerGeometries(int mrgId, CancellationToken token = default)
+ {
+ return GetJsonAsync($"getGazetteerGeometries.jsonld/{mrgId}/", token);
+ }
+
+ ///
+ /// Gets all possible types
+ ///
+ ///
+ ///
+ public Task GetGazetteerTypes(CancellationToken token = default)
+ {
+ return GetJsonAsync("getGazetteerTypes.json/", token);
+ }
+
+ ///
+ /// Gets a list of the first 100 matching records for given name
+ ///
+ /// The name to search for
+ /// Adds a '%'-sign before and after the GazetteerName (SQL LIKE function). Default=true
+ /// Uses Levenshtein query to find nearest matches. Default=false
+ /// One or more placetypeIDs. See to retrieve a list of placetypeIDs. Default=(empty)
+ /// Language (ISO 639-1 code). Default=(empty)
+ /// Start record number, in order to page through next batch of results. Default=0
+ /// Number of records to retrieve. Default=100; max=100
+ ///
+ ///
+ public Task GetGazetteerRecords(string name, bool like = true, bool fuzzy = false, IEnumerable? typeIds = null, string? language = null, int offset = 0, int count = 100, CancellationToken token = default)
+ {
+ var parameters = new Dictionary
+ {
+ {"like", like},
+ {"fuzzy", fuzzy},
+ {"typeID", typeIds},
+ {"language", language},
+ {"offset", offset},
+ {"count", count},
+ };
+ return GetJsonAsync($"getGazetteerRecordsByName.json/{name}/", parameters, token);
+ }
+
+ ///
+ /// Gets a list of the first 100 matching records for all given names
+ ///
+ /// The name to search for
+ /// Adds a '%'-sign before and after the GazetteerName (SQL LIKE function). Default=true
+ /// Uses Levenshtein query to find nearest matches. Default=false
+ ///
+ ///
+ public Task?> GetGazetteerRecords(IEnumerable names, bool like = true, bool fuzzy = false, CancellationToken token = default)
+ {
+ var namesPath = new StringBuilder();
+ foreach (var name in names)
+ {
+ if(string.IsNullOrWhiteSpace(name)) continue;
+ namesPath.Append($"{name}/");
+ }
+
+ if (namesPath.Length == 0) throw new ArgumentException("It must contain at least one name", nameof(names));
+
+ return GetJsonAsync>($"getGazetteerRecordsByNames.json/{like.ToString().ToLowerInvariant()}/{fuzzy.ToString().ToLowerInvariant()}/{namesPath}", token);
+ }
+
+ ///
+ /// Gets the Linked Data Event Stream feed
+ ///
+ /// Feed start range date time
+ /// Feed end range date time
+ ///
+ ///
+ public Task GetFeed(DateTime startDateTime, DateTime endDateTime, CancellationToken token = default)
+ {
+ // page=2023-07-31T15%3A00%3A00Z%2F2023-07-31T16%3A00%3A00Z
+ // page=2023-07-31T15:00:00Z/2023-07-31T16:00:00Z
+ // 2023-07-11T22:05:53.8948950+01:00/2023-08-11T22:05:53.8962167+01:00
+ // 2023-07-11T21:06:43.0750477Z/2023-08-11T21:06:43.0763630Z
+ var parameters = new KeyValuePair("page", $"{startDateTime.ToUniversalTime():O}/{endDateTime.ToUniversalTime():O}");
+ return GetJsonAsync("getFeed.jsonld", parameters, token);
+ }
+
+ ///
+ /// Gets the Linked Data Event Stream feed
+ ///
+ ///
+ ///
+ public Task GetFeed(CancellationToken token = default)
+ {
+ return GetJsonAsync("getFeed.jsonld", token);
+
+ }
+
+ ///
+ /// Gets WMS information for the given MRGID
+ ///
+ /// The MRGID to search for
+ ///
+ ///
+ public Task GetgetGazetteerWmSes(int mrgId, CancellationToken token = default)
+ {
+ return GetJsonAsync($"getGazetteerWMSes.json/{mrgId}/", token);
+ }
+
+ ///
+ /// Gets the first 100 related records for the given MRGID
+ ///
+ /// The MRGID to search for
+ ///
+ ///
+ ///
+ ///
+ public Task GetGazetteerRelations(int mrgId, MrRelationDirection direction = MrRelationDirection.Upper, MrRelationType type = MrRelationType.PartOf, CancellationToken token = default)
+ {
+ var parameters = new Dictionary
+ {
+ {"direction", direction},
+ {"type", type},
+ };
+ return GetJsonAsync($"getGazetteerRelationsByMRGID.json/{mrgId}/", parameters, token);
+ }
+
+ ///
+ /// Gets all sources per batch of 100.
+ ///
+ /// Provide offset to return next batch of 100 sources.
+ ///
+ ///
+ public Task GetGazetteerSources(int offset = 0, CancellationToken token = default)
+ {
+ var parameters = new KeyValuePair("offset", offset);
+ return GetJsonAsync("getGazetteerSources.json/", parameters, token);
+ }
+
+ ///
+ /// Gets the source name corresponding to a source ID
+ ///
+ ///
+ ///
+ ///
+ public Task GetGazetteerSource(int sourceId, CancellationToken token = default)
+ {
+ return GetJsonAsync($"getGazetteerSourceBySourceID.json/{sourceId}/", token);
+ }
+
+ ///
+ /// Gets the first 100 names for the given MRGID
+ ///
+ /// The MRGID to search for
+ ///
+ ///
+ public Task GetGazetteerNames(int mrgId, CancellationToken token = default)
+ {
+ return GetJsonAsync($"getGazetteerNamesByMRGID.json/{mrgId}/", token);
+ }
+
+ ///
+ /// Gets the first 100 records for the given source
+ ///
+ /// The source name to search for
+ ///
+ ///
+ public Task GetGazetteerRecordsBySource(string sourceName, CancellationToken token = default)
+ {
+ return GetJsonAsync($"getGazetteerRecordsBySource.json/{EscapeDataString(sourceName)}/", token);
+ }
+
+ ///
+ /// Gets all records for the given type, per batch of 100
+ ///
+ /// Use to view a complete list of types
+ /// Provide offset to return next batch of 100 records.
+ ///
+ ///
+ public Task GetGazetteerRecordsByType(string type, int offset = 0, CancellationToken token = default)
+ {
+ var parameters = new KeyValuePair("offset", offset);
+ return GetJsonAsync($"getGazetteerRecordsByType.json/{EscapeDataString(type)}/", parameters, token);
+ }
+
+ ///
+ /// Gets all gazetteer records where the geometry intersects with the given latitude and longitude per batch of 100. Results are sorted by area, smallest to largest
+ ///
+ /// A decimal number which ranges from -90 to 90
+ /// A decimal number which ranges from -180 to +180
+ /// One or more placetypeIDs. See to retrieve a list of placetypeIDs. Default=(empty)
+ /// Start record number, in order to page through next batch of results. Default=0
+ ///
+ ///
+ public Task GetGazetteerRecordsByLatLong(decimal latitude, decimal longitude, IEnumerable? typeIds = null, int offset = 0, CancellationToken token = default)
+ {
+ if (latitude is < -90 or > 90) throw new ArgumentOutOfRangeException(nameof(latitude), "Latitude must be between -90 and 90");
+ if (longitude is < -180 or > 180) throw new ArgumentOutOfRangeException(nameof(longitude), "Longitude must be between -90 and 90");
+ var parameters = new Dictionary
+ {
+ {"typeID", typeIds},
+ {"offset", offset},
+ };
+ return GetJsonAsync($"getGazetteerRecordsByLatLong.json/{latitude}/{longitude}/", parameters, token);
+ }
+ #endregion
+}
\ No newline at end of file
diff --git a/SpeciesDatabaseApi/SpeciesDatabaseApi.csproj b/SpeciesDatabaseApi/SpeciesDatabaseApi.csproj
index c632161..2ef1a36 100644
--- a/SpeciesDatabaseApi/SpeciesDatabaseApi.csproj
+++ b/SpeciesDatabaseApi/SpeciesDatabaseApi.csproj
@@ -1,3 +1 @@
-
-
-
+
diff --git a/SpeciesDatabaseApi/Worms/AphiaRecord.cs b/SpeciesDatabaseApi/Worms/AphiaRecord.cs
index e8b5080..c0a987f 100644
--- a/SpeciesDatabaseApi/Worms/AphiaRecord.cs
+++ b/SpeciesDatabaseApi/Worms/AphiaRecord.cs
@@ -62,14 +62,14 @@ public class AphiaRecord : IEquatable
///
[JsonPropertyName("unacceptreason")]
[XmlElement("unacceptreason")]
- public string? UnacceptReason { get; set; }
+ public string? UnAcceptReason { get; set; }
///
/// The AphiaID (for the ) of the currently accepted taxon. NULL if there is no currently accepted taxon.
///
[JsonPropertyName("valid_AphiaID")]
[XmlElement("valid_AphiaID")]
- public int? ValidAphiaID { get; set; }
+ public int? ValidAphiaId { get; set; }
///
/// The of the currently accepted taxon
@@ -146,7 +146,7 @@ public class AphiaRecord : IEquatable
///
[JsonPropertyName("lsid")]
[XmlElement("lsid")]
- public string lsId { get; set; } = string.Empty;
+ public string LsId { get; set; } = string.Empty;
///
/// A flag indicating whether the taxon is a marine organism, i.e. can be found in/above sea water. Possible values: 0/1/NULL
@@ -201,14 +201,14 @@ public class AphiaRecord : IEquatable
///
public override string ToString()
{
- return $"{nameof(AphiaId)}: {AphiaId}, {nameof(Url)}: {Url}, {nameof(ScientificName)}: {ScientificName}, {nameof(Authority)}: {Authority}, {nameof(TaxonRankId)}: {TaxonRankId}, {nameof(Rank)}: {Rank}, {nameof(Status)}: {Status}, {nameof(UnacceptReason)}: {UnacceptReason}, {nameof(ValidAphiaID)}: {ValidAphiaID}, {nameof(ValidName)}: {ValidName}, {nameof(ValidAuthority)}: {ValidAuthority}, {nameof(ParentNameUsageId)}: {ParentNameUsageId}, {nameof(Kingdom)}: {Kingdom}, {nameof(Phylum)}: {Phylum}, {nameof(Class)}: {Class}, {nameof(Order)}: {Order}, {nameof(Family)}: {Family}, {nameof(Genus)}: {Genus}, {nameof(Citation)}: {Citation}, {nameof(lsId)}: {lsId}, {nameof(IsMarine)}: {IsMarine}, {nameof(IsBrackish)}: {IsBrackish}, {nameof(IsFreshwater)}: {IsFreshwater}, {nameof(IsTerrestrial)}: {IsTerrestrial}, {nameof(IsExtinct)}: {IsExtinct}, {nameof(MatchType)}: {MatchType}, {nameof(Modified)}: {Modified}";
+ return $"{nameof(AphiaId)}: {AphiaId}, {nameof(Url)}: {Url}, {nameof(ScientificName)}: {ScientificName}, {nameof(Authority)}: {Authority}, {nameof(TaxonRankId)}: {TaxonRankId}, {nameof(Rank)}: {Rank}, {nameof(Status)}: {Status}, {nameof(UnAcceptReason)}: {UnAcceptReason}, {nameof(ValidAphiaId)}: {ValidAphiaId}, {nameof(ValidName)}: {ValidName}, {nameof(ValidAuthority)}: {ValidAuthority}, {nameof(ParentNameUsageId)}: {ParentNameUsageId}, {nameof(Kingdom)}: {Kingdom}, {nameof(Phylum)}: {Phylum}, {nameof(Class)}: {Class}, {nameof(Order)}: {Order}, {nameof(Family)}: {Family}, {nameof(Genus)}: {Genus}, {nameof(Citation)}: {Citation}, {nameof(LsId)}: {LsId}, {nameof(IsMarine)}: {IsMarine}, {nameof(IsBrackish)}: {IsBrackish}, {nameof(IsFreshwater)}: {IsFreshwater}, {nameof(IsTerrestrial)}: {IsTerrestrial}, {nameof(IsExtinct)}: {IsExtinct}, {nameof(MatchType)}: {MatchType}, {nameof(Modified)}: {Modified}";
}
public bool Equals(AphiaRecord? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
- return AphiaId == other.AphiaId && Url.Equals(other.Url) && ScientificName == other.ScientificName && Authority == other.Authority && TaxonRankId == other.TaxonRankId && Rank == other.Rank && Status == other.Status && UnacceptReason == other.UnacceptReason && ValidAphiaID == other.ValidAphiaID && ValidName == other.ValidName && ValidAuthority == other.ValidAuthority && ParentNameUsageId == other.ParentNameUsageId && Kingdom == other.Kingdom && Phylum == other.Phylum && Class == other.Class && Order == other.Order && Family == other.Family && Genus == other.Genus && Citation == other.Citation && lsId == other.lsId && IsMarine == other.IsMarine && IsBrackish == other.IsBrackish && IsFreshwater == other.IsFreshwater && IsTerrestrial == other.IsTerrestrial && IsExtinct == other.IsExtinct && MatchType == other.MatchType && Nullable.Equals(Modified, other.Modified);
+ return AphiaId == other.AphiaId && Url.Equals(other.Url) && ScientificName == other.ScientificName && Authority == other.Authority && TaxonRankId == other.TaxonRankId && Rank == other.Rank && Status == other.Status && UnAcceptReason == other.UnAcceptReason && ValidAphiaId == other.ValidAphiaId && ValidName == other.ValidName && ValidAuthority == other.ValidAuthority && ParentNameUsageId == other.ParentNameUsageId && Kingdom == other.Kingdom && Phylum == other.Phylum && Class == other.Class && Order == other.Order && Family == other.Family && Genus == other.Genus && Citation == other.Citation && LsId == other.LsId && IsMarine == other.IsMarine && IsBrackish == other.IsBrackish && IsFreshwater == other.IsFreshwater && IsTerrestrial == other.IsTerrestrial && IsExtinct == other.IsExtinct && MatchType == other.MatchType && Nullable.Equals(Modified, other.Modified);
}
///
@@ -241,8 +241,8 @@ public override int GetHashCode()
hashCode.Add(TaxonRankId);
hashCode.Add(Rank);
hashCode.Add(Status);
- hashCode.Add(UnacceptReason);
- hashCode.Add(ValidAphiaID);
+ hashCode.Add(UnAcceptReason);
+ hashCode.Add(ValidAphiaId);
hashCode.Add(ValidName);
hashCode.Add(ValidAuthority);
hashCode.Add(ParentNameUsageId);
@@ -253,7 +253,7 @@ public override int GetHashCode()
hashCode.Add(Family);
hashCode.Add(Genus);
hashCode.Add(Citation);
- hashCode.Add(lsId);
+ hashCode.Add(LsId);
hashCode.Add(IsMarine);
hashCode.Add(IsBrackish);
hashCode.Add(IsFreshwater);
diff --git a/SpeciesDatabaseApi/Worms/WormsClient.cs b/SpeciesDatabaseApi/Worms/WormsClient.cs
index a9d8a50..1ab3ea3 100644
--- a/SpeciesDatabaseApi/Worms/WormsClient.cs
+++ b/SpeciesDatabaseApi/Worms/WormsClient.cs
@@ -52,7 +52,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaAttributeKeysById(int attributeId = 0, bool includeInherited = true, CancellationToken token = default)
{
- var parameters = new KeyValuePair("include_children", includeInherited);
+ var parameters = new KeyValuePair("include_children", includeInherited);
return GetJsonAsync($"AphiaAttributeKeysByID/{attributeId}", parameters, token);
}
@@ -65,7 +65,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaAttributesByAphiaId(int aphiaId, bool includeInherited = false, CancellationToken token = default)
{
- var parameters = new KeyValuePair("include_inherited", includeInherited);
+ var parameters = new KeyValuePair("include_inherited", includeInherited);
return GetJsonAsync($"AphiaAttributesByAphiaID/{aphiaId}", parameters, token);
}
@@ -89,7 +89,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaIDsByAttributeKeyId(int attributeId, int offset = 1, CancellationToken token = default)
{
- var parameters = new KeyValuePair("offset", offset);
+ var parameters = new KeyValuePair("offset", offset);
return GetJsonAsync($"AphiaIDsByAttributeKeyID/{attributeId}", parameters, token);
}
#endregion
@@ -117,7 +117,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaExternalIdByAphiaId(int aphiaId, ExternalIdentifierType type, CancellationToken token = default)
{
- var parameters = new KeyValuePair("type", type);
+ var parameters = new KeyValuePair("type", type);
return GetJsonAsync($"AphiaExternalIDByAphiaID/{aphiaId}", parameters, token);
}
@@ -130,7 +130,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaRecordByExternalId(string externalId, ExternalIdentifierType type, CancellationToken token = default)
{
- var parameters = new KeyValuePair("type", type);
+ var parameters = new KeyValuePair("type", type);
return GetJsonAsync($"AphiaRecordByExternalID/{externalId}", parameters, token);
}
#endregion
@@ -160,7 +160,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaChildrenByAphiaId(int aphiaId, bool marineOnly = true, int offset = 1, CancellationToken token = default)
{
- var parameters = new Dictionary
+ var parameters = new Dictionary
{
{"marine_only", marineOnly},
{"offset", offset},
@@ -188,7 +188,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
/// NULL when no match is found; -999 when multiple matches are found; an integer(AphiaID) when one exact match was found
public Task GetAphiaIdByName(string scientificName, bool marineOnly = true, CancellationToken token = default)
{
- var parameters = new KeyValuePair("marine_only", marineOnly);
+ var parameters = new KeyValuePair("marine_only", marineOnly);
return GetJsonAsync($"AphiaIDByName/{Uri.EscapeDataString(scientificName)}", parameters, token);
}
@@ -235,7 +235,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaRecordsByAphiaIds(IEnumerable aphiaIds, CancellationToken token = default)
{
- var parameters = aphiaIds.Select(aphiaId => new KeyValuePair("aphiaids[]", aphiaId));
+ var parameters = aphiaIds.Select(aphiaId => new KeyValuePair("aphiaids[]", aphiaId));
return GetJsonAsync("AphiaRecordsByAphiaIDs", parameters, token);
}
@@ -250,7 +250,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaRecordsByDate(DateTime startDate, DateTime endDate, bool marineOnly = true, int offset = 1, CancellationToken token = default)
{
- var parameters = new Dictionary
+ var parameters = new Dictionary
{
{"startdate", startDate.ToString("O")},
{"enddate", endDate.ToString("O")},
@@ -271,8 +271,8 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task?> GetAphiaRecordsByMatchNames(IEnumerable scientificNames, bool marineOnly = true, CancellationToken token = default)
{
- var parameters = scientificNames.Select(aphiaId => new KeyValuePair("scientificnames[]", aphiaId)).ToList();
- parameters.Add(new KeyValuePair("marine_only", marineOnly));
+ var parameters = scientificNames.Select(aphiaId => new KeyValuePair("scientificnames[]", aphiaId)).ToList();
+ parameters.Add(new ("marine_only", marineOnly));
return GetJsonAsync>("AphiaRecordsByMatchNames", parameters, token);
}
@@ -287,7 +287,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaRecordsByName(string scientificName, bool like = true, bool marineOnly = true, int offset = 1, CancellationToken token = default)
{
- var parameters = new Dictionary
+ var parameters = new Dictionary
{
{"like", like},
{"marine_only", marineOnly},
@@ -308,9 +308,9 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task?> GetAphiaRecordsByNames(IEnumerable scientificNames, bool like = false, bool marineOnly = true, CancellationToken token = default)
{
- var parameters = scientificNames.Select(aphiaId => new KeyValuePair("scientificnames[]", aphiaId)).ToList();
- parameters.Add(new KeyValuePair("like", like));
- parameters.Add(new KeyValuePair("marine_only", marineOnly));
+ var parameters = scientificNames.Select(aphiaId => new KeyValuePair("scientificnames[]", aphiaId)).ToList();
+ parameters.Add(new ("like", like));
+ parameters.Add(new ("marine_only", marineOnly));
return GetJsonAsync>("AphiaRecordsByNames", parameters, token);
}
@@ -324,7 +324,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaRecordsByTaxonRankId(int taxonId, int belongsToAphiaId, int offset = 1, CancellationToken token = default)
{
- var parameters = new Dictionary
+ var parameters = new Dictionary
{
{"belongsTo", belongsToAphiaId},
{"offset", offset},
@@ -341,7 +341,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaSynonymsByAphiaId(int aphiaId, int offset = 1, CancellationToken token = default)
{
- var parameter = new KeyValuePair("offset", offset);
+ var parameter = new KeyValuePair("offset", offset);
return GetJsonAsync($"AphiaSynonymsByAphiaID/{aphiaId}", parameter, token);
}
@@ -354,7 +354,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaTaxonRanksById(int taxonId = -1, int aphiaId = -1, CancellationToken token = default)
{
- var parameter = new KeyValuePair("AphiaID", aphiaId);
+ var parameter = new KeyValuePair("AphiaID", aphiaId);
return GetJsonAsync($"AphiaTaxonRanksByID/{taxonId}", parameter, token);
}
@@ -367,7 +367,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
///
public Task GetAphiaTaxonRanksByName(string taxonRank = "", int aphiaId = -1, CancellationToken token = default)
{
- var parameter = new KeyValuePair("AphiaID", aphiaId);
+ var parameter = new KeyValuePair("AphiaID", aphiaId);
return GetJsonAsync($"AphiaTaxonRanksByName/{Uri.EscapeDataString(taxonRank)}", parameter, token);
}
#endregion
@@ -383,7 +383,7 @@ public WormsClient(HttpClient? httpClient = null) : base(DefaultApiAddress, http
/// A representing the asynchronous operation.
public Task GetAphiaRecordsByVernacular(string vernacular, bool like = false, int offset = 1, CancellationToken token = default)
{
- var parameters = new Dictionary
+ var parameters = new Dictionary
{
{nameof(like), like},
{nameof(offset), offset},
diff --git a/SpeciesDatabaseCmd/IucnCommand.cs b/SpeciesDatabaseCmd/IucnCommand.cs
index 9a3e574..076cdb6 100644
--- a/SpeciesDatabaseCmd/IucnCommand.cs
+++ b/SpeciesDatabaseCmd/IucnCommand.cs
@@ -20,7 +20,7 @@ internal static class IucnCommand
internal static Command CreateCommand()
{
- var command = new Command(Client.ClientAcronym.ToUpper(), $"Query - {Client.ClientFullName}")
+ var command = new Command(Client.ClientAcronym.ToUpper(), Program.GetRootCommandDescription(Client))
{
VersionCommand(),
diff --git a/SpeciesDatabaseCmd/MrCommand.cs b/SpeciesDatabaseCmd/MrCommand.cs
new file mode 100644
index 0000000..fc424de
--- /dev/null
+++ b/SpeciesDatabaseCmd/MrCommand.cs
@@ -0,0 +1,319 @@
+using System.CommandLine;
+using SpeciesDatabaseApi.Mr;
+
+namespace SpeciesDatabaseCmd;
+
+internal static class MrCommand
+{
+ private static readonly MrClient Client = new();
+
+ private static readonly Argument MrgIdArgument = new("mrgid", "The MRGID to search for.");
+ private static readonly Argument NameArgument = new("name", "The name to search for.");
+ private static readonly Argument NamesArgument = new("names", "The names to search for.");
+ private static readonly Argument TypeIdsArgument = new("type-ids", "One or more placetypeIDs.");
+ private static readonly Argument OffsetArgument = new( "offset", () => 0, "Provide offset to return next batch of 100 sources.");
+ private static readonly Argument SourceIdArgument = new( "source-id", "The SourceID to search for.");
+ private static readonly Argument SourceNameArgument = new( "source-name", "The source name to search for.");
+ private static readonly Argument TypeNameArgument = new( "type-name", "The type name to search for.");
+
+ private static readonly Option LanguageOption = new(new[] { "--language" }, "Language (ISO 639-1 code).");
+ private static readonly Option LikeOption = new(new[] { "-l", "--like" }, () => true, "Adds a '%'-sign before and after the GazetteerName (SQL LIKE function).");
+ private static readonly Option FuzzyOption = new(new[] { "-f", "--fuzzy" }, () => false, "Uses Levenshtein query to find nearest matches.");
+ private static readonly Option OffsetOption = new(new[]{"-o", "--offset"}, "Start record number, in order to page through next batch of results.");
+ private static readonly Option CountOption = new(new[]{"-c", "--count"}, () => 100, "number of records to retrieve. max=100.");
+
+ internal static Command CreateCommand()
+ {
+ var command = new Command(Client.ClientAcronym.ToUpper(), Program.GetRootCommandDescription(Client))
+ {
+ GazetteerRecordCommand(),
+ FullGazetteerRecordCommand(),
+
+ GazetteerGeometriesCommand(),
+ GazetteerTypesCommand(),
+ GazetteerRecordsByNameCommand(),
+ GazetteerRecordsByNamesCommand(),
+
+ FeedCommand(),
+
+ GazetteerWmSesCommand(),
+ GazetteerRelationsCommand(),
+ GazetteerSourcesCommand(),
+ GazetteerSourceCommand(),
+ GazetteerNamesCommand(),
+ GazetteerRecordsBySourceCommand(),
+ GazetteerRecordsByTypeCommand(),
+ GazetteerRecordsByLatLongCommand()
+ };
+
+ return command;
+ }
+
+ private static Command GazetteerRecordCommand()
+ {
+ var command = new Command("GazetteerRecord", "Gets one record for the given MRGID.")
+ {
+ MrgIdArgument
+ };
+
+ command.SetHandler(async (mrgId) =>
+ {
+ var result = await Client.GetGazetteerRecord(mrgId);
+ Program.Print(result);
+ }, MrgIdArgument);
+
+ return command;
+ }
+
+ private static Command FullGazetteerRecordCommand()
+ {
+ var command = new Command("FullGazetteerRecord", "Gets one record for the given MRGID.")
+ {
+ MrgIdArgument
+ };
+
+ command.SetHandler(async (mrgId) =>
+ {
+ var result = await Client.GetFullGazetteerRecord(mrgId);
+ Program.Print(result);
+ }, MrgIdArgument);
+
+ return command;
+ }
+
+ private static Command GazetteerGeometriesCommand()
+ {
+ var command = new Command("GazetteerGeometries", "Gets all geometries associated with a gazetteer record.")
+ {
+ MrgIdArgument
+ };
+
+ command.SetHandler(async (mrgId) =>
+ {
+ var result = await Client.GetGazetteerGeometries(mrgId);
+ Program.Print(result);
+ }, MrgIdArgument);
+
+ return command;
+ }
+
+ private static Command GazetteerTypesCommand()
+ {
+ var command = new Command("GazetteerTypes", "Gets all possible types.")
+ {
+ };
+
+ command.SetHandler(async () =>
+ {
+ var result = await Client.GetGazetteerTypes();
+ Program.Print(result);
+ });
+
+ return command;
+ }
+
+ private static Command GazetteerRecordsByNameCommand()
+ {
+ var command = new Command("GazetteerRecordsByName", "Gets a list of the first 100 matching records for given name.")
+ {
+ NameArgument,
+ TypeIdsArgument,
+
+ LikeOption,
+ FuzzyOption,
+ LanguageOption,
+ OffsetOption,
+ CountOption,
+ };
+
+ command.SetHandler(async (name, typeIds, like, fuzzy, language, offset, count) =>
+ {
+ var result = await Client.GetGazetteerRecords(name, like, fuzzy, typeIds, language, offset, count);
+ Program.Print(result);
+ }, NameArgument, TypeIdsArgument, LikeOption, FuzzyOption, LanguageOption, OffsetOption, CountOption);
+
+ return command;
+ }
+
+ private static Command GazetteerRecordsByNamesCommand()
+ {
+ var command = new Command("GazetteerRecordsByNames", "Gets a list of the first 100 matching records for given names.")
+ {
+ NamesArgument,
+
+ LikeOption,
+ FuzzyOption,
+ };
+
+ command.SetHandler(async (names, like, fuzzy) =>
+ {
+ var result = await Client.GetGazetteerRecords(names, like, fuzzy);
+ if (result is null)
+ {
+ Program.Print(result);
+ return;
+ }
+
+ foreach (var record in result)
+ {
+ Program.Print(record);
+ }
+
+ }, NamesArgument, LikeOption, FuzzyOption);
+
+ return command;
+ }
+
+ private static Command FeedCommand()
+ {
+ var command = new Command("Feed", "Gets the Linked Data Event Stream feed")
+ {
+ };
+
+ command.SetHandler(async () =>
+ {
+ var result = await Client.GetFeed();
+ Program.Print(result);
+ });
+
+ return command;
+ }
+
+ private static Command GazetteerWmSesCommand()
+ {
+ var command = new Command("GazetteerWMSes", "Gets WMS information for the given MRGID.")
+ {
+ MrgIdArgument
+ };
+
+ command.SetHandler(async (mrgId) =>
+ {
+ var result = await Client.GetgetGazetteerWmSes(mrgId);
+ Program.Print(result);
+ }, MrgIdArgument);
+
+ return command;
+ }
+
+ private static Command GazetteerRelationsCommand()
+ {
+ var directionArgument = new Argument("direction", () => MrRelationDirection.Upper);
+ var typeArgument = new Argument("type", () => MrRelationType.PartOf);
+ var command = new Command("GazetteerRelations", "Gets the first 100 related records for the given MRGID.")
+ {
+ MrgIdArgument,
+ directionArgument,
+ typeArgument
+ };
+
+ command.SetHandler(async (mrgId, direction, type) =>
+ {
+ var result = await Client.GetGazetteerRelations(mrgId, direction, type);
+ Program.Print(result);
+ }, MrgIdArgument, directionArgument, typeArgument);
+
+ return command;
+ }
+
+ private static Command GazetteerSourcesCommand()
+ {
+ var command = new Command("GazetteerSources", "Gets all sources per batch of 100.")
+ {
+ OffsetArgument
+ };
+
+ command.SetHandler(async (offset) =>
+ {
+ var result = await Client.GetGazetteerSources(offset);
+ Program.Print(result);
+ }, OffsetArgument);
+
+ return command;
+ }
+
+ private static Command GazetteerSourceCommand()
+ {
+ var command = new Command("GazetteerSource", "Gets the source name corresponding to a source ID.")
+ {
+ SourceIdArgument
+ };
+
+ command.SetHandler(async (sourceId) =>
+ {
+ var result = await Client.GetGazetteerSource(sourceId);
+ Program.Print(result);
+ }, SourceIdArgument);
+
+ return command;
+ }
+
+ private static Command GazetteerNamesCommand()
+ {
+ var command = new Command("GazetteerNames", "Gets the first 100 names for the given MRGID.")
+ {
+ MrgIdArgument
+ };
+
+ command.SetHandler(async (mrgId) =>
+ {
+ var result = await Client.GetGazetteerNames(mrgId);
+ Program.Print(result);
+ }, MrgIdArgument);
+
+ return command;
+ }
+
+ private static Command GazetteerRecordsBySourceCommand()
+ {
+ var command = new Command("GazetteerRecordsBySource", "Gets the first 100 records for the given source.")
+ {
+ SourceNameArgument
+ };
+
+ command.SetHandler(async (sourceName) =>
+ {
+ var result = await Client.GetGazetteerRecordsBySource(sourceName);
+ Program.Print(result);
+ }, SourceNameArgument);
+
+ return command;
+ }
+
+ private static Command GazetteerRecordsByTypeCommand()
+ {
+ var command = new Command("GazetteerRecordsByType", "Gets the first 100 records for the given source.")
+ {
+ TypeNameArgument,
+ OffsetArgument
+ };
+
+ command.SetHandler(async (typeName, offset) =>
+ {
+ var result = await Client.GetGazetteerRecordsByType(typeName, offset);
+ Program.Print(result);
+ }, TypeNameArgument, OffsetArgument);
+
+ return command;
+ }
+
+ private static Command GazetteerRecordsByLatLongCommand()
+ {
+ var latitudeArgument = new Argument("latitude", "A decimal number which ranges from -90 to 90");
+ var longitudeArgument = new Argument("longitude", "A decimal number which ranges from -180 to +180");
+ var command = new Command("GazetteerRecordsByLatLong", "Gets all gazetteer records where the geometry intersects with the given latitude and longitude per batch of 100. Results are sorted by area, smallest to largest.")
+ {
+ latitudeArgument,
+ longitudeArgument,
+ TypeIdsArgument,
+ OffsetOption
+ };
+
+ command.SetHandler(async (latitude, longitude, typeIds, offset) =>
+ {
+ var result = await Client.GetGazetteerRecordsByLatLong(latitude, longitude, typeIds, offset);
+ Program.Print(result);
+ }, latitudeArgument, longitudeArgument, TypeIdsArgument, OffsetOption);
+
+ return command;
+ }
+}
\ No newline at end of file
diff --git a/SpeciesDatabaseCmd/Program.cs b/SpeciesDatabaseCmd/Program.cs
index 138c7ed..19db217 100644
--- a/SpeciesDatabaseCmd/Program.cs
+++ b/SpeciesDatabaseCmd/Program.cs
@@ -1,5 +1,6 @@
using System.Collections;
using System.CommandLine;
+using SpeciesDatabaseApi;
namespace SpeciesDatabaseCmd;
@@ -10,7 +11,8 @@ static async Task Main(string[] args)
var rootCommand = new RootCommand("Query specific taxonomy and species database")
{
WormsCommand.CreateCommand(),
- IucnCommand.CreateCommand()
+ IucnCommand.CreateCommand(),
+ MrCommand.CreateCommand()
};
try
@@ -25,6 +27,8 @@ static async Task Main(string[] args)
return 1;
}
+ internal static string GetRootCommandDescription(BaseClient client) => $"Query - {client.ClientFullName} ({client.WebsiteUrl})";
+
internal static string FormatResults(string? text)
{
if (string.IsNullOrWhiteSpace(text)) return string.Empty;
diff --git a/SpeciesDatabaseCmd/Properties/launchSettings.json b/SpeciesDatabaseCmd/Properties/launchSettings.json
index 37f5ae3..fb0247c 100644
--- a/SpeciesDatabaseCmd/Properties/launchSettings.json
+++ b/SpeciesDatabaseCmd/Properties/launchSettings.json
@@ -47,6 +47,18 @@
"IUCN SpeciesCitationById 2467 europe": {
"commandName": "Project",
"commandLineArgs": "IUCN SpecieCitationById 2467 europe"
+ },
+ "MR GazetteerRecordsByName belgian": {
+ "commandName": "Project",
+ "commandLineArgs": "MR GazetteerRecordsByName belgian"
+ },
+ "MR GazetteerRecordsByNames belgian portugal spain": {
+ "commandName": "Project",
+ "commandLineArgs": "MR GazetteerRecordsByNames belgian portugal spain"
+ },
+ "MR Feed": {
+ "commandName": "Project",
+ "commandLineArgs": "MR Feed"
}
}
}
\ No newline at end of file
diff --git a/SpeciesDatabaseCmd/WormsCommand.cs b/SpeciesDatabaseCmd/WormsCommand.cs
index be0a044..117070b 100644
--- a/SpeciesDatabaseCmd/WormsCommand.cs
+++ b/SpeciesDatabaseCmd/WormsCommand.cs
@@ -1,5 +1,4 @@
using System.CommandLine;
-using SpeciesDatabaseApi;
using SpeciesDatabaseApi.Worms;
namespace SpeciesDatabaseCmd;
@@ -34,7 +33,7 @@ internal static class WormsCommand
internal static Command CreateCommand()
{
- var command = new Command(Client.ClientAcronym.ToUpper(), $"Query - {Client.ClientFullName}")
+ var command = new Command(Client.ClientAcronym.ToUpper(), Program.GetRootCommandDescription(Client))
{
//Attributes
AphiaAttributeKeysByIdCommand(),