Skip to content

Commit

Permalink
Separate cluster and matches enpoints (benetech#183)
Browse files Browse the repository at this point in the history
  • Loading branch information
stepan-anokhin committed Nov 10, 2020
1 parent 52de02b commit bca6a0b
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 23 deletions.
2 changes: 1 addition & 1 deletion server/server/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Disable flake8 issue F401 as we need these imports to configure api
# but not going to re-export them from the __init__
from . import scenes, matches, files, errors, videos # noqa: F401
from . import scenes, matches, files, errors, videos, cluster # noqa: F401
from .blueprint import api

# Explicitly reexport api
Expand Down
44 changes: 44 additions & 0 deletions server/server/api/cluster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from http import HTTPStatus

from flask import jsonify, request, abort

from db.access.matches import FileMatchesRequest, MatchesDAO
from db.schema import Files
from .blueprint import api
from .helpers import parse_positive_int, Fields, parse_positive_float, parse_fields
from ..model import Transform, database

# Optional file fields
FILE_FIELDS = Fields(Files.exif, Files.signature, Files.meta, Files.scenes)


def parse_params(file):
"""Parse request parameters."""
req = FileMatchesRequest(file=file)
req.limit = parse_positive_int(request.args, 'limit', 20)
req.offset = parse_positive_int(request.args, 'offset', 0)
req.hops = parse_positive_int(request.args, 'hops', 1)
req.min_distance = parse_positive_float(request.args, 'min_distance', 0.0)
req.max_distance = parse_positive_float(request.args, 'max_distance', 1.0)
req.preload = parse_fields(request.args, 'include', FILE_FIELDS)
return req


@api.route('/files/<int:file_id>/cluster', methods=['GET'])
def list_file_matches(file_id):
file = database.session.query(Files).get(file_id)

# Handle file not found
if file is None:
abort(HTTPStatus.NOT_FOUND.value, f"File id not found: {file_id}")

req = parse_params(file)
resp = MatchesDAO.list_file_matches(req, database.session)

include_flags = {field.key: True for field in req.preload}
return jsonify({
'files': [Transform.file_dict(file, **include_flags) for file in resp.files],
'matches': [Transform.match_dict(match) for match in resp.matches],
'total': resp.total,
'hops': req.hops,
})
46 changes: 24 additions & 22 deletions server/server/api/matches.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,46 @@
from http import HTTPStatus

from flask import jsonify, request, abort
from sqlalchemy.orm import joinedload

from db.access.matches import FileMatchesRequest, MatchesDAO
from db.schema import Files
from db.access.files import FilesDAO
from db.schema import Matches, Files
from .blueprint import api
from .helpers import parse_positive_int, Fields, parse_positive_float, parse_fields
from .helpers import parse_positive_int, Fields, parse_fields
from ..model import Transform, database

# Optional file fields
FILE_FIELDS = Fields(Files.exif, Files.signature, Files.meta, Files.scenes)


def parse_params(file):
"""Parse request parameters."""
req = FileMatchesRequest(file=file)
req.limit = parse_positive_int(request.args, 'limit', 20)
req.offset = parse_positive_int(request.args, 'offset', 0)
req.hops = parse_positive_int(request.args, 'hops', 1)
req.min_distance = parse_positive_float(request.args, 'min_distance', 0.0)
req.max_distance = parse_positive_float(request.args, 'max_distance', 1.0)
req.preload = parse_fields(request.args, 'include', FILE_FIELDS)
return req


@api.route('/files/<int:file_id>/matches', methods=['GET'])
def list_file_matches(file_id):
limit = parse_positive_int(request.args, 'limit', 20)
offset = parse_positive_int(request.args, 'offset', 0)
include_fields = parse_fields(request.args, 'include', FILE_FIELDS)

file = database.session.query(Files).get(file_id)

# Handle file not found
if file is None:
abort(HTTPStatus.NOT_FOUND.value, f"File id not found: {file_id}")

req = parse_params(file)
resp = MatchesDAO.list_file_matches(req, database.session)
query = FilesDAO.file_matches(file_id, database.session).options(
joinedload(Matches.match_video_file),
joinedload(Matches.query_video_file)
)

# Preload file fields
query = FILE_FIELDS.preload(query, include_fields, Matches.match_video_file)
query = FILE_FIELDS.preload(query, include_fields, Matches.query_video_file)

# Get requested slice
total = query.count()
items = query.offset(offset).limit(limit).all()

include_flags = {field.key: True for field in req.preload}
include_flags = {field: True for field in include_fields}
return jsonify({
'files': [Transform.file_dict(file, **include_flags) for file in resp.files],
'matches': [Transform.match_dict(match) for match in resp.matches],
'total': resp.total,
'hops': req.hops,
'items': [Transform.file_match_dict(item, file_id, **include_flags) for item in items],
'total': total,
'offset': offset,
})
1 change: 1 addition & 0 deletions server/server/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ def file_match_dict(match, file_id, *, meta=False, signature=False, scenes=False
else:
matched = match.match_video_file
return {
"id": match.id,
"distance": match.distance,
"file": Transform.file_dict(matched, meta=meta, signature=signature, scenes=scenes, exif=exif)
}
Expand Down

0 comments on commit bca6a0b

Please sign in to comment.