Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Weather/Time Context every minute, without needing a location change #140

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .meteor/packages
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ fortawesome:fontawesome
dburles:collection-helpers
meanware:gridfs
nudelta2015:[email protected]
littledata:synced-cron
1 change: 1 addition & 0 deletions .meteor/versions
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ iron:[email protected]
iron:[email protected]
[email protected]
[email protected]
littledata:[email protected]
[email protected]
[email protected]
[email protected]
Expand Down
106 changes: 78 additions & 28 deletions imports/api/UserMonitor/locations/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,7 @@ export const onLocationUpdate = (uid, location, callback) => {

// get affordances and begin coordination process
getAffordancesFromLocation(uid, location, retrievePlaces, function (uid, bgLocationObject, affordances) {

log.info(`success: getAffordancesFromLocation`);
// get affordances via affordance aware
let user = Meteor.users.findOne({_id: uid});
if (!user) {
log.error(`uid = ${uid} passed to getAffordancesFromLocation did not correspond to a user `);
return;
}
let userAffordances = user.profile.staticAffordances;
affordances = Object.assign({}, affordances, userAffordances);
affordances = affordances !== null ? affordances : {};
serverLog.call({message: `affordances ${affordances}`});
affordances = mergeUsersStaticAffordances(uid, affordances);

// blocking, since everything in system works off of Locations collection
updateLocationInDb(uid, bgLocationObject, affordances);
Expand All @@ -90,26 +79,85 @@ export const onLocationUpdate = (uid, location, callback) => {
insertEstimatedLocationLog(uid, bgLocationObject, affordances, rawLocationId);
callback(uid);

// clear assignments and begin matching
let newLoc = Locations.findOne({uid: uid});
if (!newLoc) {
log.error(`uid = ${uid} did not have a current location in Locations collection`);
return;
}
let newAffs = newLoc.affordances; // newest affordances
// TODO(rlouie): Ask Kapil -- why do this intersection???
let sharedKeys = _.intersection(Object.keys(newAffs), Object.keys(affordances));
let sharedAffs = [];
_.forEach(sharedKeys, (key) => {
sharedAffs[key] = newAffs[key];
});

decomissionFromAssignmentsIfAppropriate(uid, sharedAffs);
sendToMatcher(uid, sharedAffs);
coordinateUsersToNeeds(uid, affordances);
});
}
};

export const onTimeElapsedUpdateTimeWeatherContext = (uid, callback) => {
// (1) gets the last location for the user
let currentLocation = Locations.findOne({uid: uid});
if (!currentLocation) {
log.error(`uid = ${uid} did not have a current location in Locations collection`);
return;
}

const backgroundGeolocationObject = {
coords: {
latitude: currentLocation.lat,
longitude: currentLocation.lng,
}
}

// (2) uses this to get the newest time and weather features
// FIXME: I don't have time to write/test this optimization of only getting time/weather. It'll get place affordances too (cached of course), and update the location in the normal way
const retrievePlaces = true;
getAffordancesFromLocation(uid, backgroundGeolocationObject, retrievePlaces, function(uid, bgLocationObject, affordances) {
affordances = mergeUsersStaticAffordances(uid, affordances);

// (3) update the user's newest affordances (time and weather features), in the Locations collection
updateLocationInDb(uid, bgLocationObject, affordances);
callback(uid);

// (4) coordinate users to needs
coordinateUsersToNeeds(uid, affordances);
});
};

/**
* Queries the static affordances defined in the Users collection, and merges that Object with current environmental affordances
* @param {*} uid
* @param {*} affordances
* @returns affordance dictionary, with static affordances merged
*/
const mergeUsersStaticAffordances = function(uid, affordances) {
let user = Meteor.users.findOne({_id: uid});
if (!user) {
log.error(`uid = ${uid} passed to getAffordancesFromLocation did not correspond to a user `);
return;
}
let userAffordances = user.profile.staticAffordances;
affordances = Object.assign({}, affordances, userAffordances);
affordances = affordances !== null ? affordances : {};
serverLog.call({message: `affordances ${affordances}`});
return affordances;
};

/**
* Accesses the current location and affordance information;
* Clears assignments for a user if they have exited certain situations;
* then sends them to the matcher.
* @param {*} uid
* @param {*} affordances
*/
const coordinateUsersToNeeds = (uid, affordances) => {
let newLoc = Locations.findOne({uid: uid});
if (!newLoc) {
log.error(`uid = ${uid} did not have a current location in Locations collection`);
return;
}
let newAffs = newLoc.affordances; // newest affordances
// TODO(rlouie): Ask Kapil -- why do this intersection???
let sharedKeys = _.intersection(Object.keys(newAffs), Object.keys(affordances));
let sharedAffs = [];
_.forEach(sharedKeys, (key) => {
sharedAffs[key] = newAffs[key];
});

decomissionFromAssignmentsIfAppropriate(uid, sharedAffs);
sendToMatcher(uid, sharedAffs);
};

/**
* Finds the matches (findMatchesFunction in User::Experience Matcher) for the user for a user's
* location update and sends found matches to the OpportunisticCoordinator.
Expand Down Expand Up @@ -385,6 +433,8 @@ const updateLocationInDb = (uid, location, affordances) => {
};




const insertEstimatedLocationLog = (uid, location, affordances, rawLocationId) => {
let lat = location.coords.latitude;
let lng = location.coords.longitude;
Expand Down
1 change: 1 addition & 0 deletions imports/api/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const CONFIG = {
MODE: process.env.MODE || "local",
DEBUG: true,
BUNDLE_IDENTIFIER: 'edu.northwestern.delta.D',
CONTEXT_POLL_INTERVAL: 60, // seconds
};

export const AUTH = {
Expand Down
21 changes: 21 additions & 0 deletions imports/startup/server/fixtures.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { Random } from 'meteor/random'
import {SyncedCron} from 'meteor/littledata:synced-cron';

import { CONFIG } from '../../api/config.js';
import { Experiences, Incidents } from '../../api/OCEManager/OCEs/experiences.js';
Expand All @@ -14,6 +15,7 @@ import { CONSTANTS } from "../../api/Testing/testingconstants";
import { createIncidentFromExperience, startRunningIncident } from "../../api/OCEManager/OCEs/methods.js";
import { findUserByUsername } from '../../api/UserMonitor/users/methods';
import { Detectors } from "../../api/UserMonitor/detectors/detectors";
import { onTimeElapsedUpdateTimeWeatherContext } from '../../api/UserMonitor/locations/methods';

Meteor.startup(() => {
log.debug(`Running in mode: ${process.env.MODE}`);
Expand All @@ -24,6 +26,25 @@ Meteor.startup(() => {
createTestData();
}
}

SyncedCron.add({
name: 'Update weather and time context for all users at a set interval',
schedule: function(parser) {
// parser is a later.parse object
return parser.text(`every ${CONFIG.CONTEXT_POLL_INTERVAL} seconds`);
},
job: function() {
let locationObjects = Locations.find().fetch()
locationObjects.forEach(location => {
onTimeElapsedUpdateTimeWeatherContext(location.uid, function(uid) {
console.log(`Updated weather and time context for user ${uid}`);
});
});
}
});

SyncedCron.start();

});

Meteor.methods({
Expand Down
Loading