Skip to content

Commit

Permalink
Release (#364)
Browse files Browse the repository at this point in the history
* chore(deps): bump guardian_db from 2.0.3 to 2.1.0

Bumps [guardian_db](https://github.com/ueberauth/guardian_db) from 2.0.3 to 2.1.0.
- [Release notes](https://github.com/ueberauth/guardian_db/releases)
- [Changelog](https://github.com/ueberauth/guardian_db/blob/master/CHANGELOG.md)
- [Commits](ueberauth/guardian_db@v2.0.3...v2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

* chore(deps): bump atomex from 0.3.0 to 0.4.1

Bumps [atomex](https://github.com/Betree/atomex) from 0.3.0 to 0.4.1.
- [Release notes](https://github.com/Betree/atomex/releases)
- [Changelog](https://github.com/Betree/atomex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Betree/atomex/commits/0.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

* Updated readme status badges

* chore(deps): bump ecto_sql from 3.5.4 to 3.6.0

Bumps [ecto_sql](https://github.com/elixir-ecto/ecto_sql) from 3.5.4 to 3.6.0.
- [Release notes](https://github.com/elixir-ecto/ecto_sql/releases)
- [Changelog](https://github.com/elixir-ecto/ecto_sql/blob/master/CHANGELOG.md)
- [Commits](https://github.com/elixir-ecto/ecto_sql/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

* 10 statements out of videos (#309)

* Add graphql api to request (empty) statements

Statements are empty for now.

* Query statements

* Add rest API route to query comments through statement

This allow to decouple comments from video

* Add filters on Statement.query_list

* Add video field in graphql statement object

* Remove commented code

* Remove work on statement rest api

* fix(statements): iterate on feedback

* Add speaker_id filter to statements

* fix(StatementsQuery): usage with null + simplify

Co-authored-by: Benjamin Piouffle <benjamin@opencollective.com>

* Create Dependabot config file (#329)

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* feat: Add reputation changes to user actions (#350)

* fix(Actions): compute_reputation query (#363)

* Reputation changes in activity log (#366)

* feat: Reputation changes in activity log

* enhancement(ActivityLog): Ignore self actions in :target

* enhancement: Insert revert action when reversing vote (#367)

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: Wid-Ga毛l Destin <wid.destin@gmail.com>
Co-authored-by: vguen <v.guennou7@gmail.com>
  • Loading branch information
4 people committed Sep 29, 2021
1 parent 2784eb9 commit ab6a4ca
Show file tree
Hide file tree
Showing 22 changed files with 335 additions and 32 deletions.
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: mix
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<p align="center"><img src="https://avatars0.githubusercontent.com/u/28169525?s=200&v=4" height="100"/></p>
<h1 align="center"><a href="https://captainfact.io">CaptainFact.io</a></h1>
<p align="center"><a href="https://discord.gg/2Qd7hMz" title="Discord"><img src="https://discordapp.com/api/guilds/416782744748687361/widget.png" alt="Discord"></a>
<p align="center">
<a href="https://discord.gg/2Qd7hMz" title="Discord"><img src="https://discordapp.com/api/guilds/416782744748687361/widget.png" alt="Discord"></a>
<a href="https://twitter.com/CaptainFact_io" title="Twitter"><img src="https://img.shields.io/twitter/follow/CaptainFact_io.svg?style=social&label=Follow"></a>
<a href="https://opencollective.com/captainfact_io" title="Backers on Open Collective"><img src="https://opencollective.com/captainfact_io/backers/badge.svg"></a>
</p>
<p align="center">
<a href="./LICENSE"><img src="https://img.shields.io/github/license/CaptainFact/captain-fact-api.svg" alt="AGPL3"></a>
<a href="https://travis-ci.com/CaptainFact/captain-fact-api"><img src="https://travis-ci.com/CaptainFact/captain-fact-api.svg?branch=staging"></a>
<a href="https://github.com/CaptainFact/captain-fact-api/releases"><img src="https://img.shields.io/github/v/release/CaptainFact/captain-fact-api" alt="GitHub release" /></a>
<a href="https://github.com/CaptainFact/captain-fact-api/actions/workflows/ci.yml"><img src="https://github.com/CaptainFact/captain-fact-api/actions/workflows/ci.yml/badge.svg?branch=staging" alt='CI Status' /></a>
<a href='https://coveralls.io/github/CaptainFact/captain-fact-api?branch=staging'><img src='https://coveralls.io/repos/github/CaptainFact/captain-fact-api/badge.svg?branch=staging' alt='Coverage Status' /></a>
</p>
<hr/>
Expand Down
15 changes: 13 additions & 2 deletions apps/cf/lib/actions/action_creator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ defmodule CF.Actions.ActionCreator do
alias DB.Schema.Speaker
alias DB.Schema.Statement
alias DB.Schema.Comment
alias CF.Actions.ReputationChange

# Create

Expand Down Expand Up @@ -226,12 +227,17 @@ defmodule CF.Actions.ActionCreator do
creators like `action_create` or `action_delete`.
"""
def action(user_id, entity, action_type, params \\ []) do
{author_reputation_change, target_reputation_change} =
ReputationChange.for_action(action_type, entity)

UserAction.changeset(
%UserAction{},
Enum.into(params, %{
user_id: user_id,
type: action_type,
entity: entity
entity: entity,
author_reputation_change: author_reputation_change,
target_reputation_change: target_reputation_change
})
)
end
Expand All @@ -241,11 +247,16 @@ defmodule CF.Actions.ActionCreator do
creators like `action_admin_delete`.
"""
def admin_action(entity, action_type, params \\ []) do
{author_reputation_change, target_reputation_change} =
ReputationChange.for_action(action_type, entity)

UserAction.changeset_admin(
%UserAction{},
Enum.into(params, %{
type: action_type,
entity: entity
entity: entity,
author_reputation_change: author_reputation_change,
target_reputation_change: target_reputation_change
})
)
end
Expand Down
19 changes: 19 additions & 0 deletions apps/cf/lib/actions/actions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,25 @@ defmodule CF.Actions do
|> where([a], a.inserted_at <= ^datetime_end)
end

@spec compute_reputation!(%User{}) :: integer
def compute_reputation!(user) do
DB.Schema.UserAction
|> where([a], a.user_id == ^user.id or a.target_user_id == ^user.id)
|> select(
[a],
coalesce(
sum(
fragment(
"CASE WHEN user_id = ? THEN author_reputation_change ELSE target_reputation_change END",
^user.id
)
),
0
)
)
|> DB.Repo.one!()
end

# ---- Private methods ----

defp age_filter(query, -1),
Expand Down
65 changes: 58 additions & 7 deletions apps/cf/lib/actions/reputation_change.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ defmodule CF.Actions.ReputationChange do
Calculate reputation changes.
"""

import Ecto.Query
alias DB.Schema.{User, UserAction}
alias CF.Actions
alias CF.Actions.ReputationChangeConfigLoader
alias CF.Actions.ReputationChange

# Reputation changes definition
# @external_resource specify the file dependency to compiler
Expand All @@ -26,19 +28,20 @@ defmodule CF.Actions.ReputationChange do
for given action type / entity.
"""
def for_action(%UserAction{type: type, entity: entity}) do
case Map.get(@actions, type) do
nil -> {0, 0}
res when is_map(res) -> Map.get(res, entity) || {0, 0}
res when is_tuple(res) -> res
end
for_action(type, entity)
end

def for_action(type) when is_atom(type),
do: Map.get(@actions, type) || {0, 0}

@spec for_action(atom(), atom()) :: any()
def for_action(type, entity) when is_atom(type) and is_atom(entity),
do: get_in(@actions, [type, entity]) || {0, 0}
def for_action(type, entity) when is_atom(type) and is_atom(entity) do
case Map.get(@actions, type) do
nil -> {0, 0}
res when is_map(res) -> Map.get(res, entity) || {0, 0}
res when is_tuple(res) -> res
end
end

@doc """
Get reputation change as an integer for admin action (email confirmed, abusive
Expand Down Expand Up @@ -95,7 +98,55 @@ defmodule CF.Actions.ReputationChange do
"""
def actions_types, do: @actions_types

@doc """
Returns the full map of actions <> reputation changes
"""
def actions, do: @actions

def daily_gain_limit, do: @daily_gain_limit

def daily_loss_limit, do: @daily_loss_limit

def update_all_actions_reputations do
Enum.each(ReputationChange.actions(), &update_for_actions_entry/1)
end

defp update_for_actions_entry(
{action_type, {author_reputation_change, target_reputation_change}}
) do
DB.Schema.UserAction
|> where([a], a.type == ^dump_action_type!(action_type))
|> DB.Repo.update_all(
set: [
author_reputation_change: author_reputation_change,
target_reputation_change: target_reputation_change
]
)
end

defp update_for_actions_entry({action_type, details}) when is_map(details) do
Enum.each(details, fn {entity, {author_reputation_change, target_reputation_change}} ->
DB.Schema.UserAction
|> where(
[a],
a.type == ^dump_action_type!(action_type) and a.entity == ^dump_entity_type!(entity)
)
|> DB.Repo.update_all(
set: [
author_reputation_change: author_reputation_change,
target_reputation_change: target_reputation_change
]
)
end)
end

defp dump_action_type!(action_type) do
{:ok, action_type_id} = DB.Type.UserActionType.dump(action_type)
action_type_id
end

defp dump_entity_type!(entity) do
{:ok, entity_id} = DB.Type.Entity.dump(entity)
entity_id
end
end
16 changes: 13 additions & 3 deletions apps/cf/lib/comments/comments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,25 @@ defmodule CF.Comments do

# Delete prev directly vote if any (without logging a delete action)
Multi.new()
|> Multi.delete_all(:prev_vote, Vote.user_comment_vote(user, comment), returning: [:value])
|> Multi.delete_all(:prev_votes, Vote.user_comment_vote(user, comment), returning: [:value])
|> Multi.run(:revert_vote, fn _repo, %{prev_votes: {_count, prev_votes}} ->
unless Enum.empty?(prev_votes) do
prev_vote = List.first(prev_votes)
prev_vote_type = Vote.vote_type(user, comment, prev_vote.value)
revert_vote_type = reverse_vote_type(prev_vote_type)
DB.Repo.insert(action_revert_vote(user.id, video_id, revert_vote_type, comment))
else
{:ok, nil}
end
end)
|> Multi.insert(:vote, Vote.changeset_new(user, comment, value))
|> Multi.insert(:vote_action, action_vote(user.id, video_id, vote_type, comment))
|> Repo.transaction()
|> case do
{:ok, %{vote: vote, prev_vote: {0, []}}} ->
{:ok, %{vote: vote, prev_votes: {0, []}}} ->
{:ok, comment, %{vote | comment_id: comment.id, comment: comment}, 0}

{:ok, %{vote: vote, prev_vote: {_, [%{value: prev_value}]}}} ->
{:ok, %{vote: vote, prev_votes: {_, [%{value: prev_value}]}}} ->
{:ok, comment, %{vote | comment_id: comment.id, comment: comment}, prev_value}

{:error, _, reason, _} ->
Expand Down
2 changes: 1 addition & 1 deletion apps/cf/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ defmodule CF.Mixfile do
{:bcrypt_elixir, "~> 1.0"},
{:guardian, "~> 2.0.0"},
{:guardian_phoenix, "~> 2.0.0"},
{:guardian_db, "~> 2.0.0"},
{:guardian_db, "~> 2.1.0"},
{:floki, "~> 0.20.2"},
{:html_entities, "~> 0.3"},
{:httpoison, "~> 0.11"},
Expand Down
22 changes: 22 additions & 0 deletions apps/cf_graphql/lib/resolvers/statements.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule CF.Graphql.Resolvers.Statements do
@moduledoc """
Resolver for `DB.Schema.Statement`
"""

alias Kaur.Result

import Ecto.Query
import Absinthe.Resolution.Helpers, only: [batch: 3]

alias DB.Repo
alias DB.Schema.Statement

# Queries

def paginated_list(_root, args = %{offset: offset, limit: limit}, _info) do
Statement
|> Statement.query_list(Map.get(args, :filters, []))
|> Repo.paginate(page: offset, page_size: limit)
|> Result.ok()
end
end
20 changes: 15 additions & 5 deletions apps/cf_graphql/lib/resolvers/users.ex
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,21 @@ defmodule CF.Graphql.Resolvers.Users do
end

@watched_entities ~w(video speaker statement comment fact)a
@action_banned [
@watched_actions [
:action_banned_bad_language,
:action_banned_spam,
:action_banned_irrelevant,
:action_banned_not_constructive
:action_banned_not_constructive,
:email_confirmed
]

@doc """
Resolve user actions history
"""
def activity_log(user, %{offset: offset, limit: limit}, _) do
def activity_log(user, %{offset: offset, limit: limit, direction: direction}, _) do
UserAction
|> where([a], a.user_id == ^user.id and a.entity in ^@watched_entities)
|> or_where([a], a.target_user_id == ^user.id and a.type in ^@action_banned)
|> where([a], a.entity in ^@watched_entities or a.type in ^@watched_actions)
|> filter_by_user_action_direction(user, direction)
|> DB.Query.order_by_last_inserted_desc()
|> Repo.paginate(page: offset, page_size: limit)
|> Result.ok()
Expand All @@ -94,4 +95,13 @@ defmodule CF.Graphql.Resolvers.Users do
def videos_added(user, %{offset: offset, limit: limit}, _) do
{:ok, CF.Videos.added_by_user(user, page: offset, page_size: limit)}
end

defp filter_by_user_action_direction(query, user, direction) when direction == :all,
do: where(query, [a], a.user_id == ^user.id or a.target_user_id == ^user.id)

defp filter_by_user_action_direction(query, user, direction) when direction == :author,
do: where(query, [a], a.user_id == ^user.id)

defp filter_by_user_action_direction(query, user, direction) when direction == :target,
do: where(query, [a], a.target_user_id == ^user.id and a.user_id != ^user.id)
end
3 changes: 2 additions & 1 deletion apps/cf_graphql/lib/schema/input_objects.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule CF.Graphql.Schema.InputObjects do
use Absinthe.Schema.Notation

import_types(CF.Graphql.Schema.InputObjects.{
VideoFilter
VideoFilter,
StatementFilter
})
end
14 changes: 14 additions & 0 deletions apps/cf_graphql/lib/schema/input_objects/statement_filter.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
defmodule CF.Graphql.Schema.InputObjects.StatementFilter do
@moduledoc """
Represent the possible filters to apply to statement.
"""

use Absinthe.Schema.Notation
use Absinthe.Ecto, repo: DB.Repo

@desc "Props to filter statements on"
input_object :statement_filter do
field(:commented, :boolean)
field(:speaker_id, :id)
end
end
8 changes: 8 additions & 0 deletions apps/cf_graphql/lib/schema/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ defmodule CF.Graphql.Schema do
resolve(&Resolvers.Videos.get/3)
end

@desc "Get all statements"
field :statements, :paginated_statements do
arg(:filters, :statement_filter)
arg(:offset, :integer, default_value: 1)
arg(:limit, :integer, default_value: 10)
resolve(&Resolvers.Statements.paginated_list/3)
end

@desc "Get user public info"
field :user, :user do
arg(:id, :id)
Expand Down
14 changes: 13 additions & 1 deletion apps/cf_graphql/lib/schema/types/statement.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ defmodule CF.Graphql.Schema.Types.Statement do
use Absinthe.Ecto, repo: DB.Repo
import CF.Graphql.Schema.Utils

import_types(CF.Graphql.Schema.Types.{Speaker, Comment})
import_types(CF.Graphql.Schema.Types.{Paginated, Speaker, Comment})

@desc "A transcript or a description of the picture"
object :statement do
Expand All @@ -27,5 +27,17 @@ defmodule CF.Graphql.Schema.Types.Statement do
resolve(assoc(:comments))
complexity(join_complexity())
end

@desc "The video associated with this statement"
field :video, :video do
resolve(assoc(:video))
complexity(join_complexity())
end
end

@desc "A list a paginated statements"
object :paginated_statements do
import_fields(:paginated)
field(:entries, list_of(:statement))
end
end
2 changes: 2 additions & 0 deletions apps/cf_graphql/lib/schema/types/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ defmodule CF.Graphql.Schema.Types.User do
import_types(CF.Graphql.Schema.Types.Notification)

enum(:notifications_filter, values: [:all, :seen, :unseen])
enum(:activity_log_direction, values: [:all, :author, :target])

@desc "A user registered on the website"
object :user do
Expand Down Expand Up @@ -49,6 +50,7 @@ defmodule CF.Graphql.Schema.Types.User do
complexity(join_complexity())
arg(:offset, :integer, default_value: 1)
arg(:limit, :integer, default_value: 10)
arg(:direction, :activity_log_direction, default_value: :author)
resolve(&Resolvers.Users.activity_log/3)
end

Expand Down
Loading

0 comments on commit ab6a4ca

Please sign in to comment.