center-randomize icon indicating copy to clipboard operation
center-randomize copied to clipboard

[LOGIC_IMPROVEMENT] Handle no centers are within the absolute distance threshold

Open sumanashrestha opened this issue 10 months ago • 1 comments

If no centers are within the absolute distance threshold, it always chooses the same center(the closest one) Example: If school A doesn't have any center within threshold (7km). But has 5 centers near threshold but slightly different distance like 7.1, 7.2 km etc. This doesn't choose different center every year. In this case, there is no check for PREF_CUTOFF either. So even if it was problematic last year, same center will still be chosen.

originally reported in #2

sumanashrestha avatar Apr 21 '24 17:04 sumanashrestha

Well to address this issue we need to modify the 'centers_within_distance' function to consider the preference cutoff (PREF_CUTOFF) even when no centers are within the absolute distance threshold. We should ensure that the closest center is not always chosen in such cases.

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}
     
     def sort_key(c):
         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 is 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 no centers within the preferred distance and above the preference cutoff,
         # return centers sorted by preference score and distance, excluding those below PREF_CUTOFF
         all_centers = [center_to_dict(c, haversine_distance(float(school_lat), float(school_long), float(c.get('lat')), float(c.get('long')))) for c in centers if school['scode'] != c['cscode']]
         qualified_centers = [c for c in all_centers if get_pref(school['scode'], c['cscode']) > PREF_CUTOFF]
         if qualified_centers:
             return sorted(qualified_centers, key=sort_key)
         else:
             return [center_to_dict(nearest_center, nearest_distance)]

Now what it does with this modification is if no centers are within the absolute distance threshold and no centers meet the preference cutoff, it will still allocate a center, but it will choose based on preference score and distance, excluding those below the preference cutoff. This ensures that the same center is not chosen every time if there are other options available. Thank you for the great work!

0xSolidityWeb3 avatar Apr 22 '24 05:04 0xSolidityWeb3

fixed in #37

sumanashrestha avatar May 05 '24 16:05 sumanashrestha