Skip to content

Commit

Permalink
Update patch tsn record function, update interface/type names and fil…
Browse files Browse the repository at this point in the history
…e locations
  • Loading branch information
NickPhura committed Feb 1, 2024
1 parent c1629e2 commit b883a9c
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 84 deletions.
2 changes: 1 addition & 1 deletion api/src/paths/taxonomy/taxon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ GET.apiDoc = {
required: ['tsn', 'label'],
properties: {
tsn: {
type: 'number'
type: 'integer'
},
label: {
type: 'string'
Expand Down
2 changes: 1 addition & 1 deletion api/src/paths/taxonomy/taxon/tsn/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ GET.apiDoc = {
required: ['tsn', 'label'],
properties: {
tsn: {
type: 'number'
type: 'integer'
},
label: {
type: 'string'
Expand Down
16 changes: 8 additions & 8 deletions api/src/repositories/taxonomy-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getKnex } from '../database/db';
import { ApiExecuteSQLError } from '../errors/api-error';
import { BaseRepository } from './base-repository';

export const ItisTaxonRecord = z.object({
export const TaxonRecord = z.object({
taxon_id: z.number(),
itis_tsn: z.number(),
bc_taxon_code: z.string().nullable(),
Expand All @@ -20,7 +20,7 @@ export const ItisTaxonRecord = z.object({
revision_count: z.number()
});

export type ItisTaxonRecord = z.infer<typeof ItisTaxonRecord>;
export type TaxonRecord = z.infer<typeof TaxonRecord>;

/**
* Taxonomy Repository
Expand All @@ -34,13 +34,13 @@ export class TaxonomyRepository extends BaseRepository {
* Get taxon records by TSN id.
*
* @param {number[]} tsnIds
* @return {*} {Promise<ItisTaxonRecord>}
* @return {*} {Promise<TaxonRecord>}
* @memberof TaxonomyRepository
*/
async getTaxonByTsnIds(tsnIds: number[]): Promise<ItisTaxonRecord[]> {
async getTaxonByTsnIds(tsnIds: number[]): Promise<TaxonRecord[]> {
const queryBuilder = getKnex().queryBuilder().select('*').from('taxon').whereIn('itis_tsn', tsnIds);

const response = await this.connection.knex(queryBuilder, ItisTaxonRecord);
const response = await this.connection.knex(queryBuilder, TaxonRecord);

return response.rows;
}
Expand All @@ -54,7 +54,7 @@ export class TaxonomyRepository extends BaseRepository {
* @param {string} commonName
* @param {Record<any, any>} itisData
* @param {string} itisUpdateDate
* @return {*} {Promise<ItisTaxonRecord>}
* @return {*} {Promise<TaxonRecord>}
* @memberof TaxonomyRepository
*/
async addItisTaxonRecord(
Expand All @@ -63,7 +63,7 @@ export class TaxonomyRepository extends BaseRepository {
commonName: string | null,
itisData: Record<any, any>,
itisUpdateDate: string
): Promise<ItisTaxonRecord> {
): Promise<TaxonRecord> {
const sqlStatement = SQL`
INSERT INTO
taxon
Expand All @@ -85,7 +85,7 @@ export class TaxonomyRepository extends BaseRepository {
*;
`;

const response = await this.connection.sql(sqlStatement, ItisTaxonRecord);
const response = await this.connection.sql(sqlStatement, TaxonRecord);

if (response.rowCount !== 1) {
throw new ApiExecuteSQLError('Failed to insert new taxon record', [
Expand Down
40 changes: 10 additions & 30 deletions api/src/services/itis-service.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,10 @@
import axios from 'axios';
import { getLogger } from '../utils/logger';
import { TaxonSearchResult } from './taxonomy-service';

const defaultLog = getLogger('services/taxonomy-service');

export interface ITaxonomySource {
unit_name1: string;
unit_name2: string;
unit_name3: string;
taxon_authority: string;
code: string;
tty_kingdom: string;
tty_name: string;
english_name: string;
note: string | null;
end_date: string | null;
parent_id: number | null;
parent_hierarchy: { id: number; level: string }[];
}

export interface IItisSearchResponse {
export type ItisSolrSearchResponse = {
commonNames: string[];
kingdom: string;
name: string;
Expand All @@ -27,13 +13,7 @@ export interface IItisSearchResponse {
tsn: string;
updateDate: string;
usage: string;
}

export interface IItisSearchResult {
tsn: number;
label: string;
scientificName: string;
}
};

/**
* Service for retrieving and processing taxonomic data from the Integrated Taxonomic Information System (ITIS).
Expand All @@ -48,10 +28,10 @@ export class ItisService {
* Returns the ITIS search species Query.
*
* @param {*} searchTerms
* @return {*} {(Promise<IItisSearchResult[]>)}
* @return {*} {(Promise<TaxonSearchResult[]>)}
* @memberof TaxonomyService
*/
async searchItisByTerm(searchTerms: string[]): Promise<IItisSearchResult[]> {
async searchItisByTerm(searchTerms: string[]): Promise<TaxonSearchResult[]> {
const url = await this.getItisSolrTermSearchUrl(searchTerms);

defaultLog.debug({ label: 'searchItisByTerm', message: 'url', url });
Expand All @@ -69,10 +49,10 @@ export class ItisService {
* Returns the ITIS search by TSN.
*
* @param {number[]} searchTsnIds
* @return {*} {(Promise<IItisSearchResponse[]>)}
* @return {*} {(Promise<ItisSolrSearchResponse[]>)}
* @memberof TaxonomyService
*/
async searchItisByTSN(searchTsnIds: number[]): Promise<IItisSearchResponse[]> {
async searchItisByTSN(searchTsnIds: number[]): Promise<ItisSolrSearchResponse[]> {
const url = await this.getItisSolrTsnSearchUrl(searchTsnIds);

defaultLog.debug({ label: 'searchItisByTSN', message: 'url', url });
Expand All @@ -89,11 +69,11 @@ export class ItisService {
/**
* Cleans up the ITIS search response data.
*
* @param {IItisSearchResponse[]} data
* @param {ItisSolrSearchResponse[]} data
* @memberof TaxonomyService
*/
_sanitizeItisData = (data: IItisSearchResponse[]): IItisSearchResult[] => {
return data.map((item: IItisSearchResponse) => {
_sanitizeItisData = (data: ItisSolrSearchResponse[]): TaxonSearchResult[] => {
return data.map((item: ItisSolrSearchResponse) => {
const commonName = (item.commonNames && item.commonNames[0].split('$')[1]) || item.scientificName;

return {
Expand Down
76 changes: 32 additions & 44 deletions api/src/services/taxonomy-service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IDBConnection } from '../database/db';
import { ItisTaxonRecord, TaxonomyRepository } from '../repositories/taxonomy-repository';
import { ItisService } from './itis-service';
import { TaxonomyRepository, TaxonRecord } from '../repositories/taxonomy-repository';
import { ItisService, ItisSolrSearchResponse } from './itis-service';

export interface ITaxonomySource {
unit_name1: string;
Expand All @@ -17,22 +17,11 @@ export interface ITaxonomySource {
parent_hierarchy: { id: number; level: string }[];
}

export interface IItisSearchResponse {
commonNames: string[];
kingdom: string;
name: string;
parentTSN: string;
scientificName: string;
tsn: string;
updateDate: string;
usage: string;
}

export interface IItisSearchResult {
export type TaxonSearchResult = {
tsn: number;
label: string;
scientificName: string;
}
};

/**
* Service for retrieving and processing taxonomic data from BioHub.
Expand All @@ -51,62 +40,61 @@ export class TaxonomyService {
* Get taxon records by TSN ids.
*
* @param {number[]} tsnIds
* @return {*} {Promise<IItisSearchResult[]>}
* @return {*} {Promise<TaxonSearchResult[]>}
* @memberof TaxonomyService
*/
async getTaxonByTsnIds(tsnIds: number[]): Promise<IItisSearchResult[]> {
async getTaxonByTsnIds(tsnIds: number[]): Promise<TaxonSearchResult[]> {
// Search for taxon records in the database
const taxon = await this.taxonRepository.getTaxonByTsnIds(tsnIds);
const existingTaxonRecords = await this.taxonRepository.getTaxonByTsnIds(tsnIds);

// If taxon records are found, return them
if (taxon.length > 0) {
return this._sanitizeTaxonRecordsData(taxon);
}
const missingTsnIds = tsnIds.filter((tsnId) => !existingTaxonRecords.find((item) => item.itis_tsn === tsnId));

// If no taxon records are found, search ITIS for the taxon records
const itisService = new ItisService();
const itisResponse = await itisService.searchItisByTSN(tsnIds);
if (missingTsnIds.length) {
// If the local database does not contain a record for all of the requested ids, search ITIS for the missing
// taxon records, patching the missing records in the local database in the process
const itisService = new ItisService();
const itisResponse = await itisService.searchItisByTSN(missingTsnIds);

const taxonRecords: ItisTaxonRecord[] = [];
for (const itisRecord of itisResponse) {
// Add the taxon record to the database
const taxonRecord = await this.addItisTaxonRecord(itisRecord);
taxonRecords.push(taxonRecord);
for (const itisRecord of itisResponse) {
// Add the taxon record to the database
const newTaxonRecord = await this.addItisTaxonRecord(itisRecord);
existingTaxonRecords.push(newTaxonRecord);
}
}

// Return the taxon records
return this._sanitizeTaxonRecordsData(taxonRecords);
// Missing ids patched, return taxon records for all requested ids
return this._sanitizeTaxonRecordsData(existingTaxonRecords);
}

_sanitizeTaxonRecordsData(itisData: ItisTaxonRecord[]): IItisSearchResult[] {
return itisData.map((item: ItisTaxonRecord) => {
_sanitizeTaxonRecordsData(taxonRecords: TaxonRecord[]): TaxonSearchResult[] {
return taxonRecords.map((item: TaxonRecord) => {
return {
tsn: item.itis_tsn,
label: item.common_name || item.itis_scientific_name,
scientificName: item.itis_scientific_name
} as IItisSearchResult;
};
});
}

/**
* Adds a new taxon record.
*
* @param {IItisSearchResponse} itisResponse
* @return {*} {Promise<ItisTaxonRecord>}
* @param {ItisSolrSearchResponse} itisSolrResponse
* @return {*} {Promise<TaxonRecord>}
* @memberof TaxonomyService
*/
async addItisTaxonRecord(itisResponse: IItisSearchResponse): Promise<ItisTaxonRecord> {
async addItisTaxonRecord(itisSolrResponse: ItisSolrSearchResponse): Promise<TaxonRecord> {
let commonName = null;
if (itisResponse.commonNames) {
commonName = itisResponse.commonNames && itisResponse.commonNames[0].split('$')[1];
if (itisSolrResponse.commonNames) {
commonName = itisSolrResponse.commonNames && itisSolrResponse.commonNames[0].split('$')[1];
}

return this.taxonRepository.addItisTaxonRecord(
Number(itisResponse.tsn),
itisResponse.scientificName,
Number(itisSolrResponse.tsn),
itisSolrResponse.scientificName,
commonName,
itisResponse,
itisResponse.updateDate
itisSolrResponse,
itisSolrResponse.updateDate
);
}

Expand Down

0 comments on commit b883a9c

Please sign in to comment.