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


## tl;dr (для тех, кто в теме)

* `cd mini_fiction`
* `virtualenv env`
* `. env/bin/activate`
* `make develop`
* `mkdir media`
* `mini_fiction seed`
* `mini_fiction createsuperuser`
* `mini_fiction runserver`


## Быстрый старт (развёртывание минимального окружения для разработки)

* Устанавливаем Python версии 3.4 или новее (ОС не имеет значения)

* Скачиваем mini_fiction, выполнив команду `git clone` или просто скачав
  и распаковав zip-архив, и переходим в консоли в каталог с проектом:

```
cd mini_fiction
```

* Опционально, но желательно, чтобы не мусорить в системе: устанавливаем
  `virtualenv` и создаём виртуальное окружение, в каталоге с проектом
  выполнив команду:

```
virtualenv env
```

* Входим в созданное окружение командой `. env/bin/activate` (*sh) или
  `.\env\Scripts\activate` (командная строка Windows)

* [Устанавливаем lxml](http://lxml.de/installation.html). В зависимости
  от ОС процесс может быть разный. Где-то можно собрать из исходников,
  где-то ставится бинарный пакет (для Windows, например, его можно
  найти [здесь](http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml)).
  Если не используется virtualenv, можно воспользоваться репозиториями
  дистрибутивов Linux.

* Устанавливаем проект командой `make develop` (пользователям Windows
  придётся открыть `Makefile` и перепечатать команды вручную или
  использовать make из MinGW)

* Создаём каталог под загружаемые данные вроде картинок персонажей или
  аватарок (путь к нему можно изменить в настройках):

```
mkdir media
```

* Инициализируем базу данных (будет создан файл `db.sqlite3`):

```
mini_fiction seed
```

* Создаём учётную запись администратора:

```
mini_fiction createsuperuser
```

* Запускаем сервер для разработки:

```
mini_fiction runserver
```

* Сайт станет доступен по адресу `http://localhost:5000/`. После
  авторизации доступна админка по адресу `http://localhost:5000/admin/`.
  Для корректной работы сайта следует создать хотя бы один жанр.


## Изменение настроек

* Создаём файл `local_settings.py` (в нём хранятся локальные настройки,
  которые нет смысла публиковать) с примерно таким содержимым:

```python
from mini_fiction.settings import Development


class Local(Development):
    SECRET_KEY = 'some-random-string'
    ...  # здесь и далее все ваши настройки
```

* Запускаем `mini_fiction runserver` (или gunicorn или любой другой
  wsgi-сервер) в том же каталоге, в котором находится `local_settings.py`:
  сайт его найдёт и автоматически подхватит. Если же не подхватит, попробуйте
  переменную окружения `PYTHONPATH=.` (не забудьте про `export PYTHONPATH` в
  юниксовых шеллах).

Для боевого (production) окружения наследуйте класс с настройками от
модуля `mini_fiction.settings.Config` вместо `Development`. Это важно,
так как в режиме разработки доступны некоторые средства для удалённого
выполнения кода. (А ещё крайне желательно прописать случайную строку в
`SECRET_KEY` для защиты пользовательских сессий.)

Скорее всего, вы захотите изменить домен по умолчанию `localhost:5000`
на какой-нибудь другой. Для этого пропишите настройку
`SERVER_NAME = 'example.com'` (если порт нестандартный, его тоже нужно
прописать через двоеточие). Если у вас настроен HTTPS, пропишите
`PREFERRED_URL_SCHEME = 'https'` для прописывания этого протокола во всех
ссылках сайта. Другие примеры настроек можно посмотреть в файле
`local_settings.example.py`.

Все настройки, предлагаемые к изменению ниже, прописываются в этом файле
(не забудьте про отступ в четыре пробела — см. `local_settings.example.py`).

Вы также можете переопределить модуль с настройками через переменную окружения
`MINIFICTION_SETTINGS`. Например, если вы создали файл настроек
`my_settings.py` с классом настроек `SuperProd`, пропишите следующую
переменную окружения:

```
export MINIFICTION_SETTINGS=my_settings.SuperProd
```

Файл `my_settings.py` может располагаться везде, откуда его сможет
импортировать Python.


## Установка дополнительных плюшек

Всё описанное выше это хорошо, только вот не поддерживает поиска,
аватарок и MySQL. Для них следует установить дополнительные модули
(которые собираются из исходников, с чем бывают проблемы на некоторых ОС,
поэтому это всё опционально).

Ниже приводятся примеры команд для Ubuntu, но можно найти аналоги для
почти любой современной ОС.

Для сборки зависимостей из исходников:

```
sudo apt-get install git build-essential python3-dev
```


### MySQL

* `sudo apt-get install libmysqlclient18` — библиотека для работы с
  MySQL

* `sudo apt-get install libmysqlclient-dev` нужен для сборки
  Python-модуля

* `pip3 install mysqlclient` — установка Python-модуля

* На MySQL-сервере создайте базу данных, не забыв указать кодировку utf8mb4
  (в отличие от просто utf8, utf8mb4 корректно хранит Emoji и прочий юникод):

```
create database mini_fiction character set utf8mb4;
```

* Подключаем в настройках:

```python
DATABASE_ENGINE = 'mysql'
DATABASE = {
    'host': '127.0.0.1',
    'port': 3306,
    'user': 'fanfics',  # Не забудьте вписать свои данные!
    'passwd': 'fanfics',
    'db': 'mini_fiction',
    'charset': 'utf8mb4',
}
```


### Поиск рассказов (Sphinx)

* Для подключения к Sphinx используется mysqlclient. Установите его как
  описано выше (первые три пункта).

* Для подключения к Sphinx, очевидно, нужен сам Sphinx :)

```
sudo apt-get install sphinxsearch
```

* Создаём каталоги для работы Sphinx (вы можете переопределить путь
  настройкой `SPHINX_ROOT`):

```
mkdir sphinx && mkdir sphinx/binlog
```

* Включаем Sphinx в настройках:

```python
SPHINX_DISABLED = False
```

* Теперь можно запустить Sphinx. (Примечание: `sphinxconf.sh` просто
  запускает `mini_fiction sphinxconf`, который генерирует настройки;
  не забудьте про включение виртуального окружения (при его наличии) и
  `MINIFICTION_SETTINGS`, иначе что-нибудь пойдёт не так.)

```
searchd --config sphinxconf.sh
```

* Если база данных уже не пустая, а поиск ещё пустой, то загружаем данные
  в индексы Sphinx:

```
mini_fiction initsphinx
```


### Загрузка аватарок

Для их обработки требуется Pillow, который и следует установить.

* `sudo apt-get install libjpeg-turbo8 libpng12-0 libwebp5 libtiff5 python-tk`
  — библиотеки для работы с картинками (и Tk для Pillow)

* `sudo apt-get install libtiff5-dev libjpeg-turbo8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev libpng12-dev`
  — dev-файлы для них же, чтобы собрать Pillow

* `pip3 install Pillow`

* Теперь можно включить загрузку аватарок в настройках:

```python
AVATARS_UPLOADING = True
```

Аватарки сохраняются в каталоге `media`, который по умолчанию создаётся
в каталоге, где запущен mini_fiction. Вы можете переопределить путь к
каталогу настройкой `MEDIA_ROOT`.


### Воркер Celery для производительности

Обновление индекса поиска и отправка почты могут быть делами небыстрыми,
поэтому целесообразно выполнить их не на веб-сервере, а в отдельном
процессе. Кроме того, есть задачи, выполняемые по расписанию (создание
публичного дампа некоторых настроек сайта каждую ночь). Для всего этого
используется Celery.

* Для связи с отдельным процессом по умолчанию используется Redis:

```
sudo apt-get install redis-server
```

* Включаем использование отдельных воркеров в настройках:

```python
CELERY_CONFIG = dict(Config.CELERY_CONFIG)
CELERY_CONFIG['task_always_eager'] = False
```

* Включаем сам воркер:

```
celery -A mini_fiction worker --loglevel=INFO
```

* Для выполнения задач по расписанию нужна пиналка, которая будет отправлять
  эти задачи воркеру в указанное в настройках время:

```
celery -A mini_fiction beat
```

* Кроме того, есть вариант объединения воркера и пиналки в одном процессе,
  хотя он не рекомендуется документацией Celery:

```
celery -A mini_fiction worker -B --loglevel=INFO
```


Для мониторинга его состояния по адресу `http://localhost:5555/` можно
установить и запустить Flower:

```
pip3 install flower
flower -A mini_fiction
```

Управление через консоль также доступно:

```
celery -A mini_fiction status
```


### Кэширование в memcached

Просто установите и запустите его. Его использование прописано в
настройках по умолчанию.


### ReCaptcha при регистрации

```python
RECAPTCHA_PUBLIC_KEY = '6Ldcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
RECAPTCHA_PRIVATE_KEY = '6Ldczzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'
NOCAPTCHA = False
```


### Статика в отдельном каталоге

Если не хочется прописывать в nginx длинный путь к Python-пакету (который
к тому же при обновлении сломается), можно собрать статику в свой каталог
и прописать в nginx путь уже к нему. Для этого пропишите в настройках
этот каталог:

```python
STATIC_ROOT = '/path/to/static'
```

А потом соберите туда статику:

```
mini_fiction collectstatic
```

Недостаток: эту команду нужно выполнять при каждом обновлении
mini_fiction.

Теперь можно раздавать статику через nginx, примерно вот так:

```
location /static {
    alias /path/to/static;
}
```

Браузеры будут кэшировать статику. После обновления статики можно
принудительно очистить кэш у пользователей, прописав новую циферку в
настройку `STATIC_V`:

```python
STATIC_V = '1'   # а потом 2, 3, 4 и так далее
```

Эта циферка допишется ко всем ссылкам на статику, и браузеры её скачают
заново.


## Прикручивание своего дизайна

На момент написания этого файла полноценного движка тем не
предоставляется, зато можно переопределять шаблоны Jinja2 и дополнять
статику. Делается это с помощью следующих параметров
(`local_settings.py`):

* `STATIC_V = '1'` — строка, которая дописывается в конце каждой ссылки на
  статический файл. Необходима для принудительной чистки кэша браузеров
  пользователей. Не забывайте её менять после каждого обновления статики
  (если вы вообще включили её кэширование в настройках вашего веб-сервера).

* `LOCALSTATIC_ROOT = '/path/to/localstatic'` — путь к каталогу с вашей
  статикой. После добавления данного параметра файлы из этого каталога
  становятся доступны по пути `http://example.com/localstatic/` и
  появляется возожность вставлять ссылки в шаблонах через
  `{{ url_for('localstatic', filename='foo/bar.baz') }}`.

* `LOCALTEMPLATES = '/path/to/templates'` — каталог с шаблонами Jinja2.
  При добавлении этого параметра любой загружаемый шаблон ищется сперва в
  указанном вами каталоге, а если он не найден, то берётся шаблон по
  умолчанию. Скорее всего, вам потребуется лишь создать свой `base.html`,
  прописав в нём собственные шапку и CSS.

* `FAVICON_URL = '/localstatic/path/to/favicon.ico'` — ссылка на иконку
  сайта. Может как находиться в localstatic, так и вести на любой другой
  сайт.

* `DEFAULT_USERPIC = {...}` — ссылка на пользовательскую аватарку по
  умолчанию. Словарь; при наличии `endpoint` вызывается Flask-функция
  `url_for` с указанными в словаре аргументами, в противном случае берётся
  ссылка из ключа `url`.

Можно попробовать поискать архивы и репозитории с готовой конфигурацией
всего этого.
