Beispiel #1
0
    def submit(self, d):
        '''
        Submit today's journal (yesterday if 00:00 - 04:00)
        '''
        date = None
        _date = self.request.get('date')
        if _date:
            date = tools.fromISODate(_date)
        task_json = tools.getJson(self.request.get('tasks'))  # JSON
        params = tools.gets(self,
                            strings=['lat', 'lon', 'tags_from_text'],
                            json=['data'],
                            lists=['tags'])
        logging.debug(params)
        if params.get('data'):
            if not params.get('tags'):
                params['tags'] = []
            jrnl = MiniJournal.Create(self.user, date)
            jrnl.Update(**params)
            jrnl.parse_tags()
            jrnl.put()

            if task_json:
                # Save new tasks for tomorrow
                tasks = []
                for t in task_json:
                    if t:
                        task = Task.Create(self.user, t)
                        tasks.append(task)
                ndb.put_multi(tasks)
            self.success = True
            self.message = "Journal submitted!"

        self.set_response({'journal': jrnl.json() if jrnl else None})
Beispiel #2
0
 def today(self, d):
     '''
     Get today's journal (yesterday if early morning)
     '''
     jrnl = MiniJournal.Get(self.user)
     self.set_response({'journal': jrnl.json() if jrnl else None},
                       success=True)
    def test_stateful_journal_submission(self):
        # Journal submissions ask multiple questions and require
        # state to be kept in a conversation_state object (memcached)

        # Setup journal questions for account
        # settings = tools.getJson(self.u.settings)

        NARR = "Productive! #Hacked a few things with @JuliaSpiegel"
        RATING = 7

        conversation = [
            # (User message, Flow reply)
            ("daily report", "A few words on your day?", False),  # narrative
            (NARR, "How was the day?", False),  # day_rating
            ("?", JOURNAL.INVALID_REPLY + " " + JOURNAL.INVALID_SUFFIX_NUMERIC,
             False),
            ("%s" % RATING, JOURNAL.TOP_TASK_PROMPT, False),
            ("Finish hacking the machine", JOURNAL.TOP_TASK_PROMPT_ADDTL,
             False),
            ("done", "Report submitted!", True)
        ]
        for message, expected_reply, expected_end_of_convo in conversation:
            action, params = self.ca.parse_message(message)
            reply, message_data, end_convo = self.ca.respond_to_action(
                action, parameters=params)
            self.assertEqual(expected_end_of_convo, end_convo)
            self.assertEqual(reply, expected_reply)

        # Confirm journal saved properly
        jrnl = MiniJournal.Get(self.u)
        self.assertIsNotNone(jrnl)
        rating = jrnl.get_data_value('day_rating')
        self.assertEqual(rating, RATING)
        narrative = jrnl.get_data_value('narrative')
        self.assertEqual(narrative, NARR)

        # Confirm we have tags from narrative
        tags = JournalTag.All(self.u)
        self.assertEqual(len(tags), 2)
        for t in tags:
            if t.person():
                self.assertEqual(t.name, "JuliaSpiegel")
            else:
                self.assertEqual(t.name, "Hacked")

        # Confirm we created tasks
        tasks = Task.Open(self.u)
        self.assertEqual(len(tasks), 2)  # One added in journal
        self.assertEqual(tasks[0].title, "Finish hacking the machine")

        # Try to submit again
        action, params = self.ca.parse_message("daily journal")
        reply, message_data, end_convo = self.ca.respond_to_action(
            action, parameters=params)
        self.assertEqual(reply, JOURNAL.ALREADY_SUBMITTED_REPLY)
    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 #5
0
    def test_journal_report(self):
        jrnl = MiniJournal.Create(self.u, date=date(2017, 4, 5))
        jrnl.Update(lat="-1.289744",
                    lon="36.7694933",
                    tags=[],
                    data={'happiness': 9})
        jrnl.put()

        self._test_report({'type': REPORT.JOURNAL_REPORT},
                          [['Date', 'Tags', 'Location', 'Data'],
                           [
                               "2017-04-05", "", "\"-1.289744,36.7694933\"",
                               "\"{\"\"happiness\"\": 9}\""
                           ]])
Beispiel #6
0
 def update(self, d):
     '''
     '''
     id = self.request.get('id')
     params = tools.gets(self,
         strings=['tags_from_text'],
         json=['data']
     )
     jrnl = None
     if id:
         jrnl = MiniJournal.get_by_id(id, parent=self.user.key)
         jrnl.Update(**params)
         jrnl.parse_tags()
         jrnl.put()
         self.success = True
         self.message = "Journal updated"
     else:
         self.message = "Malformed request - id required"
     self.set_response({
         'journal': jrnl.json() if jrnl else None
     })
Beispiel #7
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 #8
0
 def _journal(self, message=""):
     DONE_MESSAGES = ["done", "that's all", "exit", "finished", "no"]
     MODES = ['questions', 'tasks', 'end']
     settings = tools.getJson(self.user.settings, {})
     questions = settings.get('journals', {}).get('questions', [])
     end_convo = False
     if questions:
         jrnl = MiniJournal.Get(self.user)
         if jrnl:
             return (JOURNAL.ALREADY_SUBMITTED_REPLY, True)
         else:
             if not self.cs:
                 self.cs = self._create_conversation_state()
                 self.cs.set_state('mode', 'questions')
             mode = self.cs.state.get('mode')
             mode_finished = False
             save_response = True
             last_question = None
             # Receive user message
             if mode == 'tasks':
                 is_done = message.lower().strip() in DONE_MESSAGES
                 mode_finished = is_done
                 save_response = not is_done
             elif mode == 'questions':
                 last_q_index = self.cs.state.get('last_q_index', -1)
                 last_question = last_q_index == len(questions) - 1
                 mode_finished = last_question
                 save_response = True
             if save_response:
                 successful_add = self.cs.add_message_from_user(message)
                 if not successful_add:
                     reply = self.cs.invalid_reply(
                     ) if mode == 'questions' else JOURNAL.INVALID_TASK
                     return (reply, False)
             mode_index = MODES.index(mode)
             if mode_finished:
                 mode = MODES[mode_index + 1]
                 self.cs.set_state('mode', mode)
             reply = None
             # Generate next reply
             if mode == 'questions':
                 next_q_index = last_q_index + 1
                 q = questions[next_q_index]
                 reply = q.get('text')
                 name = q.get('name')
                 response_type = q.get('response_type')
                 pattern = JOURNAL.PATTERNS.get(response_type)
                 store_number = response_type in JOURNAL.NUMERIC_RESPONSES
                 self.cs.expect_reply(
                     pattern, name,
                     store_number=store_number)  # Store as name
                 self.cs.set_state('last_q_index', next_q_index)
             elif mode == 'tasks':
                 # Ask to add tasks
                 tasks = self.cs.response_data.get('tasks', [])
                 additional = len(tasks) > 0
                 reply = JOURNAL.TOP_TASK_PROMPT_ADDTL if additional else JOURNAL.TOP_TASK_PROMPT
                 self.cs.expect_reply(JOURNAL.PTN_TEXT_RESPONSE,
                                      'tasks',
                                      store_array=True)  # Store as name
             elif mode == 'end':
                 # Finish and submit
                 task_names = []
                 if 'tasks' in self.cs.response_data:
                     task_names = self.cs.response_data.pop('tasks')
                 jrnl = MiniJournal.Create(self.user)
                 jrnl.Update(data=self.cs.response_data)
                 jrnl.parse_tags()
                 jrnl.put()
                 tasks = []
                 if task_names:
                     for tn in task_names:
                         task = Task.Create(self.user, tn)
                         tasks.append(task)
                 ndb.put_multi(tasks)
                 reply = "Report submitted!"
                 end_convo = True
             if reply:
                 self.cs.set_message_to_user(reply)
             if end_convo:
                 self._expire_conversation()
             else:
                 self._set_conversation_state()
             return (reply, end_convo)
     else:
         return ("Please visit flowdash.co to set up journal questions",
                 True)
Beispiel #9
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)