You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

377 lines
6.9 KiB

defmodule Mirage.Notes do
@moduledoc """
The Notes context.
"""
import Ecto.Query, warn: false
alias Mirage.Repo
alias Mirage.Notes.{Note, Topic, NoteNote, NoteTopic, NoteLink}
@note_preloads [:links, :topics, :backlinks]
@topic_preloads [notes: @note_preloads]
defmacro contains(content, search_term) do
quote do
fragment(
"? %> ?",
unquote(content),
unquote(search_term)
)
end
end
def search_notes(query) do
query = String.downcase(query)
q =
from n in Note,
limit: 5,
select: n,
preload: ^@note_preloads,
where: contains(n.content, ^query),
or_where: contains(n.title, ^query),
order_by: [:views]
Repo.all(q)
end
@doc """
Returns the list of notes.
## Examples
iex> list_notes()
[%Note{}, ...]
"""
def list_notes do
q =
from n in Note,
limit: 10,
select: n,
order_by: [desc: :inserted_at],
preload: ^@note_preloads
Repo.all(q)
end
def list_notes(:at) do
q =
from n in Note,
where: like(n.title, "@%"),
limit: 3,
select: n,
preload: ^@note_preloads
Repo.all(q)
end
def list_notes(:last_edited) do
q =
from n in Note,
limit: 3,
select: n,
order_by: [desc: n.updated_at],
preload: ^@note_preloads
Repo.all(q)
end
def list_notes(:unlinked) do
q =
from n in Note,
full_join: nn in NoteNote,
on: nn.target_id == n.id or nn.source_id == n.id,
where: not (like(n.title, "@%") or like(n.title, "____-__-__")),
where: is_nil(nn.target_id) or is_nil(nn.source_id),
select: n,
limit: 3,
preload: ^@note_preloads
Repo.all(q)
end
def list_notes(:untagged) do
q =
from n in Note,
full_join: t in NoteTopic,
on: t.note_id == n.id,
where: not (like(n.title, "@%") or like(n.title, "____-__-__")),
where: is_nil(t.id),
select: n,
limit: 3,
preload: ^@note_preloads
Repo.all(q)
end
def list_notes(:today) do
date_title = today_title()
q =
from n in Note,
where: n.title == ^date_title,
select: n
case Repo.one(q) do
%Note{} = note ->
[note]
nil ->
{:ok, note} =
create_note(%{
title: date_title,
content: "Note for #{date_title}"
})
[note]
end
end
def today_title() do
Timex.now() |> Timex.format!("{YYYY}-{0M}-{D}")
end
@doc """
Gets a single note.
Raises `Ecto.NoResultsError` if the Note does not exist.
## Examples
iex> get_note!(123)
%Note{}
iex> get_note!(456)
** (Ecto.NoResultsError)
"""
def get_note!(id), do: Repo.get!(Note, id)
def get_note(id), do: Repo.get(Note, id)
def get_note_by_title(title), do: Repo.get_by(Note, title: title)
def preload_note(note), do: Repo.preload(note, @note_preloads)
def view_note!(note),
do:
update_note!(note, %{
views: note.views + 1,
viewed_at: DateTime.utc_now()
})
@doc """
Creates a note.
## Examples
iex> create_note(%{field: value})
{:ok, %Note{}}
iex> create_note(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_note(attrs \\ %{}) do
%Note{}
|> Note.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a note.
## Examples
iex> update_note(note, %{field: new_value})
{:ok, %Note{}}
iex> update_note(note, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_note(%Note{} = note, attrs) do
note
|> Note.changeset(attrs)
|> Repo.update()
end
def update_note!(%Note{} = note, attrs) do
note
|> Note.changeset(attrs)
|> Repo.update!()
end
@doc """
Deletes a note.
## Examples
iex> delete_note(note)
{:ok, %Note{}}
iex> delete_note(note)
{:error, %Ecto.Changeset{}}
"""
def delete_note(%Note{} = note) do
Repo.delete(note)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking note changes.
## Examples
iex> change_note(note)
%Ecto.Changeset{data: %Note{}}
"""
def change_note(%Note{} = note, attrs \\ %{}) do
Note.changeset(note, attrs)
end
@doc """
Links a note to all references notes in the content-field
## Examples
A content field with the following content:
```
This is a link to [[42|some other note]]
```
will link this note to the note with id 42.
"""
def link_note(%Note{} = note) do
multi = Ecto.Multi.new()
query = from(n in NoteNote, where: n.source_id == ^note.id)
refs =
note
|> Mirage.Markdown.get_references()
|> Enum.map(&String.to_integer/1)
# Delete all old references for this note
multi =
multi
|> Ecto.Multi.delete_all(:delete_links, query)
# Create a link to all referenced notes
multi =
refs
|> Enum.map(fn ref -> %NoteNote{source_id: note.id, target_id: ref} end)
|> Enum.reduce(multi, fn link_struct, multi ->
Ecto.Multi.insert(multi, {:link, link_struct.target_id}, link_struct)
end)
Repo.transaction(multi)
end
@doc """
Returns the list of topics.
## Examples
iex> list_topics()
[%Topic{}, ...]
"""
def list_topics do
Repo.all(Topic)
|> Repo.preload(@topic_preloads)
end
@doc """
Gets a single topic.
Raises `Ecto.NoResultsError` if the Note topic does not exist.
## Examples
iex> get_topic!(123)
%Topic{}
iex> get_topic!(456)
** (Ecto.NoResultsError)
"""
def get_topic!(id), do: Repo.get!(Topic, id)
def preload_topic(topic), do: Repo.preload(topic, @topic_preloads)
@doc """
Creates a topic.
## Examples
iex> create_topic(%{field: value})
{:ok, %Topic{}}
iex> create_topic(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_topic(attrs \\ %{}) do
%Topic{}
|> Topic.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a topic.
## Examples
iex> update_topic(topic, %{field: new_value})
{:ok, %Topic{}}
iex> update_topic(topic, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_topic(%Topic{} = topic, attrs) do
topic
|> Topic.changeset(attrs)
|> Repo.update()
end
@doc """
Deletes a topic.
## Examples
iex> delete_topic(topic)
{:ok, %Topic{}}
iex> delete_topic(topic)
{:error, %Ecto.Changeset{}}
"""
def delete_topic(%Topic{} = topic) do
Repo.delete(topic)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking topic changes.
## Examples
iex> change_topic(topic)
%Ecto.Changeset{data: %Topic{}}
"""
def change_topic(%Topic{} = topic, attrs \\ %{}) do
Topic.changeset(topic, attrs)
end
def create_note_link(attrs) do
%NoteLink{}
|> NoteLink.changeset(attrs)
|> Repo.insert()
end
end