Skip to content

Commit

Permalink
SIMSBIOHUB 347/341 - Names from shapefiles (#1146)
Browse files Browse the repository at this point in the history
* Added some additional logic to the API to extract a name and description from the provided shape file when creating sampling sites.
* It will search the keys of the geometry's properties for "name" or "label" and "des" or "desc" or "descr" (exact match), then use the value for the sampling site name and description respectively.
* Added a character limit to the sampling site name field in the edit form.

---------

Co-authored-by: JeremyQuartech <[email protected]>
  • Loading branch information
GrahamS-Quartech and JeremyQuartech authored Oct 31, 2023
1 parent e5158d6 commit ac199ba
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 15 deletions.
17 changes: 11 additions & 6 deletions api/src/repositories/sample-location-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export const SampleLocationRecord = z.object({
export type SampleLocationRecord = z.infer<typeof SampleLocationRecord>;

// Insert Object for Sample Locations
export type InsertSampleLocationRecord = Pick<SampleLocationRecord, 'survey_id' | 'name' | 'description' | 'geojson'>;
export type InsertSampleLocationRecord = Pick<SampleLocationRecord, 'survey_id' | 'description' | 'geojson'> & {
name: string | undefined;
};

// Update Object for Sample Locations
export type UpdateSampleLocationRecord = Pick<
Expand Down Expand Up @@ -152,6 +154,10 @@ export class SampleLocationRepository extends BaseRepository {
* @memberof SampleLocationRepository
*/
async insertSampleLocation(sample: InsertSampleLocationRecord): Promise<SampleLocationRecord> {
const shapeNameOrQuery = sample.name
? SQL`${sample.name}`
: SQL`(SELECT concat('Sample Site ', (SELECT count(survey_sample_site_id) + 1 FROM survey_sample_site sss WHERE survey_id = ${sample.survey_id})))`;

const sqlStatement = SQL`
INSERT INTO survey_sample_site (
survey_id,
Expand All @@ -160,11 +166,10 @@ export class SampleLocationRepository extends BaseRepository {
geojson,
geography
) VALUES (
${sample.survey_id},
(SELECT concat('Sample Site ', (SELECT count(survey_sample_site_id) + 1 FROM survey_sample_site sss WHERE survey_id = ${sample.survey_id}))),
${sample.description},
${sample.geojson},
`;
${sample.survey_id},`.append(shapeNameOrQuery).append(SQL`,
${sample.description},
${sample.geojson},
`);
const geometryCollectionSQL = generateGeometryCollectionSQL(sample.geojson);

sqlStatement.append(SQL`
Expand Down
28 changes: 23 additions & 5 deletions api/src/services/sample-location-service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Feature } from '@turf/helpers';
import { Feature, Geometry, GeometryCollection, Properties } from '@turf/helpers';
import { IDBConnection } from '../database/db';
import {
SampleLocationRecord,
Expand Down Expand Up @@ -58,19 +58,37 @@ export class SampleLocationService extends DBService {
/**
* Inserts survey Sample Locations.
*
* It is a business requirement to use strings from the properties field of provided geometry
* to determine the name and description of sampling locations when possible.
*
* If there is no string contained in the fields 'name', 'label' to be used in our db,
* the system will auto-generate a name of 'Sampling Site #x', where x is taken from the greatest value
* integer id + 1 in the db.
*
* @param {PostSampleLocations} sampleLocations
* @return {*} {Promise<SampleLocationRecord[]>}
* @memberof SampleLocationService
*/
async insertSampleLocations(sampleLocations: PostSampleLocations): Promise<SampleLocationRecord[]> {
const methodService = new SampleMethodService(this.connection);

const shapeFileFeatureName = (geometry: Feature<Geometry | GeometryCollection, Properties>): string | undefined => {
const nameKey = Object.keys(geometry.properties ?? {}).find(
(key) => key.toLowerCase() === 'name' || key.toLowerCase() === 'label'
);
return nameKey && geometry.properties ? geometry.properties[nameKey].substring(0, 50) : undefined;
};
const shapeFileFeatureDesc = (geometry: Feature<Geometry | GeometryCollection, Properties>): string | undefined => {
const descKey = Object.keys(geometry.properties ?? {}).find(
(key) => key.toLowerCase() === 'desc' || key.toLowerCase() === 'descr' || key.toLowerCase() === 'des'
);
return descKey && geometry.properties ? geometry.properties[descKey].substring(0, 250) : undefined;
};
// Create a sample location for each feature found
const promises = sampleLocations.survey_sample_sites.map((item, index) => {
const promises = sampleLocations.survey_sample_sites.map((item) => {
const sampleLocation = {
survey_id: sampleLocations.survey_id,
name: `Sample Site ${index + 1}`, // Business requirement to default the names to Sample Site # on creation
description: sampleLocations.description,
name: shapeFileFeatureName(item), // If this function returns undefined, insertSampleLocation will auto-generate a name instead.
description: shapeFileFeatureDesc(item) || sampleLocations.description,
geojson: item
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface ISampleSiteEditFormProps {

export const samplingSiteYupSchema = yup.object({
sampleSite: yup.object({
name: yup.string().default(''),
name: yup.string().default('').max(50, 'Maximum 50 characters.'),
description: yup.string().default('').nullable(),
survey_sample_sites: yup
.array(yup.object())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ const SampleSiteGeneralInformationForm: React.FC = (props) => {
<CustomTextField
name="sampleSite.name"
label="Name"
other={{
required: true
}}
other={{ placeholder: 'Maximum 50 characters', required: true }}
/>
</Grid>
<Grid item xs={12}>
Expand Down

0 comments on commit ac199ba

Please sign in to comment.