autotagging

This commit is contained in:
Inhji 2023-09-13 20:59:15 +02:00
parent e17582823c
commit 780a645fee
11 changed files with 128 additions and 59 deletions

View file

@ -189,6 +189,29 @@
@apply hidden; @apply hidden;
} }
} }
/* === CARD LIST (ADMIN) === */
.card-list {
@apply flex flex-col gap-3 mt-6;
}
/* === CARD (ADMIN) === */
.card {
@apply bg-neutral-100 dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 p-3 rounded;
& header {
& h2 {
@apply text-xl leading-normal;
}
}
& footer {
@apply flex gap-3 text-sm;
}
}
} }
/* /*

View file

@ -58,9 +58,11 @@ defmodule Chiya.Notes do
Chiya.Flop.validate_and_run(q, params, for: Chiya.Notes.Note) Chiya.Flop.validate_and_run(q, params, for: Chiya.Notes.Note)
end end
def list_apply_notes(regex) do def list_apply_notes(%Chiya.Tags.Tag{} = tag) do
Note Note
|> where([n], fragment("? ~ ?", n.name, ^regex)) |> where([n], fragment("? ~ ?", n.name, ^tag.regex))
|> or_where([n], fragment("? ~ ?", n.url, ^tag.regex))
|> or_where([n], fragment("? ~ ?", n.content, ^tag.regex))
|> Repo.all() |> Repo.all()
end end

View file

@ -15,5 +15,6 @@ defmodule Chiya.Notes.NoteTag do
note_tag note_tag
|> cast(attrs, [:note_id, :tag_id]) |> cast(attrs, [:note_id, :tag_id])
|> validate_required([:note_id, :tag_id]) |> validate_required([:note_id, :tag_id])
|> unique_constraint([:note_id, :tag_id])
end end
end end

View file

@ -38,9 +38,10 @@ defmodule Chiya.Tags do
|> Repo.all() |> Repo.all()
end end
def list_admin_tags(params) do def list_admin_tags() do
q = q =
Tag Tag
|> order_by(:name)
|> with_preloads() |> with_preloads()
Repo.all(q) Repo.all(q)

View file

@ -8,6 +8,13 @@ defmodule Chiya.Tags.TagUpdater do
alias Chiya.{Notes, Tags} alias Chiya.{Notes, Tags}
alias Chiya.Notes.Note alias Chiya.Notes.Note
@doc """
Updates a tag for the given note.
## Examples
iex> update_tags({:ok, note}, "foo,bar")
"""
def update_tags({:ok, %Note{} = note}, attrs) do def update_tags({:ok, %Note{} = note}, attrs) do
note note
|> Notes.preload_note() |> Notes.preload_note()
@ -20,27 +27,19 @@ defmodule Chiya.Tags.TagUpdater do
{:error, changeset} {:error, changeset}
end end
@doc """ def update_tags(%Note{} = note, %{tags_string: new_tags} = attrs) when is_map(attrs) do
Updates the tags for the given note
## Examples
iex> update_tags(note, "foo,bar")
"""
def update_tags(note, %{tags_string: new_tags} = attrs) when is_map(attrs) do
update_tags(note, new_tags) update_tags(note, new_tags)
end end
def update_tags(note, %{"tags_string" => new_tags} = attrs) when is_map(attrs) do def update_tags(%Note{} = note, %{"tags_string" => new_tags} = attrs) when is_map(attrs) do
update_tags(note, new_tags) update_tags(note, new_tags)
end end
def update_tags(note, attrs) when is_map(attrs) do def update_tags(%Note{} = note, attrs) when is_map(attrs) do
note note
end end
def update_tags(note, new_tags) when is_binary(new_tags) do def update_tags(%Note{} = note, new_tags) when is_binary(new_tags) do
update_tags(note, split_tags(new_tags)) update_tags(note, split_tags(new_tags))
end end
@ -62,14 +61,7 @@ defmodule Chiya.Tags.TagUpdater do
|> remove_tags(old_tags -- new_tags) |> remove_tags(old_tags -- new_tags)
end end
defp split_tags(tags_string) when is_binary(tags_string) do def add_tags(note, tags) do
tags_string
|> String.split(",")
|> Enum.map(&String.trim/1)
|> Enum.filter(&(String.length(&1) > 0))
end
defp add_tags(note, tags) do
tags tags
|> Enum.uniq() |> Enum.uniq()
|> Enum.each(&add_tag(note, &1)) |> Enum.each(&add_tag(note, &1))
@ -77,6 +69,13 @@ defmodule Chiya.Tags.TagUpdater do
note note
end end
defp split_tags(tags_string) when is_binary(tags_string) do
tags_string
|> String.split(",")
|> Enum.map(&String.trim/1)
|> Enum.filter(&(String.length(&1) > 0))
end
defp add_tag(%{id: note_id} = note, tag) when is_binary(tag) do defp add_tag(%{id: note_id} = note, tag) when is_binary(tag) do
slug = Slugger.slugify_downcase(tag) slug = Slugger.slugify_downcase(tag)
@ -100,7 +99,13 @@ defmodule Chiya.Tags.TagUpdater do
tag_id: tag.id tag_id: tag.id
} }
{:ok, _note_tag} = Notes.create_note_tag(attrs) case Notes.create_note_tag(attrs) do
{:ok, _note_tag} ->
true
{:error, changeset} ->
Logger.warn(inspect(changeset))
end
end end
end end

View file

@ -8,29 +8,26 @@ defmodule ChiyaWeb.PageController do
defp put_assigns(conn, opts) do defp put_assigns(conn, opts) do
conn conn
|> assign(:page_header, true) |> assign(:page_header, true)
|> assign(:page_title, "no title")
end end
def home(conn, params) do def home(conn, params) do
settings = conn.assigns.settings settings = conn.assigns.settings
{channel, notes, meta} = if settings.home_channel_id do
case settings.home_channel_id do channel = Channels.get_channel!(settings.home_channel_id)
nil -> {:ok, {notes, meta}} = Chiya.Notes.list_home_notes(channel, params)
nil
id -> render(conn, :home,
channel = Channels.get_channel!(id) channel: channel,
{:ok, {notes, meta}} = Chiya.Notes.list_home_notes(channel, params) notes: notes,
{channel, notes, meta} meta: meta,
end page_title: "Home",
page_header: false
render(conn, :home, )
channel: channel, else
notes: notes, render_error(conn, :not_found)
meta: meta, end
page_title: "Home",
page_header: false
)
end end
def channel(conn, %{"slug" => channel_slug}) do def channel(conn, %{"slug" => channel_slug}) do

View file

@ -1,6 +1,6 @@
defmodule ChiyaWeb.PageHTML do defmodule ChiyaWeb.PageHTML do
use ChiyaWeb, :html_public use ChiyaWeb, :html_public
import ChiyaWeb.Format, only: [pretty_datetime: 1, pretty_date: 1, datetime: 1] import ChiyaWeb.Format, only: [pretty_datetime: 1, pretty_date: 1]
embed_templates "page_html/*" embed_templates "page_html/*"

View file

@ -2,8 +2,8 @@ defmodule ChiyaWeb.TagController do
use ChiyaWeb, :controller use ChiyaWeb, :controller
alias Chiya.Tags alias Chiya.Tags
def index(conn, params) do def index(conn, _params) do
tags = Chiya.Tags.list_admin_tags(params) tags = Chiya.Tags.list_admin_tags()
render(conn, :index, tags: tags) render(conn, :index, tags: tags)
end end
@ -13,13 +13,26 @@ defmodule ChiyaWeb.TagController do
render(conn, :show, tag: tag) render(conn, :show, tag: tag)
end end
def apply(conn, %{"id" => id}) do def apply_prepare(conn, %{"id" => id}) do
tag = Tags.get_tag!(id) tag = Tags.get_tag!(id)
notes = Chiya.Notes.list_apply_notes(tag.regex) notes = Chiya.Notes.list_apply_notes(tag)
render(conn, :apply_prepare, tag: tag, notes: notes) render(conn, :apply_prepare, tag: tag, notes: notes)
end end
def apply_run(conn, %{"id" => id}) do
tag = Tags.get_tag!(id)
notes = Chiya.Notes.list_apply_notes(tag)
Enum.each(notes, fn note ->
IO.inspect("Updating note: #{note.name}")
Chiya.Tags.TagUpdater.add_tags(note, [tag.slug])
end)
notes = Chiya.Notes.list_apply_notes(tag)
render(conn, :apply_prepare, tag: tag, notes: notes)
end
def edit(conn, %{"id" => id}) do def edit(conn, %{"id" => id}) do
IO.inspect(id) IO.inspect(id)
tag = Tags.get_tag!(id) tag = Tags.get_tag!(id)

View file

@ -1,22 +1,22 @@
<.header> <.header>
Apply Tag <%= @tag.name %> Apply Tag <%= @tag.name %>
<:subtitle>These notes will get the tag.</:subtitle> <:subtitle><%= Enum.count(@notes) %> notes will get the tag.</:subtitle>
<:actions> <:actions>
<.link href={~p"/admin/tags/#{@tag}/apply"}> <.link href={~p"/admin/tags/#{@tag}/apply/run"}>
<.button>Apply tag</.button> <.button>Apply tag</.button>
</.link> </.link>
</:actions> </:actions>
</.header> </.header>
<section> <section class="card-list">
<%= for note <- @notes do %> <%= for note <- @notes do %>
<article class="bg-slate-100 dark:bg-slate-800 text-slate-900 dark:text-slate-100 p-3 rounded"> <article class="card">
<header> <header>
<h2 class="text-xl leading-normal"> <h2>
<a href={"/admin/notes/#{note.id}"}><%= note.name %></a> <a href={"/admin/notes/#{note.id}"}><%= note.name %></a>
</h2> </h2>
</header> </header>
<footer class="flex gap-3 text-sm "> <footer>
<span>Updated <%= from_now(note.updated_at) %></span> <span>Updated <%= from_now(note.updated_at) %></span>
<span>Published <%= from_now(note.published_at) %></span> <span>Published <%= from_now(note.published_at) %></span>
</footer> </footer>

View file

@ -3,17 +3,43 @@
<:subtitle>Tags are tags.</:subtitle> <:subtitle>Tags are tags.</:subtitle>
</.header> </.header>
<section class="flex flex-col gap-3 mt-6"> <section class="card-list mt-8">
<%= for tag <- @tags do %> <h3 class="leading-normal text-xl">With Regex</h3>
<article class="bg-neutral-100 dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 p-3 rounded">
<%= for tag <- Enum.filter(@tags, fn t -> !!t.regex end) do %>
<article class="card">
<header> <header>
<h2 class="text-xl leading-normal"> <h2>
<a href={"/admin/tags/#{tag.id}"}><%= tag.name %></a> <a href={"/admin/tags/#{tag.id}"}><%= tag.name %></a>
</h2> </h2>
</header> </header>
<footer class="flex gap-3 text-sm "> <footer>
<span><%= Enum.count(tag.notes) %> notes</span> <span><%= Enum.count(tag.notes) %> notes</span>
<span>Updated <%= from_now(tag.updated_at) %></span> <span>Updated <%= from_now(tag.updated_at) %></span>
<%= if String.length(tag.regex || "") > 0 do %>
<span>Regex: <%= tag.regex %></span>
<% end %>
</footer>
</article>
<% end %>
</section>
<section class="card-list mt-8">
<h3 class="leading-normal text-xl">Without Regex</h3>
<%= for tag <- Enum.filter(@tags, fn t -> !t.regex end) do %>
<article class="card">
<header>
<h2>
<a href={"/admin/tags/#{tag.id}"}><%= tag.name %></a>
</h2>
</header>
<footer>
<span><%= Enum.count(tag.notes) %> notes</span>
<span>Updated <%= from_now(tag.updated_at) %></span>
<%= if String.length(tag.regex || "") > 0 do %>
<span>Regex: <%= tag.regex %></span>
<% end %>
</footer> </footer>
</article> </article>
<% end %> <% end %>

View file

@ -77,7 +77,8 @@ defmodule ChiyaWeb.Router do
resources "/tokens", TokenController, only: [:index, :show, :new, :create, :delete] resources "/tokens", TokenController, only: [:index, :show, :new, :create, :delete]
resources "/tags", TagController, only: [:index, :edit, :update, :show] resources "/tags", TagController, only: [:index, :edit, :update, :show]
get "/tags/:id/apply", TagController, :apply get "/tags/:id/apply", TagController, :apply_prepare
get "/tags/:id/apply/run", TagController, :apply_run
get "/notes/import", NoteController, :import_prepare get "/notes/import", NoteController, :import_prepare
post "/notes/import", NoteController, :import_run post "/notes/import", NoteController, :import_run