처음부터 시작하는 Django 데이터 적재(3)

June
None
Published in
14 min readFeb 23, 2023
출처: https://acutrans.com/top-10-most-commonly-spoken-languages-in-the-world/

안녕하세요. 휴먼스케이프 june입니다.

이번시간엔 적재한 임상연구를 한글로 번역하고, 적재 단계를 나누는 법에 대해 알아보도록 하겠습니다.

프로젝트를 진행한 코드는 깃허브에 올라가 있으니 참고하시기 바랍니다.

임상연구 번역

임상연구 번역은 아래 패키지를 이용해 진행합니다.

먼저 번역 패키지를 설치해줍니다.

poetry add translate

임상연구를 번역하기 전, 데이터 구조를 어떻게 가져갈지에 대해 생각해야 합니다.

임상연구를 번역해 저장하면 기존 영문 임상연구 정보가 한글로 바뀝니다.

우리는 원본 임상정보 데이터를 훼손하지 않고 번역 데이터를 추가하는 방식으로 가려 합니다.

  1. study를 묶는 테이블을 생성합니다: 보기엔 깔끔할 수 있으나 조회 시 join이 필요해 성능 이슈가 생길 수 있습니다.
  2. 기존 study model에 locale 컬럼을 추가하고, 원본 임상연구를 판별하기 위해 original_study_id를 추가해 번역 임상연구의 original_study_id 컬럼에 원본 임상연구의 id를 넣습니다: 깔끔하게 그루핑되어있지 않아 보기 불편할 수 있으나, 조회 시 ct_id를 기준으로 join없이 가져올 수 있어 성능 관점에서 이점이 있습니다.

여기서는 2번 방법을 적용하도록 하겠습니다.

이 경우 기존 ct_id 필드의 unique옵션을 삭제합니다.

ct_id를 기준으로 locale 당 하나의 임상연구만 존재해야 하므로 ct_id, locale필드에 unique 제약을 걸어놓으면 정신건강에 매우 좋습니다.

study model에 locale, original_study_id 컬럼을 추가합니다.

class Study(models.Model):
nct_id = models.CharField(verbose_name="임상연구 번호", max_length=50)
results_first_submitted_date = models.DateField(verbose_name="최초 제출 날짜", null=True, blank=True)
last_update_submitted_date = models.DateField(verbose_name="최근 수정 날짜", null=True, blank=True)
start_date = models.DateField(verbose_name="임상연구 시작 날짜", null=True, blank=True)
completion_date = models.DateField(verbose_name="임상연구 종료 날짜", null=True, blank=True)
title = models.TextField(verbose_name="제목", null=True, blank=True)
overall_status = models.CharField(max_length=50, verbose_name="진행 상태", null=True, blank=True)
phase = models.CharField(max_length=50, verbose_name="임상 단계", null=True, blank=True)
enrollment = models.IntegerField(verbose_name="대상자 수", null=True, blank=True)
original_study = models.ForeignKey('self', null=True, blank=True, related_name='translated_studies', verbose_name="원본 임상연구(study) 고유번호", on_delete=models.CASCADE)
locale = models.CharField(max_length=2, null=True, blank=True, verbose_name="언어코드")

class Meta:
unique_together = ('original_study', 'locale',)

(foreignkey field는 column name에 _id 접미사가 자동으로 붙습니다.)

(foreignkey field에서 ‘self’를 인자로 넣으면 스스로와 foreign key로 연결합니다.)

이제 batch_tasks에서 study를 인자로 받아 번역한 dict를 반환하는 translate_study 함수를 생성합니다.

from translate import Translator

def translate(text):
if text is None:
return None
translator = Translator(to_lang='ko')
return translator.translate(text)

def translate_study(study):
"""
임상 연구 데이터를 번역하는 메소드
"""
return {
'nct_id': study.nct_id,
'title': translate(study.title),
'results_first_submitted_date': study.results_first_submitted_date,
'last_update_submitted_date': study.last_update_submitted_date,
'start_date': study.start_date,
'completion_date': study.completion_date,
'overall_status': translate(study.overall_status),
'phase': translate(study.phase),
'enrollment': study.enrollment,
'interventions': [
{
'intervention_type': translate(intervention.intervention_type),
'name': translate(intervention.name),
'description': translate(intervention.description),
}
for intervention in study.interventions.all()
],
'conditions': [
{
'name': translate(condition.name),
}
for condition in study.conditions.all()
],
'eligibilities': [
{
'gender': translate(eligibility.gender),
'minimum_age': eligibility.minimum_age,
'maximum_age': eligibility.maximum_age,
'healthy_volunteers': translate(eligibility.healthy_volunteers),
'criteria': translate(eligibility.criteria),
}
for eligibility in study.eligibilities.all()
],
'locale': 'ko',
'original_study': study.pk
}

이제 임상연구 번역까진 잘 되는걸 볼 수 있습니다.

하지만 데이터 구조가 조금 맘에 들지 않습니다.

intervention의 번역본을 보기 위해선 study까지 탐색한 후 번역 study를 찾아 intervention 부분을 봐야합니다.

이런 방식은 데이터 구조가 복잡해질수록 오래걸릴뿐더러 intervention의 번역본 매핑이 정확히 되어있지 않아 엉뚱한 번역본을 찾을 수 있습니다.

intervention과 같은 study에 연결된 모델도 번역본과 연결지어줍시다.

class Intervention(models.Model):
study = models.ForeignKey(Study, on_delete=models.CASCADE, related_name="interventions")
intervention_type = models.CharField(max_length=500, verbose_name="치료 타입", null=True, blank=True)
name = models.TextField(null=True, blank=True)
description = models.TextField(null=True, blank=True)
original_intervention = models.ForeignKey('self', null=True, blank=True, related_name='translated_interventions', verbose_name="원본 의약품(intervention) 고유번호", on_delete=models.CASCADE)
locale = models.CharField(max_length=2, verbose_name="언어코드", null=True, blank=True)

class Condition(models.Model):
studies = models.ManyToManyField(Study, related_name="conditions")
name = models.CharField(max_length=500, null=True, blank=True)
original_condition = models.ForeignKey('self', null=True, blank=True, related_name='translated_conditions', verbose_name="원본 질환(condition) 고유번호", on_delete=models.CASCADE)
locale = models.CharField(max_length=2, verbose_name="언어코드", null=True, blank=True)

class Eligibility(models.Model):
study = models.ForeignKey(Study, on_delete=models.CASCADE, related_name="eligibilities")
gender = models.TextField(verbose_name="성별", null=True, blank=True)
minimum_age = models.CharField(max_length=30, verbose_name="최소 나이", null=True, blank=True)
maximum_age = models.CharField(max_length=30, verbose_name="최대 나이", null=True, blank=True)
healthy_volunteers = models.TextField(null=True, blank=True)
criteria = models.TextField(null=True, blank=True)
original_eligibility = models.ForeignKey('self', null=True, blank=True, related_name='translated_eligibility', verbose_name="원본 선정조건(eligibility) 고유번호", on_delete=models.CASCADE)
locale = models.CharField(max_length=2, verbose_name="언어코드", null=True, blank=True)

또, translate_study 함수도 수정해줍니다.


def translate_study(study):
"""
임상 연구 데이터를 번역하는 메소드
"""
return {
'nct_id': study.nct_id,
'title': translate(study.title),
'results_first_submitted_date': study.results_first_submitted_date,
'last_update_submitted_date': study.last_update_submitted_date,
'start_date': study.start_date,
'completion_date': study.completion_date,
'overall_status': translate(study.overall_status),
'phase': translate(study.phase),
'enrollment': study.enrollment,
'interventions': [
{
'intervention_type': translate(intervention.intervention_type),
'name': translate(intervention.name),
'description': translate(intervention.description),
'locale': 'ko',
'original_intervention': intervention.pk
}
for intervention in study.interventions.all()
],
'conditions': [
{
'name': translate(condition.name),
'locale': 'ko',
'original_condition': condition.pk
}
for condition in study.conditions.all()
],
'eligibilities': [
{
'gender': translate(eligibility.gender),
'minimum_age': eligibility.minimum_age,
'maximum_age': eligibility.maximum_age,
'healthy_volunteers': translate(eligibility.healthy_volunteers),
'criteria': translate(eligibility.criteria),
'locale': 'ko',
'original_eligibility': eligibility.pk
}
for eligibility in study.eligibilities.all()
],
'locale': 'ko',
'original_study': study.pk
}

이제 임상연구가 잘 번역되어 적재되는것을 확인할 수 있습니다.

다음시간엔 임상연구의 적재 단계(original_data 저장, convert, 번역)를 나누고, 신규 임상연구를 빠르게 업데이트 하는 방법에 대해 알아보겠습니다.

--

--