From c8a3c852d830ddf285bd219354c53076878d0288 Mon Sep 17 00:00:00 2001 From: Ka-Ping Yee Date: Sun, 15 May 2016 03:36:35 -0700 Subject: [PATCH 1/3] Filter interviewees by geo distance, using #query[ll]=...&query[miles]=... --- src/backend/data/schema.js | 32 ++++++++++++++++++++--- src/frontend/components/CallAssignment.js | 32 ++++++++++++++++++++--- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/backend/data/schema.js b/src/backend/data/schema.js index 7003c46f..6efc7ff1 100644 --- a/src/backend/data/schema.js +++ b/src/backend/data/schema.js @@ -456,6 +456,14 @@ const GraphQLListContainer = new GraphQLObjectType({ interfaces: [nodeInterface] }) +const GeoPoint = new GraphQLInputObjectType({ + name: 'GeoPoint', + fields: { + lat: {type: GraphQLFloat}, + lon: {type: GraphQLFloat}, + } +}) + const GraphQLUser = new GraphQLObjectType({ name: 'User', description: 'User of ground control', @@ -554,10 +562,13 @@ const GraphQLUser = new GraphQLObjectType({ intervieweeForCallAssignment: { type: GraphQLPerson, args: { - callAssignmentId: {type: GraphQLString} + callAssignmentId: {type: GraphQLString}, + center: {type: GeoPoint}, + radiusMeters: {type: GraphQLFloat} }, - resolve: async(user, {callAssignmentId}, {rootValue}) => { - + resolve: async( + user, {callAssignmentId, center, radiusMeters}, {rootValue} + ) => { let localCallAssignmentId = fromGlobalId(callAssignmentId) if (localCallAssignmentId.type !== 'CallAssignment') localCallAssignmentId = callAssignmentId @@ -647,6 +658,21 @@ const GraphQLUser = new GraphQLObjectType({ if (userAddress) query = query.whereNot('bsd_people.cons_id', userAddress.cons_id) + // Filter by distance from a geographical point. + // Spatial ref 4326 is WGS 84, in degrees + // Spatial ref 900913 is Google Web Mercator, in meters + if (center && radiusMeters > 0) { + query = query.whereRaw(` + ST_DWithin(bsd_addresses.geom, + ST_Transform( + ST_SetSRID(ST_MakePoint(${center.lon}, ${center.lat}), 4326), + 900913 + ), + ${radiusMeters} + ) + `) + } + log.info(`Running query: ${query}`) let person = await query diff --git a/src/frontend/components/CallAssignment.js b/src/frontend/components/CallAssignment.js index 3ebc24a9..05bc2ca8 100644 --- a/src/frontend/components/CallAssignment.js +++ b/src/frontend/components/CallAssignment.js @@ -12,6 +12,7 @@ import SubmitCallSurvey from '../mutations/SubmitCallSurvey' import CallStatsBar from './CallStatsBar' import MutationHandler from './MutationHandler' import {PhoneNumberFormat, PhoneNumberUtil} from 'google-libphonenumber' +import getDefaultRelayParams from '../helpers/getDefaultRelayParams' const phoneUtil = PhoneNumberUtil.getInstance() const SurveyRenderers = { @@ -408,8 +409,29 @@ ${userFirstName}` } } +// Convert the hash parameters 'query[ll]' and 'query[miles]' into the +// API arguments for the center and radius of the filter region. +var center = null; +var radiusMeters = null; + +var hashParams = getDefaultRelayParams({ + ll: null, // URL hash parameter: point in lat,lon format + miles: null // URL hash parameter: radius in miles +}) +var llParts = (hashParams.ll || '').split(',') +if (llParts.length === 2) { + center = {lat: Number(llParts[0]), lon: Number(llParts[1])} +} +if (hashParams.miles) { + radiusMeters = Number(hashParams.miles) * 1609.34 +} + export default Relay.createContainer(CallAssignment, { - initialVariables: { id: '' }, + initialVariables: { + id: '', + center: center, + radiusMeters: radiusMeters + }, fragments: { callAssignment: () => Relay.QL` fragment on CallAssignment { @@ -428,9 +450,11 @@ export default Relay.createContainer(CallAssignment, { fragment on User { id firstName - allCallsMade:callsMade(forAssignmentId:$id) - completedCallsMade:callsMade(forAssignmentId:$id,completed:true) - intervieweeForCallAssignment(callAssignmentId:$id) { + allCallsMade: callsMade(forAssignmentId: $id) + completedCallsMade: callsMade(forAssignmentId: $id, completed: true) + intervieweeForCallAssignment( + callAssignmentId: $id, center: $center, radiusMeters: $radiusMeters + ) { id prefix firstName From 458a2e6a6b824198cd1a1977381bcf2fe86df918 Mon Sep 17 00:00:00 2001 From: Ka-Ping Yee Date: Tue, 17 May 2016 12:51:14 -0700 Subject: [PATCH 2/3] Split #query[ll]= into #query[lat]= and #query[lon]= params. --- src/frontend/components/CallAssignment.js | 25 +++++++++-------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/frontend/components/CallAssignment.js b/src/frontend/components/CallAssignment.js index 05bc2ca8..f7f3407c 100644 --- a/src/frontend/components/CallAssignment.js +++ b/src/frontend/components/CallAssignment.js @@ -409,22 +409,17 @@ ${userFirstName}` } } -// Convert the hash parameters 'query[ll]' and 'query[miles]' into the -// API arguments for the center and radius of the filter region. -var center = null; -var radiusMeters = null; - -var hashParams = getDefaultRelayParams({ - ll: null, // URL hash parameter: point in lat,lon format - miles: null // URL hash parameter: radius in miles +// Convert the hash parameters 'query[lat]', 'query[lon]', and 'query[miles]' +// into the API arguments for the center and radius of the filter region. +const hashParams = getDefaultRelayParams({ + lat: null, // latitude in degrees north + lon: null, // longitude in degrees east + miles: null // radius in miles }) -var llParts = (hashParams.ll || '').split(',') -if (llParts.length === 2) { - center = {lat: Number(llParts[0]), lon: Number(llParts[1])} -} -if (hashParams.miles) { - radiusMeters = Number(hashParams.miles) * 1609.34 -} +const center = (isFinite(hashParams.lat) && isFinite(hashParams.lon)) ? + {lat: hashParams.lat, lon: hashParams.lon} : null +const radiusMeters = isFinite(hashParams.miles) ? + hashParams.miles * 1609.34 : null export default Relay.createContainer(CallAssignment, { initialVariables: { From 8c06640b3658633371e24632f9e3a26787646b88 Mon Sep 17 00:00:00 2001 From: Ka-Ping Yee Date: Sat, 21 May 2016 05:55:31 -0700 Subject: [PATCH 3/3] Filter just official events when #query[official]=true is given. --- src/backend/data/schema.js | 9 +++++++-- .../survey-renderers/PhonebankRSVPSurvey.js | 11 +++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/backend/data/schema.js b/src/backend/data/schema.js index 6efc7ff1..e156650d 100644 --- a/src/backend/data/schema.js +++ b/src/backend/data/schema.js @@ -809,9 +809,10 @@ const GraphQLPerson = new GraphQLObjectType({ type: new GraphQLList(GraphQLEvent), args: { within: {type: GraphQLInt}, - type: {type: GraphQLString} + type: {type: GraphQLString}, + officialOnly: {type: GraphQLBoolean} }, - resolve: async(person, {within, type}, {rootValue}) => { + resolve: async(person, {within, type, officialOnly}, {rootValue}) => { let address = await getPrimaryAddress(person); let boundingDistance = within / 69 let eventTypes = null @@ -827,6 +828,10 @@ const GraphQLPerson = new GraphQLObjectType({ .where('flag_approval', false) .whereNot('is_searchable', 0) + if (officialOnly) { + query = query.where('is_official', true) + } + if (eventTypes) query = query.whereIn('event_type_id', eventTypes.map((type) => type.event_type_id)) diff --git a/src/frontend/components/survey-renderers/PhonebankRSVPSurvey.js b/src/frontend/components/survey-renderers/PhonebankRSVPSurvey.js index 71f314c9..97d914f9 100644 --- a/src/frontend/components/survey-renderers/PhonebankRSVPSurvey.js +++ b/src/frontend/components/survey-renderers/PhonebankRSVPSurvey.js @@ -9,6 +9,7 @@ import FontIcon from 'material-ui/lib/font-icon' import SideBarLayout from '../SideBarLayout' import GCSelectField from '../forms/GCSelectField' import GCBooleanField from '../forms/GCBooleanField' +import getDefaultRelayParams from '../../helpers/getDefaultRelayParams' const WEEKDAY_DATE_FORMAT = 'dddd, MMMM Do' @@ -544,9 +545,15 @@ class PhonebankRSVPSurvey extends React.Component { } } +const hashParams = getDefaultRelayParams({ + official: null // boolean +}) +const officialOnly = hashParams.official ? true : false; + export default Relay.createContainer(PhonebankRSVPSurvey, { initialVariables: { - type: 'phonebank' + type: 'phonebank', + officialOnly: officialOnly }, fragments: { currentUser: () => Relay.QL` @@ -562,7 +569,7 @@ export default Relay.createContainer(PhonebankRSVPSurvey, { latitude longitude } - nearbyEvents(within:20, type:$type) { + nearbyEvents(within:20, type:$type, officialOnly:$officialOnly) { id eventIdObfuscated name