def test_retrieve_history(self): # Toggle today and yesterday (create 2 habitdays) marked_done, hd = HabitDay.Toggle(self.habit_read, datetime.today()) marked_done, hd = HabitDay.Toggle(self.habit_read, datetime.today() - timedelta(days=1)) hd_keys = HabitDay.All(self.habit_read.key) self.assertEqual(len(hd_keys), 2)
def test_toggle(self): # Mark done (creating new habit day) marked_done, hd = HabitDay.Toggle(self.habit_run, datetime.today()) self.assertTrue(marked_done) self.assertIsNotNone(hd) self.assertTrue(hd.done) # Mark not done marked_done, hd = HabitDay.Toggle(self.habit_run, datetime.today()) self.assertFalse(marked_done) self.assertIsNotNone(hd) self.assertFalse(hd.done)
def test_delete_history(self): marked_done, hd = HabitDay.Toggle(self.habit_read, datetime.today()) marked_done, hd = HabitDay.Toggle(self.habit_run, datetime.today()) self.habit_read.delete_history() # Schedules background task self.execute_tasks_until_empty() hd_keys = HabitDay.All(self.habit_read.key) self.assertEqual(len(hd_keys), 0) # Confirm both deleted # Confirm habit_run not affected hd_keys = HabitDay.All(self.habit_run.key) self.assertEqual(len(hd_keys), 1) # Confirm still in db
def _habit_or_task_report(self, item_name): ''' Mark a habit or a task as complete ''' handled = False speech = None if item_name: habits = Habit.Active(self.user) for h in habits: if item_name.lower() in h.name.lower(): # TODO: Timezone? done, hd = HabitDay.Toggle(h, datetime.today().date(), force_done=True) encourage = random.choice(HABIT_DONE_REPLIES) speech = "%s '%s' is marked as complete." % (encourage, h.name) handled = True break if not handled: # Check tasks tasks = Task.Recent(self.user) for t in tasks: if item_name.lower() in t.title.lower(): t.mark_done() t.put() speech = "Task '%s' is marked as complete." % (t.title) handled = True break if not handled: speech = "I'm not sure what you mean by '%s'." % item_name else: speech = "I couldn't tell what habit or task you completed." return speech
def _habit_status(self): habits = Habit.All(self.user) today = self.user.local_time().date() habitday_keys = [ ndb.Key('HabitDay', HabitDay.ID(h, today), parent=self.user.key) for h in habits ] habitdays = ndb.get_multi(habitday_keys) n_habits_done = 0 habits_committed_undone = [] habits_done = [] for hd in habitdays: if hd: habit = hd.habit.get() if hd.committed and not hd.done: if habit: habits_committed_undone.append(habit.name) if hd.done: habits_done.append(habit.name) n_habits_done += 1 if habits: if n_habits_done: text = "Good work on doing %d %s (%s)!" % ( n_habits_done, tools.pluralize('habit', n_habits_done), tools.english_list(habits_done)) else: text = "No habits done yet." if habits_committed_undone: text += " Don't forget you've committed to %s." % tools.english_list( habits_committed_undone) else: text = "You haven't added any habits yet. Try saying 'add habit run'" return text
def get(self, d): hack_id = self.request.get('hack_id') res = {} if hack_id == 'index_quotes_readables': page = self.request.get_range('page') PAGE_SIZE = 50 index_lookup = {} # index_name -> (index, list of items) for q in Quote.query().fetch(limit=PAGE_SIZE, offset=page * PAGE_SIZE): sd, index = q.update_sd(index_put=False) if index and index.name not in index_lookup: index_lookup[index.name] = (index, [sd]) else: index_lookup[index.name][1].append(sd) for r in Readable.query().fetch(limit=PAGE_SIZE, offset=page * PAGE_SIZE): sd, index = r.update_sd(index_put=False) if index and index.name not in index_lookup: index_lookup[index.name] = (index, [sd]) else: index_lookup[index.name][1].append(sd) if index_lookup: n = 0 for index_tuple in index_lookup.values(): index, items = index_tuple index.put(items) n += len(items) res['result'] = "Put %d items in %d indexes" % (n, len(index_tuple)) res['page'] = page elif hack_id == 'normalize_key_props': dbp = [] for hd in HabitDay.query().iter(): habit_key = hd.habit if habit_key.parent() is None: # Need to update hd.habit = ndb.Key('User', hd.key.parent().id(), 'Habit', int(habit_key.id())) dbp.append(hd) res['habitdays'] = len(dbp) ndb.put_multi(dbp) dbp = [] for jrnl in MiniJournal.query().iter(): changes = False for i, tag_key in enumerate(jrnl.tags): if tag_key.parent() is None: # Need to update jrnl.tags[i] = ndb.Key('User', jrnl.key.parent().id(), 'JournalTag', tag_key.id()) changes = True if changes: dbp.append(jrnl) res['journals'] = len(dbp) ndb.put_multi(dbp) else: res['result'] = 'hack_id not found' self.json_out(res)
def _habit_or_task_report(self, item_name): ''' Mark a habit or a task as complete ''' handled = False speech = None if item_name: habits = Habit.Active(self.user) for h in habits: if item_name.lower() in h.name.lower(): d = self.user.local_time().date() encourage = random.choice(HABIT_DONE_REPLIES) if h.has_daily_count(): done, hd = HabitDay.Increment(h, d) speech = "%s The count of '%s' has been increased to %d " % ( encourage, h.name, hd.count) remaining = h.tgt_daily - hd.count speech += "(habit complete!)" if not remaining else "(%d to go)" % remaining else: done, hd = HabitDay.Toggle(h, d, force_done=True) speech = "%s '%s' is marked as complete." % (encourage, h.name) handled = True break if not handled: # Check tasks tasks = Task.Recent(self.user) for t in tasks: if item_name.lower() in t.title.lower(): t.mark_done() t.put() speech = "Task '%s' is marked as complete." % (t.title) handled = True break if not handled: speech = "I'm not sure what you mean by '%s'." % item_name else: speech = "I couldn't tell what habit or task you completed." return speech
def commit(self, d): ''' Mark done/not-done for a habit day ''' from constants import HABIT_COMMIT_REPLIES habit_id = self.request.get_range('habit_id') day_iso = self.request.get('date') habit = self.user.get(Habit, id=habit_id) hd = None if habit: hd = HabitDay.Commit(habit, tools.fromISODate(day_iso)) self.message = random.choice(HABIT_COMMIT_REPLIES) self.success = True self.set_response({'habitday': hd.json() if hd else None})
def test_habit_report(self): habit_run = Habit.Create(self.u) habit_run.Update(name="Run") habit_run.put() marked_done, hd = HabitDay.Toggle(habit_run, datetime.today()) self._test_report( {'type': REPORT.HABIT_REPORT}, [["Created", "Updated", "Date", "Habit", "Done", "Committed"], [ tools.sdatetime(hd.dt_created, fmt="%Y-%m-%d %H:%M:%S %Z"), tools.sdatetime(hd.dt_updated, fmt="%Y-%m-%d %H:%M:%S %Z"), tools.iso_date(datetime.now()), "Run", "1", "0" ]])
def range(self, d): ''' Return recent days of all active habits ''' start = self.request.get('start_date') end = self.request.get('end_date') habits = Habit.Active(self.user) habitdays = HabitDay.Range(self.user, habits, tools.fromISODate(start), until_date=tools.fromISODate(end)) self.set_response({ 'habits': [habit.json() for habit in habits], 'habitdays': tools.lookupDict(habitdays, keyprop="key_id", valueTransform=lambda hd: hd.json()) }, success=True)
def backgroundHabitDayDeletion(hkey, start_cursor=None): hkey = ndb.Key(urlsafe=hkey) done = False maxit = 50 its = 0 while not done and its < maxit: hds = HabitDay.All(hkey) if hds: ndb.delete_multi(hds) logging.debug("Deleted %d habit days from hkey=%s" % (len(hds), hkey)) else: done = True its += 1
def detail(self, id, d): with_days = self.request.get_range('with_days', default=0) habit = None habitdays = [] if id: habit = self.user.get(Habit, id=id) if habit: if with_days: since = datetime.today() - timedelta(days=with_days) habitdays = HabitDay.Range(self.user, [habit], since) self.success = True self.set_response({ 'habit': habit.json() if habit else None, 'habitdays': [hd.json() for hd in habitdays if hd] })
def recent(self, d): ''' Return recent days of all active habits ''' self.success = True days = self.request.get_range('days', default=5) habits = Habit.Active(self.user) start_date = datetime.today() - timedelta(days=days) habitdays = HabitDay.Range(self.user, habits, start_date) self.set_response({ 'habits': [habit.json() for habit in habits], 'habitdays': tools.lookupDict(habitdays, keyprop="key_id", valueTransform=lambda hd: hd.json()) })
def toggle(self, d): ''' Mark done/not-done for a habit day ''' from constants import HABIT_DONE_REPLIES habit_id = self.request.get_range('habit_id') day_iso = self.request.get('date') habit = Habit.get_by_id(habit_id, parent=self.user.key) hd = None if habit: marked_done, hd = HabitDay.Toggle(habit, tools.fromISODate(day_iso)) if marked_done: self.message = random.choice(HABIT_DONE_REPLIES) self.success = True self.set_response({'habitday': hd.json() if hd else None})
def _habit_commit(self, habit_param_raw): handled = False speech = None if habit_param_raw: habits = Habit.Active(self.user) for h in habits: if habit_param_raw.lower() in h.name.lower(): # TODO: Timezone? hd = HabitDay.Commit(h, datetime.today().date()) encourage = random.choice(HABIT_COMMIT_REPLIES) speech = "You've committed to '%s' today. %s" % (h.name, encourage) handled = True break if not handled: speech = "I'm not sure what you mean by '%s'. You may need to create a habit before you can commit to it." % habit_param_raw else: speech = "I couldn't tell what habit you want to commit to." return speech
def get(self, d): # TODO: Async fetches with_habits = self.request.get_range('with_habits', default=0) == 1 with_tracking = self.request.get_range('with_tracking', default=1) == 1 with_goals = self.request.get_range('with_goals', default=1) == 1 with_tasks = self.request.get_range('with_tasks', default=1) == 1 date_start = self.request.get('date_start') date_end = self.request.get('date_end') dt_start, dt_end = tools.fromISODate(date_start), tools.fromISODate( date_end) iso_dates = [] habits = [] today = datetime.today() habitdays = [] goals = [] journals, iso_dates = MiniJournal.Fetch(self.user, dt_start, dt_end) if with_habits: habits = Habit.Active(self.user) habitdays = HabitDay.Range(self.user, habits, dt_start, dt_end) if with_tracking: tracking_days = TrackingDay.Range(self.user, dt_start, dt_end) if with_goals: goals = Goal.Year(self.user, today.year) if with_tasks: tasks = Task.DueInRange(self.user, dt_start, dt_end, limit=100) self.set_response( { 'dates': iso_dates, 'journals': [j.json() for j in journals if j], 'habits': [h.json() for h in habits], 'goals': [g.json() for g in goals], 'tasks': [t.json() for t in tasks], 'tracking_days': [p.json() for p in tracking_days], 'habitdays': tools.lookupDict(habitdays, keyprop="key_id", valueTransform=lambda hd: hd.json()) }, success=True)
def setUp(self): self.set_application(tst_app) self.setup_testbed() self.init_datastore_stub() self.init_memcache_stub() self.init_taskqueue_stub() self.init_mail_stub() self.register_search_api_stub() self.init_app_basics() self.u = u = self.users[0] self.u.Update(name="George") self.u.put() h = Habit.Create(u) h.Update(name="Run") h.put() done, hd = HabitDay.Toggle(h, datetime.today()) t = Task.Create(u, "Dont forget the milk") t.put() g = Goal.CreateMonthly(u, date=datetime.today().date()) g.Update(text=["Get it done", "Also get exercise"]) g.put()
def fetch_daily_panel_data(self, since=None, until=None): self._maybe_get_habits() self._maybe_get_journal_questions() if not since: since = datetime.combine( (datetime.now() - timedelta(days=self.days_ago)).date(), time(0, 0)) if not until: until = datetime.combine( (datetime.now() - timedelta(days=self.days_ago_end)).date(), time(0, 0)) rows = [] habitdays_by_day = tools.partition( HabitDay.Range(self.user, self.habits.values(), since, until_date=until), lambda hd: tools.iso_date(hd.date)) tasks_by_day = tools.partition( Task.DueInRange(self.user, since, until, limit=500), lambda t: tools.iso_date(t.dt_due)) readables_by_day = tools.partition( Readable.Fetch(self.user, read=True, since=tools.iso_date(since), until=tools.iso_date(until)), lambda r: tools.iso_date(r.dt_read)) journals, iso_dates = MiniJournal.Fetch(self.user, start=since, end=until) journals_by_day = tools.partition( journals, lambda jrnl: tools.iso_date(jrnl.date)) cursor = since while cursor <= until: iso_date = tools.iso_date(cursor) tasks = tasks_by_day.get(iso_date, []) habitdays = habitdays_by_day.get(iso_date, []) readables = readables_by_day.get(iso_date, []) journals = journals_by_day.get(iso_date, []) journal = journals[0] if journals else None tasks_done = tasks_undone = habits_done = habits_cmt = habits_cmt_undone = items_read = 0 row = {} for t in tasks: if t.is_done(): tasks_done += 1 else: tasks_undone += 1 habits_checklist = self.habits.keys() # list of habit IDs for hd in habitdays: hid = hd.habit.id() h = self.habits.get(hid) if h: habits_checklist.remove(hid) row[self._habit_col(h)] = 'true' if hd.done else 'false' if hd.done: habits_done += 1 if hd.committed: habits_cmt += 1 if not hd.done: habits_cmt_undone += 1 if habits_checklist: # Missing habit-days, need to create columns anyway for hid in habits_checklist: h = self.habits.get(hid) if h: row[self._habit_col(h)] = 'false' items_read = len(readables) fav_items_read = len([r for r in readables if r.favorite]) row.update({ "id": iso_date, "date": iso_date, "tasks_done": tasks_done, "tasks_undone": tasks_undone, "habits_done": habits_done, "habits_cmt": habits_cmt, "habits_cmt_undone": habits_cmt_undone, "items_read": items_read, "fav_items_read": fav_items_read }) for q in self.journal_questions: name = q.get('name') value = None if journal: value = journal.get_data_value(name) numeric = q.get( 'response_type') in JOURNAL.NUMERIC_RESPONSES if numeric: value = tools.safe_number(value, default=0) elif isinstance(value, basestring): value = tools.removeNonAscii(value) else: value = str(value) if value else "" row[self._journal_col(q)] = value rows.append(row) cursor += timedelta(days=1) return rows
def get(self, d): hack_id = self.request.get('hack_id') res = {} if hack_id == 'index_quotes_readables': page = self.request.get_range('page') PAGE_SIZE = 50 index_lookup = {} # index_name -> (index, list of items) for q in Quote.query().fetch(limit=PAGE_SIZE, offset=page * PAGE_SIZE): sd, index = q.update_sd(index_put=False) if index and index.name not in index_lookup: index_lookup[index.name] = (index, [sd]) else: index_lookup[index.name][1].append(sd) for r in Readable.query().fetch(limit=PAGE_SIZE, offset=page * PAGE_SIZE): sd, index = r.update_sd(index_put=False) if index and index.name not in index_lookup: index_lookup[index.name] = (index, [sd]) else: index_lookup[index.name][1].append(sd) if index_lookup: n = 0 for index_tuple in index_lookup.values(): index, items = index_tuple index.put(items) n += len(items) res['result'] = "Put %d items in %d indexes" % ( n, len(index_tuple)) res['page'] = page elif hack_id == 'normalize_key_props': dbp = [] for hd in HabitDay.query().iter(): habit_key = hd.habit if habit_key.parent() is None: # Need to update hd.habit = ndb.Key('User', hd.key.parent().id(), 'Habit', int(habit_key.id())) dbp.append(hd) res['habitdays'] = len(dbp) ndb.put_multi(dbp) dbp = [] for jrnl in MiniJournal.query().iter(): changes = False for i, tag_key in enumerate(jrnl.tags): if tag_key.parent() is None: # Need to update jrnl.tags[i] = ndb.Key('User', jrnl.key.parent().id(), 'JournalTag', tag_key.id()) changes = True if changes: dbp.append(jrnl) res['journals'] = len(dbp) ndb.put_multi(dbp) else: res['result'] = 'hack_id not found' self.json_out(res)