Source code for nlpmed_portal.nlp.tasks
# SPDX-FileCopyrightText: Copyright (C) 2026 Omid Jafari <omidjafari.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.db import transaction
from django.db.models import F
from nlpmed_portal.annotations.models import Patient
from nlpmed_portal.nlp.models import NlpProcessJob
from nlpmed_portal.nlp.models import NLPSetting
from nlpmed_portal.nlp.utils import process_patient_api_call
from nlpmed_portal.nlp.utils import process_patient_write_db
CHUNK_SIZE = 10
[docs]
def chunked_nlp_master_task(job_id: int) -> None:
try:
job = NlpProcessJob.objects.get(pk=job_id)
except NlpProcessJob.DoesNotExist:
return
job.mark_in_progress()
patient_list = job.patient_pks
job.total_count = len(patient_list)
job.save(update_fields=["total_count", "updated_at"])
project = job.project
try:
nlp_setting = NLPSetting.objects.get(project=project)
except NLPSetting.DoesNotExist:
job.job_status = "failed"
job.error_message = f"NLPSetting does not exist for project: {project.id}"
job.save()
return
chunked_lists = [
patient_list[i : i + CHUNK_SIZE] for i in range(0, len(patient_list), CHUNK_SIZE)
]
for patient_chunk in chunked_lists:
process_patient_chunk(
job_id,
patient_chunk,
nlp_setting.id,
)
finalize_nlp_job(job_id)
[docs]
def process_patient_chunk(
job_id: int,
patient_chunk: list[int],
nlp_setting_id: int,
) -> None:
try: # ruff: ignore[too-many-statements-in-try-clause]
job = NlpProcessJob.objects.get(pk=job_id)
if job.job_status == "failed":
return
nlp_setting = NLPSetting.objects.get(pk=nlp_setting_id)
for pat_pk in patient_chunk:
if job.job_status == "failed":
break
try:
patient = Patient.objects.get(pk=pat_pk)
except Patient.DoesNotExist:
continue
notes = patient.notes.all()
if not notes.exists():
continue
response_json = process_patient_api_call(nlp_setting, pat_pk, notes)
with transaction.atomic():
process_patient_write_db(nlp_setting, response_json, notes)
job.completed_count = F("completed_count") + len(patient_chunk)
job.save(update_fields=["completed_count", "updated_at"])
except Exception as exc:
NlpProcessJob.objects.filter(pk=job_id).update(
job_status="failed",
error_message=str(exc),
)
raise
[docs]
def finalize_nlp_job(job_id: int) -> None:
try:
job = NlpProcessJob.objects.get(pk=job_id)
if job.job_status != "failed":
job.mark_completed()
except NlpProcessJob.DoesNotExist:
pass