Management-команды

Запускаются через django-admin внутри контейнера:

docker compose run --rm web django-admin <команда> [аргументы]

или из shell контейнера (make shell) — ./manage.py <команда>.

Ниже — только команды портала. Штатные команды Django (migrate, createsuperuser, collectstatic, makemigrations, shell) работают как обычно.

initialize

django-admin initialize

Загружает начальные фикстуры: типы антенн, режимы модуляции и прочие справочники. Выполняется один раз при разворачивании новой базы. То же делает djangoctl.sh initialize.

fetch_data

django-admin fetch_data

Загружает спутники и передатчики из SatNOGS DB (DB_API_ENDPOINT). Ручной аналог периодической задачи fetch_data, которая идёт раз в час.

update_station_last_seen

django-admin update_station_last_seen <station_id> [<station_id> ...]

Обновляет отметку последнего появления для перечисленных станций. Принимает один или несколько числовых идентификаторов. Нужна, когда станцию надо вернуть на связь руками, не дожидаясь очередного обращения её клиента.

recalculate_observation_ratings

django-admin recalculate_observation_ratings          # предпросмотр
django-admin recalculate_observation_ratings --apply  # запись

Сбрасывает завышенные оценки «good», записанные багом в ветке data_upload функции rate_observation. По умолчанию — сухой прогон; запись только по --apply.

Команда намеренно консервативна: она трогает наблюдение, только если выполнены все условия — статус 100, есть демодулированные данные, режим передачи вниз CW или FM, и waterfall_status пуст (то есть водопад никто не вычитывал). Такие наблюдения возвращаются в 0 («unknown»), а не в «bad».

Полное описание проблемы и рассуждение о том, почему нельзя откатить точнее, — в Оценка наблюдений.

confirm_satellite_reentries

django-admin confirm_satellite_reentries          # предпросмотр
django-admin confirm_satellite_reentries --apply  # запись

То же решение, что принимает суточная задача с тем же именем: перевод подтверждённо сошедших аппаратов в статус re-entered с заполнением даты схода. По умолчанию — сухой прогон; запись только по --apply, --limit N ограничивает число аппаратов за прогон.

Предпросмотр печатает по строке на аппарат (номер NORAD, имя, сработавшее правило, дата, которая будет записана) и общее число будущих наблюдений этих спутников — команда их не трогает, решение снимать расписание остаётся за оператором.

Оба правила и порядок отката описаны в Спутники и передатчики.

release_precheck

django-admin release_precheck

Проверки данных перед обновлением портала — то, на чём упадут миграции релиза: дубли NORAD в синтетическом диапазоне 70000–100000 (0039 ставит на него уникальность), строки в таблицах входа через соцсети (0043 их удаляет и отказывается при непустых), и число неприменённых миграций. Ничего не пишет; при находках печатает их и завершается с ошибкой. Запускать до кнопки выкатки образом новой версии на базе прода:

docker run --rm --env-file .env --network <сеть compose> <образ новой версии> django-admin release_precheck

Что делать с находками — в CHANGELOG, раздел «Требует действий».

dump_satellite_descriptions

django-admin dump_satellite_descriptions > descriptions.json
django-admin dump_satellite_descriptions --limit 50 > batch.json

Выгружает описания спутников в JSON — исходящая половина обмена для перевода. Портал не подключает сервисов перевода: текст переводится снаружи и возвращается тем же файлом.

Отдаются те аппараты, у которых хотя бы одна из шести языковых колонок заполнена и хотя бы одна пуста, то есть ровно те, где есть с чего переводить и куда. Полностью пустой аппарат — не задача на перевод, полностью заполненный уже готов. --limit N режет выдачу для перевода порциями.

В stdout идёт только JSON и ничего больше, поэтому вывод можно перенаправлять в файл.

load_satellite_descriptions

django-admin load_satellite_descriptions descriptions.json          # предпросмотр
django-admin load_satellite_descriptions descriptions.json --apply  # запись
django-admin load_satellite_descriptions -                          # из stdin

Загружает переведённый JSON обратно. По умолчанию — сухой прогон; запись только по --apply.

Два правила решают, что будет записано:

  • поле, названное в locked_fields этого спутника, не трогается никогда — в этом и состоит смысл блокировки (см. Спутники и передатчики);

  • значение, совпадающее с уже записанным, записью не считается.

Отсюда идемпотентность: повторный прогон того же файла ничего не меняет, а выгрузка, пропущенная обратно через эту команду, — пустая операция. Спутник с неизвестным sat_id считается и пропускается, а не роняет загрузку: каталог живёт, и партия перевода недельной давности не должна падать целиком из-за одного слитого аппарата.

Отчёт печатает, сколько записано, сколько полей пропущено по блокировке и сколько аппаратов не нашлось в каталоге.

backfill_analytics

django-admin backfill_analytics [--force] [--since YYYY-MM-DD]

Считает суточные метрики аналитики за все прошедшие дни начиная с даты создания самой старой станции. Ставит задачу backfill_historical_metrics в очередь Celery и сразу возвращает управление — результата в выводе команды не будет, смотреть логи воркера.

Аргумент

Назначение

--force

Пересчитать дни, для которых метрики уже есть. Нужен после добавления новых полей метрик — иначе существующие строки останутся со значениями по умолчанию

--since

Дата начала в формате YYYY-MM-DD вместо даты создания старейшей станции

Подробнее — Аналитика.

copy_demoddata_content

django-admin copy_demoddata_content [--workers N]

Копирует файлы кадров и их payload_json из хранилища в таблицу DemodDataContent, откуда их отдаёт /api/demoddata/. Пока у кадра нет копии, API читает его файл, то есть делает GET в S3 на каждую строку страницы. Новые кадры получают копию сами (в задаче decode_current_frame), поэтому команда нужна один раз, для кадров, принятых до её появления.

Идёт от новых кадров к старым пачками по 1000: файлы пачки читают --workers потоков (по умолчанию 32), а в базу пачка пишется одним INSERT. Кадр, у которого уже есть копия, не трогается. Поэтому прерванный прогон можно запустить снова, и он продолжит с того места, где остановился. Кадр, файл которого не читается, записывается в лог и пропускается: API продолжает читать его из файла, как раньше.

Каждая пачка печатает прогресс: сколько скопировано и пропущено, последний id и скорость в кадрах в секунду. Для оценки места под базу остановите команду на первых ~100 тыс. кадрах и разделите pg_total_relation_size('base_demoddatacontent') на число строк в таблице.