Skip to content

Commit

Permalink
Merge branch 'master' into identify
Browse files Browse the repository at this point in the history
  • Loading branch information
kueda committed Mar 19, 2016
2 parents 615046f + 757c498 commit ab79058
Show file tree
Hide file tree
Showing 120 changed files with 2,661 additions and 9,263 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Expand Up @@ -25,7 +25,7 @@ config/deploy
config/*.mapnik.xml
config/*.ogr.vrt
elasticsearch
node_modules/
node_modules
public/assets
public/lists
public/observations
Expand All @@ -51,4 +51,4 @@ lib/ratatosk/spec/debug.log
notes.txt
xmp.xml

config/environments/development.rb
config/environments/development.rb
2 changes: 1 addition & 1 deletion Gemfile
Expand Up @@ -108,7 +108,7 @@ gem 'rgeo-geojson'
gem 'activerecord-postgis-adapter', :git => 'git://github.com/kueda/activerecord-postgis-adapter.git', :branch => 'activerecord42'

group :production do
gem 'newrelic_rpm'
gem 'newrelic_rpm', '~> 3.15.0'
end

group :test, :development, :prod_dev do
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Expand Up @@ -429,7 +429,7 @@ GEM
net-ssh (>= 2.6.5)
net-ssh (2.9.2)
netrc (0.10.2)
newrelic_rpm (3.9.9.275)
newrelic_rpm (3.15.0.314)
nokogiri (1.6.6.2)
mini_portile (~> 0.6.0)
non-stupid-digest-assets (1.0.4)
Expand Down Expand Up @@ -734,7 +734,7 @@ DEPENDENCIES
machinist
mobile-fu!
mocha
newrelic_rpm
newrelic_rpm (~> 3.15.0)
nokogiri
non-stupid-digest-assets
objectify-xml
Expand Down
152 changes: 131 additions & 21 deletions app/assets/javascripts/ang/controllers/observation_search.js
@@ -1,4 +1,4 @@
var application = angular.module( "ObservationSearch", [
var application = angular.module( "ObservationSearch", [
"infinite-scroll",
"templates",
"ehFilters", // angular-capitalize
Expand Down Expand Up @@ -125,6 +125,9 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
$scope.setupPlaceSearchbox( );
$scope.determineFieldNames( );
$scope.setupTaxonAutocomplete( );
$scope.setupInatPlaceAutocomplete( );
$scope.setupUserAutocomplete( );
$scope.setupProjectAutocomplete( );
$scope.setupBrowserStateBehavior( );
$scope.filtersInitialized = true;
// someone searched with taxon_name, but no mathing taxon was found,
Expand Down Expand Up @@ -220,6 +223,12 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
};
scope.moreFiltersHidden = moreFiltersHidden;
} );
$scope.$watch( "params.user_id", function( ) {
$scope.updateUserAutocomplete( );
});
$scope.$watch( "params.project_id", function( ) {
$scope.updateProjectAutocomplete( );
});
// watch for place selections, unselections
$scope.$watch( "selectedPlace", function( ) {
if( $scope.selectedPlace && $scope.selectedPlace.id ) {
Expand All @@ -228,10 +237,12 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
$scope.mapBoundsIcon = null;
$scope.alignMapOnSearch = true;
$scope.params.place_id = $scope.selectedPlace.id;
$scope.updatePlaceAutocomplete( );
}
} else {
} else if( !_.isArray( $scope.params.place_id) ) {
$scope.alignMapOnSearch = false;
$scope.params.place_id = "any";
$scope.updatePlaceAutocomplete( );
}
});
$scope.initializeTaxonParams = function( ) {
Expand All @@ -241,25 +252,34 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
if( $scope.params.taxon_id ) {
// load taxon auto name and photo for autocomplete. Send locale
// params to we load the right taxon name for the users's prefs
TaxaFactory.show( $scope.params.taxon_id, shared.localeParams( ) ).then( function( response ) {
taxa = TaxaFactory.responseToInstances( response );
if( taxa.length > 0 ) {
$scope.selectedTaxon = taxa[ 0 ];
TaxaFactory.show( $scope.params.taxon_id, iNaturalist.localeParams( ) ).
then( function( response ) {
taxa = TaxaFactory.responseToInstances( response );
if( taxa.length > 0 ) {
$scope.selectedTaxon = taxa[ 0 ];
}
$scope.updateTaxonAutocomplete( );
$scope.taxonInitialized = true;
}
$scope.updateTaxonAutocomplete( );
$scope.taxonInitialized = true;
});
);
} else {
// this will remove the autocomlete image since there's no taxon
$scope.updateTaxonAutocomplete( );
$scope.taxonInitialized = true;
}
};
$scope.initializePlaceParams = function( ) {
if( $scope.params.place_id ) {
$scope.params.place_id = $scope.params["place_id[]"] || $scope.params.place_id;
if( _.isString( $scope.params.place_id ) ) {
$scope.params.place_id = _.filter( $scope.params.place_id.split(","), _.identity );
}
if( _.isArray( $scope.params.place_id ) && $scope.params.place_id.length === 1 ) {
$scope.params.place_id = $scope.params.place_id[0];
}
if( $scope.params.place_id && !_.isArray( $scope.params.place_id ) ) {
$scope.params.place_id = parseInt( $scope.params.place_id );
}
if( $scope.params.place_id ) {
if( $scope.params.place_id && !_.isArray( $scope.params.place_id ) ) {
// load place name and polygon from ID
PlacesFactory.show( $scope.params.place_id ).then( function( response ) {
places = PlacesFactory.responseToInstances( response );
Expand Down Expand Up @@ -291,6 +311,9 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
// set params from the URL and lookup any Taxon or Place selections
$scope.setInitialParams = function( ) {
var initialParams = _.extend( { }, $scope.defaultParams, $location.search( ) );
if( initialParams.verifiable === "true" ) {
initialParams.verifiable = true;
}
// turning the key taxon_ids[] into taxon_ids
if( initialParams["taxon_ids[]"] ) {
initialParams.taxon_ids = initialParams["taxon_ids[]"];
Expand Down Expand Up @@ -376,8 +399,9 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
createdDateType: $scope.params.createdDateType
});
// prepare current settings to store in browser state history
currentState = { params: stateParams, selectedPlace: $scope.selectedPlace,
selectedTaxon: $scope.selectedTaxon,
currentState = { params: stateParams,
selectedPlace: JSON.stringify( $scope.selectedPlace ),
selectedTaxon: JSON.stringify( $scope.selectedTaxon ),
mapBounds: $scope.mapBounds ? $scope.mapBounds.toJSON( ) : null,
mapBoundsIcon: $scope.mapBoundsIcon };
currentSearch = urlParams;
Expand Down Expand Up @@ -486,7 +510,7 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
$scope.numberTaxaShown = 15;
$scope.numberIdentifiersShown = 15;
$scope.numberObserversShown = 15;
$scope.observersSort = "observationCount";
$scope.observersSort = "-observationCount";
options = options || { };
$scope.updateBrowserLocation( options );
$scope.observations = [ ];
Expand All @@ -495,7 +519,7 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
$scope.observers = [ ];
$scope.resetStats( );
var processedParams = ObservationsFactory.processParamsForAPI( _.extend( { },
$scope.params, shared.localeParams( ) ),
$scope.params, iNaturalist.localeParams( ) ),
$scope.possibleFields);
// recording there was some location in the search. That will be used
// to hide the `Redo Search` button until the map moves
Expand Down Expand Up @@ -578,7 +602,7 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
$scope.pagination.section += 1;
$scope.pagination.searching = true;
var processedParams = ObservationsFactory.processParamsForAPI(
_.extend( { }, $scope.params, shared.localeParams( ),
_.extend( { }, $scope.params, iNaturalist.localeParams( ),
{ page: $scope.apiPage( ), per_page: $scope.pagination.perSection } ), $scope.possibleFields);
ObservationsFactory.search( processedParams ).then( function( response ) {
if( ( response.data.total_results <= ( response.data.page * response.data.per_page ) ) ||
Expand Down Expand Up @@ -644,6 +668,7 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
$rootScope.$emit( "hideNearbyPlace" );
$scope.selectedPlace = place;
$scope.removeSelectedBounds( );
$scope.updatePlaceAutocomplete( );
};
$scope.filterByBounds = function( ) {
$rootScope.$emit( "hideNearbyPlace" );
Expand Down Expand Up @@ -698,6 +723,7 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
if( !$( "#filter-container" ).is( e.target ) &&
$( "#filter-container" ).has( e.target ).length === 0 &&
$( ".open" ).has( e.target ).length === 0 &&
$( e.target ).parents('.ui-autocomplete').length === 0 &&
$( e.target ).parents('.ui-datepicker').length === 0 &&
$( e.target ).parents('.ui-datepicker-header').length === 0 &&
$( e.target ).parents('.ui-multiselect-menu').length === 0 &&
Expand Down Expand Up @@ -839,9 +865,9 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
resetOnChange: false,
bootstrapClear: true,
search_external: false,
taxon_id_el: $( "#filters input[name='taxon_id']" ),
id_el: $( "#filters input[name='taxon_id']" ),
afterSelect: function( result ) {
$scope.selectedTaxon = new iNatModels.Taxon( result.item );
$scope.selectedTaxon = result.item;
$scope.params.taxon_id = result.item.id;
$scope.searchAndUpdateStats( );
},
Expand All @@ -856,16 +882,97 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
};
$scope.updateTaxonAutocomplete = function( ) {
if( $scope.selectedTaxon ) {
var t = new iNatModels.Taxon( $scope.selectedTaxon );
$( "input[name='taxon_name']" ).trigger( "assignTaxon", t );
$( "input[name='taxon_name']" ).trigger( "assignSelection", $scope.selectedTaxon );
} else {
$( "#filters input[name='taxon_name']" ).trigger( "search" );
}
};
$scope.setupInatPlaceAutocomplete = function( ) {
$( "input[name='inat_place_name']" ).placeAutocomplete({
resetOnChange: false,
bootstrapClear: true,
id_el: $( "#filters input[name='place_id']" ),
afterSelect: function( result ) {
$scope.filterByPlace( result.item );
if(!$scope.$$phase) { $scope.$digest( ); }
},
afterUnselect: function( ) {
$scope.removeSelectedPlace( );
if(!$scope.$$phase) { $scope.$digest( ); }
}
});
};
$scope.updatePlaceAutocomplete = function( ) {
if( $scope.selectedPlace ) {
$scope.selectedPlace.title = $scope.selectedPlace.display_name;
$( "input[name='inat_place_name']" ).
trigger( "assignSelection", $scope.selectedPlace );
} else {
$( "#filters input[name='inat_place_name']" ).trigger( "search" );
}
};
$scope.setupUserAutocomplete = function( ) {
$( "input[name='user_name']" ).userAutocomplete({
resetOnChange: false,
bootstrapClear: true,
id_el: $( "#filters input[name='user_id']" ),
afterSelect: function( result ) {
$scope.params.user_id = result.item.login;
if(!$scope.$$phase) { $scope.$digest( ); }
},
afterUnselect: function( ) {
$scope.params.user_id = null;
if(!$scope.$$phase) { $scope.$digest( ); }
}
});
$scope.updateUserAutocomplete( );
};
$scope.updateUserAutocomplete = function( ) {
if( $scope.params.user_id ) {
$( "input[name='user_name']" ).
trigger( "assignSelection",
{ id: $scope.params.user_id, title: $scope.params.user_id } );
} else {
$( "#filters input[name='user_name']" ).trigger( "search" );
}
};
$scope.setupProjectAutocomplete = function( ) {
$( "input[name='project_name']" ).projectAutocomplete({
resetOnChange: false,
bootstrapClear: true,
id_el: $( "#filters input[name='project_id']" ),
afterSelect: function( result ) {
$scope.params.project_id = result.item.slug;
if(!$scope.$$phase) { $scope.$digest( ); }
},
afterUnselect: function( ) {
$scope.params.project_id = null;
if(!$scope.$$phase) { $scope.$digest( ); }
}
});
$scope.updateProjectAutocomplete( );
};
$scope.updateProjectAutocomplete = function( ) {
if( $scope.params.project_id ) {
$( "input[name='project_name']" ).
trigger( "assignSelection",
{ id: $scope.params.project_id, title: $scope.params.project_id } );
} else {
$( "#filters input[name='project_name']" ).trigger( "search" );
}
};
$scope.setupBrowserStateBehavior = function( ) {
window.onpopstate = function( event ) {
var state = _.extend( { }, event.state || $scope.initialBrowserState );
var previousParams = _.extend( { }, $scope.defaultParams, state.params );
// needed to serialize some objects for storing in browser state
// so now turn them back into object instances for comparison
if( state.selectedTaxon ) {
state.selectedTaxon = new iNatModels.Taxon( JSON.parse( state.selectedTaxon ) );
}
if( state.selectedPlace ) {
state.selectedPlace = new iNatModels.Place( JSON.parse( state.selectedPlace ) );
}
// we could set place and taxon below, and that should not run searches
$scope.searchDisabled = true;
$scope.mapBoundsIcon = state.mapBoundsIcon;
Expand Down Expand Up @@ -951,6 +1058,9 @@ function( ObservationsFactory, PlacesFactory, TaxaFactory, shared, $scope, $root
$scope.params.user_id = CURRENT_USER.login;
}
};
$scope.canShowObservationFields = function( ) {
return ($scope.params.observationFields && _.size( $scope.params.observationFields ) > 0);
}
$scope.setObservationFields = function( ) {
var urlParams = $location.search( );
// Put the URL params that correspond to observation fields in their own part of the scope
Expand Down Expand Up @@ -1230,7 +1340,7 @@ function( ObservationsFactory, PlacesFactory, shared, $scope, $rootScope ) {
I18n.t( "loading" ) + "</div>", options );
var time = new Date( ).getTime( );
$scope.infoWindowCallbackStartTime = time;
ObservationsFactory.show( observation_id, shared.localeParams( ) ).then( function( response ) {
ObservationsFactory.show( observation_id, iNaturalist.localeParams( ) ).then( function( response ) {
observations = ObservationsFactory.responseToInstances( response );
if( observations.length > 0 ) {
$scope.infoWindowObservation = observations[ 0 ];
Expand Down
73 changes: 73 additions & 0 deletions app/assets/javascripts/ang/controllers/project_stats_controller.js
@@ -0,0 +1,73 @@
var application = angular.module( "ProjectStats", [
"templates",
"iNatAPI"
]);

// used for displaying HTML returned from methods
application.filter( "sanitize", [ "$sce", function( $sce ) {
return function( safeHTML ) {
return $sce.trustAsHtml( safeHTML );
};
}]);

application.controller( "ProjectStatsController", [ "ObservationsFactory", "shared", "$scope",
function( ObservationsFactory, shared, $scope ) {
$scope.shared = shared;
// confusingly, this value gets set with /projects/show.js when it
// fetches observations. The project observations display on projects
// show still uses a Rails endpoint, so that JS was left alone for now.
$scope.observationsCount = "?";
$scope.speciesCount = "?";
$scope.observersCount = "?";
$scope.userObservationsLink = function( u ) {
return I18n.t( "x_observations_link_html", {
count: u.observation_count,
url: $scope.searchURL + "&user_id=" + u.login
});
};
$scope.userSpeciesLink = function( u ) {
return I18n.t( "x_species_link_html", {
count: u.species_count,
url: $scope.searchURL + "&user_id=" + u.login +
"&hrank=species&lrank=species&view=species"
});
};
$scope.taxonObservationsLink = function( t ) {
return I18n.t( "x_observations_link_html", {
count: t.result_count,
url: $scope.searchURL + "&taxon_id=" + t.id
});
};
$scope.initParams = function( p ) {
$scope.observationSearchParams = _.extend( { }, p );
};
$scope.$watch( "observationSearchParams", function( ) {
var statsParams = _.extend( { }, $scope.observationSearchParams,
iNaturalist.localeParams( ), { per_page: 5 });
$scope.searchURL = "/observations?" + $.param($scope.observationSearchParams);
ObservationsFactory.speciesCounts( statsParams ).then( function( response ) {
$scope.speciesCount = response.data.total_results;
$scope.taxa = _.map( response.data.results, function( r ) {
var t = new iNatModels.Taxon( r.taxon );
t.result_count = r.count;
return t;
});
});
ObservationsFactory.observers( statsParams ).then( function( response ) {
$scope.observersCount = response.data.total_results;
$scope.observers = _.map( response.data.results, function( r ) {
var u = new iNatModels.User( r.user );
u.observation_count = r.observation_count;
return u;
});
});
var speciesObserversParams = _.extend( { }, statsParams, { order_by: "species_count" });
ObservationsFactory.observers( speciesObserversParams ).then( function( response ) {
$scope.speciesObservers = _.map( response.data.results, function( r ) {
var u = new iNatModels.User( r.user );
u.species_count = r.species_count;
return u;
});
});
});
}]);

0 comments on commit ab79058

Please sign in to comment.