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})
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)
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}\"" ]])
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 })
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 _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)
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)