Created
August 25, 2017 23:42
-
-
Save mangalaman93/bba6675c3ca7273e27506b6a967664b1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"flag" | |
"io/ioutil" | |
"log" | |
"sort" | |
) | |
/*************************** meeting.go ***************************/ | |
type Invitee struct { | |
Id int `json:"id"` | |
Rank int `json:"rank"` | |
} | |
type Meeting struct { | |
MeetingId int `json:"meeting_id"` | |
OrganiserId int `json:"organiser_id"` | |
Rank int `json:"rank"` | |
BeginTime int64 `json:"begin_time"` | |
EndTime int64 `json:"end_time"` | |
Invitees []*Invitee `json:"invitees"` | |
} | |
func (m *Meeting) AverageInviteeRank() float64 { | |
sum := 0 | |
for _, i := range m.Invitees { | |
sum += i.Rank | |
} | |
return float64(sum) / float64(len(m.Invitees)) | |
} | |
func IsConflicting(m1, m2 *Meeting) bool { | |
if m1.BeginTime < m2.BeginTime { | |
return m1.EndTime > m2.BeginTime | |
} else { | |
return m2.EndTime > m1.BeginTime | |
} | |
} | |
// for sorting based on meeting begin time | |
type MeetingSlice []*Meeting | |
func (ms MeetingSlice) Len() int { | |
return len(ms) | |
} | |
func (ms MeetingSlice) Swap(i, j int) { | |
ms[i], ms[j] = ms[j], ms[i] | |
} | |
func (ms MeetingSlice) Less(i, j int) bool { | |
return ms[i].BeginTime < ms[j].BeginTime | |
} | |
// for sorting based on meeting priority | |
type MeetingPrioritySlice struct { | |
Meetings []*Meeting | |
CRE ConflictResolutionEngine | |
OwnerId int | |
} | |
func (mps MeetingPrioritySlice) Len() int { | |
return len(mps.Meetings) | |
} | |
func (mps MeetingPrioritySlice) Swap(i, j int) { | |
mps.Meetings[i], mps.Meetings[j] = mps.Meetings[j], mps.Meetings[i] | |
} | |
func (mps MeetingPrioritySlice) Less(i, j int) bool { | |
return mps.CRE.Less(mps.OwnerId, mps.Meetings[i], mps.Meetings[j]) | |
} | |
/*************************** calendar.go ***************************/ | |
type Calendar struct { | |
OwnerId int `json:"owner_id"` | |
Meetings []*Meeting `json:"meetings` | |
} | |
type EmptySlot struct { | |
BeginTime int64 `json:"begin_time"` | |
EndTime int64 `json:"end_time"` | |
} | |
func (c *Calendar) findConflicts() [][]*Meeting { | |
conflicts := make([][]*Meeting, 0) | |
for i := 0; i < len(c.Meetings); i++ { | |
for j := i + 1; j < len(c.Meetings); j++ { | |
if IsConflicting(c.Meetings[i], c.Meetings[j]) { | |
conflicts = append(conflicts, []*Meeting{c.Meetings[i], c.Meetings[j]}) | |
} | |
} | |
} | |
return conflicts | |
} | |
// TODO: we currently modify the list of meetings in a | |
// calendar to the resolved list of meetings | |
func (c *Calendar) resolveConflicts(cre ConflictResolutionEngine) []*Meeting { | |
mps := MeetingPrioritySlice{ | |
Meetings: c.Meetings, | |
CRE: cre, | |
OwnerId: c.OwnerId, | |
} | |
sort.Sort(mps) | |
for i := 0; i < len(c.Meetings); i++ { | |
if c.Meetings[i] == nil { | |
continue | |
} | |
for j := i + 1; j < len(c.Meetings); j++ { | |
if c.Meetings[j] == nil { | |
continue | |
} | |
if IsConflicting(c.Meetings[i], c.Meetings[j]) { | |
c.Meetings[j] = nil | |
} | |
} | |
} | |
resolved_meetings := make([]*Meeting, 0) | |
for i := 0; i < len(c.Meetings); i++ { | |
if c.Meetings[i] != nil { | |
// TODO: pointer copy only! | |
resolved_meetings = append(resolved_meetings, c.Meetings[i]) | |
} | |
} | |
c.Meetings = resolved_meetings | |
return c.Meetings | |
} | |
func (c *Calendar) FindFreeSlots() []*EmptySlot { | |
sort.Sort(MeetingSlice(c.Meetings)) | |
empty_slots := make([]*EmptySlot, 0) | |
for i := 1; i < len(c.Meetings); i++ { | |
if c.Meetings[i-1].EndTime != c.Meetings[i].BeginTime { | |
empty_slots = append(empty_slots, &EmptySlot{ | |
BeginTime: c.Meetings[i-1].EndTime, | |
EndTime: c.Meetings[i].BeginTime, | |
}) | |
} | |
} | |
return empty_slots | |
} | |
/*************************** engines.go ***************************/ | |
// return -1, 0, 1 | |
type Rule func(int, *Meeting, *Meeting) int | |
type ConflictResolutionEngine []Rule | |
// lower the value, higher the priority | |
func (cre ConflictResolutionEngine) Less(owner_id int, m1, m2 *Meeting) bool { | |
for _, rule := range cre { | |
ret := rule(owner_id, m1, m2) | |
if ret == 1 { | |
return true | |
} else if ret == -1 { | |
return false | |
} | |
} | |
// if no decision yet, simply return true | |
return true | |
} | |
func Rule1(owner_id int, m1, m2 *Meeting) int { | |
if m1.OrganiserId == m2.OrganiserId { | |
return 0 | |
} else { | |
if m1.OrganiserId == owner_id { | |
return 1 | |
} else if m2.OrganiserId == owner_id { | |
return -1 | |
} else { | |
return 0 | |
} | |
} | |
} | |
func Rule2(owner_id int, m1, m2 *Meeting) int { | |
if m1.Rank == m2.Rank { | |
return 0 | |
} else if m1.Rank < m2.Rank { | |
return 1 | |
} else { | |
return -1 | |
} | |
} | |
func Rule3(owner_id int, m1, m2 *Meeting) int { | |
if len(m1.Invitees) == len(m2.Invitees) { | |
return 0 | |
} else if len(m1.Invitees) > len(m2.Invitees) { | |
return 1 | |
} else { | |
return -1 | |
} | |
} | |
func Rule4(owner_id int, m1, m2 *Meeting) int { | |
m1_avg_rank := m1.AverageInviteeRank() | |
m2_avg_rank := m2.AverageInviteeRank() | |
if m1_avg_rank == m2_avg_rank { | |
return 0 | |
} else if m1_avg_rank < m2_avg_rank { | |
return 1 | |
} else { | |
return -1 | |
} | |
} | |
/*************************** input.json ***************************/ | |
// { | |
// "cmd": 1, | |
// "engine": "simple", | |
// "calendars": [ | |
// { | |
// "owner_id": 1, | |
// "meetings": [ | |
// { | |
// "meeting_id": 1, | |
// "organiser_id": 1, | |
// "rank": 1, | |
// "begin_time": 23, | |
// "end_time": 34, | |
// "invitees": [ | |
// { | |
// "id": 1, | |
// "rank": 1 | |
// } | |
// ] | |
// }, | |
// { | |
// "meeting_id": 2, | |
// "organiser_id": 1, | |
// "rank": 1, | |
// "begin_time": 26, | |
// "end_time": 35, | |
// "invitees": [ | |
// { | |
// "id": 1, | |
// "rank": 1 | |
// } | |
// ] | |
// } | |
// ] | |
// } | |
// ] | |
// } | |
/************************** main.go **************************/ | |
type Command struct { | |
Cmd int `json:"cmd"` | |
Engine string `json:"engine"` | |
Calendars []*Calendar `json:"calendars"` | |
} | |
func main() { | |
output_file := flag.String("o", "output.json", "output file to dump data into") | |
input_file := flag.String("i", "input.json", "input file to read json data from") | |
flag.Parse() | |
jdata, err := ioutil.ReadFile(*input_file) | |
if err != nil { | |
log.Println(err) | |
log.Fatalf("[ERROR] unable to read input file %s", *input_file) | |
} | |
var command Command | |
err = json.Unmarshal(jdata, &command) | |
if err != nil { | |
log.Println(err) | |
log.Fatal("[ERROR] unable to parse input file") | |
} | |
// define engines | |
SimpleEngine := ConflictResolutionEngine([]Rule{Rule1, Rule2, Rule3}) | |
ComplexEngine := ConflictResolutionEngine([]Rule{Rule1, Rule2, Rule3, Rule4}) | |
var cre ConflictResolutionEngine | |
if command.Engine == "simple" { | |
cre = SimpleEngine | |
} else if command.Engine == "complex" { | |
cre = ComplexEngine | |
} else { | |
log.Fatal("[ERROR] unknown conflict resolution engine!") | |
} | |
switch command.Cmd { | |
case 1: | |
// find list of conflicting meetings, given a calendar | |
calendar := command.Calendars[0] | |
data, err := json.MarshalIndent(calendar.findConflicts(), "", " ") | |
if err != nil { | |
log.Println(err) | |
log.Fatal("[ERROR] error in marshaling the conflicting meetings") | |
} | |
ioutil.WriteFile(*output_file, data, 0644) | |
case 2: | |
// find resolved calendar, given a calendar | |
calendar := command.Calendars[0] | |
data, err := json.MarshalIndent(calendar.resolveConflicts(cre), "", " ") | |
if err != nil { | |
log.Println(err) | |
log.Fatal("[ERROR] error in marshaling the resolved calendar") | |
} | |
ioutil.WriteFile(*output_file, data, 0644) | |
case 3: | |
// find free slots in a calendar | |
calendar := command.Calendars[0] | |
calendar.resolveConflicts(cre) | |
data, err := json.MarshalIndent(calendar.FindFreeSlots(), "", " ") | |
if err != nil { | |
log.Println(err) | |
log.Fatal("[ERROR] error in marshaling free meeting slots") | |
} | |
ioutil.WriteFile(*output_file, data, 0644) | |
case 4: | |
// find common free slot of given time in 2 given calendar | |
} | |
log.Println("[_INFO] stored output in", *output_file) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment