diff --git a/lib/philomena/image_faves.ex b/lib/philomena/image_faves.ex index 1869dc63..615247f1 100644 --- a/lib/philomena/image_faves.ex +++ b/lib/philomena/image_faves.ex @@ -6,8 +6,9 @@ defmodule Philomena.ImageFaves do import Ecto.Query, warn: false alias Ecto.Multi - alias Philomena.Images.Image alias Philomena.ImageFaves.ImageFave + alias Philomena.UserStatistics + alias Philomena.Images.Image @doc """ Creates a image_hide. @@ -25,6 +26,9 @@ defmodule Philomena.ImageFaves do Multi.new |> Multi.insert(:fave, fave) |> Multi.update_all(:inc_faves_count, image_query, inc: [faves_count: 1]) + |> Multi.run(:inc_fave_stat, fn _repo, _changes -> + UserStatistics.inc_stat(user, :images_favourited, 1) + end) end @doc """ @@ -48,6 +52,8 @@ defmodule Philomena.ImageFaves do image_query |> repo.update_all(inc: [faves_count: -faves]) + UserStatistics.inc_stat(user, :images_favourited, -faves) + {:ok, count} end) end diff --git a/lib/philomena/image_votes.ex b/lib/philomena/image_votes.ex index 95f38c4a..7998565e 100644 --- a/lib/philomena/image_votes.ex +++ b/lib/philomena/image_votes.ex @@ -6,8 +6,9 @@ defmodule Philomena.ImageVotes do import Ecto.Query, warn: false alias Ecto.Multi - alias Philomena.Images.Image alias Philomena.ImageVotes.ImageVote + alias Philomena.UserStatistics + alias Philomena.Images.Image @doc """ Creates a image_vote. @@ -28,6 +29,9 @@ defmodule Philomena.ImageVotes do Multi.new |> Multi.insert(:vote, vote) |> Multi.update_all(:inc_vote_count, image_query, inc: [upvotes_count: upvotes, downvotes_count: downvotes, score: upvotes - downvotes]) + |> Multi.run(:inc_vote_stat, fn _repo, _changes -> + UserStatistics.inc_stat(user, :votes_cast, 1) + end) end @doc """ @@ -59,6 +63,8 @@ defmodule Philomena.ImageVotes do image_query |> repo.update_all(inc: [upvotes_count: -upvotes, downvotes_count: -downvotes, score: downvotes - upvotes]) + UserStatistics.inc_stat(user, :votes_cast, -(upvotes + downvotes)) + {:ok, count} end) end diff --git a/lib/philomena/user_statistics.ex b/lib/philomena/user_statistics.ex index a476a689..6f32ec19 100644 --- a/lib/philomena/user_statistics.ex +++ b/lib/philomena/user_statistics.ex @@ -7,53 +7,7 @@ defmodule Philomena.UserStatistics do alias Philomena.Repo alias Philomena.UserStatistics.UserStatistic - - @doc """ - Returns the list of user_statistics. - - ## Examples - - iex> list_user_statistics() - [%UserStatistic{}, ...] - - """ - def list_user_statistics do - Repo.all(UserStatistic) - end - - @doc """ - Gets a single user_statistic. - - Raises `Ecto.NoResultsError` if the User statistic does not exist. - - ## Examples - - iex> get_user_statistic!(123) - %UserStatistic{} - - iex> get_user_statistic!(456) - ** (Ecto.NoResultsError) - - """ - def get_user_statistic!(id), do: Repo.get!(UserStatistic, id) - - @doc """ - Creates a user_statistic. - - ## Examples - - iex> create_user_statistic(%{field: value}) - {:ok, %UserStatistic{}} - - iex> create_user_statistic(%{field: bad_value}) - {:error, %Ecto.Changeset{}} - - """ - def create_user_statistic(attrs \\ %{}) do - %UserStatistic{} - |> UserStatistic.changeset(attrs) - |> Repo.insert() - end + alias Philomena.Users.User @doc """ Updates a user_statistic. @@ -67,38 +21,29 @@ defmodule Philomena.UserStatistics do {:error, %Ecto.Changeset{}} """ - def update_user_statistic(%UserStatistic{} = user_statistic, attrs) do - user_statistic - |> UserStatistic.changeset(attrs) - |> Repo.update() - end + def inc_stat(user, action, amount \\ 1) - @doc """ - Deletes a UserStatistic. + def inc_stat(nil, _action, _amount), do: {:ok, nil} + def inc_stat(%{id: user_id}, action, amount) + when action in [:uploads, :images_favourited, :comments_posted, :votes_cast, :metadata_updates, :forum_posts] + do + now = + DateTime.utc_now() + |> DateTime.to_unix(:second) + |> div(86400) - ## Examples + user = User |> where(id: ^user_id) + action_count = :"#{action}_count" - iex> delete_user_statistic(user_statistic) - {:ok, %UserStatistic{}} + run = fn -> + Repo.update_all(user, inc: [{action_count, amount}]) + Repo.insert( + Map.put(%UserStatistic{day: now, user_id: user_id}, action, amount), + on_conflict: [inc: [{action, amount}]], + conflict_target: [:day, :user_id] + ) + end - iex> delete_user_statistic(user_statistic) - {:error, %Ecto.Changeset{}} - - """ - def delete_user_statistic(%UserStatistic{} = user_statistic) do - Repo.delete(user_statistic) - end - - @doc """ - Returns an `%Ecto.Changeset{}` for tracking user_statistic changes. - - ## Examples - - iex> change_user_statistic(user_statistic) - %Ecto.Changeset{source: %UserStatistic{}} - - """ - def change_user_statistic(%UserStatistic{} = user_statistic) do - UserStatistic.changeset(user_statistic, %{}) + Repo.transaction(run) end end diff --git a/lib/philomena/user_statistics/user_statistic.ex b/lib/philomena/user_statistics/user_statistic.ex index c0dbeda4..27af1b68 100644 --- a/lib/philomena/user_statistics/user_statistic.ex +++ b/lib/philomena/user_statistics/user_statistic.ex @@ -4,6 +4,7 @@ defmodule Philomena.UserStatistics.UserStatistic do alias Philomena.Users.User + # fixme: rekey this on (user_id, day) schema "user_statistics" do belongs_to :user, User field :day, :integer, default: 0 @@ -18,7 +19,9 @@ defmodule Philomena.UserStatistics.UserStatistic do @doc false def changeset(user_statistic, attrs) do user_statistic - |> cast(attrs, []) - |> validate_required([]) + |> cast(attrs, [ + :uploads, :votes_cast, :comments_posted, :metadata_updates, + :images_favourited, :forum_posts + ]) end end diff --git a/lib/philomena_web/controllers/image/comment_controller.ex b/lib/philomena_web/controllers/image/comment_controller.ex index 661aadb7..a0e5184e 100644 --- a/lib/philomena_web/controllers/image/comment_controller.ex +++ b/lib/philomena_web/controllers/image/comment_controller.ex @@ -2,6 +2,7 @@ defmodule PhilomenaWeb.Image.CommentController do use PhilomenaWeb, :controller alias Philomena.{Images.Image, Comments.Comment, Textile.Renderer} + alias Philomena.UserStatistics alias Philomena.Comments alias Philomena.Images alias Philomena.Repo @@ -69,6 +70,7 @@ defmodule PhilomenaWeb.Image.CommentController do Comments.notify_comment(comment) Comments.reindex_comment(comment) Images.reindex_image(conn.assigns.image) + UserStatistics.inc_stat(conn.assigns.current_user, :comments_posted) conn |> put_flash(:info, "Comment created successfully.") diff --git a/lib/philomena_web/controllers/image/fave_controller.ex b/lib/philomena_web/controllers/image/fave_controller.ex index c36eab8b..af707f73 100644 --- a/lib/philomena_web/controllers/image/fave_controller.ex +++ b/lib/philomena_web/controllers/image/fave_controller.ex @@ -20,7 +20,7 @@ defmodule PhilomenaWeb.Image.FaveController do ) |> Multi.append(ImageVotes.delete_vote_transaction(image, user)) |> Multi.append(ImageVotes.create_vote_transaction(image, user, true)) - |> Repo.transaction() + |> Repo.isolated_transaction(:serializable) |> case do {:ok, _result} -> image = @@ -42,7 +42,7 @@ defmodule PhilomenaWeb.Image.FaveController do image = conn.assigns.image ImageFaves.delete_fave_transaction(image, user) - |> Repo.transaction() + |> Repo.isolated_transaction(:serializable) |> case do {:ok, _result} -> image = diff --git a/lib/philomena_web/controllers/image/source_controller.ex b/lib/philomena_web/controllers/image/source_controller.ex index 60b6146f..97351656 100644 --- a/lib/philomena_web/controllers/image/source_controller.ex +++ b/lib/philomena_web/controllers/image/source_controller.ex @@ -1,8 +1,9 @@ defmodule PhilomenaWeb.Image.SourceController do use PhilomenaWeb, :controller - alias Philomena.Images + alias Philomena.UserStatistics alias Philomena.Images.Image + alias Philomena.Images plug PhilomenaWeb.FilterBannedUsersPlug plug PhilomenaWeb.CaptchaPlug @@ -19,6 +20,8 @@ defmodule PhilomenaWeb.Image.SourceController do changeset = Images.change_image(image) + UserStatistics.inc_stat(conn.assigns.current_user, :metadata_updates) + conn |> put_view(PhilomenaWeb.ImageView) |> render("_source.html", layout: false, image: image, changeset: changeset) diff --git a/lib/philomena_web/controllers/image/tag_controller.ex b/lib/philomena_web/controllers/image/tag_controller.ex index 9f0c9a89..35406a59 100644 --- a/lib/philomena_web/controllers/image/tag_controller.ex +++ b/lib/philomena_web/controllers/image/tag_controller.ex @@ -1,8 +1,9 @@ defmodule PhilomenaWeb.Image.TagController do use PhilomenaWeb, :controller - alias Philomena.Images + alias Philomena.UserStatistics alias Philomena.Images.Image + alias Philomena.Images alias Philomena.Tags alias Philomena.Repo @@ -20,6 +21,7 @@ defmodule PhilomenaWeb.Image.TagController do {:ok, %{image: {image, added_tags, removed_tags}}} -> Images.reindex_image(image) Tags.reindex_tags(added_tags ++ removed_tags) + UserStatistics.inc_stat(conn.assigns.current_user, :metadata_updates) image = image diff --git a/lib/philomena_web/controllers/image/vote_controller.ex b/lib/philomena_web/controllers/image/vote_controller.ex index 8b9da65a..6e465fd0 100644 --- a/lib/philomena_web/controllers/image/vote_controller.ex +++ b/lib/philomena_web/controllers/image/vote_controller.ex @@ -18,7 +18,7 @@ defmodule PhilomenaWeb.Image.VoteController do ImageVotes.delete_vote_transaction(image, user), ImageVotes.create_vote_transaction(image, user, params["up"] == true) ) - |> Repo.transaction() + |> Repo.isolated_transaction(:serializable) |> case do {:ok, _result} -> image = @@ -40,7 +40,7 @@ defmodule PhilomenaWeb.Image.VoteController do image = conn.assigns.image ImageVotes.delete_vote_transaction(image, user) - |> Repo.transaction() + |> Repo.isolated_transaction(:serializable) |> case do {:ok, _result} -> image = diff --git a/lib/philomena_web/controllers/image_controller.ex b/lib/philomena_web/controllers/image_controller.ex index 5dba4d21..87a95246 100644 --- a/lib/philomena_web/controllers/image_controller.ex +++ b/lib/philomena_web/controllers/image_controller.ex @@ -5,6 +5,7 @@ defmodule PhilomenaWeb.ImageController do alias PhilomenaWeb.NotificationCountPlug alias Philomena.{Images, Images.Image, Comments.Comment, Galleries.Gallery, Galleries.Interaction, Textile.Renderer} alias Philomena.Servers.ImageProcessor + alias Philomena.UserStatistics alias Philomena.Interactions alias Philomena.Comments alias Philomena.Tags @@ -104,6 +105,7 @@ defmodule PhilomenaWeb.ImageController do ImageProcessor.cast(image.id) Images.reindex_image(image) Tags.reindex_tags(image.added_tags) + UserStatistics.inc_stat(conn.assigns.current_user, :uploads) conn |> put_flash(:info, "Image created successfully.") diff --git a/lib/philomena_web/controllers/topic/post_controller.ex b/lib/philomena_web/controllers/topic/post_controller.ex index 20500bf7..a90e2d4f 100644 --- a/lib/philomena_web/controllers/topic/post_controller.ex +++ b/lib/philomena_web/controllers/topic/post_controller.ex @@ -2,6 +2,7 @@ defmodule PhilomenaWeb.Topic.PostController do use PhilomenaWeb, :controller alias Philomena.{Forums.Forum, Topics.Topic, Posts} + alias Philomena.UserStatistics alias Philomena.Repo plug PhilomenaWeb.FilterBannedUsersPlug @@ -19,6 +20,7 @@ defmodule PhilomenaWeb.Topic.PostController do {:ok, %{post: post}} -> Posts.notify_post(post) Posts.reindex_post(post) + UserStatistics.inc_stat(conn.assigns.current_user, :forum_posts) conn |> put_flash(:info, "Post created successfully.") diff --git a/priv/repo/structure.sql b/priv/repo/structure.sql index cefedd19..fb2c7c15 100644 --- a/priv/repo/structure.sql +++ b/priv/repo/structure.sql @@ -2,8 +2,8 @@ -- PostgreSQL database dump -- --- Dumped from database version 11.5 (Debian 11.5-1.pgdg100+1) --- Dumped by pg_dump version 11.5 (Debian 11.5-1.pgdg100+1) +-- Dumped from database version 12.1 (Debian 12.1-1.pgdg100+1) +-- Dumped by pg_dump version 12.1 (Debian 12.1-1.pgdg90+1) SET statement_timeout = 0; SET lock_timeout = 0; @@ -18,7 +18,7 @@ SET row_security = off; SET default_tablespace = ''; -SET default_with_oids = false; +SET default_table_access_method = heap; -- -- Name: adverts; Type: TABLE; Schema: public; Owner: - @@ -3743,6 +3743,13 @@ CREATE INDEX index_user_name_changes_on_user_id ON public.user_name_changes USIN CREATE INDEX index_user_statistics_on_user_id ON public.user_statistics USING btree (user_id); +-- +-- Name: index_user_statistics_on_user_id_and_day; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_user_statistics_on_user_id_and_day ON public.user_statistics USING btree (user_id, day); + + -- -- Name: index_user_whitelists_on_user_id; Type: INDEX; Schema: public; Owner: - --