vas3k / vas3k.club

No bullshit IT community with private membership
https://vas3k.club
MIT License
805 stars 241 forks source link

Импорт публичных постов из основного клуба в dev/local среду #1214

Closed trin4ik closed 2 months ago

trin4ik commented 2 months ago

Работа над ошибками:

  1. мелкие правки по логике
  2. отлавливаем отредактированные посты
  3. перенёс из authn/management/commands в posts/management/commands

Публикую как драфт, хочу добавить ещё флаг --with-comments, для поиска он пригодится.

trin4ik commented 2 months ago

Вроде готово. Из сделанного:

Парсинг приватных и комментов сделал через апки (https://vas3k.club/apps/). Учитывая, как часто последнее время меняется вёрстка комментов, идея с парсингом html страниц быстро отпала. С другой стороны, разрабу нужно один раз зарегать аппку, указать токен при запуске команды и вуаля, более подробные данные о пользователях, приватные посты и комменты сразу доступны. Просто и безопасно. Столкнулся с тем, что иногда комменты имеют какой-то глюк с created_at. Когда дата создания ответа почему-то раньше, чем дата создания основного комментария. Это вызывает ошибку foreign key, т.к. пытаемся создать ответ на коммент, которого ещё не существует. Не стал долго разбираться, просто воткнул

connection = connections['default']
with connection.cursor() as cursor:
    cursor.execute('SET session_replication_role TO \'replica\';')

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

Готово 🌮
📄 Новых постов: 203
📌 Уже существовало: 21
📝 Отредактировано: 0
👤 Новых пользователей: 66
💬 Новых комментов: 12954

image


Теперь появилась другая проблема, пока тестил, постоянно путался, где страница клуба, а где локальная версия.

Zolotorevich commented 2 months ago
  1. для скорости: замени urllib на requests /пакет уже в проекте/ и создавай запросы с одной сессией.

  2. service_token запиши в .env, чтоб не вводить каждый раз /см. dotenv в settings проекта/

  3. Насчёт времени комментов: они отличаются на равное количество часов? Или бывает что на пару минут? Проект не юзает враппер Django для дат, возможно, в этом проблема.

  4. Не надо так:

def parse_comments(post_id, url):
    for comment in data['comments']:
         if not Comment.objects.filter(id=comment['id']).exists():
             create_user(comment['author'])

             comments.append(Comment(
                 ...
             ))

     Comment.objects.bulk_create(comments, 100)

Не делай запросы к базе внутри циклов, т.к. Django лениво подгружает данные и насилует её на каждой итерации.

У метода bulk_create доступна опция "ignore_conflicts". С ней не нужно проверять существует ли Comment — база сама проигнорирует дубликаты: Comment.objects.bulk_create(comments, ignore_conflicts=True, batch_size=100)

см: https://docs.djangoproject.com/en/5.0/ref/models/querysets/#bulk-create

Zolotorevich commented 2 months ago

Теперь появилась другая проблема, пока тестил, постоянно путался, где страница клуба, а где локальная версия.

Специально для тебя написал расширение для Хрома. Не благодари :-) vitalik_eto_dev.zip

Screenshot 2024-07-10 at 02 27 37

trin4ik commented 2 months ago

Какие люди )

  1. для скорости: замени urllib на requests /пакет уже в проекте/ и создавай запросы с одной сессией.

Ага, надо будет переделать. На самом деле нет задачи сильно ускориться, т.к. в таком случае можно упереться в лимит запросов.

  1. service_token запиши в .env, чтоб не вводить каждый раз /см. dotenv в settings проекта/

Тоже как варик ага, проверять dotenv параллельно с аргументами, однако если у тебя несколько веток, то это условно бесполезно. Т.е. какая разница, что создавать .env, что прокинуть в параметры. Если же контрибьютор работает в основном с одной версией проекта, т.е. физически с одной директорией, то смыл в этом есть.

  1. Насчёт времени комментов: они отличаются на равное количество часов? Или бывает что на пару минут? Проект не юзает враппер Django для дат, возможно, в этом проблема.

Если это про глюк времени, я думаю это связано с удалёнными комментами. Почему именно так происходит разбираться лениво, да и в целом решение со временным отключением foreign keys вполне норм.

  1. Не надо так:
def parse_comments(post_id, url):
    for comment in data['comments']:
         if not Comment.objects.filter(id=comment['id']).exists():
             create_user(comment['author'])

             comments.append(Comment(
                 ...
             ))

     Comment.objects.bulk_create(comments, 100)

Не делай запросы к базе внутри циклов, т.к. Django лениво подгружает данные и насилует её на каждой итерации.

Про ignore_conflicts в тему, надо поправить. Но в целом точечные запросы по первичному индексу в локальной базе на пару тысяч записей -- это не проблема. Т.е. в рамках данной задачи, зная что код будет запускаться только в dev/local у разраба с пустой базой, можно не обращать внимание на подобное.

Zolotorevich commented 2 months ago

Да-да, я теперь в секте освободивших Джнаго)

  1. Код парсера из одного моего проекта. Удалил лишнее, оставил только запрос страниц: parser.py.zip

Добавь import requests, скопируй всё из класса Parser в свой класс и используй:

try:
    # Request page
    page = self.get_page('http://example.com/api/v1/')

    # Do what you need with result

except ConnectionError as error:
    # Handle errors, e.g.:
    # return self.stdout.write(self.style.ERROR(error))

В лимит едва ли упрёшься, для этого как минимум нужны асинхронные запросы)

Т.е. какая разница, что создавать .env, что прокинуть в параметры.

  1. Дело не только в удобстве: с API ключами нужно обращаться как с паролями, ты это лучше меня понимаешь) Сейчас консоль их логирует в чистом виде, а ещё Джанго может отправить на сервер-сборщик логов /он подключён в проекте/.

Если это про глюк времени, я думаю это связано с удалёнными комментами. Почему именно так происходит разбираться лениво, да и в целом решение со временным отключением foreign keys вполне норм.

  1. Глянул модель комментов, она при удалении ставит флаг и обновляет время updated_at. Время created_at ставится только при создании. Может API выдаёт дату изменения, а не создания?..

  2. В рамках задачи — не спорю, не критично. Но теперь ты знаешь про ошибку и будешь писать код чуточку лучше)