Настрой контент
под пользователя!
Александр Сапронов:
a@sapronov.me
ru.linkedin.com/in/alexsapronov
Хто я такой?
● Пишу код за деньги в Welltory
○ В основном на Python
● Делаю:
○ Python Дайджест
○ Сообщество PyNSK
● Делал PyCon Siberia 2016
2
● Стартап (проекту 1.5 года)
● Изучаем образ жизни людей
● Учим соблюдать баланс эффективности и здоровья
● Рисуем красивые дашбоарды и графики
● Строим рекомендательную систему
Welltory - приложение для управления стрессом, продуктивностью
3
●
●
● Учим соблюдать баланс эффективности и здоровья
●
●
Welltory - приложение для управления стрессом, продуктивностью
4
Переходя к теме
iOS
У нас есть:
Web Android
5
а еще...
Мы пишем много контента
6
а еще...
Мы пишем много контента
7
Точнее content-writer’ы (не разработчики)
Наш контент
8
Pushes Quests Insights Charts
Emails Lectures Reports Visualizations
Askers Interface Ratings Correlations
Tips Levels
Onboardings Progress
Details
сон более 8 часов снизил твой стресс на 30%
9
%username%,
%username%,
регулярные прогулки по 45 минут
увеличивают вашу продуктивность на 23%
10
%username%,
мы заметили повышение показателя X,
рекомендуем избежать долгих поездок за рулем
11
%username%,
перерыв каждый час снизит стресс и повысит продуктивность.
Напоминать?
12
Как показать для
%username%
релевантный текст?
13
14
Привет!
15
Покажите
интересное
16
Релевантный
текстМагия системы
17
Данные
Собираем
данные о
пользователе
18
Данные
Набор
параметров
Определяем
параметры
настройки
19
Данные
Набор
параметров
Условия
Создаем
условия на
параметры
20
Данные
Набор
параметров
Условия
Релевантный
текст
Соединяем
данные и
условия
21
Эээ
22
Нууу как-бэ очевидно, да
23
Как реализовать-то?
24
У нас было так
Трактовка замера
(первый этап)
25
26
Немного контекста
27
Важная фича Welltory - замер вариабельности сердечного ритма (ВСР)
Замер
По ВСР можно оценить:
- Стресс
- Энергию
- количество и динамику
- на что тратит организм
- Здоровье в целом
- наличие некоторых хронических болезней
- Приближающуюся болезнь (проверял на себе)
- Тренированность и готовность к нагрузкам
- физическая, интеллектуальная, психологическая
- Влияние веса
- Тип регуляции (фенотип)
- ...
Трактовка замера
28
X стресс
Y энергия
Трактовка+ =
Трактовка замера
29
20 стресс
70 энергия
Вы справляетесь со
всеми нагрузками
+ =
Трактовка замера
30
60 стресс
10 энергия
Слишком сильное
нервное напряжение,
а сил почти нет.
+ =
1 этап - таблицы контента
31
Как внедрить
● Создаем таблицы
○ X -> Text
○ X, Y -> Text
● Из таблиц достаем нужный текст
Таблица контента - соответствие контента и значения параметра
1 этап - таблицы контента
32
Плюсы
● Простота использования
● Легко тестировать код
○ Если X и Y, то Text
Таблица контента - соответствие контента и значения параметра
1 этап - таблицы контента
33
Плюсы
● Простота использования
● Легко тестировать код
○ Если X и Y, то Text
Минусы
● Кол-во параметров = размерность таблицы
● Условие выборки находятся в коде
Таблица контента - соответствие контента и значения параметра
Хочу комбинировать условия
(с) content writer
Комбинация условий
Если X больше N или Y меньше M, то Text
35
Если
Есть давление и
Индекс Кердо меньше -15
То
Ваша нервная система находится в очень
спокойном состоянии. Это может быть связано с ….
Комбинация условий
36
Объекты-комбинаторы
(второй этап)
38
Данные
Набор
параметров
Условия
Релевантный
текст
Вспомним
общую схему
39
Данные
Набор
параметров
Условия
Релевантный
текст
Так вот:
Уносим ВСЁ в БД
2 этап - объекты-комбинаторы
40
Создаем модели
● Текст
● Параметр
● Условие
2 этап - объекты-комбинаторы
41
Создаем модели
● Текст
● Параметр
● Условие
Контент на двух языках
2 этап - объекты-комбинаторы
42
Создаем модели
● Текст
● Параметр
● Условие
Контент на двух языках
Способ получения значения
● Из какой модели какое поле достать
● Функция расчета параметра
2 этап - объекты-комбинаторы
43
Создаем модели
● Текст
● Параметр
● Условие
Контент на двух языках
Способ получения значения
Комбинатор
● Сравнивает значение параметра
с настройками
Модель условия
44
class Condition(models.Model):
text = models.ForeignKey(Text)
type = models.IntegerField(choices=CONDITION_CHOICE,
default=CONDITION_RANGE)
argument1 = models.CharField(...)
argument2 = models.CharField(...)
argument3 = models.CharField(...)
Храним
тип оператора и
операнды
Метод сравнения
45
def compare(self, value):
if self.type == CONDITION_GT:
return value > float(self.argument1)
elif self.type == CONDITION_RANGE:
return float(self.argument1) <= value <= float(self.argument2)
elif self.type == CONDITION_FUNCTION:
function = getattr(transcript_condition_functions, self.argument3)
return function(value)
...
Принимаем значение
параметра и
проверяем условие
Как использовать
1. Создаем объект Параметра
a. Определяем способ получения значения
2. Создаем объект Текста
3. Создаем объект Условия
4. Связываем одно с другим
a. Тексты с Параметрами
b. Параметры или Тексты с условиями
5. Выгружаем тексты
6. Для каждого текста получаем список его Параметров и Условий
7. Проверяем каждое условие на истину
8. …
9. Profit 46
Примеры условий
47
48
Примеры условий
49
Примеры условий
2 этап - объекты-комбинаторы
50
Плюсы
● Рабочий метод
● Можно комбинировать условия
● Все управляется через админку
2 этап - объекты-комбинаторы
51
Плюсы
● Рабочий метод
● Можно комбинировать условия
● Все управляется через админку
Минусы
● Мутная реализация и поддержка
● Тяжело переиспользовать для разных фич
● Сложно тестировать
● Логика “И”/ “ИЛИ” / “НЕ” требует лишних объектов в БД
● По опыту: тип параметра жестко завязан на набор условий для него
Хочу переиспользовать условия
(с) ленивый/умный разработчик
Гибкие условия
(третий этап, сейчас)
3 этап - гибкие условия
Хочется
● Можно комбинировать условия через ИЛИ / И / НЕ
● Можно переиспользовать условия
● Вычисление значения и обработка лежат в одном месте
● Добавление параметра = добавить класс
54
django-conditions
https://github.com/RevolutionTech/django-conditions/
django-conditions
56
Инструмент переноса гибкой логики в Django Admin
● JSONField на стероидах
● Можно комбинировать условия через И / ИЛИ / НЕ
● Поддерживает условия
○ True/False без выбора аргумента
○ True/False с выбором аргумента
○ Сравнение значений
● Логика проверки условий = Python класс
django-conditions
57
Инструмент переноса гибкой логики в Django Admin
{
"all": [
"INFO_RR_MORNING_COUNT <= 1",
"USER_TIME_OF_DAY morning"
]
}
Пример условия
class UserTimeOfDayCondition(Condition):
condstr = 'USER_TIME_OF_DAY'
keys_allowed = ['morning', 'day', 'evening', 'night']
def eval_bool(self, user, **kwargs):
key = kwargs.get('key', self.key)
return key == get_time_of_day(user.user_time)
58
True/False условие
Обработчик
Пример условия
class UserTimeOfDayCondition(Condition):
condstr = 'USER_TIME_OF_DAY'
keys_allowed = ['morning', 'day', 'evening', 'night']
def eval_bool(self, user, **kwargs):
key = kwargs.get('key', self.key)
return key == get_time_of_day(user.user_time)
59
True/False условие
Обработчик
Хитрость для тестов:
в key запихать проверяемое значение
Пример условия
class UserDaysFromSignUpCondition(CompareCondition):
condstr = 'USER_DAYS_FROM_SIGN_UP'
cast_operand = int
def eval_operand(self, user, **kwargs):
return (datetime.now() - user.created_at).days
60
Условие на
сравнение
Пример условия
class UserDaysFromSignUpCondition(CompareCondition):
condstr = 'USER_DAYS_FROM_SIGN_UP'
cast_operand = int
def eval_operand(self, user, **kwargs):
return (datetime.now() - user.created_at).days
61
Условие на
сравнение
Обработчик
Пример условия
class UserDaysFromSignUpCondition(CompareCondition):
condstr = 'USER_DAYS_FROM_SIGN_UP'
cast_operand = int
def eval_operand(self, user, **kwargs):
return (datetime.now() - user.created_at).days
62
Условие на
сравнение
Обработчик
Тип операнда
int и float автоматически
поддерживают операции > >= <= < ==
Пример условия
class UserDaysFromSignUpCondition(CompareCondition):
condstr = 'USER_DAYS_FROM_SIGN_UP'
cast_operand = int
def eval_operand(self, user, **kwargs):
return (datetime.now() - user.created_at).days
63
В kwargs можно
передать что
угодно.
Как использовать
1. Создаем условия
2. Создаем модель с полем
from super_app import condition_types
...
conditions = ConditionsField(definitions=conditions_from_module(condition_types))
3. Создаем объекты модели
4. Проверяем условия в коде
64
Как проверять условия
from conditions import eval_conditions
"""
user - User object
data - Словарь данных. Содержит предварительно посчитаные данные
queryset - QuerySet модели с полем conditions
"""
for item in queryset:
val = eval_conditions(item, 'conditions', user, **data)
if val:
print("Match!!!")
return item
65
66
Плюсы
● Можно комбинировать условия (И / ИЛИ / НЕ)
● Легко добавлять новые условия
● Можно переиспользовать для разных фич
Минусы
● Требует дисциплины
○ Нейминг условий
○ Способ написания условий (пишите атомарные условия)
● Тяжело расширить интерфейс управления
● Одним запросом не получится получить релевантный тексты
3 этап - гибкие условия
a@sapronov.me
ru.linkedin.com/in/alexsapronov
Питоны кончились…
Вопросы?
67

Настрой контент под пользователя!