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

Degree of Separation #46

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
307 changes: 307 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
package main
// try to run `go run main.go amitabh-bachchan rajinikanth`
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"sync"
"time"
)

type movieMember interface {
GetName() string
GetURL() string
GetRole() string
}

type movieMemberWrapper struct {
Members []movieMember
}

func (m movieMemberWrapper) GetURL() string {
if len(m.Members) > 0 {
return m.Members[0].GetURL()
}
return ""
}

func (m movieMemberWrapper) AddMembers(members ...movieMember) {
m.Members = append(m.Members, members...)
}

type dos struct {
Movie string
Person1, Role1 string
Person2, Role2 string
}

type personInfo struct {
Name string
URL string
Movies []movie
Cast []cast
Crew []crew
}

type movie struct {
Name string `json:"name"`
URL string `json:"url"`
Type string `json:"type"`
Role string `json:"role"`
}

type cast struct {
Name string `json:"name"`
URL string `json:"url"`
Role string `json:"role"`
}

type crew struct {
Name string `json:"name"`
URL string `json:"url"`
Role string `json:"role"`
}

func (c cast) GetName() string {
return c.Name
}

func (c crew) GetName() string {
return c.Name
}
func (c cast) GetURL() string {
return c.URL
}

func (c crew) GetURL() string {
return c.URL
}

func (c cast) GetRole() string {
return c.Role
}
func (c crew) GetRole() string {
return c.Role
}

const source = "http://data.moviebuff.com/"

type MovieBuff struct {
Source, Destination string
Person1, Person2 *personInfo
Person2Movies map[string]movie
VisitedPerson map[string]bool
Visit []string
Visited map[string]bool
Link map[string]dos
Mutex sync.Mutex
}

var (
movieBuff MovieBuff
totalRequest uint
)

func main() {
args := os.Args[1:]

if len(args) != 2 {
log.Fatalln("Please provide two actor names")
}

if args[0] == args[1] {
log.Fatalln("Degree of separation: 0 (Same actor)")
}

movieBuff.Person2Movies, movieBuff.Visited, movieBuff.Link, movieBuff.VisitedPerson = make(map[string]movie), make(map[string]bool), make(map[string]dos), make(map[string]bool)

if err := processPersonData(args[0], args[1]); err != nil {
log.Fatalln(err.Error())
}

t1 := time.Now()

dos, err := findDos()
if err != nil {
log.Fatalln(err.Error())
}

t2 := time.Now()

fmt.Printf("\nDegree of separation: %d\n\n", len(dos))
for i, d := range dos {
fmt.Printf("%d. Movie: %s\n %s: %s\n %s: %s\n\n", i+1, d.Movie, d.Role1, d.Person1, d.Role2, d.Person2)
}

fmt.Println("Total HTTP request sent: ", totalRequest)
fmt.Println("Time taken: ", t2.Sub(t1))
}

func processPersonData(person1, person2 string) error {
pn1, err := fetchData(person1)
if err != nil {
return err
}

pn2, err := fetchData(person2)
if err != nil {
return err
}

movieBuff.Mutex.Lock()
defer movieBuff.Mutex.Unlock()

if len(pn1.Movies) > len(pn2.Movies) {
movieBuff.Source, movieBuff.Destination = person2, person1
movieBuff.Person1, movieBuff.Person2 = pn2, pn1
} else {
movieBuff.Source, movieBuff.Destination = person1, person2
movieBuff.Person1, movieBuff.Person2 = pn1, pn2
}

for _, movie := range movieBuff.Person2.Movies {
movieBuff.Person2Movies[movie.URL] = movie
}

movieBuff.Visit = append(movieBuff.Visit, movieBuff.Source)
movieBuff.Visited[movieBuff.Source] = true

return nil
}

func findDos() ([]dos, error) {
var d []dos
for {
for _, person := range movieBuff.Visit {
person1, err := fetchData(person)
if err != nil {
if isEOFError(err) {
continue
}
continue
//return nil, err
}

for _, p1movie := range person1.Movies {
if movieBuff.Person2Movies[p1movie.URL].URL == p1movie.URL {
if _, found := movieBuff.Link[person1.URL]; found {
d = append(d, movieBuff.Link[person1.URL], dos{
p1movie.Name, person1.Name, p1movie.Role,
movieBuff.Person2.Name, movieBuff.Person2Movies[p1movie.URL].Role,
})
} else {
d = append(d, dos{
p1movie.Name, person1.Name, p1movie.Role,
movieBuff.Person2.Name, movieBuff.Person2Movies[p1movie.URL].Role,
})
}
return d, nil
}
}

for _, p1movie := range person1.Movies {
if movieBuff.Visited[p1movie.URL] {
continue
}

movieBuff.Visited[p1movie.URL] = true

p1moviedetail, err := fetchData(p1movie.URL)
if err != nil {
if isEOFError(err) {
continue
}
continue
//return nil, err
}

addToVisit(
movieMemberWrapper{Members: castToMovieMember(movieBuff.Person1.Cast)},
movieMemberWrapper{Members: crewToMovieMember(movieBuff.Person1.Crew)},
movieMemberWrapper{Members: castToMovieMember(person1.Cast)},
movieMemberWrapper{Members: crewToMovieMember(person1.Crew)},
)

for _, p1moviecast := range p1moviedetail.Cast {
if movieBuff.Visited[p1moviecast.URL] {
continue
}

movieBuff.Visited[p1moviecast.URL] = true
movieBuff.Visit = append(movieBuff.Visit, p1moviecast.URL)
movieBuff.Link[p1moviecast.URL] = dos{
p1movie.Name, person1.Name, p1movie.Role, p1moviecast.Name, p1moviecast.Role,
}
}

for _, p1moviecrew := range p1moviedetail.Crew {
if movieBuff.Visited[p1moviecrew.URL] {
continue
}

movieBuff.Visited[p1moviecrew.URL] = true
movieBuff.Visit = append(movieBuff.Visit, p1moviecrew.URL)
movieBuff.Link[p1moviecrew.URL] = dos{
p1movie.Name, person1.Name, p1movie.Role, p1moviecrew.Name, p1moviecrew.Role,
}
}
}
}
}
}

func fetchData(url string) (*personInfo, error) {
fmt.Println(url)
time.Sleep(100 * time.Millisecond)
resp, err := http.Get(source + url)
if err != nil {
return nil, err
}
defer resp.Body.Close()

result, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}

var pi personInfo
err = json.Unmarshal(result, &pi)
if err != nil {
return nil, err
}

totalRequest++
return &pi, nil
}

func addToVisit(members ...movieMemberWrapper) {
for _, member := range members {
for _, m := range member.Members {
movieBuff.Visit = append(movieBuff.Visit, m.GetURL())
movieBuff.Link[m.GetURL()] = dos{}
}
}
}

func isEOFError(err error) bool {
return err != nil && err.Error() == "EOF"
}

func castToMovieMember(castList []cast) []movieMember {
var members []movieMember
for _, c := range castList {
members = append(members, movieMember(c))
}
return members
}

func crewToMovieMember(crewList []crew) []movieMember {
var members []movieMember
for _, c := range crewList {
members = append(members, movieMember(c))
}
return members
}