Compare commits

...

1 Commits

Author SHA1 Message Date
Shadowfacts c90f2d48e2
Add filter web UI 2019-03-10 21:39:31 -04:00
14 changed files with 229 additions and 13 deletions

View File

@ -19,8 +19,10 @@ defmodule Frenzy.Feed do
field :last_updated, :utc_datetime field :last_updated, :utc_datetime
field :site_url, :string field :site_url, :string
field :title, :string field :title, :string
field :filter_enabled, :boolean
has_many :items, Frenzy.Item, on_delete: :delete_all has_many :items, Frenzy.Item, on_delete: :delete_all
has_one :filter, Frenzy.Filter, on_delete: :delete_all
timestamps() timestamps()
end end
@ -28,7 +30,8 @@ defmodule Frenzy.Feed do
@doc false @doc false
def changeset(feed, attrs) do def changeset(feed, attrs) do
feed feed
|> cast(attrs, [:title, :feed_url, :site_url, :last_updated]) |> cast(attrs, [:title, :feed_url, :site_url, :last_updated, :filter_enabled])
|> cast_assoc(:filter, required: true)
|> validate_required([:feed_url]) |> validate_required([:feed_url])
end end
end end

22
lib/frenzy/filter.ex Normal file
View File

@ -0,0 +1,22 @@
defmodule Frenzy.Filter do
use Ecto.Schema
import Ecto.Changeset
schema "filters" do
field :mode, :string
field :score, :integer
belongs_to :feed, Frenzy.Feed
has_many :rules, Frenzy.Rule, on_delete: :delete_all
timestamps()
end
@doc false
def changeset(filter, attrs) do
filter
|> cast(attrs, [:mode, :score])
|> cast_assoc(:rules)
|> validate_required([:mode, :score])
end
end

22
lib/frenzy/rule.ex Normal file
View File

@ -0,0 +1,22 @@
defmodule Frenzy.Rule do
use Ecto.Schema
import Ecto.Changeset
schema "rules" do
field :mode, :string
field :property, :string
field :param, :string
field :weight, :integer
belongs_to :filter, Frenzy.Filter
timestamps()
end
@doc false
def changeset(rule, attrs) do
rule
|> cast(attrs, [:mode, :property, :param, :weight])
|> validate_required([:mode, :property, :param, :weight])
end
end

View File

@ -81,6 +81,7 @@ defmodule Frenzy.UpdateFeeds do
feed = Repo.preload(feed, :items) feed = Repo.preload(feed, :items)
Enum.map(rss.items, fn entry -> Enum.map(rss.items, fn entry ->
# todo: use Repo.exists for this
if !Enum.any?(feed.items, fn item -> item.guid == entry.id end) do if !Enum.any?(feed.items, fn item -> item.guid == entry.id end) do
create_item(feed, entry) create_item(feed, entry)
end end
@ -100,7 +101,7 @@ defmodule Frenzy.UpdateFeeds do
{:err, reason} -> {:err, reason} ->
Logger.warn("Unable to fetch article for #{url}: #{reason}") Logger.warn("Unable to fetch article for #{url}: #{reason}")
entry.content || entry.summary entry.description
end end
changeset = changeset =

View File

@ -1,34 +1,61 @@
defmodule FrenzyWeb.FeedController do defmodule FrenzyWeb.FeedController do
use FrenzyWeb, :controller use FrenzyWeb, :controller
alias Frenzy.{Repo, Feed, Item} alias Frenzy.{Repo, Feed, Filter, Item}
alias FrenzyWeb.Router.Helpers, as: Routes alias FrenzyWeb.Router.Helpers, as: Routes
alias FrenzyWeb.Endpoint alias FrenzyWeb.Endpoint
import Ecto.Query import Ecto.Query
def index(conn, _params) do def index(conn, _params) do
render(conn, "index.html", feeds: Repo.all(Feed)) render(conn, "index.html", feeds: Repo.all(Feed))
end end
def show(conn, %{"id" => id}) do def show(conn, %{"id" => id}) do
feed = Repo.get(Feed, id) feed = Repo.get(Feed, id) |> Repo.preload(:filter)
items = Repo.all(from Item, where: [feed_id: ^id], order_by: [desc: :date]) items = Repo.all(from Item, where: [feed_id: ^id], order_by: [desc: :date])
render(conn, "show.html", %{ render(conn, "show.html", %{
feed: feed, feed: feed,
items: items items: items
}) })
end end
def new(conn, _params) do def new(conn, _params) do
changeset = Feed.changeset(%Feed{}, %{}) changeset = Feed.changeset(%Feed{}, %{})
render(conn, "new.html", changeset: changeset) render(conn, "new.html", changeset: changeset)
end end
def create(conn, %{"feed" => feed}) do def create(conn, %{"feed" => feed}) do
changeset = Feed.changeset(%Feed{}, feed) changeset =
Feed.changeset(
%Feed{
filter: %Filter{
mode: "accept",
score: 0,
rules: []
}
},
feed
)
{:ok, feed} = Repo.insert(changeset) {:ok, feed} = Repo.insert(changeset)
redirect(conn, to: Routes.feed_path(Endpoint, :index)) redirect(conn, to: Routes.feed_path(Endpoint, :index))
end end
def enable_filter(conn, %{"id" => id}) do
feed = Repo.get(Feed, id) |> Repo.preload(:filter)
changeset = Feed.changeset(feed, %{filter_enabled: true})
Repo.update(changeset)
redirect(conn, to: Routes.feed_path(Endpoint, :show, id))
end
def disable_filter(conn, %{"id" => id}) do
feed = Repo.get(Feed, id)
changeset = Feed.changeset(feed, %{filter_enabled: false})
Repo.update(changeset)
redirect(conn, to: Routes.feed_path(Endpoint, :show, id))
end
def delete(conn, %{"id" => id}) do def delete(conn, %{"id" => id}) do
feed = Repo.get(Feed, id) feed = Repo.get(Feed, id)
{:ok, _} = Repo.delete(feed) {:ok, _} = Repo.delete(feed)

View File

@ -0,0 +1,36 @@
defmodule FrenzyWeb.FilterController do
use FrenzyWeb, :controller
alias Frenzy.{Repo, Feed, Filter, Rule}
alias FrenzyWeb.Router.Helpers, as: Routes
alias FrenzyWeb.Endpoint
import Ecto.Query
def edit(conn, %{"id" => id}) do
filter = Repo.get(Filter, id) |> Repo.preload(:rules)
changeset = Filter.changeset(filter, %{})
render(conn, "edit.html", changeset: changeset)
end
def update(conn, %{"id" => id, "filter" => filter_params}) do
filter = Repo.get(Filter, id) |> Repo.preload(:rules)
changeset = Filter.changeset(filter, filter_params)
Repo.update(changeset)
redirect(conn, to: Routes.filter_path(Endpoint, :edit, id))
end
def add_rule(conn, %{"id" => id}) do
filter = Repo.get(Filter, id)
changeset =
Ecto.build_assoc(filter, :rules, %{
property: "title",
mode: "contains_string",
param: "",
weight: 5
})
Repo.insert(changeset)
redirect(conn, to: Routes.filter_path(Endpoint, :edit, id))
end
end

View File

@ -12,7 +12,6 @@ defmodule FrenzyWeb.Router do
pipeline :api do pipeline :api do
plug :accepts, ["json"] plug :accepts, ["json"]
end end
scope "/", FrenzyWeb do scope "/", FrenzyWeb do
@ -23,6 +22,11 @@ defmodule FrenzyWeb.Router do
get "/", FeedController, :index get "/", FeedController, :index
resources "/feeds", FeedController, except: [:edit, :update] resources "/feeds", FeedController, except: [:edit, :update]
post "/feeds/:id/refresh", FeedController, :refresh post "/feeds/:id/refresh", FeedController, :refresh
post "/feeds/:id/enable_filter", FeedController, :enable_filter
post "/feeds/:id/disable_filter", FeedController, :disable_filter
resources "/filters", FilterController, only: [:edit, :update]
post "/filters/:id/add_rule", FilterController, :add_rule
resources "/items", ItemController, only: [:show] resources "/items", ItemController, only: [:show]
post "/items/:id/read", ItemController, :read post "/items/:id/read", ItemController, :read

View File

@ -2,10 +2,26 @@
<%= submit "Refresh Feed" %> <%= submit "Refresh Feed" %>
<% end %> <% end %>
<%= if @feed.filter_enabled do %>
<%= form_tag Routes.feed_path(@conn, :disable_filter, @feed.id), method: :post do %>
<%= submit "Disable Filter" %>
<% end %>
<%= form_tag Routes.filter_path(@conn, :edit, @feed.filter.id), method: :get do %>
<%= submit "Edit Filter" %>
<% end %>
<% else %>
<%= form_tag Routes.feed_path(@conn, :enable_filter, @feed.id), method: :post do %>
<%= submit "Enable Filter" %>
<% end %>
<% end %>
<%= form_tag Routes.feed_path(@conn, :delete, @feed.id), method: :delete do %> <%= form_tag Routes.feed_path(@conn, :delete, @feed.id), method: :delete do %>
<%= submit "Delete Feed" %> <%= submit "Delete Feed" %>
<% end %> <% end %>
<table> <table>
<%= for item <- @items do %> <%= for item <- @items do %>
<tr <%= if item.read do %>class="item-read"<% end %>> <tr <%= if item.read do %>class="item-read"<% end %>>

View File

@ -0,0 +1,43 @@
<h2>Filter</h2>
<%= form_for @changeset, Routes.filter_path(@conn, :update, @changeset.data.id), fn form -> %>
<div class="form-group">
<label for="mode">Mode</label>
<%= select form, :mode, [{"Accept", :accept}, {"Reject", :reject}] %>
</div>
<div class="form-group">
<label for="score">Score</label>
<%= number_input form, :score %>
</div>
<table>
<tr>
<th>Property</th>
<th>Mode</th>
<th>Param</th>
<th>Weight</th>
</tr>
<%= inputs_for form, :rules, fn p -> %>
<tr>
<td>
<%= select p, :property, [{"URL", :url}, {"Title", :title}, {"Author", :author}, {"Content", :content}] %>
</td>
<td>
<%= select p, :mode, [{"contains", :contains_string}, {"matches regex", :matches_regex}] %>
</td>
<td>
<%= text_input p, :param %>
</td>
<td>
<%= number_input p, :weight %>
</td>
</tr>
<% end %>
</table>
<div class="form-group">
<%= submit "Update Filter" %>
</div>
<% end %>
<%= form_tag Routes.filter_path(@conn, :add_rule, @changeset.data.id), method: :post do %>
<%= submit "Add Rule" %>
<% end %>

View File

@ -0,0 +1,3 @@
defmodule FrenzyWeb.FilterView do
use FrenzyWeb, :view
end

View File

@ -10,6 +10,5 @@ defmodule Frenzy.Repo.Migrations.CreateFeeds do
timestamps() timestamps()
end end
end end
end end

View File

@ -0,0 +1,16 @@
defmodule Frenzy.Repo.Migrations.CreateRules do
use Ecto.Migration
def change do
create table(:filters) do
add :mode, :string
add :score, :integer
add :rules, :map
add :feed_id, references(:feeds)
timestamps()
end
# create index(:rules, [:filter])
end
end

View File

@ -0,0 +1,9 @@
defmodule Frenzy.Repo.Migrations.FeedsAddFilter do
use Ecto.Migration
def change do
alter table(:feeds) do
add :filter_enabled, :boolean
end
end
end

View File

@ -0,0 +1,15 @@
defmodule Frenzy.Repo.Migrations.CreateRules do
use Ecto.Migration
def change do
create table(:rules) do
add :mode, :string
add :property, :string
add :param, :string
add :weight, :integer
add :filter_id, references(:filters)
timestamps()
end
end
end