Skip to content

Commit

Permalink
Merge pull request #88 from Salesforce-org-Impact-Labs/housing-status
Browse files Browse the repository at this point in the history
Housing status
  • Loading branch information
mshanemc authored Jun 30, 2020
2 parents cdf30ac + d4a376c commit 4d2f9b9
Show file tree
Hide file tree
Showing 24 changed files with 578 additions and 61 deletions.
3 changes: 3 additions & 0 deletions force-app/main/default/classes/Scoring.cls
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,12 @@ public with sharing class Scoring {
} else if (rec.getValue(metricIterator.Label) != null) {
// the metric has a matching property on the ServiceRecommendationObject (ex: Distance) that is populated
serviceValue = (decimal) rec.getValue(metricIterator.Label);
} else if (rec.Matches.contains(metricIterator.Label)) {
serviceValue = 1;
} else {
continue;
}

if (serviceValue != null) {
// still here? we must have a service value. Create and calculate the new indicator
ServiceRecommendation.Indicator indicator = new ServiceRecommendation.Indicator();
Expand Down
16 changes: 16 additions & 0 deletions force-app/main/default/classes/ServiceRecommendation.cls
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ public class ServiceRecommendation {
@AuraEnabled
public List<Indicator> Indicators { get; set; }

@AuraEnabled
public List<String> Matches { get; set; }

// dynamic getter for supporting returning certain properties
public Decimal getValue(string property) {
if (property == 'Distance') {
Expand All @@ -81,6 +84,19 @@ public class ServiceRecommendation {
public Decimal IndicatorPercentile { get; set; }
}

public ServiceRecommendation addIndicator(
string typ,
string value,
decimal pct
) {
Indicator i = new Indicator();
i.IndicatorPercentile = pct;
i.IndicatorType = typ;
i.IndicatorValue = value;
this.Indicators.add(i);
return this;
}

public class Comment {
@AuraEnabled
public String CommentText { get; set; }
Expand Down
45 changes: 40 additions & 5 deletions force-app/main/default/classes/Test_getRecommendations.cls
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ public with sharing class Test_getRecommendations {
static final decimal lat = 37.7897;
static final decimal lon = -122.397;

static testMethod void testAddComment() {
test.startTest();
id newFeedItemId = getRecommendations.addNewComment(
getbadServiceId(),
'not that great'
);
test.stopTest();

FeedItem post = [
SELECT id, type, parentid
FROM FeedItem
WHERE id = :newFeedItemId
];
system.assertEquals(getbadServiceId(), post.ParentId);
}

static testMethod void GoodServiceLooksGood() {
test.startTest();
List<ServiceRecommendation> recs = getRecommendations.getRecommendations(
Expand All @@ -33,23 +49,28 @@ public with sharing class Test_getRecommendations {
System.assert(recs[goodServiceIndex].Distance >= 0);
System.assertEquals(1, recs[goodServiceIndex].Preferred);

System.assert(recs[goodServiceIndex].Matches.contains('Homeless'));
System.assertEquals(1, recs[goodServiceIndex].Comments.size());
System.assertEquals(
'It is great',
recs[goodServiceIndex].Comments[0].CommentText
);
// check for preferred factor
boolean DistanceMetricExists = false;

system.assertEquals(
true,
indicatorExists('Distance', recs[goodServiceIndex])
);
for (
ServiceRecommendation.Indicator indicator : recs[goodServiceIndex]
.Indicators
) {
if (indicator.IndicatorType == 'Distance') {
DistanceMetricExists = true;
}
// check for preferred factor
if (indicator.IndicatorType == 'Preferred') {
system.assertEquals('1', indicator.IndicatorValue);
}
if (indicator.IndicatorType == 'Homeless') {
system.assertEquals('1', indicator.IndicatorValue);
}
}
// system.assert(DistanceMetricExists);
}
Expand Down Expand Up @@ -153,6 +174,7 @@ public with sharing class Test_getRecommendations {
profile.Last_Name__c = c.LastName;
profile.Location__Latitude__s = lat;
profile.Location__Longitude__s = lon;
profile.Homeless__c = true;
insert Profile;

// create 2 services
Expand All @@ -164,6 +186,7 @@ public with sharing class Test_getRecommendations {
GoodService.Preferred__c = true;
GoodService.Location__Latitude__s = lat;
GoodService.Location__Longitude__s = lon;
GoodService.Homeless__c = true;
Services.add(GoodService);

Service__c TeenService = new Service__c();
Expand Down Expand Up @@ -237,4 +260,16 @@ public with sharing class Test_getRecommendations {
private static id getBadServiceId() {
return [SELECT id FROM Service__c WHERE Name = :badServiceName].Id;
}

private static Boolean indicatorExists(
string indicatorToFind,
ServiceRecommendation SR
) {
for (ServiceRecommendation.Indicator indicator : sr.Indicators) {
if (indicator.IndicatorType == indicatorToFind) {
return true;
}
}
return false;
}
}
95 changes: 41 additions & 54 deletions force-app/main/default/classes/getRecommendations.cls
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ public with sharing class getRecommendations {
@AuraEnabled
public static list<ServiceRecommendation> getRecommendations(id contactId) {
Client_Profile__c CP = getProfile(contactId);
List<Service__c> services = primaryQuery(CP);
List<Service__c> services = getRecommendationsServiceQuery.primaryQuery(CP);

// convert to ServiceRecommendation
list<ServiceRecommendation> output = servicesToRecommendations(services);
Expand All @@ -15,67 +15,24 @@ public with sharing class getRecommendations {
output = getComments(output);
// do the Star Rating for each service
output = addStars(output);

output = addMatches(output, CP);
// final Relevancy score + factors
output = Scoring.scoreRecommendations(output);

return output;
}

// list of services with any hard filters
// hard filters are currently Age
public static list<Service__c> primaryQuery(Client_Profile__c client) {
decimal age = calculateAge(client.Date_Of_Birth__c);
decimal defaultMaxDistance = [
SELECT Default_Max_Distance__c
FROM RefRecSettings__mdt
]
.Default_Max_Distance__c;
return [
SELECT
id,
Name,
Account__c,
Account__r.Name,
Description__c,
City__c,
Phone__c,
Street__c,
Type__c,
Website__c,
Zip_Code__c,
Preferred__c,
DISTANCE(
Location__c,
GEOLOCATION(:client.Location__Latitude__s,
:client.Location__Longitude__s
),
'mi'
) dist
FROM Service__c
WHERE
(Minimum_Age__c = null
OR Minimum_Age__c < :age)
AND (Maximum_Age__c = null
OR Maximum_Age__c > :age)
AND DISTANCE(
Location__c,
GEOLOCATION(:client.Location__Latitude__s,
:client.Location__Longitude__s
),
'mi'
) < :defaultMaxDistance
];
}

public static List<ServiceRecommendation> servicesToRecommendations(
List<Service__c> services
) {
List<ServiceRecommendation> output = new List<ServiceRecommendation>();
for (Service__c service : services) {
ServiceRecommendation SR = new ServiceRecommendation();
SR.Service = service;
SR.Distance = (decimal)service.get('dist');
SR.Distance = (decimal) service.get('dist');
SR.Relevance = 0;
SR.Matches = new List<String>();
SR.ServiceId = service.Id;
SR.ProviderName = service.Account__r.Name;
SR.AccountId = service.Account__c;
Expand All @@ -90,7 +47,7 @@ public with sharing class getRecommendations {
SR.totalRatings = 0;
SR.Indicators = new List<ServiceRecommendation.Indicator>();
SR.Comments = new List<ServiceRecommendation.Comment>();
SR.Preferred = service.Preferred__c ? 1 : 0; //translate checkbox to 1,0 value
SR.Preferred = service.Preferred__c ? 1 : 0; //translate checkbox to 1,0 value
output.add(SR);
}
return output;
Expand Down Expand Up @@ -130,6 +87,31 @@ public with sharing class getRecommendations {
return recs;
}

public static list<ServiceRecommendation> addMatches(
List<ServiceRecommendation> recs,
Client_Profile__c CP
) {
for (ServiceRecommendation rec : recs) {
// housing fields are all checkboxes on both sides
if (rec.Service.Homeless__c && CP.Homeless__c) {
rec.Matches.add('Homeless');
}
if (rec.Service.Near_Homeless__c && CP.Near_Homeless__c) {
rec.Matches.add('NearHomeless');
}
if (rec.Service.Runaway__c && CP.Runaway__c) {
rec.Matches.add('Runaway');
}
if (rec.Service.Home_Owner__c && CP.Home_Owner__c) {
rec.Matches.add('Owner');
}
if (rec.Service.Home_Renter__c && CP.Home_Renter__c) {
rec.Matches.add('Renter');
}
}
return recs;
}

public static list<ServiceRecommendation> addHides(
List<ServiceRecommendation> recs,
id contactId
Expand Down Expand Up @@ -224,7 +206,16 @@ public with sharing class getRecommendations {

public static Client_Profile__c getProfile(id contactId) {
Client_Profile__c output = [
SELECT id, Date_Of_Birth__c, Location__Latitude__s, Location__Longitude__s
SELECT
id,
Date_Of_Birth__c,
Location__Latitude__s,
Location__Longitude__s,
Near_Homeless__c,
Homeless__c,
Runaway__c,
Home_Owner__c,
Home_Renter__c
FROM Client_Profile__c
WHERE Contact__c = :contactId
];
Expand All @@ -245,10 +236,6 @@ public with sharing class getRecommendations {
return output;
}

public static decimal calculateAge(Date DOB) {
return DOB.daysBetween(system.today()) / 365;
}

public static list<id> IDsFromRecommendations(
List<ServiceRecommendation> recs
) {
Expand Down
57 changes: 57 additions & 0 deletions force-app/main/default/classes/getRecommendationsServiceQuery.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
public with sharing class getRecommendationsServiceQuery {

// list of services with any hard filters
// hard filters are currently Age
public static list<Service__c> primaryQuery(Client_Profile__c client) {
decimal age = calculateAge(client.Date_Of_Birth__c);
decimal defaultMaxDistance = [
SELECT Default_Max_Distance__c
FROM RefRecSettings__mdt
]
.Default_Max_Distance__c;
return [
SELECT
id,
Name,
Account__c,
Account__r.Name,
Description__c,
City__c,
Phone__c,
Street__c,
Type__c,
Website__c,
Zip_Code__c,
Preferred__c,
Near_Homeless__c,
Homeless__c,
Runaway__c,
Home_Owner__c,
Home_Renter__c,
DISTANCE(
Location__c,
GEOLOCATION(:client.Location__Latitude__s,
:client.Location__Longitude__s
),
'mi'
) dist
FROM Service__c
WHERE
(Minimum_Age__c = null
OR Minimum_Age__c < :age)
AND (Maximum_Age__c = null
OR Maximum_Age__c > :age)
AND DISTANCE(
Location__c,
GEOLOCATION(:client.Location__Latitude__s,
:client.Location__Longitude__s
),
'mi'
) < :defaultMaxDistance
];
}

public static decimal calculateAge(Date DOB) {
return DOB.daysBetween(system.today()) / 365;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>48.0</apiVersion>
<status>Active</status>
</ApexClass>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<label>Homeless</label>
<protected>false</protected>
<values>
<field>Based_on_Service_Field__c</field>
<value xsi:type="xsd:boolean">false</value>
</values>
<values>
<field>Calculate_User_Level__c</field>
<value xsi:type="xsd:boolean">false</value>
</values>
<values>
<field>Description__c</field>
<value xsi:type="xsd:string">weighting factor for matches on housing status</value>
</values>
<values>
<field>Org_Level__c</field>
<value xsi:type="xsd:boolean">true</value>
</values>
<values>
<field>Org_Maximum__c</field>
<value xsi:type="xsd:double">1.0</value>
</values>
<values>
<field>Org_Minimum__c</field>
<value xsi:type="xsd:double">0.0</value>
</values>
<values>
<field>Org_SOQL_Query__c</field>
<value xsi:nil="true"/>
</values>
<values>
<field>Service_Level_SOQL_Query__c</field>
<value xsi:nil="true"/>
</values>
<values>
<field>User_ID__c</field>
<value xsi:nil="true"/>
</values>
<values>
<field>Weighting__c</field>
<value xsi:type="xsd:double">5.0</value>
</values>
</CustomMetadata>
Loading

0 comments on commit 4d2f9b9

Please sign in to comment.