Skip to content

Commit

Permalink
Allow to add annotations to the job queue by ID
Browse files Browse the repository at this point in the history
  • Loading branch information
marcospri committed Jan 23, 2025
1 parent 0754b0e commit b83c048
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 1 deletion.
15 changes: 14 additions & 1 deletion h/services/job_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class Priority:
SINGLE_ITEM = 1
SINGLE_USER = 100
SINGLE_GROUP = 100
BETWEEN_TIMES = 1000
BETWEEN_TIMES = 1_000
BY_IDS = 1_000


class JobQueueService:
Expand Down Expand Up @@ -60,6 +61,18 @@ def add_by_id(self, name, annotation_id, tag, force=False, schedule_in=None):
where = [Annotation.id == annotation_id]
self.add_where(name, where, tag, Priority.SINGLE_ITEM, force, schedule_in)

def add_by_ids(
self, name, annotation_ids: list[str], tag, force=False, schedule_in=None
):
"""
Queue annotations by ID.
:param annotation_ids: List of annotation IDs to be queued, in the
application-level URL-safe format
"""
where = [Annotation.id.in_(annotation_ids)]
self.add_where(name, where, tag, Priority.BY_IDS, force, schedule_in)

def add_by_user(self, name, userid: str, tag, force=False, schedule_in=None):
"""
Queue all a user's annotations.
Expand Down
9 changes: 9 additions & 0 deletions h/tasks/job_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,12 @@ def add_annotations_from_group(name, groupid, tag, force=False, schedule_in=None
celery.request.find_service(name="queue_service").add_by_group(
name, groupid, tag, force=force, schedule_in=schedule_in
)


@celery.task
def add_annotations_by_ids(
name, annotation_ids: list[str], tag, force=False, schedule_in=None
):
celery.request.find_service(name="queue_service").add_by_ids(
name, annotation_ids, tag, force=force, schedule_in=schedule_in
)
8 changes: 8 additions & 0 deletions h/templates/admin/search.html.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,13 @@

{{ force_checkbox("reindex_group_force") }}
{% endcall %}

{% call reindex_form(heading="Process all by ID", action="reindex_ids") %}
<div class="form-group">
<label for="annotation_ids">Annotation IDs</label>
<textarea required class="form-control" name="annotation_ids" id="annotation_ids"></textarea>
</div>
{{ force_checkbox("reindex_ids_force") }}
{% endcall %}
{% endblock %}

36 changes: 36 additions & 0 deletions h/views/admin/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from pyramid.view import view_config, view_defaults

from h import models, tasks
from h.db.types import URLSafeUUID
from h.security import Permission


Expand Down Expand Up @@ -96,6 +97,41 @@ def reindex_group(self):
f"Began reindexing annotations in group {groupid} ({group.name})"
)

@view_config(
request_method="POST",
request_param="reindex_ids",
require_csrf=True,
renderer="h:templates/admin/search.html.jinja2",
)
def queue_annotations_by_id(self):
annotation_ids = self._annotation_ids_from_text_area(
self.request.params["annotation_ids"]
)
force = bool(self.request.params.get("reindex_ids_force"))

tasks.job_queue.add_annotations_by_ids.delay(
self.request.params["name"], annotation_ids, tag="reindex_ids", force=force
)
return self._notify_reindexing_started("Began reindexing annotations by ID.")

def _annotation_ids_from_text_area(self, textarea: str) -> list[str]:
ids = [
annotation_id.strip()
for annotation_id in textarea.split("\n")
if annotation_id.strip()
]
annotation_ids = []
for annotation_id in ids:
# If the ID looks like an hex UUID, convert it to URL-safe
if len(annotation_id) == 36:
annotation_ids.append(URLSafeUUID.hex_to_url_safe(annotation_id))
continue

# Otherwise assume it's already URL-safe
annotation_ids.append(annotation_id)

return annotation_ids

def _notify_reindexing_started(self, message):
self.request.session.flash(message, "success")
return HTTPFound(self.request.route_url("admin.search"))
21 changes: 21 additions & 0 deletions tests/unit/h/services/job_queue_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,27 @@ def test_add_by_id(self, svc, add_where):
where = add_where.call_args[0][1]
assert where[0].compare(Annotation.id == sentinel.annotation_id)

def test_add_annotations_by_ids(self, svc, add_where):
svc.add_by_ids(
sentinel.name,
[sentinel.id_1, sentinel.id_2],
sentinel.tag,
schedule_in=sentinel.schedule_in,
force=sentinel.force,
)

add_where.assert_called_once_with(
sentinel.name,
[Any.instance_of(BinaryExpression)],
sentinel.tag,
Priority.BY_IDS,
sentinel.force,
sentinel.schedule_in,
)

where = add_where.call_args[0][1]
assert where[0].compare(Annotation.id.in_([sentinel.id_1, sentinel.id_2]))

def test_add_annotations_between_times(self, svc, add_where):
svc.add_between_times(
sentinel.name,
Expand Down
19 changes: 19 additions & 0 deletions tests/unit/h/tasks/job_queue_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,25 @@ def test_it(self, queue_service):
)


class TestAddAnnotationsByIDs:
def test_it(self, queue_service):
job_queue.add_annotations_by_ids(
sentinel.name,
sentinel.annotation_ids,
sentinel.tag,
force=sentinel.force,
schedule_in=sentinel.schedule_in,
)

queue_service.add_by_ids.assert_called_once_with(
sentinel.name,
sentinel.annotation_ids,
sentinel.tag,
force=sentinel.force,
schedule_in=sentinel.schedule_in,
)


@pytest.fixture(autouse=True)
def celery(patch, pyramid_request):
cel = patch("h.tasks.job_queue.celery")
Expand Down
22 changes: 22 additions & 0 deletions tests/unit/h/views/admin/search_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,28 @@ def test_reindex_group_errors_if_group_not_found(
with pytest.raises(NotFoundError, match="Group def456 not found"):
views.reindex_group()

def test_queue_annotaions_by_id(self, views, tasks, pyramid_request):
pyramid_request.params = {
"annotation_ids": """
cdff42be-2fc0-11ef-ae06-37653ab647c1
zf9Cvi_AEe-uBjdlOrZHwQ
""",
"name": "jobname",
}

views.queue_annotations_by_id()

tasks.job_queue.add_annotations_by_ids.delay.assert_called_once_with(
"jobname",
["zf9Cvi_AEe-uBjdlOrZHwQ", "zf9Cvi_AEe-uBjdlOrZHwQ"],
tag="reindex_ids",
force=False,
)
assert pyramid_request.session.peek_flash("success") == [
"Began reindexing annotations by ID."
]

@pytest.fixture
def views(self, pyramid_request, queue_service): # pylint:disable=unused-argument
return SearchAdminViews(pyramid_request)
Expand Down

0 comments on commit b83c048

Please sign in to comment.