Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tratamento de publicações patrocinadas no backend (API) #1719

Closed
wants to merge 9 commits into from

Conversation

Rafatcb
Copy link
Collaborator

@Rafatcb Rafatcb commented Jun 9, 2024

Mudanças realizadas

Implementação do backend para o issue #1491.

Endpoints

Novos endpoints:

  • POST /api/v1/sponsored_contents
  • GET /api/v1/sponsored_contents/[username]
  • PATCH /api/v1/sponsored_contents/[username]/[slug]

Endpoints com comportamento modificado para atender tanto publicações patrocinadas quanto conteúdos normais:

  • GET /api/v1/contents
  • GET /api/v1/contents/[username]/[slug]
  • PATCH /api/v1/contents/[username]/[slug]
  • POST /api/v1/contents/[username]/[slug]/tabcoins
  • GET /api/v1/contents/[username]/[slug]/thumbnail
  • GET /api/v1/contents/[username]/[slug]/children
  • GET /api/v1/contents/[username]/[slug]/parent
  • DELETE /api/v1/users/[username]

Características dessa implementação

Feature Ação
create:sponsored_content Criar uma publicação patrocinada
update:sponsored_content Editar uma publicação patrocinada própria
update:sponsored_content:others Editar uma publicação patrocinada de outra pessoa
  • Ao receber um voto, a publicação patrocinada tem 1 TabCash subtraído do seu estoque.
  • Para votar em uma publicação patrocinada, o usuário deve ter 2 TabCoins.
  • Ao votar em uma publicação patrocinada, o usuário que votou recebe 1 TabCoin.`
  • O usuário pode ter no máximo 1 publicação patrocinada simultaneamente.
  • Podem existir no máximo 5 publicações patrocinadas simultaneamente no TabNews.
  • Um usuário pode votar no máximo 1 vez em cada publicação patrocinada.
  • A "fórmula de conversão" da quantidade de TabCash disponível na publicação patrocinada para TabCoins (utilizado para o cálculo de score para o rank de Relevantes) é: tabcash ^ 0.6 / 1.6, que gera uma curva como ilustrada abaixo, onde alguns pontos para referência são:
TabCash investido Equivalente em TabCoins
20 3,77
100 9,9
200 15,0
500 26,02
1000 39,44

Gráfico conforme tabela acima

  • Cada hora em que a publicação patrocinada está ativa, perde o equivalente a 1 TabCoin no cálculo do score.
  • Cada voto que a publicação recebe, perde 1 TabCash e recebe (voto positivo) ou perde (voto negativo) 1 TabCoin.
  • Cada comentário que a publicação recebe, perde 1 TabCash.

Os limites de publicações patrocinadas simultaneamente que defini podem ser ampliados futuramente, mas acho importante algo mais restrito para termos um controle maior nessa fase inicial e entendermos o que funciona e o que não funciona.

Pendências/pontos de atenção

  • Validação deactivate_at && deactivate_at < new Date(): essa condição é utilizada em alguns lugares diferentes do código, mas não encontrei uma boa forma para encapsular isso melhor. Acredito que o ideal seria já tratar na própria query, mas fiquei em dúvida sobre como implementar sem complicar ainda mais as consultas no models/content.
  • Endpoint POST /api/v1/contents/[username]/[slug]/tabcoins: fiquei em dúvida se deveria retornar tabcoins_credit e tabcoins_debit com o valor 0 como é feito em alguns outros endpoints.
  • Cálculo de ranqueamento: o cálculo para exibir a publicação patrocinada na lista de Relevantes precisa de ajustes antes do merge. Creio que ficaria melhor limitar a quantidade por página, ou fazer algo parecido com os grupos (1..6) que temos para as publicações.
  • Desativar publicação patrocinada por tempo: não fiz uma publicação patrocinada ser desativada após determinado período porque não consegui pensar numa boa forma de consumir TabCash (na tabela sponsored_content_tabcash_operations) conforme o tempo passa, e acredito que quando isso for implementado, essa parte do cálculo poderá sair da consulta em rankingQueries.
  • Limite de votos por publicação: o limite de votos por publicação (1) está sendo verificado apenas pelo nome do usuário. Talvez faça sentido também criar um limite por IP, então estou apenas ressaltando isso para que outras pessoas possam opinar.
  • Custo de TabCash por comentário: talvez faça sentido considerar apenas comentários de pessoas diferentes, ou desconsiderar os comentários do próprio autor. Imagino que para o momento, todo comentário pode ter um custo para a publicação, visto que todos comentários são considerados no ranqueamento.
  • Índice em deactivate_at: experimentei criar um índice para o sponsored_contents.deactivate_at com pgm.createIndex('sponsored_contents', ['deactivate_at']), mas nos testes que fiz ele não era utilizado. As consultas envolvendo essa coluna são sempre WHERE deactivate_at IS NULL OR deactivate_at > NOW(), ou apenas WHERE deactivate_at IS NULL.

Tipo de mudança

  • Nova funcionalidade

Checklist:

  • As modificações não geram novos logs de erro ou aviso (warning).
  • Eu adicionei testes que provam que a correção ou novo recurso funciona conforme esperado.
  • Tanto os novos testes quanto os antigos estão passando localmente.

@Rafatcb Rafatcb added back Envolve modificações no backend novo recurso Nova funcionalidade/recurso labels Jun 9, 2024
Copy link

vercel bot commented Jun 9, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
tabnews ❌ Failed (Inspect) Jun 9, 2024 11:47pm

Copy link
Collaborator Author

@Rafatcb Rafatcb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deixei o PR em draft porque ainda há algumas pendências que gostaria da sugestão de como implementar. Além disso, estou deixando dois comentários aqui no código sobre pontos que não consegui resolver da forma ideal.

Como o PR ficou bem grande, já que é praticamente toda a funcionalidade na parte do backend, a revisão deve ser difícil e demorada. Apesar disso, cada commit representa certas funcionalidades, então talvez seja mais fácil revisar commit a commit. Se terem alguma sugestão para facilitar a revisão, ou dúvidas sobre o código, podem falar que eu respondo.

Comment on lines +512 to +527
sponsored_content: function () {
return Joi.object()
.required()
.concat(schemas.id())
.concat(schemas.owner_id())
.concat(schemas.slug())
.concat(schemas.title())
.concat(schemas.body())
.concat(schemas.source_url())
.concat(schemas.created_at())
.concat(schemas.updated_at())
.concat(schemas.published_at())
.concat(schemas.deactivate_at())
.concat(schemas.owner_username())
.concat(schemas.tabcoins());
},
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tentei algumas formas diferentes, mas não consegui fazer o required funcionar reaproveitando o schema com concat.

Comment on lines +317 to +323
if (feature === 'read:sponsored_content:list') {
filteredOutputValues = output.map((content) => {
return validator(content, {
sponsored_content_complete: 'required',
});
});
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Não consegui pensar numa boa forma para tratar esse retorno de modo que apenas o próprio usuário e “moderadores” possam ver essas informações completas, deixando o endpoint "desabilitado" para os outros usuários, ou pelo menos não retornando informações como tabcash.

O endpoint que utiliza este filtro é o GET /api/v1/sponsored_contents/[username].

@aprendendofelipe
Copy link
Collaborator

Massa @Rafatcb! 🤩🤩🤩

Mesmo antes de ver o código, já dá para comentar sobre o texto.

Endpoints com comportamento modificado para atender tanto publicações patrocinadas quanto conteúdos normais:

  • GET /api/v1/contents
  • GET /api/v1/contents/[username]/[slug]
  • PATCH /api/v1/contents/[username]/[slug]
  • POST /api/v1/contents/[username]/[slug]/tabcoins
  • GET /api/v1/contents/[username]/[slug]/thumbnail
  • GET /api/v1/contents/[username]/[slug]/children
  • GET /api/v1/contents/[username]/[slug]/parent
  • DELETE /api/v1/users/[username]

😱 Todos fazem sentido serem modificados? Não é melhor criar novos endpoints e deixar a funcionalidade mais desacoplada?

  • Podem existir no máximo 5 publicações patrocinadas simultaneamente no TabNews.

Faz sentido limitar a quantidade de publicações exibidas simultaneamente em uma página, mas não a quantidade de publicações que podem estar ativas no sistema.

A quantidade ativa deve ser grande para que a exibição seja otimizada, para dar mais visibilidade para quem contribuiu mais (quem tem mais TabCash). Essa otimização tem que considerar todo mundo que quiser publicar, não só os 5 primeiros que conseguirem.

  • A "fórmula de conversão" da quantidade de TabCash disponível na publicação patrocinada para TabCoins (utilizado para o cálculo de score para o rank de Relevantes) é: tabcash ^ 0.6 / 1.6, que gera uma curva como ilustrada abaixo, onde alguns pontos para referência são:

Publicidades devem ser classificadas entre as publicidades, e conteúdos entre conteúdos. O fato das publicidades aparecerem na lista de conteúdos não implica na existência de uma conversão entre os rankings.

  • Cada hora em que a publicação patrocinada está ativa, perde o equivalente a 1 TabCoin no cálculo do score.

Não precisa considerar o tempo. Podemos considerar apenas as exibições e interações, sendo que deve existir um peso bem maior nas interações do que nas exibições, o que nos permite começar considerando apenas as interações.

Limite de tempo pode ser apenas o que a pessoa que publicou definiu.

Os limites de publicações patrocinadas simultaneamente que defini podem ser ampliados futuramente, mas acho importante algo mais restrito para termos um controle maior nessa fase inicial e entendermos o que funciona e o que não funciona.

Com apenas 5 publicações ativas por vez, ficaria difícil tirar conclusões válidas, pois o acaso pesaria muito.

  • Limite de votos por publicação: o limite de votos por publicação (1) está sendo verificado apenas pelo nome do usuário. Talvez faça sentido também criar um limite por IP, então estou apenas ressaltando isso para que outras pessoas possam opinar.

Por IP pode manter a mesma regra atual.

  • Custo de TabCash por comentário: talvez faça sentido considerar apenas comentários de pessoas diferentes, ou desconsiderar os comentários do próprio autor. Imagino que para o momento, todo comentário pode ter um custo para a publicação, visto que todos comentários são considerados no ranqueamento.

O ranqueamento atual não conta todos os comentários, mas apenas 1 de cada diferente usuário.

Como o PR ficou bem grande, já que é praticamente toda a funcionalidade na parte do backend, a revisão deve ser difícil e demorada. Apesar disso, cada commit representa certas funcionalidades, então talvez seja mais fácil revisar commit a commit. Se terem alguma sugestão para facilitar a revisão, ou dúvidas sobre o código, podem falar que eu respondo.

Não tem nada que dê para isolar em PRs separados? Mesmo que seja algo que não vá ser utilizado antes da implementação completa, mas se tiver partes que não precisam de muito debate, ou que não dependam de muitas decisões sobre outras partes, pode facilitar a revisão.

@Rafatcb
Copy link
Collaborator Author

Rafatcb commented Jun 10, 2024

😱 Todos fazem sentido serem modificados? Não é melhor criar novos endpoints e deixar a funcionalidade mais desacoplada?

Modifiquei para que as funcionalidades de hoje continuem funcionando com as publicações patrocinadas. Por exemplo, um aplicativo hoje que utilize algumas dessas rotas para buscar a lista de publicações trará a patrocinada; ao entrar na publicação, permitirá buscar a lista de comentários; ao entrar no comentário, permitirá buscar o pai; ao editar, continuará funcionando para editar os valores em comum (slug, title, body) etc.

O ranqueamento atual não conta todos os comentários, mas apenas 1 de cada diferente usuário.

Interpretei errado, então.

Não tem nada que dê para isolar em PRs separados? Mesmo que seja algo que não vá ser utilizado antes da implementação completa, mas se tiver partes que não precisam de muito debate, ou que não dependam de muitas decisões sobre outras partes, pode facilitar a revisão.

Cada commit poderia ser um PR, com pouca ou nenhuma adaptação.

Mas algumas coisas seriam melhor avaliadas enxergando o todo, como no caso da migração criando as tabelas e funções (c497fa0), para para entender a necessidade de cada campo, tabela, índice e função. Por exemplo, só percebi a necessidade de balance_type em sponsored_content_tabcoin_operations ao tratar publicações patrocinadas no "nuke".

Separar complicaria um pouco. Se cada PR puder ter migrações junto, visto que é uma funcionalidade não liberada, complicaria menos do que criar em pares PR de Migração + PR de funcionalidade.

@Rafatcb
Copy link
Collaborator Author

Rafatcb commented Jun 15, 2024

Vou fechar este PR. Depois abrirei outros PRs conforme comentei, para colocarmos em produção a funcionalidade em pequenas partes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
back Envolve modificações no backend novo recurso Nova funcionalidade/recurso
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants