Permalink
Browse files

Add package_user_settings and implement following

  • Loading branch information...
rrrene committed Jun 17, 2016
1 parent 12a2c27 commit 2d3f87c71e73a919b4e9d895a9f7ee1199ea79a2
Showing with 781 additions and 226 deletions.
  1. +10 −2 lib/hex_faktor/email_publisher.ex
  2. +21 −12 lib/hex_faktor/notification_mailer.ex
  3. +46 −2 lib/hex_faktor/notification_publisher.ex
  4. +36 −0 lib/hex_faktor/version_helper.ex
  5. +9 −0 priv/repo/migrations/20160614164904_add_metadata_to_notifications.exs
  6. +2 −0 test/controllers/package_controller_test.exs
  7. +45 −0 test/hex_faktor/notification_publisher_test.exs
  8. +63 −0 test/hex_faktor/version_helper_test.exs
  9. +7 −1 test/support/fixtures.ex
  10. +2 −1 web/controllers/email_controller.ex
  11. +16 −8 web/controllers/notification_controller.ex
  12. +31 −23 web/controllers/package_controller.ex
  13. +2 −0 web/controllers/page_controller.ex
  14. +4 −1 web/models/notification.ex
  15. +56 −11 web/persistence/notification.ex
  16. +29 −0 web/persistence/package_user_settings.ex
  17. +1 −0 web/static/css/component.motd-banner.scss
  18. +52 −0 web/static/css/component.notification-item.scss
  19. +116 −0 web/static/css/component.package-list-item.scss
  20. +0 −36 web/static/css/components.news.scss
  21. +61 −0 web/static/css/keep.scss
  22. +5 −1 web/static/js/user_channel.js
  23. +0 −28 web/templates/component/news.html.eex
  24. +1 −1 web/templates/component/notification-counter.html.eex
  25. +13 −0 web/templates/component/notification-item-package.html.eex
  26. +15 −0 web/templates/component/notification-item-project.html.eex
  27. +23 −18 web/templates/email/notifications.html.eex
  28. +12 −0 web/templates/email/status_report.html.eex
  29. +3 −0 web/templates/layout/_sidebar.html.eex
  30. +2 −2 web/templates/notification/index.html.eex
  31. +19 −45 web/templates/package/_notification_settings.html.eex
  32. +14 −22 web/templates/package/index.html.eex
  33. +27 −7 web/templates/package/show.html.eex
  34. +0 −5 web/views/component_view.ex
  35. +38 −0 web/views/view_helpers.ex
@@ -6,6 +6,10 @@ defmodule HexFaktor.EmailPublisher do
require Logger
#
# notifications
#
def send_daily_emails do
all_users = User.find_by_verified_email_frequency("daily")
user_ids = all_users |> Enum.map(&(&1.id))
@@ -39,6 +43,10 @@ defmodule HexFaktor.EmailPublisher do
end
end
#
# status report
#
@doc "Sends status reports in the weekly email"
def send_weekly_emails do
all_users = User.find_by_verified_email_frequency("weekly")
@@ -50,9 +58,9 @@ defmodule HexFaktor.EmailPublisher do
defp send_status_report(user) do
{_, active_projects, outdated_projects} = ProjectProvider.user_projects(user)
package_notifications = Notification.all_unseen_for_packages_for(user, [:package])
#IO.puts "Sending mail to #{user.email}"
case NotificationMailer.send_status_report(user, active_projects, outdated_projects) do
case NotificationMailer.send_status_report(user, active_projects, outdated_projects, package_notifications) do
:ok ->
Notification.mark_as_email_sent_for_user!(user)
Logger.info "[email] #{now} status_report sent to <#{user.email}>"
@@ -55,20 +55,21 @@ defmodule HexFaktor.NotificationMailer do
:ok
end
def status_report(user, active_projects, outdated_projects) do
def status_report(user, active_projects, outdated_projects, package_notifications) do
[
title: "You have #{outdated_projects |> Enum.count} projects with outdated dependencies!",
html_title: "HexFaktor - Weekly Report",
email: user.email,
base_url: @base_url,
unsubcribe_url: settings_url,
active_projects: active_projects,
outdated_projects: outdated_projects
outdated_projects: outdated_projects,
package_notifications: package_notifications
]
end
def send_status_report(user, active_projects, outdated_projects) do
assigns = status_report(user, active_projects, outdated_projects)
def send_status_report(user, active_projects, outdated_projects, package_notifications) do
assigns = status_report(user, active_projects, outdated_projects, package_notifications)
send_email to: user.email,
from: @from,
subject: "[HexFaktor] #{assigns[:title]}",
@@ -91,13 +92,21 @@ defmodule HexFaktor.NotificationMailer do
defp to_map(notifications) do
notifications
|> Enum.reduce(%{}, fn(notification, memo) ->
key = {notification.project.name, notification.project_id, notification.git_branch_id}
if memo[key] do
Map.put(memo, key, [notification | memo[key]])
else
Map.put(memo, key, [notification])
end
end)
|> Enum.reduce(%{}, &reduce_notifications/2)
end
defp reduce_notifications(notification, memo) do
key =
if notification.package do
{:package, notification.package.name, notification.project_id, nil}
else
{:project, notification.project.name, notification.project_id, notification.git_branch_id}
end
if memo[key] do
Map.put(memo, key, [notification | memo[key]])
else
Map.put(memo, key, [notification])
end
end
end
@@ -5,13 +5,57 @@ defmodule HexFaktor.NotificationPublisher do
alias HexFaktor.Broadcast
alias HexFaktor.ProjectAccess
alias HexFaktor.VersionHelper
alias Refaktor.Persistence.GitBranch
alias HexFaktor.Persistence.Notification
alias HexFaktor.Persistence.ProjectUser
alias Refaktor.Job.Elixir.Deps.Model.DepsObject
alias HexFaktor.Persistence.PackageUserSettings
#
# packages
#
def handle_new_package_update(package) do
newest_version =
package
|> VersionHelper.newest_version
kind_of_release =
newest_version
|> VersionHelper.kind_of_release
all_user_ids =
PackageUserSettings.find_user_ids_by_package_id_for(package.id,
kind_of_release)
reason_hash =
["package", package.id, newest_version]
|> to_reason_hash()
all_user_ids
|> Enum.each(&handle_package_notification(&1, package, newest_version, reason_hash))
end
defp handle_package_notification(user_id, package, newest_version, reason_hash) do
attributes = Notification.build_for_package(user_id, package, newest_version, reason_hash)
case Notification.ensure_for_package(user_id, package, newest_version, reason_hash) do
nil -> # Notification with `reason_hash` already existed
nil
notification ->
map =
notification
|> Map.take([:id, :package_id])
Broadcast.to_user(user_id, "notification.new", map)
end
end
#
# deps
#
def handle_new_deps_objects(deps_objects, git_branch_id) do
git_branch = GitBranch.find_by_id(git_branch_id)
@@ -29,10 +73,10 @@ defmodule HexFaktor.NotificationPublisher do
|> to_reason_hash()
all_user_ids
|> Enum.each(&handle_notification(&1, dep, git_branch, reason_hash))
|> Enum.each(&handle_dep_notification(&1, dep, git_branch, reason_hash))
end
defp handle_notification(user_id, dep, git_branch, reason_hash) do
defp handle_dep_notification(user_id, dep, git_branch, reason_hash) do
attributes = Notification.build_for_deps_object(user_id, dep, reason_hash)
if create_notification?(user_id, dep.project_id, git_branch, attributes) do
case Notification.ensure_for_deps_object(user_id, dep, reason_hash) do
@@ -0,0 +1,36 @@
defmodule HexFaktor.VersionHelper do
@doc """
Returns the kind of update for the newest release.
"""
def kind_of_release(%{releases: releases}) do
releases
|> newest_version
|> kind_of_release
end
def kind_of_release(latest_version) do
case Version.parse(latest_version) do
{:ok, v} ->
kind_of_release(v.major, v.minor, v.patch, v.pre)
_ ->
nil
end
end
def kind_of_release(_, 0, 0, []), do: :major
def kind_of_release(_, _, 0, []), do: :minor
def kind_of_release(_, _, _, []), do: :patch
def kind_of_release(_, _, _, _), do: :pre
@doc """
Returns the version number of the latest release (in terms of `updated_at`).
"""
def newest_version(%{releases: releases}) do
newest_version(releases)
end
def newest_version(releases) when is_list(releases) do
hash =
releases
|> Enum.sort_by(&(&1["updated_at"]))
|> List.last
hash["version"]
end
end
@@ -0,0 +1,9 @@
defmodule HexFaktor.Repo.Migrations.AddMetadataToNotifications do
use Ecto.Migration
def change do
alter table(:notifications) do
add :metadata, :map
end
end
end
@@ -1,6 +1,8 @@
defmodule HexFaktor.PackageControllerTest do
use HexFaktor.ConnCase
alias HexFaktor.PackageController
@existent_package_id 1
@@ -0,0 +1,45 @@
defmodule HexFaktor.NotificationPublisherTest do
use HexFaktor.ConnCase
alias HexFaktor.Persistence.Package
alias HexFaktor.Persistence.Notification
alias HexFaktor.NotificationPublisher
alias HexFaktor.Persistence.PackageUserSettings
@test_package_id 1
@test_user_id 1
setup do
package_user_settings =
PackageUserSettings.ensure(@test_package_id, @test_user_id)
{:ok, %{"package_user_settings" => package_user_settings}}
end
def notification_count do
Notification.latest_for(@test_user_id, 1000)
|> Enum.count
end
test "the truth" do
assert PackageUserSettings.find(@test_package_id, @test_user_id)
assert 0 == notification_count()
test_package = Package.find_by_id(@test_package_id)
NotificationPublisher.handle_new_package_update(test_package)
assert 1 == notification_count()
NotificationPublisher.handle_new_package_update(test_package)
assert 1 == notification_count()
new_release =
%{"updated_at" => "2020-01-01T00:00:00Z", "version" => "42.0.0"}
releases = [new_release] ++ test_package.releases
test_package2 =
%HexFaktor.Package{test_package | releases: releases}
NotificationPublisher.handle_new_package_update(test_package2)
assert 2 == notification_count()
end
end
@@ -0,0 +1,63 @@
defmodule HexFaktor.VersionHelperTest do
use HexFaktor.ModelCase
alias HexFaktor.Persistence.Package
alias HexFaktor.VersionHelper
defp test_package do
%HexFaktor.Package{
description: "A static code analysis tool for the Elixir language with a focus on code consistency and teaching.",
id: 1,
language: nil,
name: "credo",
project_id: 12,
releases: [
%{"updated_at" => "2015-11-16T20:46:19Z", "version" => "0.1.2"},
%{"updated_at" => "2015-11-16T19:08:47Z", "version" => "0.1.1"},
%{"updated_at" => "2015-11-16T19:03:57Z", "version" => "0.1.0"},
%{"updated_at" => "2015-11-09T12:38:50Z", "version" => "0.0.1-dev"}],
source: "hex", source_url: "https://github.com/rrrene/credo",}
end
test "kind_of_release pure" do
assert :major == "2.0.0" |> VersionHelper.kind_of_release
assert :minor == "2.1.0" |> VersionHelper.kind_of_release
assert :patch == "2.1.1" |> VersionHelper.kind_of_release
assert :pre == "2.0.0-dev" |> VersionHelper.kind_of_release
end
test "kind_of_release with a package" do
assert :patch == VersionHelper.kind_of_release(test_package)
end
test "newest_version with a package" do
assert "0.1.2" == VersionHelper.newest_version(test_package)
end
test "newest_version with a list /1" do
releases = [
%{"updated_at" => "2015-12-07T14:50:05Z", "version" => "0.2.1"},
%{"updated_at" => "2015-12-07T14:25:52Z", "version" => "0.2.0"},
%{"updated_at" => "2015-11-16T20:46:19Z", "version" => "0.1.2"},
%{"updated_at" => "2015-11-16T19:08:47Z", "version" => "0.1.1"},
%{"updated_at" => "2015-11-16T19:03:57Z", "version" => "0.1.0"},
%{"updated_at" => "2015-11-09T12:38:50Z", "version" => "0.0.1-dev"}
]
assert "0.2.1" == VersionHelper.newest_version(releases)
end
test "newest_version with a list /2" do
releases = [
%{"updated_at" => "2016-01-16T20:46:19Z", "version" => "0.1.3"},
%{"updated_at" => "2015-12-07T14:50:05Z", "version" => "0.2.1"},
%{"updated_at" => "2015-12-07T14:25:52Z", "version" => "0.2.0"},
%{"updated_at" => "2015-11-16T20:46:19Z", "version" => "0.1.2"},
%{"updated_at" => "2015-11-16T19:08:47Z", "version" => "0.1.1"},
%{"updated_at" => "2015-11-16T19:03:57Z", "version" => "0.1.0"},
%{"updated_at" => "2015-11-09T12:38:50Z", "version" => "0.0.1-dev"}
]
assert "0.1.3" == VersionHelper.newest_version(releases)
end
end
View
@@ -74,7 +74,13 @@ defmodule HexFaktor.Fixtures do
def package(1) do
%Package{
source: "hex",
name: "credo"
name: "credo",
releases: [
%{"updated_at" => "2015-11-16T20:46:19Z", "version" => "0.2.0"},
%{"updated_at" => "2015-11-16T20:46:19Z", "version" => "0.1.2"},
%{"updated_at" => "2015-11-16T19:08:47Z", "version" => "0.1.1"},
%{"updated_at" => "2015-11-16T19:03:57Z", "version" => "0.1.0"},
%{"updated_at" => "2015-11-09T12:38:50Z", "version" => "0.0.1-dev"}],
}
end
end
@@ -19,8 +19,9 @@ defmodule HexFaktor.EmailController do
def status_report(conn, params) do
user = Auth.current_user(conn)
{_, active_projects, outdated_projects} = ProjectProvider.user_projects(user)
package_notifications = Notification.all_unseen_for_packages_for(user, [:package])
assigns = NotificationMailer.status_report(user, active_projects, outdated_projects)
assigns = NotificationMailer.status_report(user, active_projects, outdated_projects, package_notifications)
render(conn, "status_report.html", assigns ++ [layout: {LayoutView, "email.html"}])
end
@@ -35,14 +35,22 @@ defmodule HexFaktor.NotificationController do
defp to_map(notifications) do
notifications
|> Enum.reduce(%{}, fn(notification, memo) ->
key = {notification.project.name, notification.project_id, notification.git_branch_id}
if memo[key] do
Map.put(memo, key, [notification | memo[key]])
else
Map.put(memo, key, [notification])
end
end)
|> Enum.reduce(%{}, &reduce_notifications/2)
end
defp reduce_notifications(notification, memo) do
key =
if notification.package do
{:package, notification.package.name, notification.project_id, nil}
else
{:project, notification.project.name, notification.project_id, notification.git_branch_id}
end
if memo[key] do
Map.put(memo, key, [notification | memo[key]])
else
Map.put(memo, key, [notification])
end
end
def mark_as_read_for_branch(conn, %{"id" => id}) do
Oops, something went wrong.

0 comments on commit 2d3f87c

Please sign in to comment.