From bfe4199f67f2915ac0abc4f709883b417ad02513 Mon Sep 17 00:00:00 2001 From: Praveen Kumar Date: Thu, 20 Mar 2025 20:21:04 +0530 Subject: [PATCH 1/3] Intial changes and structure --- README.md | 90 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index f1c342f65..9fae15cc9 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,77 @@ # Real Image Challenge 2016 -In the cinema business, a feature film is usually provided to a regional distributor based on a contract for exhibition in a particular geographical territory. +This project implements a hierarchical distribution rights management system in Go. -Each authorization is specified by a combination of included and excluded regions. For example, a distributor might be authorzied in the following manner: -``` -Permissions for DISTRIBUTOR1 -INCLUDE: INDIA -INCLUDE: UNITEDSTATES -EXCLUDE: KARNATAKA-INDIA -EXCLUDE: CHENNAI-TAMILNADU-INDIA +## Code Structure + +### Distributor Structure +```go +type Distributor struct { + Name string + Includes map[string]bool + Excludes map[string]bool + Parent *Distributor +} ``` -This allows `DISTRIBUTOR1` to distribute in any city inside the United States and India, *except* cities in the state of Karnataka (in India) and the city of Chennai (in Tamil Nadu, India). +The Distributor struct represents a distribution entity with: +- Name: Identifier for the distributor +- Includes: Locations where distribution is allowed +- Excludes: Locations where distribution is blocked +- Parent: Reference to parent distributor for hierarchical rights -At this point, asking your program if `DISTRIBUTOR1` has permission to distribute in `CHICAGO-ILLINOIS-UNITEDSTATES` should get `YES` as the answer, and asking if distribution can happen in `CHENNAI-TAMILNADU-INDIA` should of course be `NO`. Asking if distribution is possible in `BANGALORE-KARNATAKA-INDIA` should also be `NO`, because the whole state of Karnataka has been excluded. +### Key Functions -Sometimes, a distributor might split the work of distribution amount smaller sub-distiributors inside their authorized geographies. For instance, `DISTRIBUTOR1` might assign the following permissions to `DISTRIBUTOR2`: +1. **LoadLocations** + - Reads location data from a CSV file + - Creates a map of cities with their corresponding province and country + - Format: `map[CITY] = [PROVINCE, COUNTRY]` -``` -Permissions for DISTRIBUTOR2 < DISTRIBUTOR1 -INCLUDE: INDIA -EXCLUDE: TAMILNADU-INDIA -``` -Now, `DISTRIBUTOR2` can distribute the movie anywhere in `INDIA`, except inside `TAMILNADU-INDIA` and `KARNATAKA-INDIA` - `DISTRIBUTOR2`'s permissions are always a subset of `DISTRIBUTOR1`'s permissions. It's impossible/invalid for `DISTRIBUTOR2` to have `INCLUDE: CHINA`, for example, because `DISTRIBUTOR1` isn't authorized to do that in the first place. +2. **IsallowedtoDistribute** + - Determines if distribution is allowed for a given location + - Checks hierarchy in following order: + 1. Excluded locations (city, state-country, city-state-country) + 2. Included locations (city, state-country, city-state-country) + 3. Parent distributor permissions -If `DISTRIBUTOR2` authorizes `DISTRIBUTOR3` to handle just the city of Hubli, Karnataka, India, for example: -``` -Permissions for DISTRIBUTOR3 < DISTRIBUTOR2 < DISTRIBUTOR1 -INCLUDE: HUBLI-KARNATAKA-INDIA -``` -Again, `DISTRIBUTOR2` cannot authorize `DISTRIBUTOR3` with a region that they themselves do not have access to. +### Distribution Rules +- Excludes take precedence over includes +- If location is not explicitly included/excluded, check parent distributor +- If no parent exists and no explicit inclusion, distribution is denied + +## Example Usage -We've provided a CSV with the list of all countries, states and cities in the world that we know of - please use the data mentioned there for this program. *The codes you see there may be different from what you see here, so please always use the codes in the CSV*. This Readme is only an example. +```go +// Create root distributor +distributor1 := &Distributor{ + Name: "DISTRIBUTOR1", + Includes: make(map[string]bool), + Excludes: make(map[string]bool), +} -Write a program in any language you want (If you're here from Gophercon, use Go :D) that does this. Feel free to make your own input and output format / command line tool / GUI / Webservice / whatever you want. Feel free to hold the dataset in whatever structure you want, but try not to use external databases - as far as possible stick to your langauage without bringing in MySQL/Postgres/MongoDB/Redis/Etc. +// Set permissions +distributor1.AddInclude("INDIA") +distributor1.AddExclude("KARNATAKA-INDIA") -To submit a solution, fork this repo and send a Pull Request on Github. +// Check distribution rights +isAllowed := distributor1.IsallowedtoDistribute(location, locations[location]) +``` -For any questions or clarifications, raise an issue on this repo and we'll answer your questions as fast as we can. +## CSV File Format +The program expects a CSV file with the following columns: +- Column 4: City +- Column 5: Province/State +- Column 6: Country +## Dependencies +- Standard Go libraries: + - encoding/csv + - strings + - fmt + - os + +## Running the Program +```bash +go run main.go +``` +The program will read from `cities.csv` and output distribution rights for each location. From 42cf4ffd52e43ecbced944583e5c69663c672298 Mon Sep 17 00:00:00 2001 From: Praveen Kumar Date: Thu, 20 Mar 2025 20:30:41 +0530 Subject: [PATCH 2/3] Added the main.go along with services files --- go.mod | 3 ++ main.go | 62 +++++++++++++++++++++++++++++++++ services/distributor_service.go | 47 +++++++++++++++++++++++++ services/loader_service.go | 29 +++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 go.mod create mode 100644 main.go create mode 100644 services/distributor_service.go create mode 100644 services/loader_service.go diff --git a/go.mod b/go.mod new file mode 100644 index 000000000..d48cecf33 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/praveen-kumar-04/real_image_challenge + +go 1.24.0 diff --git a/main.go b/main.go new file mode 100644 index 000000000..97e0ee54c --- /dev/null +++ b/main.go @@ -0,0 +1,62 @@ +package main + +import ( + "fmt" + "github.com/praveen-kumar-04/real_image_challenge/services" +) + + +func main(){ + locations, err := services.LoadLocations("cities.csv") + + if err != nil { + fmt.Println("Error loading CSV:", err) + return + } + + distributor1 := &services.Distributor{ + Name: "DISTRIBUTOR1", + Includes: make(map[string]bool), + Excludes: make(map[string]bool), + } + + distributor1.AddInclude("INDIA") + distributor1.AddInclude("UNITEDSTATES") + distributor1.AddExclude("KARNATAKA-INDIA") + distributor1.AddExclude("CHENNAI-TAMILNADU-INDIA") + + distributor2 := &services.Distributor{ + Name: "DISTRIBUTOR2", + Includes: make(map[string]bool), + Excludes: make(map[string]bool), + Parent: distributor1, + } + + distributor2.AddInclude("INDIA") + distributor2.AddExclude("TAMILNADU-INDIA") + + + distributor3 := &services.Distributor{ + Name: "DISTRIBUTOR3", + Includes: make(map[string]bool), + Excludes: make(map[string]bool), + Parent: distributor2, + } + + distributor3.AddInclude("HUBLI-KARNATAKA-INDIA") + + current_distributor := distributor1 + + for location, _ := range locations { + + // fmt.Println(location, distributor1.IsallowedtoDistribute(location,locations[location])) + + if current_distributor.IsallowedtoDistribute(location,locations[location]) { + fmt.Printf("%s is allowed to distribute in %s-%s-%s :=> Yes\n",current_distributor.Name,location,locations[location][0],locations[location][1]) + + }else{ + fmt.Printf("%s is allowed to distribute in %s-%s-%s :=> No\n",current_distributor.Name,location,locations[location][0],locations[location][1]) + } + } + +} \ No newline at end of file diff --git a/services/distributor_service.go b/services/distributor_service.go new file mode 100644 index 000000000..653a63127 --- /dev/null +++ b/services/distributor_service.go @@ -0,0 +1,47 @@ +package services + +import( + "strings" +) + +type Distributor struct { + Name string + Includes map[string]bool + Excludes map[string]bool + Parent *Distributor +} + +func (d *Distributor) AddInclude(location string) { + location = strings.ToUpper(location) + d.Includes[location] = true +} + +func (d *Distributor) AddExclude(location string) { + location = strings.ToUpper(location) + d.Excludes[location] = true +} + + +func (d *Distributor) IsallowedtoDistribute(location string, locations []string) bool { + + // checking the cities, state, country in the exluded list to identify if the location is allowed to distribute + full :=strings.Join([]string{locations[0], locations[1]}, "-") + if d.Excludes[locations[1]] || d.Excludes[strings.Join([]string{locations[0], locations[1]}, "-")] || d.Excludes[strings.Join([]string{full,location}, "-")] { + return false + } + + + // checking the cities, state, country in the included list + if d.Includes[locations[1]] || d.Includes[strings.Join([]string{locations[0], locations[1]}, "-")] || d.Includes[strings.Join([]string{full,location}, "-")] { + if d.Parent != nil { + return d.Parent.IsallowedtoDistribute(location, locations) + } + return true + } + + if d.Parent != nil { + return d.Parent.IsallowedtoDistribute(location, locations) + } + + return false +} \ No newline at end of file diff --git a/services/loader_service.go b/services/loader_service.go new file mode 100644 index 000000000..7451e17ec --- /dev/null +++ b/services/loader_service.go @@ -0,0 +1,29 @@ +package services +import ( + "encoding/csv" + "os" + "strings" +) + +// Loading the csv file and storing the city, state, country in the map +func LoadLocations(fileloc string) (map[string][]string, error) { + locations := make(map[string][]string) + file, err := os.Open(fileloc) + if err != nil { + return locations, err + } + defer file.Close() + + reader := csv.NewReader(file) + lines, err := reader.ReadAll() + if err != nil { + return locations, err + } + + for _, line := range lines[1:] { + city, province, country := line[3], line[4], line[5] + locations[strings.ToUpper(city)] = []string{strings.ToUpper(province),strings.ToUpper(country)} + } + + return locations, nil +} \ No newline at end of file From aa1a1f4051ae52e6baf0caecbd609950f40d845f Mon Sep 17 00:00:00 2001 From: Praveen Kumar Date: Thu, 20 Mar 2025 22:11:30 +0530 Subject: [PATCH 3/3] Included CLI for user interaction --- main.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/main.go b/main.go index 97e0ee54c..6677fa816 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,44 @@ package main import ( + "bufio" "fmt" + "os" + "strconv" + "strings" "github.com/praveen-kumar-04/real_image_challenge/services" ) +func checking_list_cities(current_distributor *services.Distributor,locations map[string][]string){ + for location, _ := range locations { + if current_distributor.IsallowedtoDistribute(location,locations[location]) { + fmt.Printf("%s is allowed to distribute in %s-%s-%s :=> Yes\n",current_distributor.Name,location,locations[location][0],locations[location][1]) + + }else{ + fmt.Printf("%s is allowed to distribute in %s-%s-%s :=> No\n",current_distributor.Name,location,locations[location][0],locations[location][1]) + } + } + +} + +func distributors_list(list_of_distributors []*services.Distributor){ + fmt.Println("List of Distributors") + for ind,distributor := range list_of_distributors { + fmt.Println(ind+1,"=>",distributor.Name) + } +} + +func read_data_from_user() int{ + reader := bufio.NewReader(os.Stdin) + fmt.Println("Enter the ID of the distributor") + distributor_id, _ := reader.ReadString('\n') + distributor_id = strings.TrimSuffix(distributor_id, "\n") + distributor_id = strings.TrimSuffix(distributor_id, "\r") + id, _ := strconv.Atoi(distributor_id) + return id + +} + func main(){ locations, err := services.LoadLocations("cities.csv") @@ -45,18 +79,19 @@ func main(){ distributor3.AddInclude("HUBLI-KARNATAKA-INDIA") - current_distributor := distributor1 - - for location, _ := range locations { - // fmt.Println(location, distributor1.IsallowedtoDistribute(location,locations[location])) + list_of_distributors := []*services.Distributor{distributor1,distributor2,distributor3} - if current_distributor.IsallowedtoDistribute(location,locations[location]) { - fmt.Printf("%s is allowed to distribute in %s-%s-%s :=> Yes\n",current_distributor.Name,location,locations[location][0],locations[location][1]) - - }else{ - fmt.Printf("%s is allowed to distribute in %s-%s-%s :=> No\n",current_distributor.Name,location,locations[location][0],locations[location][1]) + for { + distributors_list(list_of_distributors) + checking_list_cities(list_of_distributors[read_data_from_user()-1],locations) + fmt.Println("Do you want to check for another distributor? (yes/no)") + reader := bufio.NewReader(os.Stdin) + choice, _ := reader.ReadString('\n') + choice = strings.TrimSuffix(choice, "\n") + choice = strings.TrimSuffix(choice, "\r") + if choice == "no" { + break } - } - +} } \ No newline at end of file