diff options
Diffstat (limited to '97suifangqa/apps/indicator/models.py')
-rw-r--r-- | 97suifangqa/apps/indicator/models.py | 318 |
1 files changed, 250 insertions, 68 deletions
diff --git a/97suifangqa/apps/indicator/models.py b/97suifangqa/apps/indicator/models.py index 87cd80f..cf9b716 100644 --- a/97suifangqa/apps/indicator/models.py +++ b/97suifangqa/apps/indicator/models.py @@ -79,6 +79,10 @@ class IndicatorCategory(models.Model): # {{{ class Indicator(models.Model): # {{{ """ 指标模型 + + type: + normal: 普通类别,用户可以follow/unfollow + basic: 基本信息指标,用于记录基本信息,不可以follow """ name = models.CharField(u"指标名称", max_length=100) pinyin = models.CharField(u"拼音", max_length=200, @@ -88,6 +92,16 @@ class Indicator(models.Model): # {{{ description = models.TextField(u"指标描述", blank=True) # Indicator 接受数据类型/格式等说明/示例 helpText = models.CharField(u"帮助", max_length=300, blank=True) + # indicator type + NORMAL_TYPE = u'NORM' + BASIC_TYPE = u'BASI' + INDICATOR_TYPES = ( + (NORMAL_TYPE, u"普通指标"), + (BASIC_TYPE, u"基本信息指标"), + ) + type = models.CharField(u"指标类型", max_length=4, + choices=INDICATOR_TYPES, + help_text=u"基本信息指标:用于记录用户的基本信息,不可以被用户关注") # 记录添加指标的用户,用户只能修改自己添加的指标 addByUser = models.ForeignKey(User, verbose_name=u"添加的用户", related_name="indicators") @@ -95,21 +109,23 @@ class Indicator(models.Model): # {{{ categories = models.ManyToManyField(IndicatorCategory, verbose_name=u"所属类别", related_name="indicators") # DATA_TYPES for indicator - INTEGER_TYPE = u'IN' # 整数型 - FLOAT_TYPE = u'FL' # 浮点型 - RANGE_TYPE = u'RG' # 范围型(eg. 250-500) + INTEGER_TYPE = u'IN' # 整数型 + FLOAT_TYPE = u'FL' # 浮点型 + RANGE_TYPE = u'RG' # 范围型(eg. 250-500) FLOAT_RANGE_TYPE = u'FR' # 浮点型/范围型,接受定值或范围 - PM_TYPE = u'PM' # +/- 型 - RADIO_TYPE = u'RD' # 单选型 - CHECKBOX_TYPE = u'CB' # 多选多 + PM_TYPE = u'PM' # +/- 型 + KIND_TYPE = u'KD' # 种类型 + RADIO_TYPE = u'RD' # 单选型 + CHECKBOX_TYPE = u'CB' # 多选多 DATA_TYPES = ( - (INTEGER_TYPE, u"整数型"), - (FLOAT_TYPE, u"浮点定值型"), - (RANGE_TYPE, u"浮点范围型"), + (INTEGER_TYPE, u"整数型"), + (FLOAT_TYPE, u"浮点定值型"), + (RANGE_TYPE, u"浮点范围型"), (FLOAT_RANGE_TYPE, u"定值或范围型"), - (PM_TYPE, u"阴阳型(+/-)"), - #(RADIO_TYPE, u"单选型"), - #(CHECKBOX_TYPE, u"多选型"), + (PM_TYPE, u"阴阳型(+/-)"), + (KIND_TYPE, u"种类型"), + #(RADIO_TYPE, u"单选型"), + #(CHECKBOX_TYPE, u"多选型"), ) dataType = models.CharField(u"数据类型", max_length=2, choices=DATA_TYPES) @@ -119,8 +135,8 @@ class Indicator(models.Model): # {{{ ordering = ['pinyin', 'id'] def __unicode__(self): - return u"< Indicator: #%s, %s, dataType %s addBy %s >"\ - % (self.id, self.name, self.dataType, + return u"< Indicator: #%s(%s), %s, dataType %s addBy %s >"\ + % (self.id, self.type, self.name, self.dataType, self.addByUser.username) def show(self): @@ -140,11 +156,11 @@ class Indicator(models.Model): # {{{ self.pinyin = p.get_pinyin(self.name) super(Indicator, self).save(**kwargs) - def check_unit(self, **kwargs): + def check_unit(self, **kwargs): # {{{ """ Check if the validity of the units specified for the indicator. A indicator must have one 'standard unit'. - if indicator.dataType in [INTEGER_TYPE, PM_TYPE], + if indicator.dataType in [INTEGER_TYPE, PM_TYPE, KIND_TYPE], then units are not needed. """ if self.dataType in [self.FLOAT_TYPE, self.RANGE_TYPE, @@ -153,13 +169,14 @@ class Indicator(models.Model): # {{{ if std_unit: return True else: - print u"Indicator id=%s 未指定标准单位" % self.id + print u"Indicator(id=%s)未指定标准单位" % self.id return False else: - print u"dataType=%s 不需要单位" % self.dataType + #print u"dataType='%s' 不需要单位" % self.dataType return True + # }}} - def _get_unit(self, type="standard"): + def _get_unit(self, type="standard"): # {{{ if type == "standard": _units = self.units.filter(standard=True) elif type == "other": @@ -167,8 +184,9 @@ class Indicator(models.Model): # {{{ else: _units = [] return list(_units) + # }}} - def get_unit(self, type="standard"): + def get_unit(self, type="standard"): # {{{ """ return a 'list' which contains the 'Unit's related to the indicator @@ -185,6 +203,17 @@ class Indicator(models.Model): # {{{ return _units else: return self._get_unit(type) + # }}} + + def get_kind(self): + """ + get the valid ValueKind's that can be used with this indicator + (1) general 'ValueKind's + (2) 'ValueKind's related to this indicator + """ + general_kinds = list(ValueKind.objects.filter(indicator=None)) + ind_kinds = list(ValueKind.objects.filter(indicator=self)) + return list(general_kinds + ind_kinds) def check_confine(self): """ @@ -225,6 +254,7 @@ class Indicator(models.Model): # {{{ 'englishName': self.englishName, 'description': self.description, 'helpText': self.helpText, + 'type': self.type, 'addByUser_id': self.addByUser.id, 'dataType': self.dataType, 'categories_id': [c.id @@ -291,6 +321,13 @@ class IndicatorRecord(models.Model): # {{{ null=True, blank=True) val_max = models.FloatField(u"数据范围上限", null=True, blank=True) + # kind (for KIND_TYPE) + kind = models.ForeignKey("ValueKind", + related_name="indicator_records", + verbose_name=u"指标种类值", + null=True, blank=True, + help_text=u"仅供'种类型'指标填写") + # notes notes = models.TextField(u"记录说明", blank=True) class Meta: @@ -309,7 +346,8 @@ class IndicatorRecord(models.Model): # {{{ if self.is_valid() and self.check_confine(): super(IndicatorRecord, self).save(**kwargs) else: - raise ValueError(u'您输入的数据不符合要求') + raise ValueError(u'Error: 您输入的数据不符合要求') + return self def is_valid(self, **kwargs): # {{{ """验证输入数据是否合法""" @@ -317,8 +355,8 @@ class IndicatorRecord(models.Model): # {{{ qs = IndicatorRecord.objects.filter(indicator=self.indicator, date=self.date) if qs and qs[0].id != self.id: - #raise ValueError(u'date="%s" 该日期已经存在记录' % self.date) - print u'date="%s" 该日期已经存在记录' % self.date + #raise ValueError(u'Error: date="%s" 该日期已经存在记录' % self.date) + print u'Error: date="%s" 该日期已经存在记录' % self.date return False # check unit sind = self.indicator @@ -327,12 +365,12 @@ class IndicatorRecord(models.Model): # {{{ # unit required ind_units = sind.get_unit(type="all") if not self.unit: - #raise ValueError(u'未填写单位') - print u'未填写单位' + #raise ValueError(u'Error: 未填写单位') + print u'Error: 未填写单位' return False elif self.unit not in ind_units: - #raise ValueError(u'所选单位与该指标不符') - print u'所选单位与该指标不符' + #raise ValueError(u'Error: 所选单位与该指标不符') + print u'Error: 所选单位与该指标不符' return False # check dataType if self.indicator.dataType == self.indicator.INTEGER_TYPE: @@ -341,8 +379,8 @@ class IndicatorRecord(models.Model): # {{{ value = int(self.value) return True except ValueError: - #raise ValueError(u'您提交的指标数据类型不正确') - print u'您提交的指标数据类型不正确' + #raise ValueError(u'Error: 您提交的指标数据类型不正确') + print u'Error: 您提交的指标数据类型不正确' return False elif self.indicator.dataType == self.indicator.FLOAT_TYPE: # 浮点型 @@ -350,17 +388,17 @@ class IndicatorRecord(models.Model): # {{{ value = float(self.value) return True except ValueError: - #raise ValueError(u'value 数据类型不正确') - print u'value 数据类型不正确' + #raise ValueError(u'Error: value 数据类型不正确') + print u'Error: value 数据类型不正确' return False elif self.indicator.dataType == self.indicator.RANGE_TYPE: # 范围型 if (self.val_max is None) or (self.val_min is None): - #raise ValueError(u'val_max 或 val_min 未填写') - print u'val_max 或 val_min 未填写' + #raise ValueError(u'Error: val_max 或 val_min 未填写') + print u'Error: val_max 或 val_min 未填写' return False if (self.val_max <= self.val_min): - #raise ValueError(u'val_max <= val_min') + #raise ValueError(u'Error: val_max <= val_min') print u'Error: val_max <= val_min' return False return True @@ -372,33 +410,44 @@ class IndicatorRecord(models.Model): # {{{ value = float(self.value) return True except ValueError: - #raise ValueError(u'value 数据类型不正确') - print u'value 数据类型不正确' + #raise ValueError(u'Error: value 数据类型不正确') + print u'Error: value 数据类型不正确' return False elif (self.val_max is not None) and (self.val_min is not None): # 范围值 if (self.val_max <= self.val_min): - #raise ValueError(u'val_max <= val_min') + #raise ValueError(u'Error: val_max <= val_min') print u'Error: val_max <= val_min' return False else: return True else: - #raise ValueError(u'您提交的指标数据不符合要求') - print u'您提交的指标数据不符合要求' + #raise ValueError(u'Error: 您提交的指标数据不符合要求') + print u'Error: 您提交的指标数据不符合要求' return False elif self.indicator.dataType == self.indicator.PM_TYPE: # +/- 型,无单位要求 if (len(self.value) == 1) and (self.value in [u'+', u'-']): return True else: - #raise ValueError(u'value 只接受 "+" 或 "-"') + #raise ValueError(u'Error: value 只接受 "+" 或 "-"') print u'Error: value 只接受 "+" 或 "-"' return False - ## TODO: RADIO_TYPE, CHECKBOX_TYPE + elif self.indicator.dataType == self.indicator.KIND_TYPE: + ## KIND_TYPE + ## only the general 'ValueKind's and the ones related + ## to this indicator can be used + valid_kinds = self.indicator.get_kind() + if self.kind in valid_kinds: + return True + else: + #raise ValueError(u'Error: kind 不符合要求') + print u'Error: kind 不符合要求' + return False elif self.indicator.dataType in [self.indicator.RADIO_TYPE, self.indicator.CHECKBOX_TYPE]: - #raise ValueError(u'RADIO_TYPE, CHECKBOX_TYPE 验证未实现') + ## TODO: RADIO_TYPE, CHECKBOX_TYPE + #raise ValueError(u'Error: RADIO_TYPE, CHECKBOX_TYPE 验证未实现') print u'Error: RADIO_TYPE, CHECKBOX_TYPE 验证未实现' return False else: @@ -478,7 +527,7 @@ class IndicatorRecord(models.Model): # {{{ # check finished return True else: - # INTEGER_TYPE or PM_TYPE + # INTEGER_TYPE or PM_TYPE or KIND_TYPE return True # }}} @@ -489,6 +538,12 @@ class IndicatorRecord(models.Model): # {{{ """ # check the indicator.dataType sind = self.indicator + # kind_dump + if self.kind: + kind_dump = self.kind.dump() + else: + kind_dump = {} + # if sind.dataType in [sind.FLOAT_TYPE, sind.RANGE_TYPE, sind.FLOAT_RANGE_TYPE]: # self.value @@ -514,6 +569,7 @@ class IndicatorRecord(models.Model): # {{{ 'val_max': val_max, 'val_min': val_min, 'unit': self.unit.dump(), + 'kind': kind_dump, 'notes': self.notes, 'record_histories_id': [rh.id for rh in self.record_histories.all()], @@ -526,6 +582,7 @@ class IndicatorRecord(models.Model): # {{{ 'val_max': self.val_max, 'val_min': self.val_min, 'unit': {}, + 'kind': kind_dump, 'notes': self.notes, 'record_histories_id': [rh.id for rh in self.record_histories.all()], @@ -586,6 +643,11 @@ class IndicatorRecord(models.Model): # {{{ raise ValueError(errmsg) else: val_min_std = None + # kind_dump + if self.kind: + kind_dump = self.kind.dump + else: + kind_dump = {} # output data data_std = { 'id': self.id, @@ -594,6 +656,7 @@ class IndicatorRecord(models.Model): # {{{ 'val_max': val_max_std, 'val_min': val_min_std, 'unit': std_unit.dump(), + 'kind': kind_dump, 'notes': self.notes, 'record_histories_id': [rh.id for rh in self.record_histories.all()], @@ -685,7 +748,15 @@ class IndicatorRecord(models.Model): # {{{ return True else: return False + elif sind.dataType == sind.KIND_TYPE: + ## KIND_TYPE + kind_id = data_std['kind_id'] + if kind_id == sic.kind.id: + return True + else: + return False elif sind.dataType in [sind.RADIO_TYPE, sind.CHECKBOX_TYPE]: + ## TODO: RADIO_TYPE, CHECKBOX_TYPE print u'Error: RADIO_TYPE, CHECKBOX_TYPE 验证未实现' #raise ValueError(u'RADIO_TYPE, CHECKBOX_TYPE 验证未实现') return None @@ -695,12 +766,17 @@ class IndicatorRecord(models.Model): # {{{ return None # }}} - def dump(self, **kwargs): + def dump(self, **kwargs): # {{{ # check if the indicator needs unit if self.unit: unit_id = self.unit.id else: unit_id = None + # kind_id + if self.kind: + kind_dump = self.kind.dump() + else: + kind_dump = {} # dump dump_data = { 'id': self.id, @@ -713,11 +789,13 @@ class IndicatorRecord(models.Model): # {{{ 'value': self.value, 'val_min': self.val_min, 'val_max': self.val_max, + 'kind': kind_dump, 'notes': self.notes, 'record_histories_id': [rh.id for rh in self.record_histories.all()], } return dump_data + # }}} # }}} @@ -744,6 +822,10 @@ class RecordHistory(models.Model): # {{{ null=True, blank=True, editable=False) val_max_bak = models.FloatField(u"原数据范围上限", null=True, blank=True, editable=False) + kind_bak = models.ForeignKey("ValueKind", + related_name="record_histories", + verbose_name=u"原种类值", + null=True, blank=True, editable=False) notes_bak = models.TextField(u"原记录说明", blank=True, editable=False) @@ -763,6 +845,7 @@ class RecordHistory(models.Model): # {{{ self.value_bak = sr.value self.val_min_bak = sr.val_min self.val_max_bak = sr.val_max + self.kind_bak = sr.kind self.notes_bak = sr.notes # save super(RecordHistory, self).save(**kwargs) @@ -773,6 +856,11 @@ class RecordHistory(models.Model): # {{{ unit_bak_id = self.unit_bak.id else: unit_bak_id = None + # kind_bak_id + if self.kind_bak: + kind_bak_id = self.kind_bak.id + else: + kind_bak_id = None # dump dump_data = { 'id': self.id, @@ -784,6 +872,7 @@ class RecordHistory(models.Model): # {{{ 'value_bak': self.value_bak, 'val_min_bak': self.val_min_bak, 'val_max_bak': self.val_max_bak, + 'kind_bak_id': kind_bak_id, 'notes_bak': self.notes_bak, } return dump_data @@ -829,23 +918,23 @@ class Unit(models.Model): # {{{ if self.id == std_unit.id: return True else: - print u"该指标已经指定了标准单位" - raise ValueError(u"该指标已经指定了标准单位") + print u"Error: 该指标已经指定了标准单位" + raise ValueError(u"Error: 该指标已经指定了标准单位") return False else: return True else: if (not self.relation): - print u"单位映射关系未填写" - raise ValueError(u"单位映射关系未填写") + print u"Error: 单位映射关系未填写" + raise ValueError(u"Error: 单位映射关系未填写") return False else: try: fsym = sympy.sympify(self.relation) return True except SympifyError: - print u"'%s' 不是合法的算术表达式" % self.relation - raise ValueError(u"'%s' 不是合法的算术表达式"\ + print u"Error: '%s' 不是合法的算术表达式" % self.relation + raise ValueError(u"Error: '%s' 不是合法的算术表达式"\ % self.relation) return False @@ -878,6 +967,10 @@ class InnateConfine(models.Model): # {{{ 如果数据类型需要单位,则必须使用"标准单位"; IndicatorRecord.is_normal() 方法需要如此; 因为 标准单位 到 其他单位 的换算没有实现。 + + KIND_TYPE: + 对于'种类型'指标,如果每个可选种类不区分正常和异常, + 且程序其他地方也不涉及是否正常,则可以随便选一个。 """ # indicator indicator = models.OneToOneField("Indicator", @@ -889,6 +982,12 @@ class InnateConfine(models.Model): # {{{ # normal value (for INTEGER_TYPE, PM_TYPE) val_norm = models.CharField(u"正常值", max_length=30, blank=True, help_text=u'填写"整数型","阴阳(+/-)型数据"') + # normal kind (for KIND_TYPE) + kind_norm = models.ForeignKey("ValueKind", + related_name="innate_confines", + verbose_name=u"正常种类值", + null=True, blank=True, + help_text=u"仅供'种类型'指标填写") # normal range human_min = models.FloatField(u"人体正常值下限", null=True, blank=True) @@ -929,10 +1028,15 @@ class InnateConfine(models.Model): # {{{ sind = self.indicator if sind.dataType in [sind.FLOAT_TYPE, sind.RANGE_TYPE, sind.FLOAT_RANGE_TYPE]: - # check unit - if not (self.unit and self.unit.standard): - raise ValueError(u'单位未填写/不是标准单位') + # check unit if given + # check the unit if related to the indicator + ind_std_unit = sind.get_unit(type="standard") + if self.unit and self.unit in ind_std_unit: + return True + else: + raise ValueError(u'ERROR: 单位未填写/不是该指标的标准单位') return False + # if (self.human_max is None) or (self.human_min is None): raise ValueError(u'Error: human_max 或 human_min 未填写') return False @@ -959,7 +1063,7 @@ class InnateConfine(models.Model): # {{{ val_norm = int(self.val_norm) return True except ValueError: - raise ValueError(u'val_norm="%s" 不是整数型值' + raise ValueError(u'ERROR: val_norm="%s" 不是整数型值' % self.val_norm) return False elif sind.dataType == sind.PM_TYPE: @@ -968,14 +1072,27 @@ class InnateConfine(models.Model): # {{{ self.val_norm in [u'+', u'-']): return True else: - raise ValueError(u'value 只接受 "+" 或 "-"') + raise ValueError(u'ERROR: value 只接受 "+" 或 "-"') + return False + elif sind.dataType == sind.KIND_TYPE: + ## KIND type + ## (KIND type indicator not need 'InnateConfine') + if not self.kind_norm: + raise ValueError(u'ERROR: kind_norm 未填写') return False - ## TODO: RADIO_TYPE, CHECKBOX_TYPE + else: + valid_kinds = self.indicator.get_kind() + if self.kind_norm in valid_kinds: + return True + else: + raise ValueError(u'ERROR: kind_norm 不符合要求') + return False elif sind.dataType in [sind.RADIO_TYPE, sind.CHECKBOX_TYPE]: - raise ValueError(u'RADIO_TYPE, CHECKBOX_TYPE 验证未实现') + ## TODO: RADIO_TYPE, CHECKBOX_TYPE + raise ValueError(u'ERROR: RADIO_TYPE, CHECKBOX_TYPE 验证未实现') return False else: - raise ValueError(u'数据不符合要求') + raise ValueError(u'ERROR: 数据不符合要求') return False # }}} @@ -985,12 +1102,18 @@ class InnateConfine(models.Model): # {{{ unit_dump = self.unit.dump() else: unit_dump = {} + # check 'kind_norm' + if self.kind_norm: + kind_norm_dump = self.kind_norm.dump() + else: + kind_norm_dump = {} # dump dump_data = { 'id': self.id, 'indicator_id': self.indicator.id, 'unit': unit_dump, 'val_norm': self.val_norm, + 'kind_norm': kind_norm_dump, 'human_min': self.human_min, 'human_max': self.human_max, 'math_min': self.math_min, @@ -1123,17 +1246,76 @@ class RelatedIndicator(models.Model): # {{{ # }}} +class ValueKind(models.Model): # {{{ + """ + 记录某指标可以可使用的"种类"值 + + 并为需要使用"种类"值的地方提供可选范围(e.g.: recommend.ResearchAtom) + 使用统一的符号(symbol)来寻找及匹配 + """ + name = models.CharField(u"名称", max_length=30) + symbol = models.CharField(u"符号", max_length=30, + help_text=u"仅能使用字母、数字和下划线,最长30字符") + description = models.TextField(u"描述", blank=True) + indicator = models.ForeignKey("Indicator", + related_name="value_kinds", verbose_name=u"关联的指标", + null=True, blank=True) + + class Meta: + verbose_name_plural = u"指标可用种类" + + def __unicode__(self): + if self.indicator: + indicator_name = self.indicator.name + else: + indicator_name = '*' + return u'< ValueKind: %s(%s) for indicator(%s) >' %\ + (self.name, self.symbol, indicator_name) + + def save(self, **kwargs): + if self.is_valid(): + super(ValueKind, self).save(**kwargs) + else: + return self + + def is_valid(self): + # check symbol + sym_regex = re.compile(r'^[_0-9a-zA-Z]+$') + if sym_regex.search(self.symbol): + return True + else: + raise ValueError(u"仅能使用字母、数字和下划线,最长30字符") + return False + + def dump(self, **kwargs): + if self.indicator: + indicator_id = self.indicator.id + else: + indicator_id = None + # + dump_data = { + 'id': self.id, + 'name': self.name, + 'symbol': self.symbol, + 'description': self.description, + 'indicator_id': indicator_id, + } + return dump_data +# }}} + + admin.site.register([ - IndicatorCategory, - Indicator, - UserIndicator, - IndicatorRecord, - RecordHistory, - Unit, - InnateConfine, - StatisticalConfine, - RelatedIndicator, - ]) + IndicatorCategory, + Indicator, + UserIndicator, + IndicatorRecord, + RecordHistory, + Unit, + InnateConfine, + StatisticalConfine, + RelatedIndicator, + ValueKind, +]) # vim: set ts=4 sw=4 tw=0 fenc=utf-8 ft=python: # |