LifeHacker & Elyssa
Привет, Лайфхакерша, я тут ковырялась с голосовым календарем, который сам назначает встречи, планирует перекусы и даже медитации — представь себе личного лайфхакера, но на максималках. Хочешь заглянуть в код и посмотрим, как сделать его круче, чем твои нынешние инструменты продуктивности?
Звучит как мечта. Давай посмотрим на репозиторий и начнем чистить лишние триггеры. Я сначала задокументирую текущий процесс, а потом добавим более продвинутое распознавание контекста, чтобы оно понимало, когда ты на совещании, а когда просто разговариваешь с коллегой. Покажи мне код, и мы сделаем его безупречным.
Вот небольшой фрагмент секции триггеров из `triggers.py` – пока что там полный хаос.
# triggers.py
from events import *
def register_triggers():
triggers = []
# Триггеры устарели, многие из них пересекаются
triggers.append(When('meeting_start').do(schedule_meeting))
triggers.append(When('meeting_end').do(clean_up_meeting))
triggers.append(When('email_sent').do(archive_email))
triggers.append(When('message_received').do(process_message))
triggers.append(When('task_created').do(create_task))
# Повторяющиеся и редко используемые
triggers.append(When('calendar_view').do(update_ui))
triggers.append(When('calendar_view').do(log_view))
triggers.append(When('calendar_view').do(cache_view))
return triggers
```
Мы можем избавиться от `calendar_view`, объединить `schedule_meeting` и `clean_up_meeting` в один обработчик с учетом контекста, и заменить простые проверки `When` на датчик контекста, который будет помечать события как "встреча", "вне экрана" и т.д. Давай набросаем простой `ContextSensor`, который будет отмечать события на основе текущего статуса календаря, а затем подключим его к циклу. Хочешь увидеть код датчика дальше?
Конечно, вот минимальный датчик, чтобы помечать события до срабатывания триггеров.
# context_sensor.py
class ContextSensor:
def __init__(self, calendar):
self.calendar = calendar # простой интерфейс с is_meeting() и т.д.
def tag(self, event):
if self.calendar.is_meeting():
event.context = 'in_meeting'
elif self.calendar.is_offscreen():
event.context = 'off_screen'
else:
event.context = 'idle'
return event
```
Затем в основном цикле:
```
sensor = ContextSensor(calendar)
for event in event_stream:
event = sensor.tag(event)
handle(event) # твой универсальный обработчик
```
Так ты можешь избавиться от старых проверок `When` и просто смотреть на `event.context`. Это даёт чистый и расширяемый хук для будущих меток. Скажи, если захочешь добавить более тонкие состояния или обработку неизвестных событий.
Замечательно, этот датчик поддерживает структуру цикла в порядке. Один небольшой штрих: сделай так, чтобы функция `tag` возвращала копию, а не изменяла исходное событие, чтобы избежать случайных побочных эффектов. И добавь контекст `unknown` для событий, которые не соответствуют ни одному правилу – так будет проще ловить ошибки на раннем этапе. Хочешь небольшую тестовую систему, чтобы проверить теги на основе фиктивного календаря?
Вот небольшая доработка и крошечная проверка, чтобы убедиться, что всё работает.
Выглядит здорово! Только одна маленькая поправка – давай выделим состояние "idle" в отдельную подпись, вместо того, чтобы относить его к "неизвестному". Так потом будет проще добавить специальную обработку для реального состояния простоя. В остальном, плетение должно выводить три контекста, как и ожидалось. Готова запускать его в основной цикл?
Поняла, просто переставь проверку бездействия до значения по умолчанию. Вот финальная настройка датчика:
```class ContextSensor:
def __init__(self, calendar):
self.calendar = calendar
def tag(self, event):
ev = copy.deepcopy(event)
if self.calendar.is_meeting():
ev.context = 'in_meeting'
elif self.calendar.is_offscreen():
ev.context = 'off_screen'
elif self.calendar.is_idle(): # add this in your calendar class
ev.context = 'idle'
else:
ev.context = 'unknown'
return ev
```
Обнови свой mock календаря:
```class FakeCalendar:
def __init__(self, state):
self.state = state
def is_meeting(self): return self.state == 'meeting'
def is_offscreen(self): return self.state == 'off_screen'
def is_idle(self): return self.state == 'idle'
```
Запусти тест снова, увидишь `in_meeting`, `off_screen`, `idle` и `unknown` как отдельные метки. Теперь можно напрямую подключить это к основному циклу и добавить отдельные обработчики для каждого контекста. Готова вставлять.
Поняла — твой финальный датчик — чистый switch-case. Сейчас запускаю работу с подключением:
state=meeting → context=in_meeting
state=off_screen → context=off_screen
state=idle → context=idle
state=unknown → context=unknown
Отлично. Подключи это в основной цикл событий, сопоставь каждый контекст с соответствующим обработчиком, и у тебя получится очень чёткий, контекстно-зависимый поток. Что дальше будем добавлять?
Отлично, теперь когда с датчиком всё в порядке, переходим к следующей крутой штуке: уведомления о простоях с прогнозированием. В общем, если ты находишься в состоянии "ожидания" дольше минуты, предлагай короткий перерыв или напоминание о растяжке. Чтобы работа не превращалась в скуку смертную. Добавь таймер в обработчике бездействия, который будет выдавать вежливое уведомление. Готова набросать это?