Skip to content

Commit

Permalink
Merge pull request #10 from German-BioImaging/add_badges
Browse files Browse the repository at this point in the history
Add badges and flake8 formatting
  • Loading branch information
Tom-TBT committed Apr 26, 2024
2 parents 03d5654 + 2490a95 commit e56d539
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 83 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/publish_pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ jobs:
name: Build and publish Python distribution to PyPI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.9'
- name: Run flake8
run: |
python -mpip install flake8
flake8 .
- name: Build a binary wheel and a source tarball
run: |
python -mpip install wheel
Expand Down
25 changes: 16 additions & 9 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
.. image:: https://github.com/German-BioImaging/omero-tagsearch/workflows/PyPI/badge.svg
:target: https://github.com/German-BioImaging/omero-tagsearch/actions

.. image:: https://badge.fury.io/py/omero-tagsearch.svg
:target: https://badge.fury.io/py/omero-tagsearch


OMERO.tagsearch
================
===============
OMERO.tagsearch is a plugin for `OMERO.web <https://github.com/ome/omero-web>`_ that enables searching of data using tags, with the search continuously refined as available search terms are entered and further term suggestions based on the entered terms are offered.
This can be used in a way that is similar to navigating a file system hierarchy.

Expand All @@ -12,9 +19,9 @@ As Python 2 has now reached end-of-life, OMERO 5.6 now
requires Python 3. With release 3.1.0 of tagsearch, the following are now required. To use tagsearch on older OMERO systems (running Python 2),
please use versions older than 3.1.0.

* Python 3.5 or later
* Python 3.8 or later
* omero-web 5.6 or later
* django 1.11 or later
* django 4.2 or later

User Documentation
==================
Expand Down Expand Up @@ -109,14 +116,14 @@ Harvard Medical School, then later extended by DPWR
Consulting Ltd.

These plugins were developed originally with the
support of [Micron Advanced Bioimaging Unit](https://micronoxford.com/)
support of `Micron Advanced Bioimaging Unit <https://micronoxford.com/>`_
funded by the Wellcome Trust Strategic Award 091911,
and [Open Microscopy](https://www.openmicroscopy.org/).
and `Open Microscopy <https://www.openmicroscopy.org/>`_.

Continued development was supported by [The Laboratory
of Systems Pharmacology, Harvard Medical School](https://hits.harvard.edu/the-program/laboratory-of-systems-pharmacology/research-program/) and
[Research Computing, Harvard Medical School](https://it.hms.harvard.edu/our-services/research-computing).
Continued development was supported by `The Laboratory
of Systems Pharmacology, Harvard Medical School <https://hits.harvard.edu/the-program/laboratory-of-systems-pharmacology/research-program/>`_ and
`Research Computing, Harvard Medical School <https://it.hms.harvard.edu/our-services/research-computing>`_.

Continued development was sponsored by
[Micron Advanced Bioimaging Unit](https://micronoxford.com/)
`Micron Advanced Bioimaging Unit <https://micronoxford.com/>`_
funded by the Wellcome Trust Strategic Award 107457.
9 changes: 5 additions & 4 deletions omero_tagsearch/forms.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from django.forms import Form, MultipleChoiceField, BooleanField, ChoiceField, RadioSelect
from django.forms import Form, MultipleChoiceField, BooleanField
from django.forms import ChoiceField, RadioSelect


class TagSearchForm(Form):
selectedTags = MultipleChoiceField()
excludedTags = MultipleChoiceField()
operation = ChoiceField(
widget = RadioSelect,
choices = (("AND", "AND"), ("OR", "OR")),
initial = "AND"
widget=RadioSelect,
choices=(("AND", "AND"), ("OR", "OR")),
initial="AND"
)
view_image = BooleanField(initial=True)
view_dataset = BooleanField(initial=True)
Expand Down
126 changes: 74 additions & 52 deletions omero_tagsearch/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ def index(request, conn=None, **kwargs):

# tree support
init = {"initially_open": None, "initially_select": []}
first_sel = None
selected = None
initially_open_owner = None

# E.g. backwards compatible support for
# path=project=51|dataset=502|image=607 (select the image)
path = request.GET.get("path", "")
i = path.split("|")[-1]
if i.split("=")[0] in ("project", "dataset", "image", "screen", "plate", "tag", "acquisition", "run", "well"):
if i.split("=")[0] in ("project", "dataset", "image", "screen",
"plate", "tag", "acquisition", "run", "well"):
init["initially_select"].append(str(i).replace("=", "-"))

# Now we support show=image-607|image-123 (multi-objects selected)
Expand Down Expand Up @@ -72,28 +73,29 @@ def index(request, conn=None, **kwargs):
# set context to 'cross-group'
conn.SERVICE_OPTS.setOmeroGroup("-1")
if first_obj == "tag":
first_sel = conn.getObject("TagAnnotation", int(first_id))
selected = conn.getObject("TagAnnotation", int(first_id))
else:
first_sel = conn.getObject(first_obj, int(first_id))
initially_open_owner = first_sel.details.owner.id.val
selected = conn.getObject(first_obj, int(first_id))
initially_open_owner = selected.details.owner.id.val
# Wells aren't in the tree, so we need parent...
if first_obj == "well":
parentNode = first_sel.getWellSample().getPlateAcquisition()
ws = selected.getWellSample()
parent_node = ws.getPlateAcquisition()
ptype = "acquisition"
# No Acquisition for this well, use Plate instead
if parentNode is None:
parentNode = first_sel.getParent()
if parent_node is None:
parent_node = selected.getParent()
ptype = "plate"
first_sel = parentNode
init["initially_open"] = ["%s-%s" % (ptype, parentNode.getId())]
selected = parent_node
init["initially_open"] = [f"{ptype}-{parent_node.getId()}"]
init["initially_select"] = init["initially_open"][:]
except:
except ValueError:
# invalid id
pass
if first_obj not in ("project", "screen"):
# need to see if first item has parents
if first_sel is not None:
for p in first_sel.getAncestry():
if selected is not None:
for p in selected.getAncestry():
# parents of tags must be tags (no OMERO_CLASS)
if first_obj == "tag":
init["initially_open"].insert(0, "tag-%s" % p.getId())
Expand All @@ -106,8 +108,8 @@ def index(request, conn=None, **kwargs):
init["initially_open"].insert(0, "orphaned-0")

# need to be sure that tree will be correct omero.group
if first_sel is not None:
switch_active_group(request, first_sel.details.group.id.val)
if selected is not None:
switch_active_group(request, selected.details.group.id.val)

# search support
global_search_form = GlobalSearchForm(data=request.GET.copy())
Expand All @@ -120,7 +122,8 @@ def index(request, conn=None, **kwargs):
url = reverse(viewname="tagsearch")

# validate experimenter is in the active group
active_group = request.session.get("active_group") or conn.getEventContext().groupId
active_group = (request.session.get("active_group")
or conn.getEventContext().groupId)
# prepare members of group...
s = conn.groupSummary(active_group)
leaders = s["leaders"]
Expand All @@ -142,12 +145,13 @@ def index(request, conn=None, **kwargs):
user_id = initially_open_owner
try:
user_id = int(user_id)
except:
except ValueError:
user_id = None

# Check is user_id is in a current group
if (
user_id not in (set([x.id for x in leaders]) | set([x.id for x in members]))
user_id not in (set([x.id for x in leaders])
| set([x.id for x in members]))
and user_id != -1
):
# All users in group is allowed
Expand Down Expand Up @@ -222,7 +226,7 @@ def get_tags(obj, tagset_d):
)

if user_id != -1:
hql += f" AND ann.details.owner.id = (:uid)"
hql += " AND ann.details.owner.id = (:uid)"
params.map = {"uid": rlong(user_id)}

return [
Expand Down Expand Up @@ -256,7 +260,8 @@ def get_tags(obj, tagset_d):
"global_search_form": global_search_form,
}
context["groups"] = myGroups
context["active_group"] = conn.getObject("ExperimenterGroup", int(active_group))
context["active_group"] = conn.getObject("ExperimenterGroup",
int(active_group))
for g in context["groups"]:
g.groupSummary() # load leaders / members
context["active_user"] = conn.getObject("Experimenter", int(user_id))
Expand Down Expand Up @@ -286,31 +291,34 @@ def tag_image_search(request, conn=None, **kwargs):

# validate experimenter is in the active group
active_group = (
request.session.get("active_group") or conn.getEventContext().groupId
request.session.get("active_group")
or conn.getEventContext().groupId
)
service_opts = conn.SERVICE_OPTS.copy()
service_opts.setOmeroGroup(active_group)

def getObjectsWithAnnotations(obj_type, include_ids, exclude_ids):
def get_annotated_obj(obj_type, in_ids, excl_ids):
# Get the images that match
params = Parameters()
params.map = {}
params.map["incl_ids"] = rlist([rlong(o) for o in set(include_ids)])
params.map["in_ids"] = rlist([rlong(o) for o in set(in_ids)])

hql = ("select link.parent.id from %sAnnotationLink link "
"where link.child.id in (:incl_ids) " % (obj_type))
if len(exclude_ids) > 0:
"where link.child.id in (:in_ids) " % (obj_type))
if len(excl_ids) > 0:
params.map["ex_ids"] = rlist([rlong(o) for o in set(excl_ids)])
hql += (" and link.parent.id not in "
"(select link.parent.id from %sAnnotationLink link "
"where link.child.id in (:excl_ids)) " % (obj_type))
params.map["excl_ids"] = rlist([rlong(o) for o in set(exclude_ids)])
"where link.child.id in (:ex_ids)) " % (obj_type))

hql += "group by link.parent.id"
if operation == "AND":
hql += f" having count (distinct link.child) = {len(include_ids)}"
hql += f" having count (distinct link.child) = {len(in_ids)}"

qs = conn.getQueryService()
return [x[0].getValue() for x in qs.projection(hql, params, service_opts)]
return [x[0].getValue() for x in qs.projection(hql,
params,
service_opts)]

context = {}
html_response = ""
Expand All @@ -320,27 +328,32 @@ def getObjectsWithAnnotations(obj_type, include_ids, exclude_ids):
preview = False
count_d = {}
if selected_tags:
image_ids = getObjectsWithAnnotations("Image", selected_tags, excluded_tags)
image_ids = get_annotated_obj("Image", selected_tags,
excluded_tags)
count_d["image"] = len(image_ids)

dataset_ids = getObjectsWithAnnotations("Dataset", selected_tags, excluded_tags)
dataset_ids = get_annotated_obj("Dataset", selected_tags,
excluded_tags)
count_d["dataset"] = len(dataset_ids)

project_ids = getObjectsWithAnnotations("Project", selected_tags, excluded_tags)
project_ids = get_annotated_obj("Project", selected_tags,
excluded_tags)
count_d["project"] = len(project_ids)

screen_ids = getObjectsWithAnnotations("Screen", selected_tags, excluded_tags)
screen_ids = get_annotated_obj("Screen", selected_tags,
excluded_tags)
count_d["screen"] = len(screen_ids)

plate_ids = getObjectsWithAnnotations("Plate", selected_tags, excluded_tags)
plate_ids = get_annotated_obj("Plate", selected_tags,
excluded_tags)
count_d["plate"] = len(plate_ids)

well_ids = getObjectsWithAnnotations("Well", selected_tags, excluded_tags)
well_ids = get_annotated_obj("Well", selected_tags,
excluded_tags)
count_d["well"] = len(well_ids)

acquisition_ids = getObjectsWithAnnotations(
"PlateAcquisition", selected_tags, excluded_tags
)
acquisition_ids = get_annotated_obj("PlateAcquisition",
selected_tags, excluded_tags)
count_d["acquisition"] = len(acquisition_ids)

if image_ids:
Expand All @@ -365,9 +378,9 @@ def getObjectsWithAnnotations(obj_type, include_ids, exclude_ids):

if well_ids:
wells = []
for well in conn.getObjects("Well", ids=well_ids):
well.name = well.getParent().name + f" - {well.getWellPos()}"
wells.append(well)
for w in conn.getObjects("Well", ids=well_ids):
w.name = f"{w.getParent().name} - {w.getWellPos()}"
wells.append(w)
manager["containers"]["well"] = wells

if acquisition_ids:
Expand All @@ -388,20 +401,23 @@ def getObjectsWithAnnotations(obj_type, include_ids, exclude_ids):

middle = time.time()

def getAnnotationsForObjects(obj_type, oids):
def get_objects_annotations(obj_type, oids):
# Get the images that match
params = Parameters()
params.map = {}
hql = (
"select distinct link.child.id from %sAnnotationLink link " % obj_type
"select distinct link.child.id " +
"from %sAnnotationLink link " % obj_type
)
if operation == "AND":
hql += "where link.parent.id in (:oids)"
params.map["oids"] = rlist([rlong(o) for o in oids])

qs = conn.getQueryService()
return [
result[0].val for result in qs.projection(hql, params, service_opts)
result[0].val for result in qs.projection(hql,
params,
service_opts)
]

# Calculate remaining possible tag navigations
Expand All @@ -421,21 +437,27 @@ def getAnnotationsForObjects(obj_type, oids):

if operation == "AND":
if image_ids:
remaining.update(getAnnotationsForObjects("Image", image_ids))
remaining.update(get_objects_annotations("Image",
image_ids))
if dataset_ids:
remaining.update(getAnnotationsForObjects("Dataset", dataset_ids))
remaining.update(get_objects_annotations("Dataset",
dataset_ids))
if project_ids:
remaining.update(getAnnotationsForObjects("Project", project_ids))
remaining.update(get_objects_annotations("Project",
project_ids))
if well_ids:
remaining.update(getAnnotationsForObjects("Well", well_ids))
remaining.update(get_objects_annotations("Well",
well_ids))
if acquisition_ids:
remaining.update(
getAnnotationsForObjects("PlateAcquisition", acquisition_ids)
)
get_objects_annotations("PlateAcquisition",
acquisition_ids))
if plate_ids:
remaining.update(getAnnotationsForObjects("Plate", plate_ids))
remaining.update(get_objects_annotations("Plate",
plate_ids))
if screen_ids:
remaining.update(getAnnotationsForObjects("Screen", screen_ids))
remaining.update(get_objects_annotations("Screen",
screen_ids))

end = time.time()
logger.info(
Expand Down
33 changes: 17 additions & 16 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,23 @@ def read_file(fname, content_type=None):
description=DESCRIPTION,
long_description=read_file("README.rst"),
classifiers=[
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Framework :: Django",
"Intended Audience :: End Users/Desktop",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: GNU Affero General Public License v3",
"Natural Language :: English",
"Operating System :: OS Independent",
"Programming Language :: JavaScript",
"Programming Language :: Python :: 3",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
"Topic :: Internet :: WWW/HTTP :: WSGI",
"Topic :: Scientific/Engineering :: Visualization",
"Topic :: Software Development :: Libraries :: " "Application Frameworks",
"Topic :: Text Processing :: Markup :: HTML",
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Framework :: Django',
'Intended Audience :: End Users/Desktop',
'Intended Audience :: Science/Research',
'License :: OSI Approved :: GNU Affero General Public License v3',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: JavaScript',
'Programming Language :: Python :: 3',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Internet :: WWW/HTTP :: WSGI',
'Topic :: Scientific/Engineering :: Visualization',
'Topic :: Software Development :: Libraries :: ' +
'Application Frameworks',
'Topic :: Text Processing :: Markup :: HTML'
],
author=AUTHOR,
author_email="dpwrussell@gmail.com",
Expand Down

0 comments on commit e56d539

Please sign in to comment.