287 lines
15 KiB
Python
Executable file
287 lines
15 KiB
Python
Executable file
# coding=utf-8
|
|
|
|
# from django.core.urlresolvers import reverse
|
|
from django.urls import reverse
|
|
from django.db import models
|
|
|
|
#from ck.models import *
|
|
#from MultiSelect import *
|
|
|
|
# Create your models here.
|
|
|
|
CONTROL_CHOICES = (
|
|
('CR', 'Complete Response'),
|
|
('PR', 'Partial Response'),
|
|
('SD', 'Stable Disease'),
|
|
('PD', 'Progressive Disease'),
|
|
)
|
|
|
|
#PRIOR_TX_CHOICES = (
|
|
# (10, 'OBS=Observation'),
|
|
# (20, 'S=Surgery'),
|
|
# (30, 'CHT=Chemotherapy'),
|
|
# (40, 'TKI=Target therapy'),
|
|
# (50, 'RT=Whole brain radiotherapy'),
|
|
# )
|
|
|
|
class PriorTx(models.Model):
|
|
value = models.CharField(max_length=3, primary_key=True)
|
|
option = models.CharField(max_length=200)
|
|
|
|
def __str__(self):
|
|
return self.option
|
|
|
|
def instance_fields(instance):
|
|
"""Returns a list of all field names on the instance."""
|
|
fields = []
|
|
for f in instance._meta.fields:
|
|
|
|
fname = f.name
|
|
# resolve picklists/choices, with get_xyz_display() function
|
|
get_choice = 'get_'+fname+'_display'
|
|
if hasattr( instance, get_choice):
|
|
value = getattr( instance, get_choice)()
|
|
else:
|
|
try :
|
|
value = getattr(instance, fname)
|
|
except:
|
|
value = None
|
|
|
|
# only display fields with values and skip some fields entirely
|
|
# if f.editable and f.name not in ('id', 'Patient') :
|
|
# if f.name not in ('id', 'Patient') :
|
|
# if not f.primary_key:
|
|
if True:
|
|
fields.append(
|
|
{
|
|
'verbose_name':f.verbose_name,
|
|
'name':f.name,
|
|
'value':value,
|
|
}
|
|
)
|
|
|
|
return fields
|
|
|
|
|
|
def instance_many_to_many(instance):
|
|
fields = []
|
|
for f in instance._meta.many_to_many:
|
|
fname = f.name
|
|
# resolve picklists/choices, with get_xyz_display() function
|
|
get_choice = 'get_'+fname+'_display'
|
|
if hasattr( instance, get_choice):
|
|
value = getattr( instance, get_choice)()
|
|
else:
|
|
try :
|
|
value = getattr(instance, fname)
|
|
except:
|
|
value = None
|
|
|
|
if value:
|
|
value = [x for x in value.all()]
|
|
|
|
fields.append(
|
|
{
|
|
'verbose_name':f.verbose_name,
|
|
'name':f.name,
|
|
'value':value,
|
|
}
|
|
)
|
|
|
|
|
|
return fields
|
|
|
|
|
|
class NPatient(models.Model):
|
|
# Patient = models.ForeignKey('ck.Patient', primary_key=True)
|
|
Patient = models.OneToOneField('ck.Patient', primary_key=True, on_delete=models.CASCADE)
|
|
|
|
LastModified = models.DateTimeField(auto_now = True)
|
|
# Name = models.CharField(max_length=200, verbose_name='病患姓名') # Patient Name
|
|
# ChartID = models.IntegerField(primary_key=True, verbose_name='病歷號碼') # Chart Number
|
|
# DOB = models.DateField( verbose_name='出生年月日') # Date of Birth
|
|
#
|
|
# SEX_CHOICES = (
|
|
# (1, 'Male'),
|
|
# (2, 'Female'),
|
|
# )
|
|
# Sex = models.IntegerField(choices=SEX_CHOICES, verbose_name='性別') # Gender
|
|
|
|
DxDate = models.DateField(null=True, blank=True, verbose_name='診斷日期') # Date of Diagnosis
|
|
|
|
|
|
HISTOLOGY_CHOICES = (
|
|
(1, 'Adenocarcinoma'),
|
|
(2, 'Squamous cell carcinoma'),
|
|
(3, 'Large cell carcinoma'),
|
|
(4, 'carcinoma, NOS'),
|
|
(5, 'Adenosquamous carcinoma'),
|
|
)
|
|
Histology = models.IntegerField(choices=HISTOLOGY_CHOICES, null=True, blank=True, verbose_name='組織學型態') # Histology Subtypes
|
|
|
|
BMDate = models.DateField(null=True, blank=True, verbose_name='初次腦轉移日期') # First Date of Diagnosis of Brain Metastases
|
|
KPS70 = models.BooleanField(null=True, verbose_name='行為狀態量表至少七十分以上') # Karnovsky Performance Status ≥70
|
|
ExtraCranialMets = models.BooleanField(null=True, verbose_name='是否有胸腔外轉移病灶') # Presence of Extrathoracic Metastasis
|
|
|
|
DistantControl = models.CharField(max_length=2, choices=CONTROL_CHOICES, null=True, blank=True, verbose_name='三個月內顱外轉移控制狀態') # Control of Extracranial Metastasis within 3 M
|
|
|
|
PrimaryControl = models.BooleanField(null=True,verbose_name='胸腔原發部位控制狀態(Controlled)') # Control of Primary (Thoracic) Disease
|
|
UpfrontSRS = models.BooleanField(null=True,verbose_name='腦轉移診斷後是否即刻進行電腦刀治療') # Upfront SRS after BM diagnosis
|
|
|
|
|
|
PRIOR_TX_CHOICES = (
|
|
('OBS', 'Observation'),
|
|
('S', 'Surgery'),
|
|
('CHT', 'Chemotherapy'),
|
|
('TKI', 'Target therapy'),
|
|
('RT', 'Whole brain radiotherapy'),
|
|
)
|
|
# PRIOR_TX_CHOICES = (
|
|
# (10, 'OBS=Observation'),
|
|
# (20, 'S=Surgery'),
|
|
# (30, 'CHT=Chemotherapy'),
|
|
# (40, 'TKI=Target therapy'),
|
|
# (50, 'RT=Whole brain radiotherapy'),
|
|
# )
|
|
# PriorTx = models.CharField(max_length=3, choices=HISTOLOGY_CHOICES, null=True, verbose_name='若非即刻進行電腦刀之治療方式') # Any Tx if SRS is deferred
|
|
# PriorTx = models.CommaSeparatedIntegerField(max_length=5, choices=PRIOR_TX_CHOICES, null=True, verbose_name='若非即刻進行電腦刀之治療方式') # Any Tx if SRS is deferred
|
|
# PriorTx = MultiSelectField(max_length=5, choices=PRIOR_TX_CHOICES, null=True, verbose_name='若非即刻進行電腦刀之治療方式') # Any Tx if SRS is deferred
|
|
priortx = models.ManyToManyField(PriorTx, blank=True, verbose_name='若非即刻進行電腦刀之治療方式') # Any Tx if SRS is deferred
|
|
|
|
PriorWBRT = models.BooleanField(null=True, verbose_name='曾接受全腦放射治療') # History of Prior Whole Brain Radiotherapy
|
|
WBRTDate = models.DateField( null=True, blank=True, verbose_name='全腦放射治療日期') # Start Date of Prior WBRT
|
|
AdjuvantWBRT = models.BooleanField(null=True, verbose_name='電腦刀後八週內進行全腦放射治療') # WBRT Within 2 Months after SRS
|
|
SRSBMDate = models.DateField( null=True, blank=True, verbose_name='進行電腦刀之腦轉移診斷日期') # Diagnosis of Brain Metastsis for SRS
|
|
BMnumber = models.IntegerField(null=True, blank=True, verbose_name='電腦刀治療腦轉移病灶總數量') # Number of Brain Mets for SRS
|
|
|
|
##########
|
|
TKI = models.BooleanField(null=True,verbose_name='同時使用標靶治療') # Concurrent Use of TKI
|
|
##########
|
|
Radionecrosis = models.BooleanField(null=True, verbose_name='放射性壞死') # Presence of Radionecrosis
|
|
# Pseudoprogression = models.BooleanField(null=True, verbose_name='MRI影像判讀偽惡化') # Pseudoprogression on MRI/MRS/MRP
|
|
RNDate = models.DateField(null=True, blank=True, verbose_name='診斷放射性壞死日期') # Date of Radionecrosis or Censor
|
|
NewBM = models.BooleanField(null=True, verbose_name='發生新腦轉移病灶') # Presence of New Brain Metastsis
|
|
|
|
NEW_BM_NUMBER_CHOICES = (
|
|
('1', '1'),
|
|
('2', '2'),
|
|
('3', '3'),
|
|
('4', '4'),
|
|
('5', '5'),
|
|
('M', '>5'),
|
|
)
|
|
NewBMNumber = models.CharField(max_length=1, choices=NEW_BM_NUMBER_CHOICES, null=True, blank=True, verbose_name='新腦轉移病灶數量') # Number of New Brain Metastasis
|
|
|
|
NewBMDate = models.DateField(null=True, blank=True, verbose_name='診斷新腦轉移病灶日期') # Date of New Brain Metastasis or Censor
|
|
|
|
SALVAGE_TX_CHOICES = (
|
|
(0, 'Supportive care'),
|
|
(1, 'Chemotherapy'),
|
|
(2, 'Target therapy'),
|
|
(3, 'WBRT'),
|
|
(4, 'SRS'),
|
|
(5, 'Surgery'),
|
|
)
|
|
SalvageTx = models.IntegerField(choices=SALVAGE_TX_CHOICES, null=True, blank=True, verbose_name='新腦轉移病灶治療方法') # Salvage Treatment
|
|
|
|
Death = models.BooleanField(null=True, verbose_name='發生死亡事件') # Death
|
|
NeuroDeath = models.BooleanField(null=True, verbose_name='因腦轉移導致死亡') # Neurogenic Death Due to Brain Metastasis
|
|
SurviveDate = models.DateField(null=True, blank=True, verbose_name='存活日期') # Date of Death or Censor
|
|
DistantProgression = models.BooleanField(null=True, verbose_name='顱外病灶惡化') # Progression of Extracranial Disease
|
|
DistantDate = models.DateField(null=True, blank=True, verbose_name='顱外病灶惡化日期') # Date of Extracranial Progression or Censor
|
|
PoorKPS = models.BooleanField(null=True, verbose_name='行為狀態惡化致生活無法自理') # Progression of Extracranial Disease
|
|
DependentDate = models.DateField(null=True, blank=True, verbose_name='生活開始無法自理日期') # Date of KPS < 70 or Censor
|
|
|
|
##########
|
|
|
|
|
|
EGFR = models.CharField(max_length=200, blank=True, null=True, verbose_name='EGFR Mutation Status')
|
|
ALK = models.CharField(max_length=200, blank=True, null=True, verbose_name='EML4-ALK Rearrangement')
|
|
KRAS = models.CharField(max_length=200, blank=True, null=True, verbose_name='K-RAS Mutation Status')
|
|
BRAF = models.CharField(max_length=200, blank=True, null=True, verbose_name='BRAF Mutation Status')
|
|
HER2 = models.CharField(max_length=200, blank=True, null=True, verbose_name='HER2 Mutation Status')
|
|
|
|
ThoracicMets = models.BooleanField(null=True, verbose_name='是否有胸腔轉移病灶') # Presence of intrathoracic Metastasis
|
|
SalvageWBRT = models.BooleanField(null=True, verbose_name='是否接受救援全腦放射治療') # Salvage WBRT after SRS
|
|
SalvageRTDate = models.DateField(null=True, blank=True, verbose_name='救援全腦放射治療日期') # Date of salvage WBRT
|
|
SRSDate = models.DateField(null=True, blank=True, verbose_name='執行第一次SRS日期') # Date of first SRS treatment
|
|
|
|
OverallResponse = models.CharField(max_length=2, choices=CONTROL_CHOICES, null=True, blank=True, verbose_name='RECIST整體最佳反應') # Combined best response according to RECIST
|
|
|
|
LocalProgression = models.BooleanField(null=True, verbose_name='RECIST整體局部病灶惡化') # Combined progression according to RECIST
|
|
LocalDate = models.DateField(null=True, blank=True, verbose_name='RECIST局部病灶惡化日期') # Date of Local Progression
|
|
PseudoPD = models.BooleanField(null=True, verbose_name='偽惡化') # Pseudoprogression
|
|
ClinicalProgression = models.BooleanField(null=True, verbose_name='臨床評估局部復發') # Clinical local control
|
|
RecurDate = models.DateField(null=True, blank=True, verbose_name='臨床評估局部復發日期') # Date of clinical local progression
|
|
|
|
LateSE = models.IntegerField(null=True, blank=True, verbose_name='SRS副作用嚴重程度') # Grading of Late Toxicity
|
|
|
|
def Physician(self):
|
|
p = None
|
|
for t in self.Patient.treatment_set.all():
|
|
if t.surgeon:
|
|
return {
|
|
'dept': 'surg',
|
|
'name': t.surgeon,
|
|
}
|
|
else:
|
|
p = t.oncologist
|
|
return {
|
|
'dept': 'onc',
|
|
'name': p,
|
|
}
|
|
|
|
|
|
def get_absolute_url(self):
|
|
# return reverse('patient_update', kwargs={'pk': self.Patient.id})
|
|
return reverse('patient_update', args=[self.Patient.id])
|
|
|
|
def meta(self):
|
|
return self._meta
|
|
|
|
def fields(self):
|
|
return instance_fields(self)
|
|
|
|
def many_to_many(self):
|
|
return instance_many_to_many(self)
|
|
|
|
|
|
class NTarget(models.Model):
|
|
# Lesion = models.ForeignKey('ck.Lesion', primary_key=True)
|
|
Lesion = models.OneToOneField('ck.Lesion', primary_key=True, on_delete=models.CASCADE)
|
|
|
|
LastModified = models.DateTimeField(auto_now = True)
|
|
|
|
# TargetID = models.IntegerField(primary_key=True, verbose_name='電腦刀腦轉移病灶編號') # ID of Brain Mets Target for SRS
|
|
|
|
RECIST = models.BooleanField(null=True, blank=True, verbose_name='是否為RECIST可測量病灶') # RECIST Measurable Lesion
|
|
BMdiameter = models.FloatField( null=True, blank=True, verbose_name='病灶最大直徑(mm)') # Diameter of Brain Metastasis
|
|
|
|
# BMvolume = models.FloatField( verbose_name='病灶體積(mL)') # Volume of Brain Metatasis
|
|
# SRSdose = models.IntegerField( verbose_name='處方劑量(cGy)') # Prescribe SRS Dose
|
|
# Fraction = models.IntegerField( verbose_name='治療次數') # Number of SRS fraction
|
|
# Isodose = models.IntegerField( verbose_name='百分等劑量線(%)') # Prescribed Isodose Line
|
|
|
|
PTV = models.BooleanField(null=True,verbose_name='是否外加PTV邊界') # Additional PTV to Brain Metastasis
|
|
|
|
# MinDose = models.FloatField( verbose_name='百分之九九病灶體積接受劑量(cGy)') # Actual Dose Covering 99% of Target
|
|
# MaxDose = models.FloatField( verbose_name='病灶最高劑量(cGy)') # Maximal Dose to Target
|
|
|
|
##########
|
|
|
|
# SRSDate = models.DateField(verbose_name='電腦刀治療日期') # Treatment Date of SRS
|
|
|
|
TargetResponse = models.CharField(max_length=2, choices=CONTROL_CHOICES, null=True, blank=True, verbose_name='RECIST最佳反應') # Best Response of Target
|
|
|
|
TargetControl = models.BooleanField(null=True, verbose_name='電腦刀治療病灶惡化(RECIST)') # RECIST Progression of SRS Target
|
|
TargetControlDate = models.DateField(null=True, blank=True, verbose_name='電腦刀治療病灶惡化日期') # Date of SRS Target Progression or Censor
|
|
|
|
Pseudoprogression = models.BooleanField(null=True, verbose_name='MRI影像判讀偽惡化') # Pseudoprogression on MRI/MRS/MRP
|
|
|
|
|
|
def get_absolute_url(self):
|
|
return reverse('target_update', args=[self.Lesion.id])
|
|
|
|
def fields(self):
|
|
return instance_fields(self)
|
|
|