def test_container_get_value_as_string(self): tac = TaskAttributeContainer() s = 'note=noteノート,repeat=1' tac.from_string(s) import taskattr self.assertEqual('noteノート', tac.get_value_as_string(taskattr.Note.KEY)) # 非文字列値の場合, 文字列値が返ってくること. self.assertEqual('1', tac.get_value_as_string(taskattr.Repeat.KEY))
def test_container_if_key_exists(self): tac = TaskAttributeContainer() # from_string s = 'note=noteノート,repeat=1' tac.from_string(s) # 一通りget self.assertEqual('noteノート', tac.get_note()) self.assertEqual(1, tac.get_repeat()) # 一通りset and 一通りto_string tac.set_note('にゅーnote') tac.set_repeat(3) new_s = tac.to_string() self.assertEqual('note=にゅーnote,repeat=3', new_s)
def test_container_if_key_not_exists(self): tac = TaskAttributeContainer() s = '' tac.from_string(s) # 存在しない時のgetはエラー self.assert_exception(tac.get_note) self.assert_exception(tac.get_repeat) # 存在しない時のsetは新規になる. tac.set_note('にゅーnote') tac.set_repeat(3) new_s = tac.to_string() self.assertEqual('note=にゅーnote,repeat=3', new_s)
def test_container_removing(self): tac = TaskAttributeContainer() s = 'note=aiueo,repeat=2' tac.from_string(s) tac.remove_attribute('note') self.assert_exception(tac.get_note) self.assertEqual('repeat=2', tac.to_string())
def _parse(self): ln = self._line try: self._type = Type(ln) self._date = Date(ln) self._section = Section(ln) self._number = Number(ln) self._category = Category(ln) self._name = Name(ln) self._est_hour = EstimationHour(ln) self._est_minute = EstimationMinutes(ln) self._act_minute = ActualMinutes(ln) self._starttime = StartTime(ln) self._endtime = EndTime(ln) except PropertyParseError as e: raise PropertyParseError('Failed to parsing the property line "%s".' % e) properties_by_str = ln[TaskLine.LENGTH:] self._attrs = TaskAttributeContainer() try: self._attrs.from_string(properties_by_str) except TaskAttributeError as e: raise PropertyParseError('Failed to parsing the attribute description "%s".' % e)
def test_container_empty_cases(self): tac = TaskAttributeContainer() tac.from_string('') self.assertEqual('', tac.to_string()) self.assert_exception(tac.get_note)
class TaskLine: LENGTH = 88 BLANK_LINE = ' '*LENGTH def __init__(self, line, systemconfig): self._line = line self._systemconfig = systemconfig self._parse() def _parse(self): ln = self._line try: self._type = Type(ln) self._date = Date(ln) self._section = Section(ln) self._number = Number(ln) self._category = Category(ln) self._name = Name(ln) self._est_hour = EstimationHour(ln) self._est_minute = EstimationMinutes(ln) self._act_minute = ActualMinutes(ln) self._starttime = StartTime(ln) self._endtime = EndTime(ln) except PropertyParseError as e: raise PropertyParseError('Failed to parsing the property line "%s".' % e) properties_by_str = ln[TaskLine.LENGTH:] self._attrs = TaskAttributeContainer() try: self._attrs.from_string(properties_by_str) except TaskAttributeError as e: raise PropertyParseError('Failed to parsing the attribute description "%s".' % e) def clone(self): import copy return copy.deepcopy(self) # get property # ------------ def get_printable_value(self, no_config=False): """ @param no_config config 設定を無視する場合に立てる. 本メソッドはデータファイルへの保存時にも利用するが, config に従うと「省略表示を行う config だった場合」に 「省略表示のままデータファイルに保存されてしまう」. これを防ぐため, config 設定を無視するオプションを用意した. なお, no_config をどう解釈するかは都度考えること. """ print_orders = [ self._type, self._date, self._section, self._number, self._category, self._name, self._est_hour, self._est_minute, self._act_minute, self._starttime, self._endtime, ] ret = '' PROPERTY_SEP = ' ' # プロパティ for inst in print_orders: ret += inst.get_printable_value() + PROPERTY_SEP # 属性 # * showattr config で指定した属性を表示. # 一致しない場合はとりあえず表示しない. # * save 時は内容をフルで出力しないといけないため出す. if no_config: ret += self._attrs.to_string() return ret attrkey = self._systemconfig.showattr.get() attrvalue = '' try: attrvalue = self._attrs.get_value_as_string(attrkey) except TaskAttributeError: pass ret += attrvalue return ret def get_type(self): return self._type.get_printable_value() def get_date(self): return self._date.get_printable_value() def get_section(self): return self._section.get_printable_value() def get_number(self): return self._number.get_printable_value() def get_category(self): return self._category.get_printable_value() def get_taskname(self): return self._name.get_printable_value() def get_est_hour(self): return self._est_hour.get_printable_value() def get_est_minute(self): return self._est_minute.get_printable_value() def get_actual_minute(self): return self._act_minute.get_printable_value() def get_starttime(self): return self._starttime.get_printable_value() def get_endtime(self): return self._endtime.get_printable_value() # get attribute # ------------- def get_note(self): return self._attrs.get_note() def get_repeat(self): return self._attrs.get_repeat() # edit # ---- def edit(self, key, value): """ @return edit後のprintable value. @exception PropertyParseError edit失敗(valueのフォーマットがおかしい) @exception EditError edit失敗(key不正や連携などedit絡みのエラー) """ return TaskEdit.edit(self, key, value) # change property # * 連携更新や省力記法展開も併せて行う. # * unittest のため, change 後の値を返却すること. # --------------- def change_date(self, value): # value の書式を解釈して, 省力記法なら展開する. v = util.DateWalker(self._systemconfig.basedate.get()).walk(value) ret = self._date.from_userinput(v).get_printable_value() self.update_type() return ret def change_section(self, value): return self._section.from_userinput(value).get_printable_value() def change_number(self, value): return self._number.from_userinput(value).get_printable_value() def change_category(self, value): return self._category.from_userinput(value).get_printable_value() def change_taskname(self, value): return self._name.from_userinput(value).get_printable_value() def change_estimate(self, value): v = value if len(value)==0: return self.change_estimate_minutes(v) elif value[-1]=='h': v = value[:-1] return self.change_estimate_hour(v) elif value[-1]=='m': v = value[:-1] return self.change_estimate_minutes(v) # デフォルトは好みで分扱い. return self.change_estimate_minutes(v) def change_estimate_minutes(self, value): ret = self._est_minute.from_userinput(value).get_printable_value() hourstr = PropertyReactor.est_hour(self._est_minute) self._est_hour.from_reactive(hourstr) return ret def change_estimate_hour(self, value): ret = self._est_hour.from_userinput(value).get_printable_value() minutestr = PropertyReactor.est_minute(self._est_hour) self._est_minute.from_reactive(minutestr) return ret def change_starttime(self, value): v = util.deploy_to_today_timestr_if_possible(value) ret = self._starttime.from_userinput(v).get_printable_value() # actual の再計算. # * 空値入力時は計算不可なので actual も空値にする. # * endtime が存在するなら, actual を再計算する. if PropertyInterface.is_empty_value(v): self.empty_actual_minute() elif not self._endtime.is_empty(): self.update_actual_minute() return ret def change_endtime(self, value): v = util.deploy_to_today_timestr_if_possible(value) # starttime が入力されてない場合は受け付けない. # ただし空値が入力された時はこの限りではない. if PropertyInterface.is_empty_value(v): pass elif self._starttime.is_empty(): raise EditError('End-time requires the Start-time') ret = self._endtime.from_userinput(v).get_printable_value() self.update_type() self.update_actual_minute() return ret # change attribute # ---------------- def change_note(self, value): self._attrs.set_note(value) def remove_note(self): import taskattr self._attrs.remove_attribute(taskattr.Note.KEY) def change_repeat(self, value): self._attrs.set_repeat(value) def remove_repeat(self): import taskattr self._attrs.remove_attribute(taskattr.Repeat.KEY) # update # ------ def empty_actual_minute(self): self._act_minute.from_userinput('') def update_actual_minute(self): actualminute = PropertyReactor.actual_minute(self._starttime, self._endtime) self._act_minute.from_reactive(actualminute) def update_type(self): newtype = PropertyReactor.type( date_property=self._date, basedate_str=self._systemconfig.basedate.get(), endtime_property=self._endtime) self._type.from_reactive(newtype) # matching by list conditions # --------------------------- def is_matched(self, conds): """ @param conds listコマンドにて与える条件文字列. `(COND1) (COND2) ...` @retval A boolean. AND MATCH のため全条件がマッチして始めてマッチとみなすことに注意. """ cond_list = conds.split(' ') for cond in cond_list: match_result = self._is_matched(cond) if not match_result: return False return True def _is_matched(self, cond): if cond=='todo': todo_types = [Type.AT, Type.TT, Type.BT, Type.UT] if self._type.get_printable_value() in todo_types: return True return False elif cond=='done': todo_types = [Type.AD, Type.TD, Type.BD, Type.UD] if self._type.get_printable_value() in todo_types: return True return False elif cond=='doing': if not self._starttime.is_empty() and \ self._endtime.is_empty() and self._act_minute.is_empty(): return True return False elif cond=='nodate': if self._date.is_empty(): return True return False elif cond[0]=='q': if len(cond)==1: return True query = cond[1:] # とりあえず部分一致. return self.get_printable_value().find(query)!=-1 else: return self._do_date_match(cond) def _do_date_match(self, cond): if self._date.is_empty(): return False # マッチ判定で使用しない曜日部分をカット. targetdatestr = self._date.get_printable_value()[:-2] datecond = util.DateCondition( targetdate_str=targetdatestr, basedate_str=self._systemconfig.basedate.get() ) return datecond.is_matched(cond) # Operation about number # ---------------------- def set_number(self, number_by_int): self._number.from_userinput(str(number_by_int)) def equals_number(self, number_by_int): return self._number.equals(number_by_int)