Concluída a migração do vB3 para o Discourse, incluindo o Legacy vB3. Obrigado pelos Likes.

Acabei de concluir, na maior parte (ainda em ambiente de testes, mas as “partes difíceis” estão basicamente prontas e a migração funciona bem), a migração de um fórum vb3 de 15 anos, que era originalmente (creiam ou não) um fórum vb2 de 2000.

Este fórum existe há cerca de 20 anos (com código PHP personalizado inimaginável, tabelas, plugins, etc.), e, na minha opinião, o Discourse é o único software de fórum que é “digno” de um projeto de migração, que comecei há cerca de uma semana, indo da viabilidade à conclusão (basicamente).

Primeiramente, quero agradecer a toda a equipe do Discourse por uma obra de arte tão notável de fórum moderno. O Discourse é verdadeiramente notável, na minha opinião (e tenho certeza que de muitos outros).

Em segundo lugar, gostaria de agradecer à equipe que codificou o script de migração original vbulletin.rb. Esse script estava cerca de 95% correto. Modifiquei esse script para fazê-lo funcionar adequadamente para vb3. Por exemplo, o vb3 não possui uma tabela filedata como o vb4 (relacionada a anexos); então, modifiquei o vbulletin.rb para fazê-lo funcionar. Além disso, havia problemas ao importar fóruns vb3 filhos como categorias, mas modifiquei o código para importar todos os fóruns como categorias de nível superior e depois escrevi alguns comandos postgres psql para criar novas relações pai-filho entre categorias (ou configurei manualmente). Houve outras “pegadinhas” (uma história para outro dia), mas esses goblins tornaram tudo divertido e desafiador, mas não intransponível.

Em terceiro lugar, gostaria de agradecer ao Sam e ao seu código lithium.rb, onde usei sua rotina import_likes como base para minha migração de vb3 thank you (de um plugin legado, não parte do vB OOTB) para discourse likes. Fiz pequenas alterações na rotina import_likes e, após algumas horas de depuração, consegui fazê-la funcionar.

Como você pode ver nesta postagem de 2018, migrada hoje:

Achei importante que os usuários tivessem seus vb thanks migrados para discourse likes, e aqui está o código que usei para migrar os likes de outras almas perdidas de fóruns vb3:

Primeiro, criei uma tabela vb3 mysql chamada user_actions e escrevi um script PHP para preenchê-la a partir da tabela legada vb post_thanks, assim (não polido, mas funcionando):

ubuntu:/var/www/includes/cron# cat thanks_to_discourse.php
 <?php

/**************************************************
+-----------+------------+-----------+-----------+
| thanker   | unixtime   | id        | thanked   |
+-----------+------------+-----------+-----------+
|         1 | 1584149592 | 303045211 | 302093876 |
| 302153369 | 1584136706 | 303045214 | 302116191 |
| 302108573 | 1584128526 | 303045211 | 302093876 |
| 302153369 | 1584126659 | 303042175 | 302116191 |
| 302153369 | 1584126400 | 303045174 | 302116191 |
| 302153369 | 1584117711 | 303045184 |         1 |
|     37898 | 1584108187 | 303045175 |         1 |
| 302181242 | 1584106664 | 303045201 | 302122047 |
| 302181242 | 1584104642 | 303045074 | 302052697 |
| 302025710 | 1584103722 | 303045184 |         1 |
+-----------+------------+-----------+-----------+
 **************************************************/

$query = 'SELECT p.threadid AS threadid,t.userid AS thanker,t.date AS unixtime,t.postid AS postid,p.userid AS thanked from post_thanks as t LEFT JOIN post p ON p.postid = t.postid';

$allthanks = $vbulletin->db->query_read($query);

/****************************************
mysql> describe user_actions;
+-----------------+------------------+------+-----+---------+----------------+
| Field           | Type             | Null | Key | Default | Extra          |
+-----------------+------------------+------+-----+---------+----------------+
| id              | int(11)          | NO   | PRI | NULL    | auto_increment |
| action_type     | int(11) unsigned | NO   |     | NULL    |                |
| user_id         | int(11) unsigned | NO   |     | NULL    |                |
| target_topic_id | int(11) unsigned | YES  |     | NULL    |                |
| target_post_id  | varchar(16)      | YES  |     | NULL    |                |
| target_user_id  | int(11) unsigned | YES  |     | NULL    |                |
| acting_user_id  | int(11) unsigned | YES  |     | NULL    |                |
| created_at      | timestamp        | YES  |     | NULL    |                |
| updated_at      | timestamp        | YES  |     | NULL    |                |
+-----------------+------------------+------+-----+---------+----------------+
9 rows in set (0.00 sec)
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);
 *************************************/

$action_code = 2; // discourse like action == 2
$target_user_id = "";
while ($action = $vbulletin->db->fetch_array($allthanks)) {

    #code to deal with how discourse identifies the first post in a topic in the post_custom_fields table
    $query = 'SELECT firstpostid FROM thread WHERE threadid =' . $action['threadid'] . ' LIMIT 1';
    $threadinfo = $vbulletin->db->query_first($query);
    if ($threadinfo['firstpostid'] == $action['postid']) {
        $action['postid'] = 'thread-' . $action['threadid'];
    }

    $update = 'INSERT INTO user_actions (action_type, user_id, target_topic_id, target_post_id, target_user_id,acting_user_id, created_at, updated_at)' .
        ' VALUES (' . $action_code . ',' .
        '"' . $action['thanked'] . '",' .
        '"' . $action['threadid'] . '",' .
        '"' . $action['postid'] . '",' .
        '"' . $target_user_id . '",' .
        '"' . $action['thanker'] . '",' .
        'FROM_UNIXTIME(' . $action['unixtime'] . '),' .
        'FROM_UNIXTIME(' . $action['unixtime'] . '))';
    $doit = $vbulletin->db->query_write($update);
}

Assim, essa tabela mysql user_actions está incluída no dump de migração.

Aqui está a rotina import_likes modificada que usei para concluir a migração no lado do Discourse:

def import_likes
    puts "\nimporting likes..."

    # created mysql user_actions table in vb3 using PHP script and included that table with migration dump
    sql = "select acting_user_id as user_id, target_post_id as post_id, created_at from user_actions"
    results = mysql_query(sql)
    puts "length() method form : #{results.count}\n\n"

    puts "skip loading unique id map"
    existing_map = {}
    PostCustomField.where(name: 'import_id').pluck(:post_id, :value).each do |post_id, import_id|
      existing_map[import_id] = post_id
      #puts "postcustomfield existing_map post_id: #{post_id} import_id #{import_id}\n"
    end

    puts "loading data into temp table"

    #manually created the temp like_data table so I could check the table after session ends
    #DB.exec("create temp table like_data(user_id integer, post_id integer, created_at timestamp without time zone)")
    
    puts "like_data temp table created"
    PostAction.transaction do
      results.each do |result|

        result["user_id"] = user_id_from_imported_user_id(result["user_id"].to_s)
        result["post_id"] = existing_map[result["post_id"].to_s]

        next unless result["user_id"] && result["post_id"]

        puts "insert like table user_id: #{result["user_id"]} post_id #{result["post_id"]}\n"

        DB.exec("INSERT INTO like_data VALUES (:user_id,:post_id,:created_at)",
          user_id: result["user_id"],
          post_id: result["post_id"],
          created_at: result["created_at"]
        )

      end
    end


    puts "creating missing post actions"
    DB.exec <<~SQL

    INSERT INTO post_actions (post_id, user_id, post_action_type_id, created_at, updated_at)
             SELECT l.post_id, l.user_id, 2, l.created_at, l.created_at FROM like_data l
             LEFT JOIN post_actions a ON a.post_id = l.post_id AND l.user_id = a.user_id AND a.post_action_type_id = 2
             WHERE a.id IS NULL
    SQL

    puts "creating missing user actions"
    DB.exec <<~SQL
    INSERT INTO user_actions (user_id, action_type, target_topic_id, target_post_id, acting_user_id, created_at, updated_at)
             SELECT pa.user_id, 1, p.topic_id, p.id, pa.user_id, pa.created_at, pa.created_at
             FROM post_actions pa
             JOIN posts p ON p.id = pa.post_id
             LEFT JOIN user_actions ua ON action_type = 1 AND ua.target_post_id = pa.post_id AND ua.user_id = pa.user_id

             WHERE ua.id IS NULL AND pa.post_action_type_id = 2
    SQL

    # reverse action
    DB.exec <<~SQL
    INSERT INTO user_actions (user_id, action_type, target_topic_id, target_post_id, acting_user_id, created_at, updated_at)
             SELECT p.user_id, 2, p.topic_id, p.id, pa.user_id, pa.created_at, pa.created_at
             FROM post_actions pa
             JOIN posts p ON p.id = pa.post_id
             LEFT JOIN user_actions ua ON action_type = 2 AND ua.target_post_id = pa.post_id AND
                ua.acting_user_id = pa.user_id AND ua.user_id = p.user_id

             WHERE ua.id IS NULL AND pa.post_action_type_id = 2
    SQL
    puts "updating like counts on posts"

    DB.exec <<~SQL
        UPDATE posts SET like_count = coalesce(cnt,0)
                  FROM (
        SELECT post_id, count(*) cnt
        FROM post_actions
        WHERE post_action_type_id = 2 AND deleted_at IS NULL
        GROUP BY post_id
    ) x
    WHERE posts.like_count <> x.cnt AND posts.id = x.post_id

    SQL

    puts "updating like counts on topics"

    DB.exec <<-SQL
      UPDATE topics SET like_count = coalesce(cnt,0)
      FROM (
        SELECT topic_id, sum(like_count) cnt
        FROM posts
        WHERE deleted_at IS NULL
        GROUP BY topic_id
      ) x
      WHERE topics.like_count <> x.cnt AND topics.id = x.topic_id

    SQL
    end
end

Espero que esta postagem ajude outros que possam migrar de vB3 para o Discourse, já que muitas pessoas usaram o plugin legado vb3 “thank you” por muitos anos.

Além disso, passei muito tempo no banco de dados do Discourse e dentro de muitos scripts ruby do Discourse, então essa migração foi complicada (não é para os fracos de coração, com certeza), mas certamente viável.

Na verdade, gostei de fazer isso. Foi a primeira vez em ruby para mim; então foi divertido começar a aprender ruby e, como uma pessoa mysql de longa data, a parte do postgres foi em grande parte rotineira.

Entraremos no ar em breve… Obrigado, Equipe do Discourse!!!

Muito bem e obrigado por compartilhar!!

Obrigado, @codinghorror

Acabei de encontrar um pequeno problema, que vou resolver. A rotina import_likes não está transferindo os curtidas no primeiro post (o do autor do tópico), apenas nas respostas.

Vou ter que me dedicar e descobrir como corrigir isso, e depois postar a rotina import_likes atualizada.

Parece que fui um pouco precipitado ao postar, pois ainda há um pequeno problema a ser resolvido antes de poder declarar vitória.

Atualização:

Adicionei este código ao script PHP do vB3 para compensar a diferença entre como o vB3 e o Discourse tratam o primeiro post de um tópico/thread:

    <?php
    # Código vB3 para lidar com a forma como o Discourse identifica o primeiro post de um tópico na tabela post_custom_fields
    $query = 'SELECT firstpostid FROM thread WHERE threadid =' . $action['threadid'] . ' LIMIT 1';
    $threadinfo = $vbulletin->db->query_first($query);
    if ($threadinfo['firstpostid'] == $action['postid']) {
        $action['postid'] = 'thread-' . $action['threadid'];
    }

Agora, funciona como pretendido e os “agradecimentos dados” no vB3 são migrados para o Discourse, incluindo o primeiro post do tópico; ajustando a tabela de migração do vB3 para corresponder à tabela post_custom_fields do Discourse.

Estou fazendo o teste final agora, mas parece que funcionará com base nos testes preliminares.

Além disso, atualizei o código na minha postagem original, adicionando o fragmento PHP acima.

Li que vocês não estão migrando as senhas porque os hashes são diferentes. Talvez vocês queiram dar uma olhada no meu plugin de migração de senhas (aqui). Ele provavelmente já oferece suporte aos hashes do VB3 e, caso não ofereça, estou aberto a adicionar essa funcionalidade.

Obrigado, Michael,

Não vamos migrar as senhas e preferimos que os usuários alterem suas senhas (e troquem por senhas mais fortes e mais longas).

Agradecemos a oferta de ajuda, de qualquer forma! Muito apreciado.

Temos um pequeno problema com o qual talvez você possa nos ajudar.

Por algum motivo, o número total de curtidas dos usuários não está sendo atualizado corretamente nas visualizações dos perfis deles, mesmo que tudo o mais pareça estar em ordem. Tentei rastrear o problema, mas minhas tentativas não estão me dando os resultados que preciso.

Você conhece uma boa maneira de atualizar manualmente esses contadores de “curtidas” nos perfis?

Qual tarefa rake você usou? E o que está errado? Todos os números estão completamente errados, ou apenas para alguns usuários, ou…?

Oi.

Para começar, tentei o único rake que fazia sentido para mim (na época, já que o problema está relacionado a user_actions e post_actions); mas não surtiu efeito (na verdade, pensando bem, eu deveria ter lido esse rake primeiro para ver exatamente o que ele fazia, LOL):

rake user_actions:rebuild    

Até agora, francamente, ainda não consegui determinar o que dá errado no script de migração import_likes. Se eu soubesse exatamente o que deu errado e onde, poderia ter consertado (muitas vezes passo horas ou dias procurando um problema que, uma vez encontrado, leva apenas um minuto para ser corrigido… a essência da programação, LOL). Restabeleci a partir de um snapshot várias vezes e executei a rotina import_likes de algumas formas diferentes (com puts extras, etc.) e revisei as tabelas. Os resultados foram inconclusivos. Isso é agravado pela minha inexperiência em ruby e no Discourse em geral, pois ainda não sou fluente no backend. (No entanto, estou ficando melhor com begin... rescue ... end… haha, que se tornou uma das minhas novas ferramentas de depuração como um rubynube).

O que observei é que a tabela temporária like_data do Postgres (no lado do Discourse) está correta e contém todos os dados no formato adequado, e as entradas coincidem tanto com a tabela de migração do MySQL quanto com o site vB.

O processo parece falhar na criação das tabelas user_actions e talvez até post_actions, pois meus testes mostraram que os dados não são transferidos corretamente para essas tabelas-chave.

Por favor, note que a migração principal do vB3 está correta. Este problema está relacionado a um plugin legado de “agradecimentos” do vB3 que estou convertendo para curtidas do Discourse. Meu próximo passo, quando tiver tempo mais tarde hoje, espero, é sentar e tentar identificar exatamente onde o processo está falhando ao transferir os dados da tabela like_data, que está correta, para user_actions e post_actions.

Minha conclusão preliminar, com base nos testes de ontem, é que o rake em ruby não ajudará, pois a falha está no processo de atualização das tabelas user_actions ou post_actions; então preciso determinar exatamente onde e por que a falha ocorre. Pode ser algo simples como uma incompatibilidade de tipo no banco de dados ou algum outro pequeno problema no código.

Nosso site Discourse ainda não está no ar, então não é uma emergência e, como muitas pessoas aqui e em outros lugares, tarefas rotineiras como lidar com imigração ou bancos se tornaram mais do que um pouco problemáticas, para dizer o mínimo, e preciso ir à cidade hoje novamente para algumas tarefas não relacionadas ao Discourse. Preferiria estar explorando o Discourse e procurando por gremlins no código.

Executei o script de migração novamente (sem nenhuma alteração no script), incluindo o script personalizado de conversão de thanks do vb3 para likes do Discourse (import_likes), e parece estar tudo certo agora.

Vou tentar sincronizar o grande “D” com o último dump do banco de dados do vB e ver o que acontece!

Muito feliz com o Discourse… Obrigado novamente por esta obra de arte moderna de fórum. Ainda estou testando, mas entrará no ar em breve.

5,6 mil curtidas dadas… Isso é muito amor :heart:

Depois de converter todos os vb thanks para discourse likes, aqui está um exemplo de um tópico migrado de 2000, há vinte anos, mostrando os curtidas migradas no tópico:

… e já estou começando a esperar ansiosamente para aprender a escrever plugins legais… :slight_smile:

Muito feliz com o Discourse e com a equipe… e especialmente com begin .... rescue ...puts "olhem para mim".... end em ruby… que salva-vidas!

É verdade… cães velhos podem aprender truques novos se você jogar alguns ossos para eles :slight_smile:

Obrigado, EQUIPE Discourse!

Há 20 anos, começamos um fórum para usuários de Unix e Linux no vB2 e, cerca de 15 anos atrás, fizemos uma “atualização” para o vB3. Hoje marca uma grande mudança para usuários de Unix e Linux em todo o mundo e para nós, com a migração para o Discourse agora em funcionamento.

Muito obrigado por criar este software e torná-lo disponível a todos como um projeto de código aberto. Sua generosidade é muito apreciada. Na minha opinião (e tenho certeza que de inúmeros outros), o Discourse é, de longe, o melhor software de fórum do planeta em 2020.

Quanto a outros usuários legados do vB3 que desejam migrar para o Discourse, essa migração não é tão “fácil” quanto fiz parecer. A menos que você seja bem versado em programação e confortável com integração direta aos seus bancos de dados via linha de comando, recomendo considerar os serviços de alguns dos profissionais talentosos e ativos aqui no meta.discourse.org.

Obrigado novamente, Equipe Discourse!

Nota: Vou adicionar algumas lições aprendidas diversas neste post no novo site: