diff --git a/requirements.txt b/requirements.txt index 681c2111..df4d1a8d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ cython virtualenv -git+https://github.com/learningequality/python-for-android@5dc7bdb3364f5ed5b573ceffdb364382ae181955#egg=python-for-android +git+https://github.com/learningequality/python-for-android@6ba12f935ff753a58d396190c24a7a387b95ef9c#egg=python-for-android diff --git a/src/java/org/learningequality/Task.java b/src/java/org/learningequality/Task.java new file mode 100644 index 00000000..7a21f3b5 --- /dev/null +++ b/src/java/org/learningequality/Task.java @@ -0,0 +1,59 @@ +package org.learningequality; + +import androidx.work.Data; +import androidx.work.WorkManager; +import androidx.work.WorkRequest; +import androidx.work.OneTimeWorkRequest; +import androidx.work.PeriodicWorkRequest; +import androidx.work.BackoffPolicy; +import androidx.work.ExistingWorkPolicy; +import androidx.work.ExistingPeriodicWorkPolicy; +import java.util.concurrent.TimeUnit; + +import org.learningequality.Kolibri.TaskworkerWorker; +import org.kivy.android.PythonActivity; + + +public class Task { + public static void enqueueIndefinitely(String id, int interval, int delay, int retryInterval) { + WorkManager workManager = WorkManager.getInstance(PythonActivity.mActivity); + Data data = TaskworkerWorker.buildInputData(id); + + PeriodicWorkRequest.Builder workRequestBuilder = new PeriodicWorkRequest.Builder( + TaskworkerWorker.class, interval, TimeUnit.SECONDS + ); + + if (retryInterval > 0) { + workRequestBuilder.setBackoffCriteria( + BackoffPolicy.LINEAR, retryInterval, TimeUnit.SECONDS + ); + } + if (delay > 0) { + workRequestBuilder.setInitialDelay(delay, TimeUnit.SECONDS); + } + workRequestBuilder.setInputData(data); + PeriodicWorkRequest workRequest = workRequestBuilder.build(); + workManager.enqueueUniquePeriodicWork(id, ExistingPeriodicWorkPolicy.KEEP, workRequest); + } + public static void enqueueOnce(String id, int delay, int retryInterval, boolean keep) { + WorkManager workManager = WorkManager.getInstance(PythonActivity.mActivity); + Data data = TaskworkerWorker.buildInputData(id); + + OneTimeWorkRequest.Builder workRequestBuilder = new OneTimeWorkRequest.Builder(TaskworkerWorker.class); + if (retryInterval > 0) { + workRequestBuilder.setBackoffCriteria( + BackoffPolicy.LINEAR, retryInterval, TimeUnit.SECONDS + ); + } + if (delay > 0) { + workRequestBuilder.setInitialDelay(delay, TimeUnit.SECONDS); + } + workRequestBuilder.setInputData(data); + OneTimeWorkRequest workRequest = workRequestBuilder.build(); + if (keep) { + workManager.enqueueUniqueWork(id, ExistingWorkPolicy.KEEP, workRequest); + } else { + workManager.enqueueUniqueWork(id, ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest); + } + } +} diff --git a/src/kolibri_tasks.py b/src/kolibri_tasks.py index 93098d7a..e4c25024 100644 --- a/src/kolibri_tasks.py +++ b/src/kolibri_tasks.py @@ -1,23 +1,9 @@ from datetime import datetime from datetime import timedelta -from android_utils import PythonActivity from jnius import autoclass - -WorkManager = autoclass("androidx.work.WorkManager") -OneTimeWorkRequestBuilder = autoclass("androidx.work.OneTimeWorkRequest$Builder") -PeriodicWorkRequestBuilder = autoclass("androidx.work.PeriodicWorkRequest$Builder") -BackoffPolicy = autoclass("androidx.work.BackoffPolicy") -ExistingWorkPolicy = autoclass("androidx.work.ExistingWorkPolicy") -ExistingPeriodicWorkPolicy = autoclass("androidx.work.ExistingPeriodicWorkPolicy") -TimeUnit = autoclass("java.util.concurrent$TimeUnit") - -TaskWorker = autoclass("org.learningequality.Kolibri.TaskWorker") - - -def get_work_manager(): - return WorkManager.getInstance(PythonActivity.mActivity) +Task = autoclass("org.learningequality.Task") def queue_task( @@ -31,35 +17,23 @@ def queue_task( ): if id: id = str(id) - data = TaskWorker.buildInputData(id) + delay = ( + max(0, (scheduled_time - datetime.now()).total_seconds()) + if scheduled_time + else 0 + ) + retry_interval = retry_interval if retry_interval else 0 if repeat is None: # Kolibri uses `None` for repeat to indicate a task that repeats indefinitely # in this case it is suitable for the Android PeriodicWorkRequest as that is # designed for indefinitely repeating tasks. - work_request = PeriodicWorkRequestBuilder( - TaskWorker._class, interval, TimeUnit.SECONDS - ) - existing_work_policy = ExistingPeriodicWorkPolicy.KEEP - enqueue_method = get_work_manager().enqueueUniquePeriodicWork + Task.enqueueIndefinitely(id, interval, delay, retry_interval) else: - work_request = OneTimeWorkRequestBuilder(TaskWorker._class) - existing_work_policy = ( - ExistingWorkPolicy.KEEP - if keep - else ExistingWorkPolicy.APPEND_OR_REPLACE - ) - enqueue_method = get_work_manager().enqueueUniqueWork - if retry_interval is not None: - work_request.setBackOffCriteria( - BackoffPolicy.LINEAR, retry_interval, TimeUnit.SECONDS - ) - if scheduled_time: - delay = max(0, (scheduled_time - datetime.now()).total_seconds()) - if delay: - work_request.setInitialDelay(delay, TimeUnit.SECONDS) - work_request.setInputData(data).build() - enqueue_method(id, existing_work_policy, work_request) + # Android has no mechanism for scheduling a limited run of repeating tasks + # so anything else is just scheduled once, and we use the task_updates function + # below to reschedule the next invocation. + Task.enqueueOnce(id, delay, retry_interval, keep) def task_updates(job, orm_job, state=None, **kwargs): @@ -80,28 +54,6 @@ def task_updates(job, orm_job, state=None, **kwargs): ) -def execute_job(job_id): - from django.db import connection as django_connection - - from kolibri.core.tasks.storage import Storage - from kolibri.core.tasks.utils import db_connection - - connection = db_connection() - - storage = Storage( - connection, schedule_hooks=[queue_task], update_hooks=[task_updates] - ) - - job = storage.get_job(job_id) - - job.execute() - - connection.dispose() - - # Close any django connections opened here - django_connection.close() - - def start_default_tasks(): from kolibri.core.analytics.tasks import schedule_ping from kolibri.core.deviceadmin.tasks import schedule_vacuum diff --git a/src/taskworker.py b/src/taskworker.py index 69b6ca40..2f978525 100644 --- a/src/taskworker.py +++ b/src/taskworker.py @@ -3,12 +3,14 @@ import initialization # noqa: F401 keep this first, to ensure we're set up for other imports from kolibri.main import initialize -from kolibri_tasks import execute_job logging.info("Starting Kolibri task worker") initialize(skip_update=True) -job_id = environ.get("PYTHON_SERVICE_ARGUMENT", "") +job_id = environ.get("PYTHON_WORKER_ARGUMENT", "") + +# Import this after we have initialized Kolibri +from kolibri.core.tasks.worker import execute_job # noqa: E402 execute_job(job_id)