Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7c302c1
PlateView.add_data() uses api_query.get_wellsample_indices()
will-moore Jan 31, 2017
a69833a
Blitz PlateAcquisition getQueryString() to filter by plate
will-moore Jan 31, 2017
a0e0063
Support /plates/:id/plateacquisitions/
will-moore Jan 31, 2017
6b2d29f
Blitz Well.getQueryString() supports filter by 'plateacquisition'
will-moore Feb 1, 2017
7c70b45
/plateacquisition/:id/wells/ filters Wells by plate_acquisition
will-moore Feb 1, 2017
048adc0
Add 'url:plateacquisitions' to plate json
will-moore Feb 8, 2017
edbb992
Support /plateacquistions/:id/ includes min/max wellsampleIndex
will-moore Feb 8, 2017
1bfe9b8
Add url 'plateacquisitions/:id/index/:idx/wells' to plate_acq json
will-moore Feb 8, 2017
9253577
Blitz Well getQueryString() supports filter by 'wellsample_index'
will-moore Feb 8, 2017
8e3e5d6
api WellsView handles 'index' to filter by wellsample_index
will-moore Feb 8, 2017
83f675e
Merge remote-tracking branch 'origin/develop' into api_wells2
will-moore Feb 20, 2017
84d177c
Add support for /plates/id/index/0/wells/
will-moore Feb 20, 2017
be36738
Add 'urls:wellsampleindex_wells' to PlateAcquisition and Plate views
will-moore Feb 20, 2017
58b6c30
Add tests for listing 'index' urls in plate/plate_acquisition
will-moore Feb 20, 2017
fc3d278
Remove duplicate imports
will-moore Feb 20, 2017
c03093a
Use 'url:' prefix for url:wellsampleindex_wells.
will-moore Feb 21, 2017
424ef1b
Fix merge conflict
will-moore Feb 21, 2017
f17506b
Tidy imports in test_api_wells.py
will-moore Feb 21, 2017
3a71a64
Tiny fix in docstring
will-moore Feb 21, 2017
1f6229e
Blitz helper add_plate_filter() used in getQueryString()
will-moore Feb 22, 2017
7918868
Merge remote-tracking branch 'origin/develop' into api_wells2
will-moore Feb 24, 2017
50efc29
flake8 fix
will-moore Feb 24, 2017
1320d39
Fix test_spw_urls
will-moore Feb 26, 2017
2b2a9b9
Use 'wellsampleindex' in urls
will-moore Feb 27, 2017
2d94baf
Update 'index' -> 'wellsampleindex' in url names
will-moore Feb 27, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 35 additions & 10 deletions components/tools/OmeroPy/src/omero/gateway/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ def getChannelsQuery():
' left outer join fetch logicalChannel.contrastMethod')


def add_plate_filter(clauses, params, opts):
"""Helper for adding 'plate' to filtering clauses and parameters."""
if opts is not None and 'plate' in opts:
clauses.append('obj.plate.id = :pid')
params.add('pid', rlong(opts['plate']))


class OmeroRestrictionWrapper (object):

def canDownload(self):
Expand Down Expand Up @@ -6038,6 +6045,21 @@ class _PlateAcquisitionWrapper (BlitzObjectWrapper):

OMERO_CLASS = 'PlateAcquisition'

@classmethod
def _getQueryString(cls, opts=None):
"""
Extend base query to handle filtering of PlateAcquisitions by Plate.
Returns a tuple of (query, clauses, params).
Supported opts: 'plate': <plate_id> to filter by Plate

:param opts: Dictionary of optional parameters.
:return: Tuple of string, list, ParametersI
"""
query, clauses, params = super(
_PlateAcquisitionWrapper, cls)._getQueryString(opts)
add_plate_filter(clauses, params, opts)
return (query, clauses, params)

def getName(self):
name = super(_PlateAcquisitionWrapper, self).getName()
if name is None:
Expand Down Expand Up @@ -6099,16 +6121,19 @@ def _getQueryString(cls, opts=None):
"""
query, clauses, params = super(
_WellWrapper, cls)._getQueryString(opts)
if opts is not None and 'plate' in opts:
clauses.append('obj.plate.id = :pid')
params.add('pid', rlong(opts['plate']))
load_images = False
load_pixels = False
load_channels = False
if opts is not None:
load_images = opts.get('load_images')
load_pixels = opts.get('load_pixels')
load_channels = opts.get('load_channels')
if opts is None:
opts = {}
add_plate_filter(clauses, params, opts)
load_images = opts.get('load_images')
load_pixels = opts.get('load_pixels')
load_channels = opts.get('load_channels')
if 'plateacquisition' in opts:
clauses.append('plateAcquisition.id = :plateAcq')
params.add('plateAcq', rlong(opts['plateacquisition']))
load_images = True
if 'wellsample_index' in opts:
clauses.append('index(wellSamples) = :wellsample_index')
params.add('wellsample_index', rint(opts['wellsample_index']))
if load_images or load_pixels or load_channels:
# NB: Using left outer join, we may get Wells with no Images
query += " left outer join fetch obj.wellSamples as wellSamples"\
Expand Down
25 changes: 25 additions & 0 deletions components/tools/OmeroWeb/omeroweb/api/api_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,31 @@
DEFAULT_LIMIT = max(1, api_settings.API_LIMIT)


def get_wellsample_indices(conn, plate_id=None, plateacquisition_id=None):
"""
Return min and max WellSample index for a Plate OR PlateAcquisition

@param conn: BlitzGateway
@param plate_id: Plate ID
@param plateacquisition_id: PlateAcquisition ID
@return A dict of parent_id: child_count
"""
ctx = deepcopy(conn.SERVICE_OPTS)
ctx.setOmeroGroup(-1)
params = ParametersI()
query = "select minIndex(ws), maxIndex(ws) from Well well " \
"join well.wellSamples ws"
if plate_id is not None:
query += " where well.plate.id=:plate_id "
params.add('plate_id', wrap(plate_id))
elif plateacquisition_id is not None:
query += " where ws.plateAcquisition.id=:plateacquisition_id"
params.add('plateacquisition_id', wrap(plateacquisition_id))
result = conn.getQueryService().projection(query, params, ctx)
result = [r for r in unwrap(result)[0] if r is not None]
return result


def get_child_counts(conn, link_class, parent_ids):
"""
Count child links for the specified parent_ids.
Expand Down
52 changes: 52 additions & 0 deletions components/tools/OmeroWeb/omeroweb/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,44 @@
GET all wells, using omero-marshal to generate json
"""

api_plate_plateacquisitions = url(
r'^v(?P<api_version>%s)/m/plates/'
'(?P<plate_id>[0-9]+)/plateacquisitions/$' % versions,
views.PlateAcquisitionsView.as_view(),
name='api_plate_plateacquisitions')
"""
GET PlateAcquisitions in Plate, using omero-marshal to generate json
"""

api_plateacquisition = url(
r'^v(?P<api_version>%s)/m/plateacquisitions/'
'(?P<object_id>[0-9]+)/$' % versions,
views.PlateAcquisitionView.as_view(),
name='api_plateacquisition')
"""
Well url to GET or DELETE a single Well
"""

api_plateacquisition_wellsampleindex_wells = url(
r'^v(?P<api_version>%s)/m/plateacquisitions/'
'(?P<plateacquisition_id>[0-9]+)/wellsampleindex/'
'(?P<index>[0-9]+)/wells/$' % versions,
views.WellsView.as_view(),
name='api_plateacquisition_wellsampleindex_wells')
"""
GET Wells from a single Index in PlateAcquisition
"""

api_plate_wellsampleindex_wells = url(
r'^v(?P<api_version>%s)/m/plates/'
'(?P<plate_id>[0-9]+)/wellsampleindex/'
'(?P<index>[0-9]+)/wells/$' % versions,
views.WellsView.as_view(),
name='api_plate_wellsampleindex_wells')
"""
GET Wells from a single Index in Plate
"""

api_plate_wells = url(
r'^v(?P<api_version>%s)/m/plates/'
'(?P<plate_id>[0-9]+)/wells/$' % versions,
Expand All @@ -185,6 +223,15 @@
GET Wells in Plate, using omero-marshal to generate json
"""

api_plateacquisition_wells = url(
r'^v(?P<api_version>%s)/m/plateacquisitions/'
'(?P<plateacquisition_id>[0-9]+)/wells/$' % versions,
views.WellsView.as_view(),
name='api_plateacquisition_wells')
"""
GET Wells in Plate, using omero-marshal to generate json
"""

api_well = url(
r'^v(?P<api_version>%s)/m/wells/(?P<object_id>[0-9]+)/$' % versions,
views.WellView.as_view(),
Expand Down Expand Up @@ -215,6 +262,11 @@
api_screen_plates,
api_plate,
api_wells,
api_plate_plateacquisitions,
api_plateacquisition,
api_plateacquisition_wellsampleindex_wells,
api_plate_wellsampleindex_wells,
api_plate_wells,
api_plateacquisition_wells,
api_well,
)
126 changes: 114 additions & 12 deletions components/tools/OmeroWeb/omeroweb/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import traceback
import json

from api_query import query_objects, get_child_counts
from api_query import query_objects, get_child_counts, get_wellsample_indices
from omero_marshal import get_encoder, get_decoder, OME_SCHEMA_URL
from omero import ValidationException
from omeroweb.connector import Server
Expand Down Expand Up @@ -125,7 +125,7 @@ def dispatch(self, *args, **kwargs):
"""Wrap other methods to add decorators."""
return super(ApiView, self).dispatch(*args, **kwargs)

def add_data(self, marshalled, request, urls=None, **kwargs):
def add_data(self, marshalled, request, conn, urls=None, **kwargs):
"""
Post-process marshalled object to add any extra data.

Expand Down Expand Up @@ -179,7 +179,7 @@ def get(self, request, object_id, conn=None, **kwargs):
ch_count = counts[object_id] if object_id in counts else 0
marshalled['omero:childCount'] = ch_count

self.add_data(marshalled, request, self.urls, **kwargs)
self.add_data(marshalled, request, conn, self.urls, **kwargs)
return {'data': marshalled}

def delete(self, request, object_id, conn=None, **kwargs):
Expand Down Expand Up @@ -264,9 +264,59 @@ class PlateView(ObjectView):
# Urls to add to marshalled object. See ProjectsView for more details
urls = {
'url:wells': {'name': 'api_plate_wells',
'kwargs': {'plate_id': 'OBJECT_ID'}}
'kwargs': {'plate_id': 'OBJECT_ID'}},
'url:plateacquisitions': {'name': 'api_plate_plateacquisitions',
'kwargs': {'plate_id': 'OBJECT_ID'}},
}

def add_data(self, marshalled, request, conn, urls=None, **kwargs):
"""Add min/max WellSampleIndex."""
marshalled = super(PlateView, self).add_data(marshalled, request, conn,
urls=urls, **kwargs)
idx = get_wellsample_indices(conn, marshalled['@id'])
marshalled['omero:wellsampleIndex'] = idx

# Add link to Wells for each WellSample index in this Plate
ws_urls = []
if len(idx) == 2:
for ws_index in range(idx[0], idx[1]+1):
version = kwargs['api_version']
extra = {'plate_id': marshalled['@id'],
'index': ws_index}
url = build_url(request, 'api_plate_wellsampleindex_wells',
version, **extra)
ws_urls.append(url)
marshalled['url:wellsampleindex_wells'] = ws_urls

return marshalled


class PlateAcquisitionView(ObjectView):
"""Handles GET for /plates/:plate_id/plateacquisitions."""

OMERO_TYPE = 'PlateAcquisition'

def add_data(self, marshalled, request, conn, urls=None, **kwargs):
"""Add min/max WellSampleIndex."""
marshalled = super(PlateAcquisitionView, self).add_data(
marshalled, request, conn, urls=urls, **kwargs)
idx = get_wellsample_indices(conn,
plateacquisition_id=marshalled['@id'])
marshalled['omero:wellsampleIndex'] = idx

# Add link to Wells for each WellSample index in this PlateAcquisition
ws_urls = []
for ws_index in range(idx[0], idx[1]+1):
version = kwargs['api_version']
extra = {'plateacquisition_id': marshalled['@id'],
'index': ws_index}
url = build_url(request,
'api_plateacquisition_wellsampleindex_wells',
version, **extra)
ws_urls.append(url)
marshalled['url:wellsampleindex_wells'] = ws_urls
return marshalled


class WellView(ObjectView):
"""Handle access to an individual Well to GET or DELETE it."""
Expand All @@ -282,9 +332,9 @@ def get_opts(self, request):
opts['load_pixels'] = True
return opts

def add_data(self, marshalled, request, urls=None, **kwargs):
def add_data(self, marshalled, request, conn, urls=None, **kwargs):
"""Add 'url:image' to any 'Image' in 'WellSamples'."""
marshalled = super(WellView, self).add_data(marshalled, request,
marshalled = super(WellView, self).add_data(marshalled, request, conn,
urls=urls, **kwargs)
image_urls = {
'url:image': {'name': 'api_image',
Expand All @@ -294,7 +344,8 @@ def add_data(self, marshalled, request, urls=None, **kwargs):
# For each WellSample, add image urls to Image
for ws in marshalled['WellSamples']:
if 'Image' in ws:
self.add_data(ws['Image'], request, image_urls, **kwargs)
self.add_data(ws['Image'], request, conn,
image_urls, **kwargs)
return marshalled


Expand Down Expand Up @@ -330,7 +381,7 @@ def get(self, request, conn=None, **kwargs):
marshalled = query_objects(conn, self.OMERO_TYPE, group,
opts, normalize)
for m in marshalled['data']:
self.add_data(m, request, self.urls, **kwargs)
self.add_data(m, request, conn, self.urls, **kwargs)
return marshalled


Expand Down Expand Up @@ -429,7 +480,9 @@ def get_opts(self, request, **kwargs):
'url:wells': {'name': 'api_plate_wells',
'kwargs': {'plate_id': 'OBJECT_ID'}},
'url:plate': {'name': 'api_plate',
'kwargs': {'object_id': 'OBJECT_ID'}}
'kwargs': {'object_id': 'OBJECT_ID'}},
'url:plateacquisitions': {'name': 'api_plate_plateacquisitions',
'kwargs': {'plate_id': 'OBJECT_ID'}},
}


Expand Down Expand Up @@ -461,6 +514,49 @@ def get_opts(self, request, **kwargs):
return opts


class PlateAcquisitionsView(ObjectsView):
"""Handles GET for /plates/:plate_id/plateacquisitions."""

OMERO_TYPE = 'PlateAcquisition'

# Urls to add to marshalled object. See ProjectsView for more details
urls = {
'url:plateacquisition': {'name': 'api_plateacquisition',
'kwargs': {'object_id': 'OBJECT_ID'}},
}

def get_opts(self, request, **kwargs):
"""Add extra parameters to the opts dict."""
opts = super(PlateAcquisitionsView, self).get_opts(request, **kwargs)
opts['order_by'] = 'lower(obj.name)'
# at /plates/:plate_id/plateacquisitions/ we have 'plate_id' in kwargs
if 'plate_id' in kwargs:
opts['plate'] = long(kwargs['plate_id'])
return opts

def add_data(self, marshalled, request, conn, urls=None, **kwargs):
"""Add min/max WellSampleIndex."""
marshalled = super(PlateAcquisitionsView, self).add_data(
marshalled, request, conn, urls=urls, **kwargs)
idx = get_wellsample_indices(conn,
plateacquisition_id=marshalled['@id'])
marshalled['omero:wellsampleIndex'] = idx

# Add link to Wells for each WellSample index in this PlateAcquisition
ws_urls = []
for ws_index in range(idx[0], idx[1]+1):
version = kwargs['api_version']
extra = {'plateacquisition_id': marshalled['@id'],
'index': ws_index}
url = build_url(request,
'api_plateacquisition_wellsampleindex_wells',
version, **extra)
ws_urls.append(url)
marshalled['url:wellsampleindex_wells'] = ws_urls

return marshalled


class WellsView(ObjectsView):
"""Handles GET for /wells/ to list available Images."""

Expand All @@ -479,18 +575,23 @@ def get_opts(self, request, **kwargs):
# at /plates/:plate_id/wells/ we have 'plate_id' in kwargs
if 'plate_id' in kwargs:
opts['plate'] = long(kwargs['plate_id'])
elif 'plateacquisition_id' in kwargs:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to allow to filter by plate id and plateacquisition id?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's a case for that, since plateacquisition ID would also define plate ID.

opts['plateacquisition'] = long(kwargs['plateacquisition_id'])
else:
# filter by query /wells/?plate=:id
plate = getIntOrDefault(request, 'plate', None)
if plate is not None:
opts['plate'] = plate
# When filtering by plate or plateacquisition, can filter by ws index
if 'index' in kwargs:
opts['wellsample_index'] = int(kwargs['index'])
# Listing Wells, load Images
opts['load_images'] = True
return opts

def add_data(self, marshalled, request, urls=None, **kwargs):
def add_data(self, marshalled, request, conn, urls=None, **kwargs):
"""Add 'url:image' to any 'Image' in 'WellSamples'."""
marshalled = super(WellsView, self).add_data(marshalled, request,
marshalled = super(WellsView, self).add_data(marshalled, request, conn,
urls=urls, **kwargs)
image_urls = {
'url:image': {'name': 'api_image',
Expand All @@ -500,7 +601,8 @@ def add_data(self, marshalled, request, urls=None, **kwargs):
# For each WellSample, add image urls to Image
for ws in marshalled['WellSamples']:
if 'Image' in ws:
self.add_data(ws['Image'], request, image_urls, **kwargs)
self.add_data(ws['Image'], request, conn,
image_urls, **kwargs)
return marshalled


Expand Down
Loading