Skip to content

Commit

Permalink
make visibility radius adjustable
Browse files Browse the repository at this point in the history
  • Loading branch information
bors-ltd committed Jun 29, 2021
1 parent acf73d0 commit 3fab76f
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 8 deletions.
10 changes: 10 additions & 0 deletions APITaxi2/schemas.py
Expand Up @@ -28,6 +28,11 @@
)


# Range to adjust the visibility of taxis to clients
TAXI_MIN_RADIUS = 100
TAXI_MAX_RADIUS = 500


class PageQueryStringMixin:
"""Used to accept a querystring param ?p, and make sure it is specified
only once.
Expand Down Expand Up @@ -289,6 +294,11 @@ class TaxiSchema(Schema):
required=False, allow_none=False,
validate=validate.OneOf(UPDATABLE_VEHICLE_STATUS)
)
# Adjustable visibility radius (if null, fallback to max radius)
radius = fields.Integer(
required=False, allow_none=False,
validate=validate.Range(min=TAXI_MIN_RADIUS, max=TAXI_MAX_RADIUS)
)

last_update = fields.Constant(None, required=False, allow_none=False)

Expand Down
56 changes: 56 additions & 0 deletions APITaxi2/tests/test_taxis.py
Expand Up @@ -143,6 +143,15 @@ def _set_taxi_status(status, hail=None, initial_status=None):
taxi, resp = _set_taxi_status('free', initial_status='free')
assert len(app.redis.zrange('taxi_status:%s' % taxi.id, 0, -1)) == 0

# Set the radius only
for radius, expected_code in [(100, 200), (90, 400), (500, 200), (501, 400)]:
resp = operateur.client.put('/taxis/%s' % taxi.id, json={
'data': [{
'radius': radius
}]
})
assert resp.status_code == expected_code


class TestTaxiPost:
def test_invalid(self, operateur):
Expand Down Expand Up @@ -540,6 +549,53 @@ def test_ok_operateur(self, app, operateur):
assert resp.json['data'][0]['id'] == my_taxi.id
assert resp.json['data'][0]['operator'] == vehicle_description.added_by.email

def test_radius(self, app, moteur):
ZUPCFactory()
now = datetime.now()
TaxiFactory(
vehicle__descriptions__radius=100, vehicle__descriptions__last_update_at=now
)
taxi_2 = TaxiFactory(
vehicle__descriptions__radius=500, vehicle__descriptions__last_update_at=now
)

lon = tmp_lon = 2.35
lat = tmp_lat = 48.86

resp = moteur.client.get('/taxis?lon=%s&lat=%s' % (lon, lat))
assert resp.status_code == 200
assert resp.json['data'] == []

# Insert locations for each operator.
for taxi in Taxi.query.options(lazyload('*')):
for description in VehicleDescription.query.options(
lazyload('*')
).filter_by(
vehicle=taxi.vehicle
).order_by(
VehicleDescription.id
):
# Move taxi a little bit further
tmp_lon += 0.0001
tmp_lat += 0.0001
self._post_geotaxi(app, tmp_lon, tmp_lat, taxi, description)

# The client is under 100 meters (~ 15.7 m)
resp = moteur.client.get('/taxis?lon=%s&lat=%s' % (lon, lat))
assert resp.status_code == 200
assert len(resp.json['data']) == 2

# The client is between 100 and 500 meters (~ 157 m)
resp = moteur.client.get('/taxis?lon=%s&lat=%s' % (lon + 0.001, lat + 0.001))
assert resp.status_code == 200
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['id'] == taxi_2.id

# The client is above 500 meters (~ 1.57 km)
resp = moteur.client.get('/taxis?lon=%s&lat=%s' % (lon + 0.01, lat + 0.01))
assert resp.status_code == 200
assert len(resp.json['data']) == 0


class TestTaxiList:

Expand Down
15 changes: 8 additions & 7 deletions APITaxi2/views/taxis.py
Expand Up @@ -247,7 +247,7 @@ def taxis_details(taxi_id):

args = request.json.get('data', [{}])[0]

# For now it is only possible to update the taxi's status.
# For now it is only possible to update the taxi's status...
if 'status' in args and args['status'] != vehicle_description.status:
taxi.last_update_at = func.now()
db.session.flush()
Expand All @@ -267,6 +267,10 @@ def taxis_details(taxi_id):
taxi_id,
args['status']
)
# ... and the radius where the taxi is visible
if 'radius' in args and args['radius'] != vehicle_description.radius:
vehicle_description.radius = args['radius']
db.session.flush()

output = schema.dump({'data': [(taxi, vehicle_description, location)]})

Expand Down Expand Up @@ -345,11 +349,6 @@ def taxis_search():
# Now we know the taxis allowed at this position are the ones from this town
# plus the potential other taxis from the ZUPC
allowed_insee_codes = {town.insee, *(town.insee for zupc in zupcs for town in zupc.allowed)}
# This variable used to be in configuration file, but I don't think it
# makes much sense to have it configurable globally. Configuration should
# ideally be fine grained, depending on day, time, location, and be even
# configurable by the taxi.
default_max_distance = 500

# Locations is a dict containing taxis close from the location given as
# param. Each taxi can report its location from several operators.
Expand All @@ -362,7 +361,7 @@ def taxis_search():
# }
#
locations = redis_backend.taxis_locations_by_operator(
params['lon'], params['lat'], default_max_distance
params['lon'], params['lat'], schemas.TAXI_MAX_RADIUS
)
debug_ctx.log_admin(
f'List of taxis around lon={params["lon"]} lat={params["lat"]}',
Expand Down Expand Up @@ -449,6 +448,8 @@ def taxis_search():
data = [
(taxi, vehicle_description, redis_location)
for taxi, (vehicle_description, redis_location) in data.items()
# Filter out of reach taxis based on each driver's preference
if redis_location.distance <= (vehicle_description.radius or schemas.TAXI_MAX_RADIUS)
]

# Sort entries by distance.
Expand Down
@@ -0,0 +1,24 @@
"""adjustable taxi radius
Revision ID: 75fcefba25f9
Revises: 5b8d8ca36aeb
Create Date: 2021-06-21 11:53:55.382470
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '75fcefba25f9'
down_revision = '5b8d8ca36aeb'
branch_labels = None
depends_on = None


def upgrade():
op.add_column('vehicle_description', sa.Column('radius', sa.Integer(), nullable=True))


def downgrade():
op.drop_column('vehicle_description', 'radius')
5 changes: 4 additions & 1 deletion APITaxi_models2/vehicle.py
Expand Up @@ -77,9 +77,12 @@ def __repr__(self):
color = db.Column(db.String(255), server_default='', nullable=False)
vehicle_id = db.Column(db.Integer, db.ForeignKey(Vehicle.id))
added_by_id = db.Column('added_by', db.Integer, db.ForeignKey('user.id'))
status = db.Column(db.Enum(*VEHICLE_STATUS, name='status_vehicle_enum'))
nb_seats = db.Column(db.Integer)

# Live data
status = db.Column(db.Enum(*VEHICLE_STATUS, name='status_vehicle_enum'))
radius = db.Column(db.Integer, nullable=True)

added_by = db.relationship('User', lazy='raise')
vehicle = db.relationship(Vehicle, lazy='raise')

Expand Down

0 comments on commit 3fab76f

Please sign in to comment.