Skip to content

Commit

Permalink
Merge branch 'main' into 89-preferred-place-locale
Browse files Browse the repository at this point in the history
  • Loading branch information
pleary authored Nov 24, 2020
2 parents 7ffebcc + 821800d commit 36c9a51
Show file tree
Hide file tree
Showing 83 changed files with 3,574 additions and 489 deletions.
2 changes: 2 additions & 0 deletions config.js.travis
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ module.exports = {
geometry_field: "geom",
dbname: "inaturalist_test"
},
staticImagePrefix: "http://localhost:3000/attachments/",
userImagePrefix: "/attachments/users/icons/",
imageProcesing: {
taxaFilePath: "/home/travis/build/inaturalist/iNaturalistAPI/vision-taxa.txt",
uploadsDir: "/tmp/",
Expand Down
8 changes: 7 additions & 1 deletion config_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,15 @@ module.exports = {
},
tileSize: 512,
debug: true,
staticImagePrefix: "http://localhost:3000/attachments/",
userImagePrefix: "/attachments/users/icons/",
imageProcesing: {
// Path to a file listing the taxonomy used in the computer vision model
taxaFilePath: "",
uploadsDir: "",
// Path to a directory where uploads should be stored. /tmp/ is fine on most
// *nix systems
uploadsDir: "/tmp/",
// Base URL for the web app returning computer vision results
tensorappURL: ""
},
redis: {
Expand Down
51 changes: 51 additions & 0 deletions lib/controllers/v1/authorized_applications_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// eslint-disable-next-line camelcase
const { authorized_applications } = require( "inaturalistjs" );
const squel = require( "squel" );
const InaturalistAPI = require( "../../inaturalist_api" );
const pgClient = require( "../../pg_client" );

const AuthorizedApplicationsController = class AuthorizedApplicationsController {
static async index( req ) {
if ( !req.userSession ) {
throw new Error( 401 );
}
const { page, perPage, offset } = InaturalistAPI.paginationData( req );
const query = squel.select( )
.field( "MAX(oauth_access_tokens.created_at)", "created_at" )
.field( "MAX(oauth_access_tokens.scopes)", "scopes" )
.field( "oauth_applications.id", "application_id" )
.field( "oauth_applications.name", "application_name" )
.field( "BOOL_AND(oauth_applications.official)", "official" )
.field( "COUNT(*) OVER()", "total_count" )
.from( "oauth_access_tokens" )
.join( "oauth_applications", null, "oauth_applications.id = oauth_access_tokens.application_id" )
.where( "oauth_access_tokens.resource_owner_id = ?", req.userSession.user_id )
.where( "oauth_access_tokens.revoked_at IS NULL" )
.group( "oauth_applications.id, oauth_applications.name" )
.offset( offset )
.limit( perPage )
.order( "oauth_applications.id" );
const sql = query.toString( );
const { rows } = await pgClient.connection.query( sql );
return {
total_results: rows.length === 0 ? 0 : parseInt( rows[0].total_count, 0 ),
page,
per_page: perPage,
results: rows.map( row => ( {
created_at: row.created_at,
scopes: row.scopes.split( " " ).sort( ),
application: {
id: row.application_id,
name: row.application_name,
official: row.official
}
} ) )
};
}

static async delete( req ) {
return InaturalistAPI.iNatJSWrap( authorized_applications.delete, req );
}
};

module.exports = AuthorizedApplicationsController;
31 changes: 15 additions & 16 deletions lib/controllers/v1/computervision_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,17 @@ const ComputervisionController = class ComputervisionController {

static async scoreObservation( req ) {
if ( !req.userSession && !req.applicationSession ) {
throw new Error( 401 );
throw util.httpError( 401, "Unauthorized" );
}
const obsID = Number( req.params.id );
if ( !obsID ) {
// return void reject( { custom_message: "ID missing", status: 422 } );
throw new Error( 422 );
throw util.httpError( 422, "Missing observation ID or UUID" );
}
const searchReq = { query: { id: obsID } };
// fetch the obs metadata
const response = await ObservationsController.search( searchReq );
if ( !response || _.isEmpty( response.results ) ) {
// return void reject( { custom_message: "Unknown observation" } );
throw new Error( 422 );
throw util.httpError( 422, "Unknown observation" );
}
const observation = response.results[0];
let photoURL;
Expand All @@ -100,8 +98,7 @@ const ComputervisionController = class ComputervisionController {
}
} );
if ( !photoURL ) {
// return void reject( { custom_message: "Observation has no scorable photos" } );
throw new Error( 422 );
throw util.httpError( 422, "Observation has no scorable photos" );
}
req.query.image_url = photoURL;
return ComputervisionController.scoreImageURL( req, { observation } );
Expand All @@ -110,11 +107,11 @@ const ComputervisionController = class ComputervisionController {
static async scoreImageURL( req, options = { } ) {
return new Promise( ( resolve, reject ) => {
if ( !req.userSession && !req.applicationSession ) {
return void reject( { error: "Unauthorized", status: 401 } );
return void reject( util.httpError( 401, "Unauthorized" ) );
}
const photoURL = req.query.image_url;
if ( !photoURL ) {
return void reject( { custom_message: "No scorable photo", status: 422 } );
return void reject( util.httpError( 422, "No scorable photo" ) );
}
// download the JPG
const parsedPhotoURL = path.parse( photoURL );
Expand Down Expand Up @@ -157,11 +154,10 @@ const ComputervisionController = class ComputervisionController {

static async scoreImage( req ) {
if ( !req.userSession && !req.applicationSession ) {
throw new Error( 401 );
throw util.httpError( 401, "Unauthorized" );
}
if ( !req.file ) {
// { custom_message: "No image provided", status: 422 }
throw new Error( 422 );
throw util.httpError( 422, "No image provided" );
}
return ComputervisionController.scoreImageUpload( req.file.path, req );
}
Expand All @@ -187,13 +183,12 @@ const ComputervisionController = class ComputervisionController {
timeout: 5000,
formData
};
const body = await requestPromise.post( options );
let json;
try {
const body = await requestPromise.post( options );
json = JSON.parse( body );
} catch ( e ) {
// { error: "Error scoring image", status: 500 };
throw new Error( 500 );
throw util.httpError( 500, "Error scoring image" );
}
const counts = _.map( json, ( score, id ) => ( {
taxon_id: Number( id ),
Expand Down Expand Up @@ -226,6 +221,9 @@ const ComputervisionController = class ComputervisionController {
}
scores = _.filter( scores, s => TFServingTaxonDescendants[req.body.taxon_id][s.taxon_id] );
}
if ( _.isEmpty( scores ) ) {
return InaturalistAPI.basicResponse( req );
}
scores = _.sortBy( scores, "count" ).reverse( );
ComputervisionController.normalizeScores( scores );
const commonAncestor = await ComputervisionController.commonAncestor( req, scores );
Expand Down Expand Up @@ -548,7 +546,8 @@ const ComputervisionController = class ComputervisionController {
query: {
lat: req.body.lat,
lng: req.body.lng,
observed_on: req.body.observed_on
observed_on: req.body.observed_on,
taxon_id: req.body.taxon_id
}
};
if ( commonAncestor ) {
Expand Down
Loading

0 comments on commit 36c9a51

Please sign in to comment.