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
Beispiel #4
0
    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
Beispiel #5
0
 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)
Beispiel #7
0
    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
Beispiel #8
0
 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})
Beispiel #9
0
    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"
             ]])
Beispiel #10
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)
Beispiel #11
0
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
Beispiel #12
0
 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]
         })
Beispiel #13
0
 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())
     })
Beispiel #14
0
 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})
Beispiel #15
0
 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
Beispiel #16
0
 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)
Beispiel #17
0
    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()
Beispiel #18
0
 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)