Skip to content

Djama1GIT/avito

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Avito Backend Trainee Test Assignment

English
Russian

Installation and Setup

  1. Install Docker and Docker Compose if they are not already installed on your system.

  2. Clone the project repository:

git clone https://github.com/Djama1GIT/avito.git
cd avito
  1. Start the project:
docker-compose up --build

User Interface

After starting the project, you can access the Swagger user interface at: http://localhost:8000/swagger/index.html.
In Swagger, you can view the available endpoints and their parameters, and also make requests to the API.

Extra

If the tables in the database were not created automatically, use:

make init_sql

Usage Examples

Main Task

Method for creating a segment. Accepts the segment's slug (name)

curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "example"}'

200: {"slug":"example"}
400: {"message":"pq: duplicate key value violates unique constraint "segments_pkey""}

Method for deleting a segment. Accepts the segment's slug (name)

curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "example"}'

200: {"slug":"example"}
400: {"message":"segment with slug example does not exist"}

Method for adding a user to a segment. Accepts a list of segment slugs to add to the user, a list of segment slugs to remove from the user, and the user's ID

curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES"], "segments_to_delete": ["AVITO_PERFORMANCE_VAS", "AVITO_DISCOUNT_30"]}'

200: {"user_id":1}
400: {"message":"error occurred while processing segment to add 'AVITO_VOICE_MESSAGES': pq: duplicate key value violates unique constraint "user_segments_pkey""}
400: {"message":"error occurred while checking segment to delete existence 'AVITO_PERFORMANCE_VAS': user(1) is not in this segment"}%

Method for retrieving a user's active segments. Accepts the user's ID

curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}'

200: {"segments":["AVITO_VOICE_MESSAGES"],"user_id":1}
200: {"segments":[],"user_id":1}

Additional Task 1

A method of obtaining the history of a user's entry/exit from a segment over a certain period in csv format.

curl -X GET http://127.0.0.1:8000/api/users/history/ -d '{"user_id": 1, "year_month": "2023-08"}'

200: {"report":"http://localhost:8000/files/reports/user_history_2023-08_1.csv","user_id":1}
400: {"message":"invalid YearMonth"}
400: {"message":"json: cannot unmarshal string into Go struct field UserHistory.user_id of type int"}

Additional Task 2

Method for adding a user to a segment with the ability to set TTL

curl -X PATCH http://127.0.0.1:8000/api/segments/ -d \
'{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES"], "segments_to_add_expiration":"2023-08-29 00:00:00", "segments_to_delete": []}'

200: {"user_id":1}
400: {"message":"parsing time "2023-08-31 00:00:" as "2006-01-02 15:04:05": cannot parse "" as "05""}

Method for deleting expired segments of a user

Automatically called using cron every minute
curl -X DELETE http://127.0.0.1:8000/api/users/expired-segments/

200: empty result

Additional Task 3

An option to specify the percentage of users who will automatically be included in the segment has been added to the segment creation method.

There are many different ways this functionality could be implemented, and after some consideration, the following decision was made:
Inclusion in the segment (or not) depends on a function that:
  1. Takes as input the user ID, segment, and probability.

  2. Hashes them.

  3. Using the formula and specified probability, returns true or false, always the same.

Function

Nuances:

  1. The number of users included in the segment may vary by up to ~1% from (specified probability * total number of users).

I deemed this acceptable, as this is a service for analysts, and quality is more important than quantity here.

Specifically, this function ensures maximum diversification of users included in the segment,

as random users from the entire population will be included in the segment,

without any particular user being included too often or too infrequently,

which would be the case with a simpler implementation.

curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES", "percentage": 50}'

200: {"slug": "AVITO_VOICE_MESSAGES"}
400: {"message": "invalid percentage"}

Extended Examples

You can use the following curls in order and get the same responses
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_PERFORMANCE_VAS"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_30"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_50"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DELIVERY_FEATURE"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_CLOUD_FEATURE"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_UNDEFINED_FEATURE"}' -w '\n'

{"slug":"AVITO_VOICE_MESSAGES"}
{"slug":"AVITO_PERFORMANCE_VAS"}
{"slug":"AVITO_DISCOUNT_30"}
{"slug":"AVITO_DISCOUNT_50"}
{"slug":"AVITO_DELIVERY_FEATURE"}
{"slug":"AVITO_CLOUD_FEATURE"}
{"slug":"AVITO_UNDEFINED_FEATURE"}

curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES", "AVITO_PERFORMANCE_VAS", "AVITO_DISCOUNT_30", "AVITO_DISCOUNT_50"], "segments_to_delete": []}'  -w '\n'

curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_DELIVERY_FEATURE", "AVITO_CLOUD_FEATURE", "AVITO_UNDEFINED_FEATURE"], "segments_to_delete": ["AVITO_VOICE_MESSAGES", "AVITO_PERFORMANCE_VAS", "AVITO_DISCOUNT_30"]}' -w '\n'

curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'

curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": [], "segments_to_delete": ["AVITO_DISCOUNT_50", "AVITO_DELIVERY_FEATURE", "AVITO_CLOUD_FEATURE", "AVITO_UNDEFINED_FEATURE"]}' -w '\n'

curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'

{"user_id":1}
{"user_id":1}
{"segments":["AVITO_CLOUD_FEATURE","AVITO_DELIVERY_FEATURE","AVITO_DISCOUNT_50","AVITO_UNDEFINED_FEATURE"],"user_id":1}
{"user_id":1}
{"segments":[],"user_id":1}

curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_PERFORMANCE_VAS"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_30"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_50"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DELIVERY_FEATURE"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_CLOUD_FEATURE"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_UNDEFINED_FEATURE"}' -w '\n'

{"slug":"AVITO_VOICE_MESSAGES"}
{"slug":"AVITO_PERFORMANCE_VAS"}
{"slug":"AVITO_DISCOUNT_30"}
{"slug":"AVITO_DISCOUNT_50"}
{"slug":"AVITO_DELIVERY_FEATURE"}
{"slug":"AVITO_CLOUD_FEATURE"}
{"slug":"AVITO_UNDEFINED_FEATURE"}

curl -X GET http://127.0.0.1:8000/api/users/history/ -d '{"user_id": 1, "year_month": "2023-08"}'

{"report":"http://localhost:8000/files/reports/user_history_2023-08_1.csv","user_id":1}

user_history_2023-08_1.csv:

1,AVITO_VOICE_MESSAGES,добавление,2023-08-29 20:33:11
1,AVITO_PERFORMANCE_VAS,добавление,2023-08-29 20:33:11
1,AVITO_DISCOUNT_30,добавление,2023-08-29 20:33:11
1,AVITO_DISCOUNT_50,добавление,2023-08-29 20:33:11
1,AVITO_DELIVERY_FEATURE,добавление,2023-08-29 20:33:12
1,AVITO_CLOUD_FEATURE,добавление,2023-08-29 20:33:12
1,AVITO_UNDEFINED_FEATURE,добавление,2023-08-29 20:33:12
1,AVITO_VOICE_MESSAGES,удаление,2023-08-29 20:33:12
1,AVITO_PERFORMANCE_VAS,удаление,2023-08-29 20:33:12
1,AVITO_DISCOUNT_30,удаление,2023-08-29 20:33:12
1,AVITO_DISCOUNT_50,удаление,2023-08-29 20:33:12
1,AVITO_DELIVERY_FEATURE,удаление,2023-08-29 20:33:12
1,AVITO_CLOUD_FEATURE,удаление,2023-08-29 20:33:12
1,AVITO_UNDEFINED_FEATURE,удаление,2023-08-29 20:33:12

curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES"], "segments_to_add_expiration":"1970-08-31 00:00:00", "segments_to_delete": []}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/users/expired-segments/ -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'

{"slug":"AVITO_VOICE_MESSAGES"}
{"user_id":1}
{"segments":["AVITO_VOICE_MESSAGES"],"user_id":1}

{"segments":[],"user_id":1}
{"slug":"AVITO_VOICE_MESSAGES"}

curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug", "percentage": 27}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug2", "percentage": 27}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 420}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 421}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 422}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug2"}' -w '\n'

{"slug":"example-slug"}
{"slug":"example-slug2"}
{"segments":["example-slug"],"user_id":420}
{"segments":["example-slug","example-slug2"],"user_id":421}
{"segments":[],"user_id":422}
{"slug":"example-slug"}
{"slug":"example-slug2"}

Russian

Установка и настройка

  1. Установите Docker и Docker Compose, если они еще не установлены на вашей системе.

  2. Клонируйте репозиторий проекта:

git clone https://github.com/Djama1GIT/avito.git
cd avito
  1. Запустите проект:
docker-compose up --build

Пользовательский интерфейс

После запуска проекта вы сможете получить доступ к пользовательскому интерфейсу Swagger по адресу:
http://localhost:8000/swagger/index.html.
В Swagger вы сможете просмотреть доступные ручки и их параметры, а также выполнять запросы к API.

Дополнительно

Если таблицы в БД не создались автоматически используйте:

make init_sql

Примеры использования

Основное задание

Метод создания сегмента. Принимает slug (название) сегмента

curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "example"}'

200: {"slug":"example"}
400: {"message":"pq: duplicate key value violates unique constraint "segments_pkey""}

Метод удаления сегмента. Принимает slug (название) сегмента

curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "example"}'

200: {"slug":"example"}
400: {"message":"segment with slug example does not exist"}

Метод добавления пользователя в сегмент. Принимает список slug (названий) сегментов которые нужно добавить пользователю, список slug (названий) сегментов которые нужно удалить у пользователя, id пользователя

curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES"], "segments_to_delete": ["AVITO_PERFORMANCE_VAS", "AVITO_DISCOUNT_30"]}'

200: {"user_id":1}
400: {"message":"error occurred while processing segment to add 'AVITO_VOICE_MESSAGES': pq: duplicate key value violates unique constraint "user_segments_pkey""}
400: {"message":"error occurred while checking segment to delete existence 'AVITO_PERFORMANCE_VAS': user(1) is not in this segment"}%

Метод получения активных сегментов пользователя. Принимает на вход id пользователя

curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}'

200: {"segments":["AVITO_VOICE_MESSAGES"],"user_id":1}
200: {"segments":[],"user_id":1}

Дополнительное задание 1

Метод получения истории попадания/выбывания пользователя из сегмента за определенный период в формате csv

curl -X GET http://127.0.0.1:8000/api/users/history/ -d '{"user_id": 1, "year_month": "2023-08"}'

200: {"report":"http://localhost:8000/files/reports/user_history_2023-08_1.csv","user_id":1}
400: {"message":"invalid YearMonth"}
400: {"message":"json: cannot unmarshal string into Go struct field UserHistory.user_id of type int"}

Дополнительное задание 2

Метод добавления пользователя в сегмент с возможностью задавать TTL

curl -X PATCH http://127.0.0.1:8000/api/segments/ -d \
'{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES"], "segments_to_add_expiration":"2023-08-29 00:00:00", "segments_to_delete": []}'

200: {"user_id":1}
400: {"message":"parsing time "2023-08-31 00:00:" as "2006-01-02 15:04:05": cannot parse "" as "05""}

Метод удаления истекших сегментов пользователя

Вызывается автоматически при помощи cron каждую минуту
curl -X DELETE http://127.0.0.1:8000/api/users/expired-segments/

200: empty result

Дополнительное задание 3

В методе создания сегмента, добавлена опция указания процента пользователей, которые будут попадать в сегмент автоматически.

Есть множество различных вариантов, как можно было бы реализовать этот функционал, и в итоге после некоторых раздумий было принято такое решение:
Попадание в сегмент(или нет) зависит от функции, которая:
  1. Принимает на вход id пользователя, сегмент, вероятность.

  2. Хеширует их

  3. По формуле, с указанной вероятность, возвращает true или false, причем всегда одинаково.

Функция

Ньюансы:

  1. Количество пользователей попавших в сегмент может изменяться до ~1%

    от (указанной вероятности * количество всех пользователей)

Я посчитал это допустимым, т.к. это сервис для аналитиков, и тут важнее качество, а не количество

А именно благодаря такой функции можно обеспечить максимальную диверсификацию попавших в сегмент пользователей,

т.к. в сегмент будут попадать случайные пользователи из всей массы, и при этом не будет такого,

что определенный пользователь попадает часто в какие-то сегменты, а другой наоборот,

если бы была использована более простая реализация.

curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES", "percentage": 50}'

200: {"slug": "AVITO_VOICE_MESSAGES"}
400: {"message": "invalid percentage"}

Расширенные примеры

Вы можете использовать curl'ы ниже по порядку и получить такие же ответы
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_PERFORMANCE_VAS"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_30"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_50"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DELIVERY_FEATURE"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_CLOUD_FEATURE"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_UNDEFINED_FEATURE"}' -w '\n'

{"slug":"AVITO_VOICE_MESSAGES"}
{"slug":"AVITO_PERFORMANCE_VAS"}
{"slug":"AVITO_DISCOUNT_30"}
{"slug":"AVITO_DISCOUNT_50"}
{"slug":"AVITO_DELIVERY_FEATURE"}
{"slug":"AVITO_CLOUD_FEATURE"}
{"slug":"AVITO_UNDEFINED_FEATURE"}

curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES", "AVITO_PERFORMANCE_VAS", "AVITO_DISCOUNT_30", "AVITO_DISCOUNT_50"], "segments_to_delete": []}'  -w '\n'

curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_DELIVERY_FEATURE", "AVITO_CLOUD_FEATURE", "AVITO_UNDEFINED_FEATURE"], "segments_to_delete": ["AVITO_VOICE_MESSAGES", "AVITO_PERFORMANCE_VAS", "AVITO_DISCOUNT_30"]}' -w '\n'

curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'

curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": [], "segments_to_delete": ["AVITO_DISCOUNT_50", "AVITO_DELIVERY_FEATURE", "AVITO_CLOUD_FEATURE", "AVITO_UNDEFINED_FEATURE"]}' -w '\n'

curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'

{"user_id":1}
{"user_id":1}
{"segments":["AVITO_CLOUD_FEATURE","AVITO_DELIVERY_FEATURE","AVITO_DISCOUNT_50","AVITO_UNDEFINED_FEATURE"],"user_id":1}
{"user_id":1}
{"segments":[],"user_id":1}

curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_PERFORMANCE_VAS"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_30"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_50"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DELIVERY_FEATURE"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_CLOUD_FEATURE"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_UNDEFINED_FEATURE"}' -w '\n'

{"slug":"AVITO_VOICE_MESSAGES"}
{"slug":"AVITO_PERFORMANCE_VAS"}
{"slug":"AVITO_DISCOUNT_30"}
{"slug":"AVITO_DISCOUNT_50"}
{"slug":"AVITO_DELIVERY_FEATURE"}
{"slug":"AVITO_CLOUD_FEATURE"}
{"slug":"AVITO_UNDEFINED_FEATURE"}

curl -X GET http://127.0.0.1:8000/api/users/history/ -d '{"user_id": 1, "year_month": "2023-08"}'

{"report":"http://localhost:8000/files/reports/user_history_2023-08_1.csv","user_id":1}

user_history_2023-08_1.csv:

1,AVITO_VOICE_MESSAGES,добавление,2023-08-29 20:33:11
1,AVITO_PERFORMANCE_VAS,добавление,2023-08-29 20:33:11
1,AVITO_DISCOUNT_30,добавление,2023-08-29 20:33:11
1,AVITO_DISCOUNT_50,добавление,2023-08-29 20:33:11
1,AVITO_DELIVERY_FEATURE,добавление,2023-08-29 20:33:12
1,AVITO_CLOUD_FEATURE,добавление,2023-08-29 20:33:12
1,AVITO_UNDEFINED_FEATURE,добавление,2023-08-29 20:33:12
1,AVITO_VOICE_MESSAGES,удаление,2023-08-29 20:33:12
1,AVITO_PERFORMANCE_VAS,удаление,2023-08-29 20:33:12
1,AVITO_DISCOUNT_30,удаление,2023-08-29 20:33:12
1,AVITO_DISCOUNT_50,удаление,2023-08-29 20:33:12
1,AVITO_DELIVERY_FEATURE,удаление,2023-08-29 20:33:12
1,AVITO_CLOUD_FEATURE,удаление,2023-08-29 20:33:12
1,AVITO_UNDEFINED_FEATURE,удаление,2023-08-29 20:33:12

curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES"], "segments_to_add_expiration":"1970-08-31 00:00:00", "segments_to_delete": []}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/users/expired-segments/ -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'

{"slug":"AVITO_VOICE_MESSAGES"}
{"user_id":1}
{"segments":["AVITO_VOICE_MESSAGES"],"user_id":1}

{"segments":[],"user_id":1}
{"slug":"AVITO_VOICE_MESSAGES"}

curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug", "percentage": 27}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug2", "percentage": 27}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 420}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 421}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 422}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug2"}' -w '\n'

{"slug":"example-slug"}
{"slug":"example-slug2"}
{"segments":["example-slug"],"user_id":420}
{"segments":["example-slug","example-slug2"],"user_id":421}
{"segments":[],"user_id":422}
{"slug":"example-slug"}
{"slug":"example-slug2"}