这是indexloc提供的服务,不要输入任何密码
Skip to content

Instantly share code, notes, and snippets.

@Galadros
Last active November 21, 2019 00:03
Show Gist options
  • Save Galadros/be1e19cddb85808298960b6e10705729 to your computer and use it in GitHub Desktop.
Save Galadros/be1e19cddb85808298960b6e10705729 to your computer and use it in GitHub Desktop.
Luma Health
// INSTRUCTIONS FOR USAGE:
// Enter the latitude and longitude separated by spaces after calling the executable.
package main
import (
"encoding/json"
"fmt"
"math/rand"
"os"
"sort"
"time"
)
//var inputDatabase string = "/home/patrick/Coding_Challenges/go_code/src/lumaHealth/patientWaitlist/full-stack-interview/sample-data/patients.json"
var inputDatabase string = "full-stack-interview/sample-data/patients.json"
var clinicLocation Location
func main() {
if len(os.Args) != 3 {
fmt.Println("Wrong number of arguments provided.")
os.Exit(1)
}
inputLatitude := string(os.Args[1])
inputLongitude := string(os.Args[2])
rand.Seed(time.Now().UTC().UnixNano())
bestPatients := getBestPatients(Location{inputLatitude, inputLongitude})
fmt.Printf("\n")
fmt.Printf("%+v\n", bestPatients)
fmt.Printf("\n")
fmt.Printf("\n")
fmt.Printf("\n")
for _, currPatient := range bestPatients {
fmt.Printf("Name: %s, Score: %f, ID: %s\n", currPatient.Name, currPatient.Scores.FinalScore, currPatient.ID)
}
}
func getBestPatients(inputLoc Location) []Patient {
patientList := make([]Patient, 0)
clinicLocation = inputLoc
// Getting a sense for the data distribution
patientData, err := os.Open(inputDatabase)
if err != nil {
fmt.Println("Couldn't parse the database file provided.")
os.Exit(1)
}
patientDecoder := json.NewDecoder(patientData)
if decodeErr := patientDecoder.Decode(&patientList); decodeErr != nil {
fmt.Println(decodeErr)
os.Exit(1)
}
lottery := CalculateScores(patientList)
sort.Sort(ByFinalScore(patientList))
outputPatients := make([]Patient, 10)
currIndex := 0
// Give a random bump to patients with low activity recorded- 15% of slots are randomly split among folks with low activity.
for i := 0; i < 10; i ++ {
if rand.Float64() < 0.15 {
randIndex := rand.Intn(len(lottery))
outputPatients[i] = lottery[randIndex]
lottery[randIndex], lottery[len(lottery)-1] = lottery[len(lottery)-1], lottery[randIndex]
lottery = lottery[:len(lottery)-1]
} else {
outputPatients[i] = patientList[currIndex]
currIndex += 1
}
}
return outputPatients
}
// In general, this calculates a score from 1-10 based off of the patient's ranking in the database.
// Returns a list of low-activity patients for later random insertion.
func CalculateScores (database []Patient) []Patient {
numPatients := float64(len(database))
sort.Sort(ByDistance(database))
for i, _ := range database {
database[i].Scores.Distance = 1 + 9*float64(i)/numPatients
}
sort.Sort(ByAge(database))
for i, _ := range database {
database[i].Scores.Age = 1 + 9*float64(i)/numPatients
}
sort.Sort(ByAccepted(database))
for i, _ := range database {
database[i].Scores.AcceptedOffers = 1 + 9*float64(i)/numPatients
}
// This gives a low value to patients with many cancelled offers and a high one to patients with few cancelled offers.
sort.Sort(ByCancelled(database))
for i, _ := range database {
database[i].Scores.CancelledOffers = 1 + 9*float64(i)/numPatients
}
sort.Sort(ByReplyTime(database))
for i, _ := range database {
database[i].Scores.AvgReplyTime = 1 + 9*float64(i)/numPatients
}
totalOffers := 0
for _, currPatient := range database {
totalOffers += currPatient.AcceptedOffers
totalOffers += currPatient.CancelledOffers
}
avgOffers := float64(totalOffers)/numPatients
// Calculate final score. Distance and cancelled offers are weighted negatively (i.e. a higher distance or higher
// number of cancelled appointments leads to a lower score).
// Given the input parameters I have (specifying a weight of 10% to age) I'm choosing to prioritize older patients,
// as they are more likely to need imminent service.
lowActPatients := make([]Patient, 0)
for i, _ := range database {
database[i].Scores.FinalScore = 0.1*database[i].Scores.Age + 0.1*database[i].Scores.Distance + 0.3*database[i].Scores.AcceptedOffers+ 0.3*database[i].Scores.CancelledOffers+ 0.2*database[i].Scores.AvgReplyTime
// Construct list of low-activity patients for possible use later
if totalOffers := float64(database[i].AcceptedOffers + database[i].CancelledOffers); totalOffers < 0.3*avgOffers {
if rand.Float64() * 0.3 - (totalOffers/avgOffers) > 0 {
lowActPatients = append(lowActPatients, database[i])
}
}
}
return lowActPatients
}
package main
import (
"fmt"
"math"
"os"
"strconv"
)
// Given the input parameters I have (specifying a weight of 10% to age) I'm choosing to prioritize older patients,
// as they are more likely to need imminent service.
type ByAge []Patient
func (t ByAge) Len() int {return len(t)}
func (t ByAge) Less(i, j int) bool {return t[i].Age < t[j].Age}
func (t ByAge) Swap(i, j int) {t[i], t[j] = t[j], t[i]}
//Sorts by distance to a clinic. Note that smaller values are better in this case.
type ByDistance []Patient
func (t ByDistance) Len() int {return len(t)}
func (t ByDistance) Less(i, j int) bool {
lat1, _ := strconv.ParseFloat(t[i].PatLocation.Latitude, 64)
long1, _ := strconv.ParseFloat(t[i].PatLocation.Longitude, 64)
lat2, _ := strconv.ParseFloat(t[j].PatLocation.Latitude, 64)
long2, _ := strconv.ParseFloat(t[j].PatLocation.Longitude, 64)
clinicLat, err1 := strconv.ParseFloat(clinicLocation.Latitude, 64)
clinicLong, err2 := strconv.ParseFloat(clinicLocation.Longitude, 64)
if !(err1 == nil && err2 == nil) {
fmt.Println("Input location invalid. Please try again.")
os.Exit(1)
}
return math.Sqrt(math.Pow(clinicLat-lat1, 2) + math.Pow(clinicLong-long1, 2)) > math.Sqrt(math.Pow(clinicLat-lat2, 2) + math.Pow(clinicLong-long2, 2))
}
func (t ByDistance) Swap(i, j int) {t[i], t[j] = t[j], t[i]}
type ByAccepted []Patient
func (t ByAccepted) Len() int {return len(t)}
func (t ByAccepted) Less(i, j int) bool {return t[i].AcceptedOffers < t[j].AcceptedOffers}
func (t ByAccepted) Swap(i, j int) {t[i], t[j] = t[j], t[i]}
// Note that the order of this sort is reversed from the previous.
type ByCancelled []Patient
func (t ByCancelled) Len() int {return len(t)}
func (t ByCancelled) Less(i, j int) bool {return t[i].CancelledOffers > t[j].CancelledOffers}
func (t ByCancelled) Swap(i, j int) {t[i], t[j] = t[j], t[i]}
type ByReplyTime []Patient
func (t ByReplyTime) Len() int {return len(t)}
func (t ByReplyTime) Less(i, j int) bool {return t[i].AvgReplyTime < t[j].AvgReplyTime}
func (t ByReplyTime) Swap(i, j int) {t[i], t[j] = t[j], t[i]}
type ByFinalScore []Patient
func (t ByFinalScore) Len() int {return len(t)}
func (t ByFinalScore) Less(i, j int) bool {return t[i].Scores.FinalScore > t[j].Scores.FinalScore}
func (t ByFinalScore) Swap(i, j int) {t[i], t[j] = t[j], t[i]}
package main
type Patient struct {
ID string `json:"id"`
Name string `json:"name"`
PatLocation Location `json:"location"`
Age int `json:"age"`
AcceptedOffers int `json:"acceptedOffers"`
CancelledOffers int `json:"canceledOffers"`
AvgReplyTime int `json:"averageReplyTime"`
Scores PatientScore //This one is just for internal sorting
}
type Location struct {
Latitude string `json:"latitude"`
Longitude string `json:"longitude"`
}
// Tracks patient rankings in order to determine which scores to assign.
type PatientScore struct {
//ID string
//Name string
Distance float64
Age float64
AcceptedOffers float64
CancelledOffers float64 //Will be weighted negatively
AvgReplyTime float64
FinalScore float64
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment