captain-fact
captain-fact copied to clipboard
Reputation log: better record inversed votes
Currently, voting down after voting up (or the opposite) just removes the old vote and replaces it with the new one, recording only one UserAction for the new vote. To make sure we register things properly with the reputation, we should record a revert_vote action before recording the new one.
Only the first part of the issue was solved in https://github.com/CaptainFact/captain-fact-api/pull/367, we still need a migration to fix the reputation for past entries.
defmodule :"Elixir.DB.Repo.Migrations.Add-missing-revert-vote-actions" do
use Ecto.Migration
def up do
Ecto.Adapters.SQL.query!(DB.Repo, """
WITH vote_actions AS (
SELECT user_id, entity, entity_id
FROM users_actions
WHERE entity IN (4,5) -- comments + sourced comments
AND "type" IN (9,10,12,13) -- Votes up/down AND their revert actions
AND entity_id IS NOT NULL -- TODO: See what TO DO WITH deleted comments
GROUP BY user_id, entity, entity_id
HAVING COUNT(*) > 1
) SELECT ua.*
FROM users_actions ua
INNER JOIN vote_actions va
ON va.user_id = ua.user_id
AND va.entity = ua.entity
AND va.entity_id = ua.entity_id
ORDER BY user_id ASC, entity ASC, entity_id ASC, inserted_at ASC, id ASC
""")
|> Map.get(:rows)
|> Enum.group_by(fn action -> "#{action.user_id}-#{action.entity}-#{action.entity_id}" end)
|> Enum.each(fn {_key, actions} -> analyze_actions(actions) end)
# TODO: Need to test with real data (local dev doesn't have occurences)
raise "abort"
end
def down do
# No rollack
end
defp analyze_actions([action | next_actions], prev_action = nil) do
check_action(action, prev_action)
analyze_actions(next_actions, action)
end
defp analyze_actions([], _prev_action = nil) do
nil
end
# Triggered when a vote up is found right after a vote down
defp check_action(action, prev_action)
when not is_nil(prev_action) and prev_action.type == 10 and action.type == 9 do
action.user_id
|> CF.Actions.ActionCreator.action_revert_vote(action.video_id, :revert_vote_down, comment)
|> Ecto.Changeset.change(inserted_at: DateTime.add(prev_action.inserted_at, 1, :microsecond))
|> DB.Repo.insert()
end
defp check_action(_action, _prev_action) do
nil
end
end