From 52cb1075a39a19ddd1e5539d8102d2d79a17714f Mon Sep 17 00:00:00 2001 From: Pradeep Chand Date: Wed, 24 Apr 2024 01:00:51 +0545 Subject: [PATCH 1/3] update: implemented class in main file, moved constant values to utils/constants.py file, moved file operations to utils/file_utils.py --- school_center.py | 381 +++++++++++++++++++---------------------- utils/constants.py | 9 + utils/custom_logger.py | 6 +- utils/file_utils.py | 37 ++++ 4 files changed, 222 insertions(+), 211 deletions(-) create mode 100644 utils/constants.py create mode 100644 utils/file_utils.py diff --git a/school_center.py b/school_center.py index c68cdf7..4257ed2 100644 --- a/school_center.py +++ b/school_center.py @@ -1,221 +1,186 @@ -OUTPUT_DIR = 'results/' - -PREF_DISTANCE_THRESHOLD = 2 # Preferred threshold distance in kilometers -ABS_DISTANCE_THRESHOLD = 7 # Absolute threshold distance in kilometers -MIN_STUDENT_IN_CENTER = 10 # minimum number of students from a school to be assigned to a center in normal circumstances -STRETCH_CAPACITY_FACTOR = 0.02 # how much can center capacity be streched if need arises -PREF_CUTOFF = -4 # Do not allocate students with pref score less than cutoff - import math import csv import random import logging import argparse -import os from typing import Dict, List from utils.custom_logger import configure_logging - - -configure_logging() - -logger = logging.getLogger(__name__) - -def create_dir(dirPath:str): - """ - Create the given directory if it doesn't exists - - Creates all the directories needed to resolve to the provided directory path - """ - if not os.path.exists(dirPath): - os.makedirs(dirPath) - -def haversine_distance(lat1, lon1, lat2, lon2): - """ - Calculate the great circle distance between two points - on the earth specified in decimal degrees - """ - # Convert decimal degrees to radians - lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2]) - - # Haversine formula - dlon = lon2 - lon1 - dlat = lat2 - lat1 - a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2 - c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) - radius_earth = 6371 # Radius of Earth in kilometers - distance = radius_earth * c - return distance - -def centers_within_distance(school: Dict[str, str], centers: Dict[str, str], distance_threshold: float) -> List[Dict[str, any]]: - """ - Return List of centers that are within given distance from school. - If there are no centers within given distance return one that is closest - Returned params : - {'cscode', 'name', 'address', 'capacity', 'lat', 'long', 'distance_km'} - - """ - def center_to_dict(c, distance): - return {'cscode': c['cscode'], 'name': c['name'], 'address': c['address'], 'capacity': c['capacity'], 'lat': c['lat'], 'long': c['long'], 'distance_km': distance} +from utils.constants import EARTH_RADIUS, OUTPUT_DIR,PREF_DISTANCE_THRESHOLD,ABS_DISTANCE_THRESHOLD,MIN_STUDENT_IN_CENTER,STRETCH_CAPACITY_FACTOR,PREF_CUTOFF +from utils.file_utils import FileUtils; +class CentersAllocation: - def sort_key(c): - # intent: sort by preference score DESC then by distance_km ASC - # leaky abstraction - sorted requires a single numberic value for each element - return c['distance_km'] * random.uniform(1,5) - get_pref(school['scode'], c['cscode'])*100 - - school_lat = school.get('lat') - school_long = school.get('long') - if len(school_lat) == 0 or len(school_long) == 0: - return [] - - within_distance = [] - nearest_distance = None; - nearest_center = None - for c in centers: - distance = haversine_distance(float(school_lat), float(school_long), float(c.get('lat')), float(c.get('long'))) - if school['scode'] == c['cscode']: - continue - if nearest_center == None or distance < nearest_distance: - nearest_center = c - nearest_distance = distance - - if distance <= distance_threshold and get_pref(school['scode'], c['cscode']) > PREF_CUTOFF: - within_distance.append(center_to_dict(c, distance)) - - if len(within_distance) > 0: - return sorted(within_distance, key=sort_key) - else: # if there are no centers within given threshold, return one that is closest - return [center_to_dict(nearest_center, nearest_distance)] - -def read_tsv(file_path: str) -> List[Dict[str, str]]: - data = [] - with open(file_path, 'r', newline='', encoding='utf-8') as file: - reader = csv.DictReader(file, delimiter='\t') - for row in reader: - data.append(dict(row)) - return data - -def read_prefs(file_path: str) -> Dict[str, Dict[str, int]]: - prefs = {} - with open(file_path, 'r', newline='', encoding='utf-8') as file: - reader = csv.DictReader(file, delimiter='\t') - for row in reader: - if prefs.get(row['scode']): - if prefs[row['scode']].get(row['cscode']): - prefs[row['scode']][row['cscode']] += int(row['pref']) - else: - prefs[row['scode']][row['cscode']] = int(row['pref']) - else: - prefs[row['scode']] = {row['cscode']: int(row['pref'])} - - return prefs + def __init__(self): + configure_logging() + self.logger = logging.getLogger(__name__) + self.allocations = {} # to track mutual allocations + self.fileUtils = FileUtils() + + def haversine_distance(self,lat1, lon1, lat2, lon2): + """ + Calculate the great circle distance between two points + on the earth specified in decimal degrees + """ + # Convert decimal degrees to radians + lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2]) + + # Haversine formula + dlon = lon2 - lon1 + dlat = lat2 - lat1 + a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2 + c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) + distance = EARTH_RADIUS * c + return distance + + def centers_within_distance(self,school: Dict[str, str], centers: Dict[str, str], distance_threshold: float) -> List[Dict[str, any]]: + """ + Return List of centers that are within given distance from school. + If there are no centers within given distance return one that is closest + Returned params : + {'cscode', 'name', 'address', 'capacity', 'lat', 'long', 'distance_km'} + + """ + def center_to_dict(c, distance): + return {'cscode': c['cscode'], 'name': c['name'], 'address': c['address'], 'capacity': c['capacity'], 'lat': c['lat'], 'long': c['long'], 'distance_km': distance} + + def sort_key(c): + # intent: sort by preference score DESC then by distance_km ASC + # leaky abstraction - sorted requires a single numberic value for each element + return c['distance_km'] * random.uniform(1,5) - self.get_pref(school['scode'], c['cscode'])*100 + + school_lat = school.get('lat') + school_long = school.get('long') + if len(school_lat) == 0 or len(school_long) == 0: + return [] + + within_distance = [] + nearest_distance = None; + nearest_center = None + for c in centers: + distance = self.haversine_distance(float(school_lat), float(school_long), float(c.get('lat')), float(c.get('long'))) + if school['scode'] == c['cscode']: + continue + if nearest_center == None or distance < nearest_distance: + nearest_center = c + nearest_distance = distance -def get_pref(scode, cscode) -> int: - if prefs.get(scode): - if prefs[scode].get(cscode): - return prefs[scode][cscode] + if distance <= distance_threshold and self.get_pref(school['scode'], c['cscode']) > PREF_CUTOFF: + within_distance.append(center_to_dict(c, distance)) + + if len(within_distance) > 0: + return sorted(within_distance, key=sort_key) + else: # if there are no centers within given threshold, return one that is closest + return [center_to_dict(nearest_center, nearest_distance)] + + def get_pref(self,scode, cscode) -> int: + if self.prefs.get(scode): + if self.prefs[scode].get(cscode): + return self.prefs[scode][cscode] + else: + return 0 else: - return 0 - else: - return 0 - -def calc_per_center(count: int) -> int: - if count <= 400: - return 100 - # elif count <= 900: - # return 200 - else: - return 200 - -def school_sort_key(s): - return (-1 if int(s['count']) > 500 else 1 ) * random.uniform(1, 100) - -def allocate(scode:str, cscode:str, count: int): - if allocations.get(scode) == None: - allocations[scode] = {cscode: count} - elif allocations[scode].get(cscode) == None: - allocations[scode][cscode] = count - else: - allocations[scode][cscode] += count - -def is_allocated(scode1: str, scode2:str) -> bool: - if allocations.get(scode1): - return allocations[scode1].get(scode2) != None - else: - return False - -parser = argparse.ArgumentParser( - prog='center randomizer', - description='Assigns centers to exam centers to students') -parser.add_argument('schools_tsv', default='schools.tsv', help="Tab separated (TSV) file containing school details") -parser.add_argument('centers_tsv', default='centers.tsv', help="Tab separated (TSV) file containing center details") -parser.add_argument('prefs_tsv', default='prefs.tsv', help="Tab separated (TSV) file containing preference scores") -parser.add_argument('-o', '--output', default='school-center.tsv', help='Output file') -parser.add_argument('-s', '--seed', action='store', metavar='SEEDVALUE', default=None, type=float, help='Initialization seed for Random Number Generator') - -args = parser.parse_args() - -random = random.Random(args.seed) #overwrites the random module to use seeded rng - -schools = sorted(read_tsv(args.schools_tsv), key= school_sort_key) -centers = read_tsv(args.centers_tsv) -centers_remaining_cap = {c['cscode']:int(c['capacity']) for c in centers} -prefs = read_prefs(args.prefs_tsv) - -remaining = 0 # stores count of non allocated students -allocations = {} # to track mutual allocations - -create_dir(OUTPUT_DIR) # Create the output directory if not exists -with open('{}school-center-distance.tsv'.format(OUTPUT_DIR), 'w', encoding='utf-8') as intermediate_file, \ -open(OUTPUT_DIR + args.output, 'w', encoding='utf-8') as a_file: - writer = csv.writer(intermediate_file, delimiter="\t") - writer.writerow(["scode", "s_count", "school_name", "school_lat", "school_long", "cscode", "center_name", "center_address", "center_capacity", "distance_km"]) - - allocation_file = csv.writer(a_file, delimiter='\t') - allocation_file.writerow(["scode", "school", "cscode", "center", "center_address", "allocation", "distance_km"]) - - for s in schools: - centers_for_school = centers_within_distance(s, centers, PREF_DISTANCE_THRESHOLD) - to_allot = int(s['count']) - per_center = calc_per_center(to_allot) - - allocated_centers = {} + return 0 + + def calc_per_center(self,count: int) -> int: + if count <= 400: + return 100 + # elif count <= 900: + # return 200 + else: + return 200 + + def school_sort_key(self,s): + return (-1 if int(s['count']) > 500 else 1 ) * random.uniform(1, 100) + + def allocate(self,scode:str, cscode:str, count: int): + if self.allocations.get(scode) == None: + self.allocations[scode] = {cscode: count} + elif self.allocations[scode].get(cscode) == None: + self.allocations[scode][cscode] = count + else: + self.allocations[scode][cscode] += count - # per_center = math.ceil(to_allot / min(calc_num_centers(to_allot), len(centers_for_school))) - for c in centers_for_school: - writer.writerow([s['scode'], s['count'], s['name-address'], s['lat'], s['long'], c['cscode'], c['name'], c['address'], c['capacity'], c['distance_km'] ]) - if is_allocated(c['cscode'], s['scode']): - continue - next_allot = min(to_allot, per_center, max(centers_remaining_cap[c['cscode']], MIN_STUDENT_IN_CENTER)) - if to_allot > 0 and next_allot > 0 and centers_remaining_cap[c['cscode']] >= next_allot: - allocated_centers[c['cscode']] = c - allocate(s['scode'], c['cscode'], next_allot) - # allocation.writerow([s['scode'], s['name-address'], c['cscode'], c['name'], c['address'], next_allot, c['distance_km']]) - to_allot -= next_allot - centers_remaining_cap[c['cscode']] -= next_allot - - if to_allot > 0: # try again with relaxed constraints and more capacity at centers - expanded_centers = centers_within_distance(s, centers, ABS_DISTANCE_THRESHOLD) - for c in expanded_centers: - if is_allocated(c['cscode'], s['scode']): - continue - stretched_capacity = math.floor(int(c['capacity']) * STRETCH_CAPACITY_FACTOR + centers_remaining_cap[c['cscode']]) - next_allot = min(to_allot, max(stretched_capacity, MIN_STUDENT_IN_CENTER)) - if to_allot > 0 and next_allot > 0 and stretched_capacity >= next_allot: - allocated_centers[c['cscode']] = c - allocate(s['scode'], c['cscode'], next_allot) - # allocation.writerow([s['scode'], s['name-address'], c['cscode'], c['name'], c['address'], next_allot, c['distance_km']]) - to_allot -= next_allot - centers_remaining_cap[c['cscode']] -= next_allot - - for c in allocated_centers.values(): - allocation_file.writerow([s['scode'], s['name-address'], c['cscode'], c['name'], c['address'], allocations[s['scode']][c['cscode']], c['distance_km']]) - - if to_allot > 0: - remaining+=to_allot - logger.warn(f"{to_allot}/{s['count']} left for {s['scode']} {s['name-address']} centers: {len(centers_for_school)}") + def is_allocated(self,scode1: str, scode2:str) -> bool: + if self.allocations.get(scode1): + return self.allocations[scode1].get(scode2) != None + else: + return False + + def start_allocation(self): + parser = argparse.ArgumentParser(prog='center randomizer',description='Assigns centers to exam centers to students') + parser.add_argument('schools_tsv', default='schools.tsv', help="Tab separated (TSV) file containing school details") + parser.add_argument('centers_tsv', default='centers.tsv', help="Tab separated (TSV) file containing center details") + parser.add_argument('prefs_tsv', default='prefs.tsv', help="Tab separated (TSV) file containing preference scores") + parser.add_argument('-o', '--output', default='school-center.tsv', help='Output file') + parser.add_argument('-s', '--seed', action='store', metavar='SEEDVALUE', default=None, type=float, help='Initialization seed for Random Number Generator') + + args = parser.parse_args() + global random + if args.seed: + random = random.Random(args.seed) #overwrites the random module to use seeded rng + + schools = sorted(self.fileUtils.read_tsv(args.schools_tsv), key= self.school_sort_key) + centers = self.fileUtils.read_tsv(args.centers_tsv) + centers_remaining_cap = {c['cscode']:int(c['capacity']) for c in centers} + self.prefs = self.fileUtils.read_prefs(args.prefs_tsv) + + remaining = 0 # stores count of non allocated students + + self.fileUtils.create_dir(OUTPUT_DIR) # Create the output directory if not exists + with open('{}school-center-distance.tsv'.format(OUTPUT_DIR), 'w', encoding='utf-8') as intermediate_file, \ + open(OUTPUT_DIR + args.output, 'w', encoding='utf-8') as a_file: + writer = csv.writer(intermediate_file, delimiter="\t") + writer.writerow(["scode", "s_count", "school_name", "school_lat", "school_long", "cscode", "center_name", "center_address", "center_capacity", "distance_km"]) + + allocation_file = csv.writer(a_file, delimiter='\t') + allocation_file.writerow(["scode", "school", "cscode", "center", "center_address", "allocation", "distance_km"]) + + for s in schools: + centers_for_school = self.centers_within_distance(s, centers, PREF_DISTANCE_THRESHOLD) + to_allot = int(s['count']) + per_center = self.calc_per_center(to_allot) + + allocated_centers = {} + + # per_center = math.ceil(to_allot / min(calc_num_centers(to_allot), len(centers_for_school))) + for c in centers_for_school: + writer.writerow([s['scode'], s['count'], s['name-address'], s['lat'], s['long'], c['cscode'], c['name'], c['address'], c['capacity'], c['distance_km'] ]) + if self.is_allocated(c['cscode'], s['scode']): + continue + next_allot = min(to_allot, per_center, max(centers_remaining_cap[c['cscode']], MIN_STUDENT_IN_CENTER)) + if to_allot > 0 and next_allot > 0 and centers_remaining_cap[c['cscode']] >= next_allot: + allocated_centers[c['cscode']] = c + self.allocate(s['scode'], c['cscode'], next_allot) + # allocation.writerow([s['scode'], s['name-address'], c['cscode'], c['name'], c['address'], next_allot, c['distance_km']]) + to_allot -= next_allot + centers_remaining_cap[c['cscode']] -= next_allot - - logger.info("Remaining capacity at each center (remaining_capacity cscode):") - logger.info(sorted([(v,k) for k, v in centers_remaining_cap.items() if v != 0])) - logger.info(f"Total remaining capacity across all centers: {sum({k:v for k, v in centers_remaining_cap.items() if v != 0}.values())}") - logger.info(f"Students not assigned: {remaining}") + if to_allot > 0: # try again with relaxed constraints and more capacity at centers + expanded_centers = self.centers_within_distance(s, centers, ABS_DISTANCE_THRESHOLD) + for c in expanded_centers: + if self.is_allocated(c['cscode'], s['scode']): + continue + stretched_capacity = math.floor(int(c['capacity']) * STRETCH_CAPACITY_FACTOR + centers_remaining_cap[c['cscode']]) + next_allot = min(to_allot, max(stretched_capacity, MIN_STUDENT_IN_CENTER)) + if to_allot > 0 and next_allot > 0 and stretched_capacity >= next_allot: + allocated_centers[c['cscode']] = c + self.allocate(s['scode'], c['cscode'], next_allot) + # allocation.writerow([s['scode'], s['name-address'], c['cscode'], c['name'], c['address'], next_allot, c['distance_km']]) + to_allot -= next_allot + centers_remaining_cap[c['cscode']] -= next_allot + + for c in allocated_centers.values(): + allocation_file.writerow([s['scode'], s['name-address'], c['cscode'], c['name'], c['address'], self.allocations[s['scode']][c['cscode']], c['distance_km']]) + + if to_allot > 0: + remaining+=to_allot + self.logger.warning(f"{to_allot}/{s['count']} left for {s['scode']} {s['name-address']} centers: {len(centers_for_school)}") + + + self.logger.info("Remaining capacity at each center (remaining_capacity cscode):") + self.logger.info(sorted([(v,k) for k, v in centers_remaining_cap.items() if v != 0])) + self.logger.info(f"Total remaining capacity across all centers: {sum({k:v for k, v in centers_remaining_cap.items() if v != 0}.values())}") + self.logger.info(f"Students not assigned: {remaining}") + +randomize = CentersAllocation() +randomize.start_allocation() \ No newline at end of file diff --git a/utils/constants.py b/utils/constants.py new file mode 100644 index 0000000..f9e998d --- /dev/null +++ b/utils/constants.py @@ -0,0 +1,9 @@ +OUTPUT_DIR = 'results/' + +PREF_DISTANCE_THRESHOLD = 2 # Preferred threshold distance in kilometers +ABS_DISTANCE_THRESHOLD = 7 # Absolute threshold distance in kilometers +MIN_STUDENT_IN_CENTER = 10 # minimum number of students from a school to be assigned to a center in normal circumstances +STRETCH_CAPACITY_FACTOR = 0.02 # how much can center capacity be streched if need arises +PREF_CUTOFF = -4 # Do not allocate students with pref score less than cutoff + +EARTH_RADIUS = 6371 \ No newline at end of file diff --git a/utils/custom_logger.py b/utils/custom_logger.py index 73cebf9..2420188 100644 --- a/utils/custom_logger.py +++ b/utils/custom_logger.py @@ -23,15 +23,15 @@ "version": 1, "formatters": { "standard": { - "datefmt": "%y-%m-%d %H:%M:%S", + "datefmt": "%Y-%m-%d %H:%M:%S", "format": "🚀 %(asctime)s - %(name)s - %(levelname)s - %(message)s \n", }, "warn": { - "datefmt": "%y-%m-%d %H:%M:%S", + "datefmt": "%Y-%m-%d %H:%M:%S", "format": "🔔 %(asctime)s - %(name)s - %(levelname)s - %(message)s \n", }, "error": { - "datefmt": "%y-%m-%d %H:%M:%S", + "datefmt": "%Y-%m-%d %H:%M:%S", "format": "❌ %(asctime)s - %(name)s - %(levelname)s - %(message)s \n", }, }, diff --git a/utils/file_utils.py b/utils/file_utils.py new file mode 100644 index 0000000..e705a7f --- /dev/null +++ b/utils/file_utils.py @@ -0,0 +1,37 @@ +''' File handler ''' +import csv +import os +from typing import Dict, List + +class FileUtils(): + + """ + Create the given directory if it doesn't exists + - Creates all the directories needed to resolve to the provided directory path + """ + def create_dir(self,dirPath:str): + if not os.path.exists(dirPath): + os.makedirs(dirPath) + + def read_tsv(self,file_path: str) -> List[Dict[str, str]]: + data = [] + with open(file_path, 'r', newline='', encoding='utf-8') as file: + reader = csv.DictReader(file, delimiter='\t') + for row in reader: + data.append(dict(row)) + return data + + def read_prefs(self,file_path: str) -> Dict[str, Dict[str, int]]: + prefs = {} + with open(file_path, 'r', newline='', encoding='utf-8') as file: + reader = csv.DictReader(file, delimiter='\t') + for row in reader: + if prefs.get(row['scode']): + if prefs[row['scode']].get(row['cscode']): + prefs[row['scode']][row['cscode']] += int(row['pref']) + else: + prefs[row['scode']][row['cscode']] = int(row['pref']) + else: + prefs[row['scode']] = {row['cscode']: int(row['pref'])} + + return prefs From ef2053c4f6a57580b37bf2dd0c4bfd2bcd8d6909 Mon Sep 17 00:00:00 2001 From: Pradeep Chand Date: Wed, 24 Apr 2024 01:53:22 +0545 Subject: [PATCH 2/3] Moved all file related functions to file_utils --- school_center.py | 11 +++++------ utils/constants.py | 1 + utils/file_utils.py | 8 ++++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/school_center.py b/school_center.py index 4257ed2..b9b43e7 100644 --- a/school_center.py +++ b/school_center.py @@ -1,12 +1,11 @@ import math -import csv import random import logging import argparse from typing import Dict, List from utils.custom_logger import configure_logging -from utils.constants import EARTH_RADIUS, OUTPUT_DIR,PREF_DISTANCE_THRESHOLD,ABS_DISTANCE_THRESHOLD,MIN_STUDENT_IN_CENTER,STRETCH_CAPACITY_FACTOR,PREF_CUTOFF +from utils.constants import CENTER_DISTANCE_OUTPUT_FILE, EARTH_RADIUS, OUTPUT_DIR,PREF_DISTANCE_THRESHOLD,ABS_DISTANCE_THRESHOLD,MIN_STUDENT_IN_CENTER,STRETCH_CAPACITY_FACTOR,PREF_CUTOFF from utils.file_utils import FileUtils; class CentersAllocation: @@ -127,12 +126,12 @@ def start_allocation(self): remaining = 0 # stores count of non allocated students self.fileUtils.create_dir(OUTPUT_DIR) # Create the output directory if not exists - with open('{}school-center-distance.tsv'.format(OUTPUT_DIR), 'w', encoding='utf-8') as intermediate_file, \ - open(OUTPUT_DIR + args.output, 'w', encoding='utf-8') as a_file: - writer = csv.writer(intermediate_file, delimiter="\t") + with self.fileUtils.openOutputFiles(CENTER_DISTANCE_OUTPUT_FILE,OUTPUT_DIR) as intermediate_file, \ + self.fileUtils.openOutputFiles(args.output,OUTPUT_DIR) as a_file: + writer = self.fileUtils.get_csv_writer(intermediate_file,delimiter='\t') writer.writerow(["scode", "s_count", "school_name", "school_lat", "school_long", "cscode", "center_name", "center_address", "center_capacity", "distance_km"]) - allocation_file = csv.writer(a_file, delimiter='\t') + allocation_file = self.fileUtils.get_csv_writer(a_file, delimiter='\t') allocation_file.writerow(["scode", "school", "cscode", "center", "center_address", "allocation", "distance_km"]) for s in schools: diff --git a/utils/constants.py b/utils/constants.py index f9e998d..da3e2f4 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -1,4 +1,5 @@ OUTPUT_DIR = 'results/' +CENTER_DISTANCE_OUTPUT_FILE = 'school-center-distance.tsv' PREF_DISTANCE_THRESHOLD = 2 # Preferred threshold distance in kilometers ABS_DISTANCE_THRESHOLD = 7 # Absolute threshold distance in kilometers diff --git a/utils/file_utils.py b/utils/file_utils.py index e705a7f..83579e6 100644 --- a/utils/file_utils.py +++ b/utils/file_utils.py @@ -3,6 +3,8 @@ import os from typing import Dict, List +from utils.constants import CENTER_DISTANCE_OUTPUT_FILE, OUTPUT_DIR + class FileUtils(): """ @@ -35,3 +37,9 @@ def read_prefs(self,file_path: str) -> Dict[str, Dict[str, int]]: prefs[row['scode']] = {row['cscode']: int(row['pref'])} return prefs + + def openOutputFiles(self,output_file:str,directory:str): + return open(('{}'+output_file).format(directory), 'w', encoding='utf-8') + + def get_csv_writer(self,file,delimiter:str): + return csv.writer(file,dialect='excel', delimiter=delimiter) \ No newline at end of file From 2ba06c276ee0d78de37905aec64d930ea501eabb Mon Sep 17 00:00:00 2001 From: Pradeep Chand Date: Wed, 24 Apr 2024 01:55:00 +0545 Subject: [PATCH 3/3] revised object name --- school_center.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/school_center.py b/school_center.py index b9b43e7..871add9 100644 --- a/school_center.py +++ b/school_center.py @@ -181,5 +181,5 @@ def start_allocation(self): self.logger.info(f"Total remaining capacity across all centers: {sum({k:v for k, v in centers_remaining_cap.items() if v != 0}.values())}") self.logger.info(f"Students not assigned: {remaining}") -randomize = CentersAllocation() -randomize.start_allocation() \ No newline at end of file +centersAllocation = CentersAllocation() +centersAllocation.start_allocation() \ No newline at end of file