diff --git a/README.md b/README.md index f1c342f65..6f7fc3ebf 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,23 @@ -# Real Image Challenge 2016 +# Distributor Permission Checker -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 is a simple Streamlit app to check distributor permissions based on `distributors.json` and `cities.csv`. -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 -``` -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). -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. +## How to Run -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. Install dependencies: + pip install -r requirements.txt -``` -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. Start the app: + streamlit run app.py -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. +3. Opens your browser at: + http://localhost -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. - -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. - -To submit a solution, fork this repo and send a Pull Request on Github. - -For any questions or clarifications, raise an issue on this repo and we'll answer your questions as fast as we can. +## Files +- app.py → Main app +- distributors.json → Distributor permission rules +- cities.csv → City/State/Country hierarchy +- requirements.txt → Dependencies diff --git a/app.py b/app.py new file mode 100644 index 000000000..785276660 --- /dev/null +++ b/app.py @@ -0,0 +1,118 @@ +import streamlit as st +import json +import csv + +# ========== Permission Checker Class ========== +class PermissionChecker: + def __init__(self, cities_file, distributors_file): + self.city_map = {} + self.distributors_file = distributors_file + self.distributors = {} + self.load_cities(cities_file) + self.load_distributors(distributors_file) + + def load_cities(self, cities_file): + with open(cities_file, newline='', encoding='utf-8') as f: + reader = csv.DictReader(f) + for row in reader: + city_code = row["City Code"].strip() + province_code = row["Province Code"].strip() + country_code = row["Country Code"].strip() + + city_key = f"{city_code}-{province_code}-{country_code}" + state_key = f"{province_code}-{country_code}" + country_key = country_code + + self.city_map[city_key] = {"state": state_key, "country": country_key} + self.city_map[state_key] = {"country": country_key} + self.city_map[country_key] = {} + + def load_distributors(self, distributors_file): + with open(distributors_file, encoding="utf-8") as f: + data = json.load(f) + for dist in data["distributors"]: + self.distributors[dist["name"]] = dist + + def save_distributors(self): + with open(self.distributors_file, "w", encoding="utf-8") as f: + json.dump({"distributors": list(self.distributors.values())}, f, indent=4) + + def resolve_permissions(self, distributor_name): + if distributor_name not in self.distributors: + return set(), set() + + dist = self.distributors[distributor_name] + include = set(dist.get("INCLUDE", [])) + exclude = set(dist.get("EXCLUDE", [])) + + if "Authorized_by" in dist: + parent_incl, parent_excl = self.resolve_permissions(dist["Authorized_by"]) + include |= parent_incl + exclude |= parent_excl + + return include, exclude + + def check_permission(self, distributor_name, place): + include, exclude = self.resolve_permissions(distributor_name) + + if place in exclude: + return "No" + + hierarchy = [place] + if place in self.city_map: + if "state" in self.city_map[place]: + hierarchy.append(self.city_map[place]["state"]) + if "country" in self.city_map[place]: + hierarchy.append(self.city_map[place]["country"]) + + for h in hierarchy: + if h in exclude: + return "No" + for h in hierarchy: + if h in include: + return "Yes" + return "No" + + +# ========== Streamlit App ========== +checker = PermissionChecker("cities.csv", "distributors.json") + +st.title("📍 Distributor Permission Checker") + +# --- Permission Checker Section --- +st.header("🔍 Check Permission") +dist_name = st.selectbox("Choose Distributor", list(checker.distributors.keys())) +place = st.text_input("Enter Place Code (e.g., KLRAI-TN-IN, TN-IN, IN)") +if st.button("Check"): + if place: + result = checker.check_permission(dist_name, place) + if result == "Yes": + st.success(f"Permission for {dist_name} in {place}: **{result}**") + else: + st.error(f"Permission for {dist_name} in {place}: **{result}**") + +# --- View Distributors Section --- +st.header("📋 Current Distributors") +for d, info in checker.distributors.items(): + st.subheader(d) + st.json(info) + +# --- Update Distributors Section --- +st.header("✏️ Update Distributor") +with st.form("update_form"): + new_name = st.text_input("Distributor Name") + include_list = st.text_area("INCLUDE (comma-separated codes)").split(",") + exclude_list = st.text_area("EXCLUDE (comma-separated codes)").split(",") + authorized_by = st.text_input("Authorized_by (optional)").strip() + submitted = st.form_submit_button("Save Distributor") + + if submitted and new_name: + checker.distributors[new_name] = { + "name": new_name, + "INCLUDE": [x.strip() for x in include_list if x.strip()], + "EXCLUDE": [x.strip() for x in exclude_list if x.strip()], + } + if authorized_by: + checker.distributors[new_name]["Authorized_by"] = authorized_by + checker.save_distributors() + st.success(f"Distributor '{new_name}' updated/added successfully!") diff --git a/distributors.json b/distributors.json new file mode 100644 index 000000000..9e3959e9b --- /dev/null +++ b/distributors.json @@ -0,0 +1,54 @@ +{ + "distributors": [ + { + "name": "DISTRIBUTOR1", + "INCLUDE": [ + "IN", + "US" + ], + "EXCLUDE": [ + "KA-IN", + "CENAI-TN-IN" + ] + }, + { + "name": "DISTRIBUTOR2", + "INCLUDE": [ + "IN" + ], + "EXCLUDE": [ + "KA-IN", + "CENAI-TN-IN" + ] + }, + { + "name": "DISTRIBUTOR3", + "Authorized_by": "DISTRIBUTOR1", + "INCLUDE": [ + "US" + ], + "EXCLUDE": [] + }, + { + "name": "DISTRIBUTOR4", + "Authorized_by": "DISTRIBUTOR3", + "INCLUDE": [ + "IN", + "US", + "CA" + ], + "EXCLUDE": [ + "IN", + "TN-IN" + ] + }, + { + "name": "DISTRIBUTOR5", + "INCLUDE": [], + "EXCLUDE": [ + "DAHNE-AL-US" + ], + "Authorized_by": "DISTRIBUTOR3" + } + ] +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..e2513300d --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +streamlit \ No newline at end of file