center-randomize
center-randomize copied to clipboard
[LOGIC_IMPROVEMENT] Handle no centers are within the absolute distance threshold
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
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!
fixed in #37