styling, require a code for prompts, admin privs, ordering, filtering, jump-to-page
This commit is contained in:
parent
7990246675
commit
eefd11d85a
|
@ -34,4 +34,6 @@ npm-debug.log
|
||||||
|
|
||||||
output.png
|
output.png
|
||||||
*.pyc
|
*.pyc
|
||||||
uploads/*
|
uploads/*
|
||||||
|
|
||||||
|
dev.secret.exs
|
|
@ -1,6 +1,11 @@
|
||||||
/* This file is for your main application CSS */
|
@import "tailwindcss/base";
|
||||||
@import "./phoenix.css";
|
@import "tailwindcss/components";
|
||||||
|
@import "tailwindcss/utilities";
|
||||||
|
|
||||||
|
/* This file is for your main application CSS */
|
||||||
|
.bg-gray {
|
||||||
|
background-color: rgb(20, 20, 20);
|
||||||
|
}
|
||||||
/* Alerts and form errors used by phx.new */
|
/* Alerts and form errors used by phx.new */
|
||||||
.alert {
|
.alert {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
@ -46,12 +51,12 @@
|
||||||
transition: opacity 1s ease-out;
|
transition: opacity 1s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phx-loading{
|
.phx-loading {
|
||||||
cursor: wait;
|
cursor: wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phx-modal {
|
.phx-modal {
|
||||||
opacity: 1!important;
|
opacity: 1 !important;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -59,15 +64,36 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
background-color: rgba(0,0,0,0.4);
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.phx-modal-content {
|
.phx-modal-content {
|
||||||
background-color: #fefefe;
|
background-color: #323232;
|
||||||
margin: 15vh auto;
|
margin: 15vh auto;
|
||||||
padding: 20px;
|
padding: 20\px;
|
||||||
border: 1px solid #888;
|
border: 1px solid #888;
|
||||||
width: 80%;
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-label {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 140px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-link {
|
||||||
|
height: 45px;
|
||||||
|
line-height: 45px;
|
||||||
|
width: 175px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btns {
|
||||||
|
width: 420px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-placeholder::placeholder {
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phx-modal-close {
|
.phx-modal-close {
|
||||||
|
@ -99,22 +125,42 @@
|
||||||
animation: 0.2s ease-out 0s normal forwards 1 fade-out-keys;
|
animation: 0.2s ease-out 0s normal forwards 1 fade-out-keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fade-in-scale-keys{
|
@keyframes fade-in-scale-keys {
|
||||||
0% { scale: 0.95; opacity: 0; }
|
0% {
|
||||||
100% { scale: 1.0; opacity: 1; }
|
scale: 0.95;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
scale: 1;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fade-out-scale-keys{
|
@keyframes fade-out-scale-keys {
|
||||||
0% { scale: 1.0; opacity: 1; }
|
0% {
|
||||||
100% { scale: 0.95; opacity: 0; }
|
scale: 1;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
scale: 0.95;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fade-in-keys{
|
@keyframes fade-in-keys {
|
||||||
0% { opacity: 0; }
|
0% {
|
||||||
100% { opacity: 1; }
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fade-out-keys{
|
@keyframes fade-out-keys {
|
||||||
0% { opacity: 1; }
|
0% {
|
||||||
100% { opacity: 0; }
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,5 @@
|
||||||
// We import the CSS which is extracted to its own file by esbuild.
|
// We import the CSS which is extracted to its own file by esbuild.
|
||||||
// Remove this line if you add a your own CSS build pipeline (e.g postcss).
|
// Remove this line if you add a your own CSS build pipeline (e.g postcss).
|
||||||
import "../css/app.css"
|
|
||||||
|
|
||||||
// If you want to use Phoenix channels, run `mix help phx.gen.channel`
|
// If you want to use Phoenix channels, run `mix help phx.gen.channel`
|
||||||
// to get started and then uncomment the line below.
|
// to get started and then uncomment the line below.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"daisyui": "^2.24.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// See the Tailwind configuration guide for advanced usage
|
||||||
|
// https://tailwindcss.com/docs/configuration
|
||||||
|
|
||||||
|
let plugin = require('tailwindcss/plugin')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
content: [
|
||||||
|
'./js/**/*.js',
|
||||||
|
'../lib/*_web.ex',
|
||||||
|
'../lib/*_web/**/*.*ex'
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
container: {
|
||||||
|
center: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
daisyui: {
|
||||||
|
themes: ["black"],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
require("@tailwindcss/typography"),
|
||||||
|
require('@tailwindcss/forms'),
|
||||||
|
plugin(({addVariant}) => addVariant('phx-no-feedback', ['&.phx-no-feedback', '.phx-no-feedback &'])),
|
||||||
|
plugin(({addVariant}) => addVariant('phx-click-loading', ['&.phx-click-loading', '.phx-click-loading &'])),
|
||||||
|
plugin(({addVariant}) => addVariant('phx-submit-loading', ['&.phx-submit-loading', '.phx-submit-loading &'])),
|
||||||
|
plugin(({addVariant}) => addVariant('phx-change-loading', ['&.phx-change-loading', '.phx-change-loading &'])),
|
||||||
|
require('daisyui')
|
||||||
|
]
|
||||||
|
}
|
|
@ -10,9 +10,11 @@ import Config
|
||||||
config :diffuser,
|
config :diffuser,
|
||||||
ecto_repos: [Diffuser.Repo]
|
ecto_repos: [Diffuser.Repo]
|
||||||
|
|
||||||
|
config :diffuser, Diffuser.Repo, migration_primary_key: [name: :id, type: :binary_id]
|
||||||
|
|
||||||
# Configures the endpoint
|
# Configures the endpoint
|
||||||
config :diffuser, DiffuserWeb.Endpoint,
|
config :diffuser, DiffuserWeb.Endpoint,
|
||||||
url: [host: "https://ai.silentsilas.com"],
|
url: [host: "127.0.0.1", port: 4000],
|
||||||
render_errors: [view: DiffuserWeb.ErrorView, accepts: ~w(html json), layout: false],
|
render_errors: [view: DiffuserWeb.ErrorView, accepts: ~w(html json), layout: false],
|
||||||
pubsub_server: Diffuser.PubSub,
|
pubsub_server: Diffuser.PubSub,
|
||||||
check_origin: false,
|
check_origin: false,
|
||||||
|
@ -52,7 +54,18 @@ config :phoenix, :json_library, Jason
|
||||||
config :waffle,
|
config :waffle,
|
||||||
storage: Waffle.Storage.Local,
|
storage: Waffle.Storage.Local,
|
||||||
# or {:system, "ASSET_HOST"}
|
# or {:system, "ASSET_HOST"}
|
||||||
asset_host: "https://ai.silentsilas.com"
|
asset_host: "http://gen.silentsilas.com"
|
||||||
|
|
||||||
|
config :tailwind,
|
||||||
|
version: "3.1.6",
|
||||||
|
default: [
|
||||||
|
args: ~w(
|
||||||
|
--config=tailwind.config.js
|
||||||
|
--input=css/app.css
|
||||||
|
--output=../priv/static/assets/app.css
|
||||||
|
),
|
||||||
|
cd: Path.expand("../assets", __DIR__)
|
||||||
|
]
|
||||||
|
|
||||||
# Import environment specific config. This must remain at the bottom
|
# Import environment specific config. This must remain at the bottom
|
||||||
# of this file so it overrides the configuration defined above.
|
# of this file so it overrides the configuration defined above.
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Config
|
||||||
|
|
||||||
# Configure your database
|
# Configure your database
|
||||||
config :diffuser, Diffuser.Repo,
|
config :diffuser, Diffuser.Repo,
|
||||||
|
migration_primary_key: [name: :id, type: :binary_id],
|
||||||
username: "postgres",
|
username: "postgres",
|
||||||
password: "postgres",
|
password: "postgres",
|
||||||
hostname: "localhost",
|
hostname: "localhost",
|
||||||
|
@ -26,7 +27,8 @@ config :diffuser, DiffuserWeb.Endpoint,
|
||||||
secret_key_base: "mLAQpOCHwqL2jI5ljtcULJFRTpsXnvFSjLRM3HjFYJF8Rc1Uqh6o7PT7GaIQ+ERA",
|
secret_key_base: "mLAQpOCHwqL2jI5ljtcULJFRTpsXnvFSjLRM3HjFYJF8Rc1Uqh6o7PT7GaIQ+ERA",
|
||||||
watchers: [
|
watchers: [
|
||||||
# Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
|
# Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
|
||||||
esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]}
|
esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
|
||||||
|
tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]}
|
||||||
]
|
]
|
||||||
|
|
||||||
# ## SSL Support
|
# ## SSL Support
|
||||||
|
@ -73,3 +75,7 @@ config :phoenix, :stacktrace_depth, 20
|
||||||
|
|
||||||
# Initialize plugs at runtime for faster development compilation
|
# Initialize plugs at runtime for faster development compilation
|
||||||
config :phoenix, :plug_init_mode, :runtime
|
config :phoenix, :plug_init_mode, :runtime
|
||||||
|
|
||||||
|
if File.exists?("config/dev.secret.exs") do
|
||||||
|
import_config "dev.secret.exs"
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,205 @@
|
||||||
|
defmodule Diffuser.Accounts do
|
||||||
|
@moduledoc """
|
||||||
|
The Accounts context.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import Ecto.Query, warn: false
|
||||||
|
alias Diffuser.Repo
|
||||||
|
|
||||||
|
alias Diffuser.Accounts.User
|
||||||
|
alias Diffuser.Generator.PromptRequest
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns the list of users.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> list_users()
|
||||||
|
[%User{}, ...]
|
||||||
|
|
||||||
|
"""
|
||||||
|
def list_users do
|
||||||
|
Repo.all(User)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets a single user.
|
||||||
|
|
||||||
|
Raises `Ecto.NoResultsError` if the User does not exist.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> get_user!(123)
|
||||||
|
%User{}
|
||||||
|
|
||||||
|
iex> get_user!(456)
|
||||||
|
** (Ecto.NoResultsError)
|
||||||
|
|
||||||
|
"""
|
||||||
|
def get_user!(id), do: Repo.get!(User, id) |> Repo.preload(votes: [:prompt_request])
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Creates a user.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> create_user(%{field: value})
|
||||||
|
{:ok, %User{}}
|
||||||
|
|
||||||
|
iex> create_user(%{field: bad_value})
|
||||||
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def create_user(attrs \\ %{}) do
|
||||||
|
%User{}
|
||||||
|
|> User.changeset(attrs)
|
||||||
|
|> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Updates a user.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> update_user(user, %{field: new_value})
|
||||||
|
{:ok, %User{}}
|
||||||
|
|
||||||
|
iex> update_user(user, %{field: bad_value})
|
||||||
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def update_user(%User{} = user, attrs) do
|
||||||
|
user
|
||||||
|
|> User.changeset(attrs)
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Deletes a user.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> delete_user(user)
|
||||||
|
{:ok, %User{}}
|
||||||
|
|
||||||
|
iex> delete_user(user)
|
||||||
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def delete_user(%User{} = user) do
|
||||||
|
Repo.delete(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns an `%Ecto.Changeset{}` for tracking user changes.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> change_user(user)
|
||||||
|
%Ecto.Changeset{data: %User{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def change_user(%User{} = user, attrs \\ %{}) do
|
||||||
|
User.changeset(user, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
alias Diffuser.Accounts.Vote
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns the list of votes.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> list_votes()
|
||||||
|
[%Vote{}, ...]
|
||||||
|
|
||||||
|
"""
|
||||||
|
def list_votes do
|
||||||
|
Repo.all(Vote)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets a single vote.
|
||||||
|
|
||||||
|
Raises `Ecto.NoResultsError` if the Vote does not exist.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> get_vote!(123)
|
||||||
|
%Vote{}
|
||||||
|
|
||||||
|
iex> get_vote!(456)
|
||||||
|
** (Ecto.NoResultsError)
|
||||||
|
|
||||||
|
"""
|
||||||
|
def get_vote!(id), do: Repo.get!(Vote, id)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Creates a vote.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> create_vote(%{field: value})
|
||||||
|
{:ok, %Vote{}}
|
||||||
|
|
||||||
|
iex> create_vote(%{field: bad_value})
|
||||||
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def create_vote(%{user: user} = attrs) do
|
||||||
|
%Vote{}
|
||||||
|
|> Vote.changeset(attrs)
|
||||||
|
|> Ecto.Changeset.put_assoc(:user, user)
|
||||||
|
|> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Updates a vote.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> update_vote(vote, %{field: new_value})
|
||||||
|
{:ok, %Vote{}}
|
||||||
|
|
||||||
|
iex> update_vote(vote, %{field: bad_value})
|
||||||
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def update_vote(%Vote{} = vote, attrs) do
|
||||||
|
vote
|
||||||
|
|> Vote.changeset(attrs)
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Deletes a vote.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> delete_vote(vote)
|
||||||
|
{:ok, %Vote{}}
|
||||||
|
|
||||||
|
iex> delete_vote(vote)
|
||||||
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def delete_vote(%Vote{} = vote) do
|
||||||
|
Repo.delete(vote)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns an `%Ecto.Changeset{}` for tracking vote changes.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> change_vote(vote)
|
||||||
|
%Ecto.Changeset{data: %Vote{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def change_vote(%Vote{} = vote, attrs \\ %{}) do
|
||||||
|
Vote.changeset(vote, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
def upvote(user, %PromptRequest{id: pr}),
|
||||||
|
do: create_vote(%{prompt_request_id: pr, user: user})
|
||||||
|
end
|
|
@ -0,0 +1,25 @@
|
||||||
|
defmodule Diffuser.Accounts.User do
|
||||||
|
use Diffuser.Schema
|
||||||
|
import Ecto.Changeset
|
||||||
|
alias Diffuser.Accounts.Vote
|
||||||
|
alias Diffuser.Generator.PromptRequest
|
||||||
|
|
||||||
|
schema "users" do
|
||||||
|
field :ip_address, :string
|
||||||
|
field :username, :string
|
||||||
|
field :code, :string
|
||||||
|
|
||||||
|
has_many :votes, Vote, on_delete: :delete_all
|
||||||
|
has_many :prompt_requests, PromptRequest, on_delete: :delete_all
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def changeset(user, attrs) do
|
||||||
|
user
|
||||||
|
|> cast(attrs, [:ip_address, :username, :code])
|
||||||
|
|> cast_assoc(:votes)
|
||||||
|
|> cast_assoc(:prompt_requests)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,20 @@
|
||||||
|
defmodule Diffuser.Accounts.Vote do
|
||||||
|
use Diffuser.Schema
|
||||||
|
import Ecto.Changeset
|
||||||
|
alias Diffuser.Generator.PromptRequest
|
||||||
|
alias Diffuser.Accounts.User
|
||||||
|
|
||||||
|
schema "votes" do
|
||||||
|
belongs_to :prompt_request, PromptRequest
|
||||||
|
belongs_to :user, User
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def changeset(vote, attrs) do
|
||||||
|
vote
|
||||||
|
|> cast(attrs, [:prompt_request_id, :user_id])
|
||||||
|
|> validate_required([])
|
||||||
|
end
|
||||||
|
end
|
|
@ -36,7 +36,9 @@ defmodule Diffuser.Generator do
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def get_prompt_request!(id),
|
def get_prompt_request!(id),
|
||||||
do: Repo.one!(from pr in PromptRequest, where: pr.id == ^id) |> Repo.preload(:images)
|
do:
|
||||||
|
Repo.one!(from pr in PromptRequest, where: pr.id == ^id)
|
||||||
|
|> Repo.preload([:votes, :images, :user])
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Creates a prompt_request.
|
Creates a prompt_request.
|
||||||
|
@ -50,9 +52,18 @@ defmodule Diffuser.Generator do
|
||||||
{:error, %Ecto.Changeset{}}
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def create_prompt_request(attrs \\ %{}) do
|
def create_prompt_request(%{"user" => user} = attrs) do
|
||||||
|
user = Diffuser.Accounts.get_user!(user)
|
||||||
|
code = Map.get(user, :code, nil)
|
||||||
|
|
||||||
|
attrs =
|
||||||
|
if is_nil(code),
|
||||||
|
do: attrs |> Map.put("code", "garbage"),
|
||||||
|
else: attrs |> Map.put("code", code)
|
||||||
|
|
||||||
%PromptRequest{}
|
%PromptRequest{}
|
||||||
|> PromptRequest.changeset(attrs)
|
|> PromptRequest.changeset(attrs)
|
||||||
|
|> Ecto.Changeset.put_assoc(:user, user)
|
||||||
|> Repo.insert()
|
|> Repo.insert()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -74,6 +85,9 @@ defmodule Diffuser.Generator do
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_prompt_request(id, attrs),
|
||||||
|
do: get_prompt_request!(id) |> update_prompt_request(attrs)
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Deletes a prompt_request.
|
Deletes a prompt_request.
|
||||||
|
|
||||||
|
@ -123,9 +137,14 @@ defmodule Diffuser.Generator do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginate_prompt_requests(params) do
|
def paginate_prompt_requests(query \\ PromptRequest, params) do
|
||||||
PromptRequest
|
search_query = Map.get(params, :query, nil)
|
||||||
|> preload(:images)
|
order_by_query = Map.get(params, :order_by, nil)
|
||||||
|
|
||||||
|
query
|
||||||
|
|> PromptRequest.search_by_prompt_or_user(search_query)
|
||||||
|
|> PromptRequest.order_by(order_by_query)
|
||||||
|
|> preload([:votes, :user, :images])
|
||||||
|> Repo.paginate(params)
|
|> Repo.paginate(params)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
defmodule Diffuser.Generator.PromptRequest do
|
defmodule Diffuser.Generator.PromptRequest do
|
||||||
use Ecto.Schema
|
use Diffuser.Schema
|
||||||
use Waffle.Ecto.Schema
|
use Waffle.Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
alias Diffuser.Generator.PromptRequestResult
|
alias Diffuser.Generator.PromptRequestResult
|
||||||
|
alias Diffuser.Accounts.{User, Vote}
|
||||||
|
|
||||||
|
@valid_codes Application.get_env(:diffuser, :valid_codes, [])
|
||||||
|
|
||||||
@primary_key {:id, Ecto.UUID, autogenerate: true}
|
|
||||||
schema "prompt_requests" do
|
schema "prompt_requests" do
|
||||||
field :prompt, :string
|
field :prompt, :string
|
||||||
field :status, :string, default: "queued"
|
field :status, :string, default: "queued"
|
||||||
field :steps, :integer
|
field :steps, :integer, default: 32
|
||||||
field :guidance_scale, :float
|
field :completed_steps, :integer, default: 0
|
||||||
|
field :guidance_scale, :float, default: 7.5
|
||||||
|
field :began_at, :utc_datetime
|
||||||
|
field :ended_at, :utc_datetime
|
||||||
|
field :code, :string
|
||||||
|
|
||||||
has_many :images, PromptRequestResult, on_delete: :delete_all
|
has_many :images, PromptRequestResult, on_delete: :delete_all
|
||||||
|
has_many :votes, Vote, on_delete: :delete_all
|
||||||
|
belongs_to :user, User
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
@ -19,7 +28,63 @@ defmodule Diffuser.Generator.PromptRequest do
|
||||||
@doc false
|
@doc false
|
||||||
def changeset(prompt_request, attrs) do
|
def changeset(prompt_request, attrs) do
|
||||||
prompt_request
|
prompt_request
|
||||||
|> cast(attrs, [:prompt, :status, :steps, :guidance_scale])
|
|> cast(attrs, [
|
||||||
|
:prompt,
|
||||||
|
:status,
|
||||||
|
:steps,
|
||||||
|
:guidance_scale,
|
||||||
|
:completed_steps,
|
||||||
|
:began_at,
|
||||||
|
:ended_at,
|
||||||
|
:code
|
||||||
|
])
|
||||||
|
|> validate_number(:steps, less_than_or_equal_to: 60)
|
||||||
|
|> validate_code()
|
||||||
|> validate_required([:prompt])
|
|> validate_required([:prompt])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp validate_code(changeset) do
|
||||||
|
validate_change(changeset, :code, fn :code, code ->
|
||||||
|
cond do
|
||||||
|
@valid_codes
|
||||||
|
|> Enum.any?(fn {_name, valid_code} ->
|
||||||
|
valid_code == code
|
||||||
|
end) ->
|
||||||
|
[]
|
||||||
|
|
||||||
|
true ->
|
||||||
|
[code: "Code is invalid."]
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_by_prompt_or_user(query, nil), do: query
|
||||||
|
|
||||||
|
def search_by_prompt_or_user(query, search_query) do
|
||||||
|
from p in query,
|
||||||
|
left_join: u in User,
|
||||||
|
on: u.id == p.user_id,
|
||||||
|
where: ilike(p.prompt, ^"%#{search_query}%")
|
||||||
|
end
|
||||||
|
|
||||||
|
def order_by(query, nil) do
|
||||||
|
from p in query,
|
||||||
|
order_by: [desc: p.inserted_at]
|
||||||
|
end
|
||||||
|
|
||||||
|
def order_by(query, "desc") do
|
||||||
|
from p in query,
|
||||||
|
join: v in Vote,
|
||||||
|
on: v.prompt_request_id == p.id,
|
||||||
|
group_by: p.id,
|
||||||
|
order_by: [desc: count(v.id)]
|
||||||
|
end
|
||||||
|
|
||||||
|
def order_by(query, "asc") do
|
||||||
|
from p in query,
|
||||||
|
left_join: v in Vote,
|
||||||
|
on: v.prompt_request_id == p.id,
|
||||||
|
group_by: p.id,
|
||||||
|
order_by: [asc: count(v.id)]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
defmodule Diffuser.Generator.PromptRequestResult do
|
defmodule Diffuser.Generator.PromptRequestResult do
|
||||||
use Ecto.Schema
|
use Diffuser.Schema
|
||||||
use Waffle.Ecto.Schema
|
use Waffle.Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
alias Diffuser.Generator.PromptRequest
|
alias Diffuser.Generator.PromptRequest
|
||||||
|
|
||||||
@primary_key {:id, Ecto.UUID, autogenerate: true}
|
|
||||||
schema "prompt_request_results" do
|
schema "prompt_request_results" do
|
||||||
field :image, Diffuser.Uploaders.Image.Type
|
field :image, Diffuser.Uploaders.Image.Type
|
||||||
belongs_to :prompt_request, PromptRequest, type: :binary_id
|
belongs_to :prompt_request, PromptRequest
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,8 +5,6 @@ defmodule Diffuser.Generator.PromptRequestWorker do
|
||||||
alias Diffuser.Repo
|
alias Diffuser.Repo
|
||||||
|
|
||||||
@path [:code.priv_dir(:diffuser), "python"] |> Path.join()
|
@path [:code.priv_dir(:diffuser), "python"] |> Path.join()
|
||||||
@steps 10
|
|
||||||
@guidance_scale 7.5
|
|
||||||
|
|
||||||
def start(%PromptRequest{} = prompt_request) do
|
def start(%PromptRequest{} = prompt_request) do
|
||||||
with {:ok, active_prompt} <-
|
with {:ok, active_prompt} <-
|
||||||
|
@ -26,24 +24,38 @@ defmodule Diffuser.Generator.PromptRequestWorker do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp update_and_broadcast_progress(%PromptRequest{id: id} = prompt_request, new_status) do
|
defp update_and_broadcast_progress(prompt_request, "in_progress"),
|
||||||
{:ok, new_prompt} =
|
do:
|
||||||
Generator.update_prompt_request(prompt_request, %{
|
update_and_broadcast_progress(prompt_request, %{
|
||||||
status: new_status,
|
status: "in_progress",
|
||||||
steps: @steps,
|
began_at: NaiveDateTime.utc_now()
|
||||||
guidance_scale: @guidance_scale
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defp update_and_broadcast_progress(prompt_request, "finished"),
|
||||||
|
do:
|
||||||
|
update_and_broadcast_progress(prompt_request, %{
|
||||||
|
status: "finished",
|
||||||
|
ended_at: NaiveDateTime.utc_now()
|
||||||
|
})
|
||||||
|
|
||||||
|
defp update_and_broadcast_progress(%PromptRequest{id: id} = prompt_request, attrs) do
|
||||||
|
{:ok, new_prompt} = Generator.update_prompt_request(prompt_request, attrs)
|
||||||
|
|
||||||
:ok = Endpoint.broadcast("request:#{id}", "request", %{prompt_request: new_prompt})
|
:ok = Endpoint.broadcast("request:#{id}", "request", %{prompt_request: new_prompt})
|
||||||
|
|
||||||
{:ok, new_prompt}
|
{:ok, new_prompt}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp call_python(_module, _func, %{id: prompt_id, prompt: prompt}) do
|
defp call_python(_module, _func, %PromptRequest{
|
||||||
|
id: prompt_id,
|
||||||
|
prompt: prompt,
|
||||||
|
steps: steps,
|
||||||
|
guidance_scale: guidance_scale
|
||||||
|
}) do
|
||||||
port =
|
port =
|
||||||
Port.open(
|
Port.open(
|
||||||
{:spawn,
|
{:spawn,
|
||||||
~s(python #{@path}/stable_diffusion.py --prompt "#{prompt}" --output "#{@path}/#{prompt_id}.png" --num-inference-steps #{@steps})},
|
~s(python #{@path}/stable_diffusion.py --prompt "#{prompt}" --output "#{@path}/#{prompt_id}.png" --num-inference-steps #{steps} --guidance-scale #{guidance_scale})},
|
||||||
[:binary]
|
[:binary]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,11 +68,18 @@ defmodule Diffuser.Generator.PromptRequestWorker do
|
||||||
{:ok, msg}
|
{:ok, msg}
|
||||||
|
|
||||||
{^port, {:data, ":step" <> step}} ->
|
{^port, {:data, ":step" <> step}} ->
|
||||||
Endpoint.broadcast("request:#{prompt_id}", "progress", step)
|
{:ok, prompt_request} =
|
||||||
|
Generator.update_prompt_request(prompt_id, %{completed_steps: step})
|
||||||
|
|
||||||
|
Endpoint.broadcast(
|
||||||
|
"request:#{prompt_id}",
|
||||||
|
"progress",
|
||||||
|
%{prompt_request: prompt_request |> Repo.preload(:images)}
|
||||||
|
)
|
||||||
|
|
||||||
python_loop(port, prompt_id)
|
python_loop(port, prompt_id)
|
||||||
|
|
||||||
{^port, result} ->
|
{^port, _result} ->
|
||||||
IO.inspect(result, label: "RESULT")
|
|
||||||
python_loop(port, prompt_id)
|
python_loop(port, prompt_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,5 +3,5 @@ defmodule Diffuser.Repo do
|
||||||
otp_app: :diffuser,
|
otp_app: :diffuser,
|
||||||
adapter: Ecto.Adapters.Postgres
|
adapter: Ecto.Adapters.Postgres
|
||||||
|
|
||||||
use Scrivener, page_size: 1
|
use Scrivener, page_size: 12
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Diffuser.Schema do
|
||||||
|
defmacro __using__(_) do
|
||||||
|
quote do
|
||||||
|
use Ecto.Schema
|
||||||
|
@primary_key {:id, :binary_id, autogenerate: true}
|
||||||
|
@foreign_key_type :binary_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,8 +1,7 @@
|
||||||
defmodule DiffuserWeb.RequestChannel do
|
defmodule DiffuserWeb.RequestChannel do
|
||||||
use Phoenix.Channel
|
use Phoenix.Channel
|
||||||
|
|
||||||
def join("request:" <> room_id, _message, socket) do
|
def join("request:" <> _room_id, _message, socket) do
|
||||||
IO.inspect(room_id)
|
|
||||||
{:ok, socket}
|
{:ok, socket}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,6 @@ defmodule DiffuserWeb.PromptRequestLive.FormComponent do
|
||||||
use DiffuserWeb, :live_component
|
use DiffuserWeb, :live_component
|
||||||
|
|
||||||
alias Diffuser.Generator
|
alias Diffuser.Generator
|
||||||
alias Diffuser.Generator.PromptRequest
|
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def update(%{prompt_request: prompt_request} = assigns, socket) do
|
def update(%{prompt_request: prompt_request} = assigns, socket) do
|
||||||
|
@ -28,30 +27,23 @@ defmodule DiffuserWeb.PromptRequestLive.FormComponent do
|
||||||
save_prompt_request(socket, socket.assigns.action, prompt_request_params)
|
save_prompt_request(socket, socket.assigns.action, prompt_request_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp save_prompt_request(socket, :edit, prompt_request_params) do
|
defp save_prompt_request(%{assigns: %{user: _user}} = socket, :new, prompt_request_params) do
|
||||||
case Generator.update_prompt_request(socket.assigns.prompt_request, prompt_request_params) do
|
|
||||||
{:ok, %PromptRequest{} = prompt_request} ->
|
|
||||||
{:noreply,
|
|
||||||
socket
|
|
||||||
|> put_flash(:info, "Prompt request updated successfully")
|
|
||||||
|> push_redirect(to: Routes.prompt_request_show_path(socket, :show, prompt_request))}
|
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
|
||||||
{:noreply, assign(socket, :changeset, changeset)}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp save_prompt_request(socket, :new, prompt_request_params) do
|
|
||||||
with {:ok, prompt_request} <- Generator.create_prompt_request(prompt_request_params) do
|
with {:ok, prompt_request} <- Generator.create_prompt_request(prompt_request_params) do
|
||||||
Diffuser.Generator.PromptRequestQueue.enqueue(prompt_request)
|
Diffuser.Generator.PromptRequestQueue.enqueue(prompt_request)
|
||||||
|
|
||||||
{:noreply,
|
{:noreply,
|
||||||
socket
|
socket
|
||||||
|> put_flash(:info, "Prompt request created successfully")
|
|> put_flash(:info, "Prompt request created successfully")
|
||||||
|> push_redirect(to: Routes.prompt_request_show_path(socket, :show, prompt_request))}
|
|> push_redirect(to: Routes.prompt_request_index_path(socket, :index))}
|
||||||
else
|
else
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
{:error, %Ecto.Changeset{} = changeset} ->
|
||||||
{:noreply, assign(socket, changeset: changeset)}
|
{:noreply, assign(socket, changeset: changeset)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp save_prompt_request(socket, :new, _) do
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> push_redirect(to: Routes.prompt_request_index_path(socket, :index))}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,20 +1,41 @@
|
||||||
<div>
|
<div class="prose grid grid-flow-col">
|
||||||
<h2><%= @title %></h2>
|
<!-- Put this part before </body> tag -->
|
||||||
|
<.form
|
||||||
|
let={f}
|
||||||
|
for={@changeset}
|
||||||
|
id="prompt_request-form"
|
||||||
|
phx-target={@myself}
|
||||||
|
phx-change="validate"
|
||||||
|
phx-submit="save">
|
||||||
|
|
||||||
<.form
|
<div class="form-control w-full">
|
||||||
let={f}
|
<label class="label">
|
||||||
for={@changeset}
|
<span class="label-text">What shall we dream of?</span>
|
||||||
id="prompt_request-form"
|
</label>
|
||||||
phx-target={@myself}
|
<%= text_input f, :prompt, class: "input" %>
|
||||||
phx-change="validate"
|
<%= error_tag f, :prompt %>
|
||||||
phx-submit="save">
|
</div>
|
||||||
|
|
||||||
<%= label f, :prompt %>
|
<div class="form-control w-full">
|
||||||
<%= text_input f, :prompt %>
|
<label class="label">
|
||||||
<%= error_tag f, :prompt %>
|
<span class="label-text">How long should it dream?</span>
|
||||||
|
</label>
|
||||||
<div>
|
<%= number_input f, :steps, class: "input", min: 1, max: 60, step: 1 %>
|
||||||
<%= submit "Save", phx_disable_with: "Saving..." %>
|
<%= error_tag f, :prompt %>
|
||||||
</div>
|
</div>
|
||||||
</.form>
|
|
||||||
|
<div class="form-control w-full">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text">How much do your words control the dream?</span>
|
||||||
|
</label>
|
||||||
|
<%= number_input f, :guidance_scale, class: "input", step: 0.1 %>
|
||||||
|
<%= error_tag f, :prompt %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= hidden_input f, :user, value: @user.id %>
|
||||||
|
|
||||||
|
<div style="text-align: end;" class="pt-6">
|
||||||
|
<%= submit "Save", phx_disable_with: "Saving...", class: "btn" %>
|
||||||
|
</div>
|
||||||
|
</.form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,46 +3,204 @@ defmodule DiffuserWeb.PromptRequestLive.Index do
|
||||||
|
|
||||||
alias Diffuser.Generator
|
alias Diffuser.Generator
|
||||||
alias Diffuser.Generator.PromptRequest
|
alias Diffuser.Generator.PromptRequest
|
||||||
|
alias Diffuser.Accounts
|
||||||
|
alias Diffuser.Accounts.User
|
||||||
|
alias DiffuserWeb.Endpoint
|
||||||
|
alias Phoenix.Socket.Broadcast
|
||||||
|
alias Diffuser.Repo
|
||||||
|
alias Ecto.Association.NotLoaded
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def mount(_params, _session, socket) do
|
def mount(params, %{"user" => user, "is_admin" => is_admin}, socket) do
|
||||||
{:ok, socket}
|
changeset = Accounts.change_user(user)
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
socket
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:params, params)
|
||||||
|
|> assign(:is_admin, is_admin)
|
||||||
|
|> assign(:user_changeset, changeset)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_params(params, _url, socket) do
|
def handle_params(params, _url, socket) do
|
||||||
page = list_prompt_requests(params)
|
page = list_prompt_requests(params) |> subscribe_to_queued_prompts()
|
||||||
socket = socket |> apply_action(socket.assigns.live_action, params) |> assign(:page, page)
|
|
||||||
|
socket =
|
||||||
|
socket
|
||||||
|
|> apply_action(socket.assigns.live_action, params)
|
||||||
|
|> assign(:page, page)
|
||||||
|
|> assign(:params, params)
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp apply_action(socket, :edit, %{"id" => id}) do
|
defp apply_action(socket, :edit, %{"id" => id}) do
|
||||||
socket
|
socket
|
||||||
|> assign(:page_title, "Edit Prompt request")
|
|> assign(:page_title, "Edit Prompt")
|
||||||
|> assign(:prompt_request, Generator.get_prompt_request!(id))
|
|> assign(:prompt_request, Generator.get_prompt_request!(id))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp apply_action(socket, :new, _params) do
|
defp apply_action(socket, :new, _params) do
|
||||||
socket
|
socket
|
||||||
|> assign(:page_title, "New Prompt request")
|
|> assign(:page_title, "New Prompt")
|
||||||
|> assign(:prompt_request, %PromptRequest{})
|
|> assign(:prompt_request, %PromptRequest{})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp apply_action(socket, :index, _params) do
|
defp apply_action(socket, :index, _params) do
|
||||||
socket
|
socket
|
||||||
|> assign(:page_title, "Listing Prompt requests")
|
|> assign(:page_title, "Listing Prompts")
|
||||||
|> assign(:prompt_request, nil)
|
|> assign(:prompt_request, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("delete", %{"id" => id}, socket) do
|
def handle_event("delete", %{"id" => id}, %{assigns: %{params: params}} = socket) do
|
||||||
prompt_request = Generator.get_prompt_request!(id)
|
prompt_request = Generator.get_prompt_request!(id)
|
||||||
{:ok, _} = Generator.delete_prompt_request(prompt_request)
|
{:ok, _} = Generator.delete_prompt_request(prompt_request)
|
||||||
|
|
||||||
{:noreply, assign(socket, :page, list_prompt_requests(%{"page" => "2"}))}
|
{:noreply, assign(socket, :page, list_prompt_requests(params))}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("upvote", %{"id" => id}, %{assigns: %{user: user, params: params}} = socket) do
|
||||||
|
prompt_request = Generator.get_prompt_request!(id)
|
||||||
|
Diffuser.Accounts.upvote(user, prompt_request)
|
||||||
|
|
||||||
|
{:noreply, assign(socket, :page, list_prompt_requests(params))}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event(
|
||||||
|
"update_user",
|
||||||
|
%{"user" => params},
|
||||||
|
%{assigns: %{user: user}} = socket
|
||||||
|
) do
|
||||||
|
case Accounts.update_user(user, params) do
|
||||||
|
{:ok, user} ->
|
||||||
|
{:noreply, assign(socket, :user, user)}
|
||||||
|
|
||||||
|
{:error, %Ecto.Changeset{} = changeset} ->
|
||||||
|
{:noreply, assign(socket, :user_changeset, changeset)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event(
|
||||||
|
"search",
|
||||||
|
%{"search" => %{"query" => query}},
|
||||||
|
%{assigns: %{params: params}} = socket
|
||||||
|
) do
|
||||||
|
params = Map.put(params, :query, query)
|
||||||
|
page = Generator.paginate_prompt_requests(PromptRequest, params)
|
||||||
|
{:noreply, socket |> assign(:params, params) |> assign(:page, page)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("clear", _, socket) do
|
||||||
|
page = Generator.paginate_prompt_requests(PromptRequest, %{page: 1})
|
||||||
|
{:noreply, socket |> assign(:params, %{page: 1}) |> assign(:page, page)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("order_by", %{"votes" => direction}, %{assigns: %{params: params}} = socket) do
|
||||||
|
direction = if direction in ["asc", "desc"], do: direction, else: nil
|
||||||
|
params = Map.put(params, :order_by, direction)
|
||||||
|
page = Generator.paginate_prompt_requests(PromptRequest, params)
|
||||||
|
{:noreply, socket |> assign(:params, params) |> assign(:page, page)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("go_to" = event, %{"jump" => %{"page" => _page} = result}, socket),
|
||||||
|
do: handle_event(event, result, socket)
|
||||||
|
|
||||||
|
def handle_event("go_to", %{"page" => page}, %{assigns: %{params: params}} = socket) do
|
||||||
|
params = Map.put(params, :page, page)
|
||||||
|
page = Generator.paginate_prompt_requests(PromptRequest, params)
|
||||||
|
{:noreply, socket |> assign(:params, params) |> assign(:page, page)}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp has_voted(nil, _), do: false
|
||||||
|
|
||||||
|
defp has_voted(%{id: user_id}, %{votes: prompt_request_votes}) do
|
||||||
|
prompt_request_votes
|
||||||
|
|> Enum.any?(fn %{user_id: id} ->
|
||||||
|
user_id == id
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp display_current_filters(params) do
|
||||||
|
query = Map.get(params, :query, nil)
|
||||||
|
order_by = Map.get(params, :order_by, nil)
|
||||||
|
page = Map.get(params, :page, 1)
|
||||||
|
|
||||||
|
query_str = if query, do: "Searching for \"#{query}\"", else: "Everything"
|
||||||
|
order_by_str = if order_by == "desc", do: " with most votes", else: ""
|
||||||
|
|
||||||
|
query_str <> order_by_str <> " on page #{page}"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp display_name(%User{username: username}) when not is_nil(username), do: username
|
||||||
|
|
||||||
|
defp display_name(%User{id: id}), do: id |> String.slice(0, 8)
|
||||||
|
|
||||||
|
defp fake_name(), do: for(_ <- 1..8, into: "", do: <<Enum.random('0123456789abcdef')>>)
|
||||||
|
|
||||||
defp list_prompt_requests(params) do
|
defp list_prompt_requests(params) do
|
||||||
Generator.paginate_prompt_requests(params)
|
Generator.paginate_prompt_requests(PromptRequest, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp total_time(%PromptRequest{began_at: began_at, ended_at: ended_at})
|
||||||
|
when not is_nil(began_at) and not is_nil(ended_at) do
|
||||||
|
"Processing Time: #{NaiveDateTime.diff(ended_at, began_at, :second)} seconds"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp total_time(_), do: ""
|
||||||
|
|
||||||
|
defp owns_prompt_request(user, %{user: %NotLoaded{} = prompt_request}),
|
||||||
|
do: user |> owns_prompt_request(prompt_request |> Repo.preload(:user))
|
||||||
|
|
||||||
|
defp owns_prompt_request(nil, _), do: false
|
||||||
|
|
||||||
|
defp owns_prompt_request(_, %{user: nil}), do: false
|
||||||
|
|
||||||
|
defp owns_prompt_request(%{ip_address: their_ip_address, id: their_id}, %{
|
||||||
|
user: %{ip_address: ip_address, id: id}
|
||||||
|
})
|
||||||
|
when their_ip_address == ip_address or their_id == id,
|
||||||
|
do: true
|
||||||
|
|
||||||
|
defp owns_prompt_request(_, _), do: false
|
||||||
|
|
||||||
|
defp subscribe_to_queued_prompts(%Scrivener.Page{entries: entries} = page) do
|
||||||
|
entries
|
||||||
|
|> Enum.each(fn %PromptRequest{id: id, status: status} ->
|
||||||
|
if status in ["queued", "in_progress"] do
|
||||||
|
Endpoint.subscribe("request:#{id}")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
page
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info(
|
||||||
|
%Broadcast{
|
||||||
|
topic: _,
|
||||||
|
event: event,
|
||||||
|
payload: %{prompt_request: %PromptRequest{} = prompt_request}
|
||||||
|
},
|
||||||
|
socket
|
||||||
|
)
|
||||||
|
when event in ["request", "progress"],
|
||||||
|
do: {:noreply, socket |> update_subscribed_prompt(prompt_request)}
|
||||||
|
|
||||||
|
defp update_subscribed_prompt(
|
||||||
|
%{assigns: %{page: %Scrivener.Page{entries: entries} = page}} = socket,
|
||||||
|
%PromptRequest{id: id} = prompt_request
|
||||||
|
) do
|
||||||
|
entries =
|
||||||
|
entries
|
||||||
|
|> Enum.map(
|
||||||
|
&if &1.id === id, do: prompt_request |> Repo.preload([:votes, :user, :images]), else: &1
|
||||||
|
)
|
||||||
|
|
||||||
|
page = Map.put(page, :entries, entries)
|
||||||
|
|
||||||
|
socket
|
||||||
|
|> assign(:page, page)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
<h1>Listing Prompt requests</h1>
|
|
||||||
|
|
||||||
<%= if @live_action in [:new, :edit] do %>
|
<%= if @live_action in [:new, :edit] do %>
|
||||||
<.modal return_to={Routes.prompt_request_index_path(@socket, :index)}>
|
<.modal return_to={Routes.prompt_request_index_path(@socket, :index)}>
|
||||||
<.live_component
|
<.live_component
|
||||||
|
@ -7,52 +5,134 @@
|
||||||
id={@prompt_request.id || :new}
|
id={@prompt_request.id || :new}
|
||||||
title={@page_title}
|
title={@page_title}
|
||||||
action={@live_action}
|
action={@live_action}
|
||||||
prompt_request={@prompt_request}}
|
prompt_request={@prompt_request}
|
||||||
|
user={@user}}
|
||||||
/>
|
/>
|
||||||
</.modal>
|
</.modal>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<table>
|
<%= if @user do %>
|
||||||
<thead>
|
<div class="p-4 max-w-7xl mx-auto bg-gray">
|
||||||
<tr>
|
<div class="collapse">
|
||||||
<th>Image</th>
|
<input type="checkbox" />
|
||||||
<th>Prompt</th>
|
<div class="collapse-title text-xl btn btn-outline">
|
||||||
|
Info
|
||||||
|
</div>
|
||||||
|
<div class="collapse-content prose">
|
||||||
|
<h3 class="pt-4">IP Address: <%= @user.ip_address %></h3>
|
||||||
|
<p>Yes. Your server, Silas, will keep a record of which IP sent what.</p>
|
||||||
|
|
||||||
<th></th>
|
<h3>Username: <%= display_name(@user) %></h3>
|
||||||
</tr>
|
<.form let={f} for={@user_changeset} phx-submit="update_user">
|
||||||
</thead>
|
<div class="form-control w-full">
|
||||||
<tbody id="prompt_requests">
|
<label class="label">
|
||||||
<%= for prompt_request <- @page.entries do %>
|
<span class="label-text">Optionally set your own username. Might I recommend <%= fake_name() %>?</span>
|
||||||
<tr id={"prompt_request-#{prompt_request.id}"}>
|
</label>
|
||||||
<td>
|
<%= text_input f, :username, class: "input input-bordered input-secondary" %>
|
||||||
<%= for result <- prompt_request.images do %>
|
<%= error_tag f, :username %>
|
||||||
<img src={"#{Diffuser.Uploaders.Image.url({result.image, result})}"} />
|
|
||||||
<% end %>
|
|
||||||
</td>
|
|
||||||
<td><%= prompt_request.prompt %></td>
|
|
||||||
|
|
||||||
<td>
|
<%= submit "Save Username", class: "btn" %>
|
||||||
<span><%= live_redirect "Show", to: Routes.prompt_request_show_path(@socket, :show, prompt_request) %></span>
|
</div>
|
||||||
<span><%= live_patch "Edit", to: Routes.prompt_request_index_path(@socket, :edit, prompt_request) %></span>
|
</.form>
|
||||||
<span><%= link "Delete", to: "#", phx_click: "delete", phx_value_id: prompt_request.id, data: [confirm: "Are you sure?"] %></span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<% end %>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div class="pagination">
|
<h3>Code: <%= if Map.get(@user, :code, nil), do: @user.code, else: "Not set" %></h3>
|
||||||
<%= if @page.page_number > 1 do %>
|
<.form let={f} for={@user_changeset} phx-submit="update_user">
|
||||||
<%= live_patch "<< Prev Page",
|
<div class="form-control w-full">
|
||||||
to: Routes.prompt_request_index_path(@socket, :index, page: @page.page_number - 1),
|
<label class="label">
|
||||||
class: "pagination-link" %>
|
<span class="label-text">You'll need a code from Silas to use the generator.</span>
|
||||||
<% end %>
|
</label>
|
||||||
|
<%= text_input f, :code, class: "input input-bordered input-secondary" %>
|
||||||
<%= if @page.page_number < @page.total_pages do %>
|
<%= error_tag f, :code %>
|
||||||
<%= live_patch "Next Page >>",
|
<%= submit "Save Code", class: "btn" %>
|
||||||
to: Routes.prompt_request_index_path(@socket, :index, page: @page.page_number + 1),
|
</div>
|
||||||
class: "pagination-link" %>
|
</.form>
|
||||||
<% end %>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<div class="p-4 max-w-7xl mx-auto grid place-items-center">
|
||||||
|
<form phx-change="search" style="width: 100%;">
|
||||||
|
<%= text_input :search, :query, placeholder: "Search for image by prompt", "phx-debounce": "1000", class: "input w-full center-placeholder" %>
|
||||||
|
</form>
|
||||||
|
<div class="pt-4" style="width: 100%; text-align: center;"><%= display_current_filters(@params) %></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span><%= live_patch "New Prompt request", to: Routes.prompt_request_index_path(@socket, :new) %></span>
|
<div class="max-w-7xl mx-auto">
|
||||||
|
<div class="p-4 flex space-x-2 justify-center">
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-outline btn-xs sm:btn-sm md:btn-md lg:btn-lg" style="min-height: 45px"><%= live_patch "New Prompt", to: Routes.prompt_request_index_path(@socket, :new), class: "pagination-link" %></button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button phx-click="order_by" phx-value-votes="desc" class="btn btn-xs sm:btn-sm md:btn-md lg:btn-lg btn-outline" style="min-height: 45px;">Most Votes</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button phx-click="clear" class="btn btn-xs sm:btn-sm md:btn-md lg:btn-lg btn-outline" style="min-height: 45px;">Reset Filter</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid gap-1 md:gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
<%= for prompt_request <- @page.entries do %>
|
||||||
|
<div class="card w-90 bg-gray shadow-xl text-primary-content">
|
||||||
|
<% result = if prompt_request.images |> length > 0, do: prompt_request.images |> List.first() %>
|
||||||
|
<%= if result do %>
|
||||||
|
<figure><img src={"#{Diffuser.Uploaders.Image.url({result.image, result})}"} /></figure>
|
||||||
|
<% else %>
|
||||||
|
<figure><%= prompt_request.status %>, <%= prompt_request.completed_steps %>/<%= prompt_request.steps %></figure>
|
||||||
|
<% end %>
|
||||||
|
<div class="card-body prose">
|
||||||
|
<h2 class="card-title"><%= prompt_request.prompt %></h2>
|
||||||
|
<p>Steps: <%= prompt_request.steps %>, Guidance Scale: <%= prompt_request.guidance_scale %><br />
|
||||||
|
<%= if prompt_request.status == "finished" do %>
|
||||||
|
<%= total_time(prompt_request) %><br />
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= case prompt_request.user do %>
|
||||||
|
<%= %NotLoaded{} -> %>
|
||||||
|
<% user -> %>
|
||||||
|
<%= if user do %>
|
||||||
|
Created by: <%= display_name(user) %> <%= if @is_admin, do: "(#{user.ip_address}, #{prompt_request.code})" %><br />
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
Votes: <%= prompt_request.votes |> Enum.count() %> <br />
|
||||||
|
|
||||||
|
<%= if has_voted(@user, prompt_request) do %>
|
||||||
|
<% else %>
|
||||||
|
<%= link "Upvote", to: "#", phx_click: "upvote", phx_value_id: prompt_request.id %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<%= if prompt_request.status == "finished" and (@is_admin or owns_prompt_request(@user, prompt_request)) do %>
|
||||||
|
<div class="card-actions justify-end">
|
||||||
|
<%= link "Delete", to: "#", phx_click: "delete", phx_value_id: prompt_request.id, data: [confirm: "Are you sure?"] %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-4 mx-auto grid place-items-center">
|
||||||
|
<span>Total results: <%= @page.total_entries %></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-group p-4 grid grid-cols-3 mx-auto pagination-btns">
|
||||||
|
<%= if @page.total_pages > 1 do %>
|
||||||
|
|
||||||
|
<%= if @page.page_number > 1 do %>
|
||||||
|
<%= link "<< Prev", to: "#", class: "btn btn-outline", phx_click: "go_to", phx_value_page: @page.page_number - 1 %>
|
||||||
|
<% else %>
|
||||||
|
<div class="btn btn-outline btn-disabled"></div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<form phx-change="go_to">
|
||||||
|
<%= select :jump, :page, 1..@page.total_pages, class: "select w-full max-w-xs", selected: @page.page_number %>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<%= if @page.page_number < @page.total_pages do %>
|
||||||
|
<%= link "Next >>", to: "#", class: "btn btn-outline", phx_click: "go_to", phx_value_page: @page.page_number + 1 %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
defmodule DiffuserWeb.PromptRequestLive.Show do
|
defmodule DiffuserWeb.PromptRequestLive.Show do
|
||||||
use DiffuserWeb, :live_view
|
use DiffuserWeb, :live_view
|
||||||
import Ecto.Query
|
|
||||||
|
|
||||||
alias Diffuser.Generator
|
alias Diffuser.Generator
|
||||||
alias Diffuser.Generator.{Image, PromptRequest}
|
alias Diffuser.Generator.PromptRequest
|
||||||
alias Diffuser.Repo
|
alias Diffuser.Repo
|
||||||
alias Phoenix.Socket.Broadcast
|
alias Phoenix.Socket.Broadcast
|
||||||
alias DiffuserWeb.Endpoint
|
alias DiffuserWeb.Endpoint
|
||||||
alias Ecto.Association.NotLoaded
|
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def mount(%{"id" => id}, _session, socket) do
|
def mount(%{"id" => id}, _session, socket) do
|
||||||
|
|
|
@ -1,18 +1,5 @@
|
||||||
<h1>Show Prompt request</h1>
|
<h1>Show Prompt request</h1>
|
||||||
|
|
||||||
<%= if @live_action in [:edit] do %>
|
|
||||||
<.modal return_to={Routes.prompt_request_show_path(@socket, :show, @prompt_request)}>
|
|
||||||
<.live_component
|
|
||||||
module={DiffuserWeb.PromptRequestLive.FormComponent}
|
|
||||||
id={@prompt_request.id}
|
|
||||||
title={@page_title}
|
|
||||||
action={@live_action}
|
|
||||||
prompt_request={@prompt_request}
|
|
||||||
return_to={Routes.prompt_request_show_path(@socket, :show, @prompt_request)}
|
|
||||||
/>
|
|
||||||
</.modal>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
|
@ -42,5 +29,4 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<span><%= live_patch "Edit", to: Routes.prompt_request_show_path(@socket, :edit, @prompt_request), class: "button" %></span> |
|
|
||||||
<span><%= live_redirect "Back", to: Routes.prompt_request_index_path(@socket, :index) %></span>
|
<span><%= live_redirect "Back", to: Routes.prompt_request_index_path(@socket, :index) %></span>
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
defmodule DiffuserWeb.Plugs.PublicIp do
|
||||||
|
@moduledoc "Get public IP address of request from x-forwarded-for header"
|
||||||
|
|
||||||
|
def init(opts), do: opts
|
||||||
|
|
||||||
|
def call(%{assigns: %{ip: _}} = conn, _opts), do: conn
|
||||||
|
|
||||||
|
def call(conn, _opts) do
|
||||||
|
process(conn, Plug.Conn.get_req_header(conn, "x-forwarded-for"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def process(%{remote_ip: remote_ip} = conn, []) do
|
||||||
|
Plug.Conn.assign(conn, :ip, to_string(:inet_parse.ntoa(remote_ip)))
|
||||||
|
end
|
||||||
|
|
||||||
|
def process(conn, vals) do
|
||||||
|
# Rewrite standard remote_ip field with value from header
|
||||||
|
ip_address = get_ip_address(conn, vals)
|
||||||
|
# See https://hexdocs.pm/plug/Plug.Conn.html
|
||||||
|
conn = %{conn | remote_ip: ip_address}
|
||||||
|
|
||||||
|
Plug.Conn.assign(conn, :ip, to_string(:inet_parse.ntoa(ip_address)))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_ip_address(conn, vals)
|
||||||
|
defp get_ip_address(%{remote_ip: remote_ip}, []), do: remote_ip
|
||||||
|
|
||||||
|
defp get_ip_address(%{remote_ip: remote_ip} = _conn, [val | _]) do
|
||||||
|
# Split into multiple values
|
||||||
|
comps =
|
||||||
|
val
|
||||||
|
|> String.split(~r{\s*,\s*}, trim: true)
|
||||||
|
# Get rid of "unknown" values
|
||||||
|
|> Enum.filter(&(&1 != "unknown"))
|
||||||
|
# Split IP from port, if any
|
||||||
|
|> Enum.map(&hd(String.split(&1, ":")))
|
||||||
|
# Filter out blanks
|
||||||
|
|> Enum.filter(&(&1 != ""))
|
||||||
|
# Parse address into :inet.ip_address tuple
|
||||||
|
|> Enum.map(&parse_address(&1))
|
||||||
|
# Elminate internal IP addreses, e.g. 192.168.1.1
|
||||||
|
|> Enum.filter(&is_public_ip(&1))
|
||||||
|
|
||||||
|
case comps do
|
||||||
|
[] -> remote_ip
|
||||||
|
[comp | _] -> comp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_address(ip) do
|
||||||
|
case :inet.parse_ipv4strict_address(to_charlist(ip)) do
|
||||||
|
{:ok, ip_address} -> ip_address
|
||||||
|
{:error, :einval} -> :einval
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp is_public_ip(ip_address) do
|
||||||
|
case ip_address do
|
||||||
|
{10, _, _, _} -> false
|
||||||
|
{192, 168, _, _} -> false
|
||||||
|
{172, second, _, _} when second >= 16 and second <= 31 -> false
|
||||||
|
{127, 0, 0, _} -> false
|
||||||
|
{_, _, _, _} -> true
|
||||||
|
:einval -> false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,37 @@
|
||||||
|
defmodule DiffuserWeb.Plugs.UserAuth do
|
||||||
|
use DiffuserWeb, :controller
|
||||||
|
import Plug.Conn
|
||||||
|
alias Diffuser.Accounts
|
||||||
|
alias Diffuser.Repo
|
||||||
|
|
||||||
|
def init(default), do: default
|
||||||
|
|
||||||
|
def call(%{remote_ip: remote_ip} = conn, _) do
|
||||||
|
remote_ip = remote_ip |> :inet_parse.ntoa() |> to_string()
|
||||||
|
|
||||||
|
case get_session(conn, :user) do
|
||||||
|
nil ->
|
||||||
|
with {:ok, user} <- Accounts.create_user(%{ip_address: remote_ip}) do
|
||||||
|
put_session(conn, :user, user |> Repo.preload(votes: [:prompt_request]))
|
||||||
|
else
|
||||||
|
{:error, _changeset} -> put_session(conn, :user, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
%Accounts.User{ip_address: user_ip} = user ->
|
||||||
|
with false <- user_ip == remote_ip,
|
||||||
|
{:ok, user} <- Accounts.update_user(user, %{ip_address: remote_ip}) do
|
||||||
|
conn |> put_session(:user, user |> Repo.preload(votes: [:prompt_request]))
|
||||||
|
else
|
||||||
|
true ->
|
||||||
|
put_session(
|
||||||
|
conn,
|
||||||
|
:user,
|
||||||
|
Accounts.get_user!(user.id) |> Repo.preload(votes: [:prompt_request])
|
||||||
|
)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
put_session(conn, :user, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,16 @@
|
||||||
|
defmodule DiffuserWeb.Plugs.ValidateAdmin do
|
||||||
|
use DiffuserWeb, :controller
|
||||||
|
import Plug.Conn
|
||||||
|
|
||||||
|
def init(default), do: default
|
||||||
|
|
||||||
|
def call(%{remote_ip: remote_ip} = conn, _) do
|
||||||
|
remote_ip = remote_ip |> :inet_parse.ntoa() |> to_string()
|
||||||
|
|
||||||
|
if remote_ip == "127.0.0.1" do
|
||||||
|
conn |> put_session(:is_admin, true)
|
||||||
|
else
|
||||||
|
conn |> put_session(:is_admin, false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule DiffuserWeb.Router do
|
defmodule DiffuserWeb.Router do
|
||||||
use DiffuserWeb, :router
|
use DiffuserWeb, :router
|
||||||
|
alias DiffuserWeb.Plugs.{PublicIp, UserAuth, ValidateAdmin}
|
||||||
|
|
||||||
pipeline :browser do
|
pipeline :browser do
|
||||||
plug :accepts, ["html"]
|
plug :accepts, ["html"]
|
||||||
|
@ -14,17 +15,18 @@ defmodule DiffuserWeb.Router do
|
||||||
plug :accepts, ["json"]
|
plug :accepts, ["json"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pipeline :authenticate do
|
||||||
|
plug PublicIp
|
||||||
|
plug UserAuth
|
||||||
|
plug ValidateAdmin
|
||||||
|
end
|
||||||
|
|
||||||
scope "/", DiffuserWeb do
|
scope "/", DiffuserWeb do
|
||||||
pipe_through :browser
|
pipe_through :browser
|
||||||
|
pipe_through :authenticate
|
||||||
|
|
||||||
get "/", PageController, :index
|
live "/", PromptRequestLive.Index, :index
|
||||||
|
live "/new", PromptRequestLive.Index, :new
|
||||||
live "/prompt_requests", PromptRequestLive.Index, :index
|
|
||||||
live "/prompt_requests/new", PromptRequestLive.Index, :new
|
|
||||||
live "/prompt_requests/:id/edit", PromptRequestLive.Index, :edit
|
|
||||||
|
|
||||||
live "/prompt_requests/:id", PromptRequestLive.Show, :show
|
|
||||||
live "/prompt_requests/:id/show/edit", PromptRequestLive.Show, :edit
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Other scopes may use custom stacks.
|
# Other scopes may use custom stacks.
|
||||||
|
@ -44,6 +46,7 @@ defmodule DiffuserWeb.Router do
|
||||||
|
|
||||||
scope "/" do
|
scope "/" do
|
||||||
pipe_through :browser
|
pipe_through :browser
|
||||||
|
pipe_through :authenticate
|
||||||
|
|
||||||
live_dashboard "/dashboard", metrics: DiffuserWeb.Telemetry
|
live_dashboard "/dashboard", metrics: DiffuserWeb.Telemetry
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<p class="alert alert-info" role="alert"
|
|
||||||
phx-click="lv:clear-flash"
|
|
||||||
phx-value-key="info"><%= live_flash(@flash, :info) %></p>
|
|
||||||
|
|
||||||
<p class="alert alert-danger" role="alert"
|
<p class="alert alert-danger" role="alert"
|
||||||
phx-click="lv:clear-flash"
|
phx-click="lv:clear-flash"
|
||||||
phx-value-key="error"><%= live_flash(@flash, :error) %></p>
|
phx-value-key="error"><%= live_flash(@flash, :error) %></p>
|
||||||
|
|
|
@ -1,30 +1,32 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" data-theme="black">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
|
||||||
<meta name="csrf-token" content={csrf_token_value()}>
|
<meta name="csrf-token" content={csrf_token_value()}>
|
||||||
<%= live_title_tag assigns[:page_title] || "Diffuser", suffix: " · Phoenix Framework" %>
|
<%= live_title_tag assigns[:page_title] || "Diffuser" %>
|
||||||
<link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/assets/app.css")}/>
|
<link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/assets/app.css")}/>
|
||||||
<script defer phx-track-static type="text/javascript" src={Routes.static_path(@conn, "/assets/app.js")}></script>
|
<script defer phx-track-static type="text/javascript" src={Routes.static_path(@conn, "/assets/app.js")}></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
|
||||||
<section class="container">
|
|
||||||
<nav>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a></li>
|
|
||||||
<%= if function_exported?(Routes, :live_dashboard_path, 2) do %>
|
|
||||||
<li><%= link "LiveDashboard", to: Routes.live_dashboard_path(@conn, :home) %></li>
|
|
||||||
<% end %>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<a href="https://phoenixframework.org/" class="phx-logo">
|
|
||||||
<img src={Routes.static_path(@conn, "/images/phoenix.png")} alt="Phoenix Framework Logo"/>
|
|
||||||
</a>
|
|
||||||
</section>
|
|
||||||
</header>
|
|
||||||
<%= @inner_content %>
|
<%= @inner_content %>
|
||||||
|
|
||||||
|
<footer class="footer footer-center p-10 bg-gray text-primary-content">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<span class="font-bold">Silent Silas</span><br>
|
||||||
|
<span class="italic">Disappointing my parents since 1993</span>
|
||||||
|
</p>
|
||||||
|
<p>Copyright Sucks © 2022 - No rights reserved</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="grid grid-flow-col gap-4">
|
||||||
|
<a target="_blank" rel="noopener noreferrer" href="https://git.silentsilas.com/silentsilas/Diffuser">
|
||||||
|
<img src="/images/GitHub-Mark-Light-120px-plus.png" style="max-height: 64px;" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
6
mix.exs
6
mix.exs
|
@ -53,7 +53,8 @@ defmodule Diffuser.MixProject do
|
||||||
{:erlport, "~> 0.10.1"},
|
{:erlport, "~> 0.10.1"},
|
||||||
{:waffle, "~> 1.1"},
|
{:waffle, "~> 1.1"},
|
||||||
{:waffle_ecto, "~> 0.0.11"},
|
{:waffle_ecto, "~> 0.0.11"},
|
||||||
{:scrivener_ecto, "~> 2.7"}
|
{:scrivener_ecto, "~> 2.7"},
|
||||||
|
{:tailwind, "~> 0.1.8", runtime: Mix.env() == :dev}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -69,7 +70,8 @@ defmodule Diffuser.MixProject do
|
||||||
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
|
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
|
||||||
"ecto.reset": ["ecto.drop", "ecto.setup"],
|
"ecto.reset": ["ecto.drop", "ecto.setup"],
|
||||||
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
|
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
|
||||||
"assets.deploy": ["esbuild default --minify", "phx.digest"]
|
# "assets.deploy": ["esbuild default --minify", "phx.digest"],
|
||||||
|
"assets.deploy": ["tailwind default --minify", "esbuild default --minify", "phx.digest"]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
1
mix.lock
1
mix.lock
|
@ -39,6 +39,7 @@
|
||||||
"scrivener_ecto": {:hex, :scrivener_ecto, "2.7.0", "cf64b8cb8a96cd131cdbcecf64e7fd395e21aaa1cb0236c42a7c2e34b0dca580", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:scrivener, "~> 2.4", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm", "e809f171687806b0031129034352f5ae44849720c48dd839200adeaf0ac3e260"},
|
"scrivener_ecto": {:hex, :scrivener_ecto, "2.7.0", "cf64b8cb8a96cd131cdbcecf64e7fd395e21aaa1cb0236c42a7c2e34b0dca580", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:scrivener, "~> 2.4", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm", "e809f171687806b0031129034352f5ae44849720c48dd839200adeaf0ac3e260"},
|
||||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||||
"swoosh": {:hex, :swoosh, "1.7.4", "f967d9b2659e81bab241b96267aae1001d35c2beea2df9c03dcf47b007bf566f", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1553d994b4cf069162965e63de1e1c53d8236e127118d21e56ce2abeaa3f25b4"},
|
"swoosh": {:hex, :swoosh, "1.7.4", "f967d9b2659e81bab241b96267aae1001d35c2beea2df9c03dcf47b007bf566f", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1553d994b4cf069162965e63de1e1c53d8236e127118d21e56ce2abeaa3f25b4"},
|
||||||
|
"tailwind": {:hex, :tailwind, "0.1.8", "3762defebc8e328fb19ff1afb8c37723e53b52be5ca74f0b8d0a02d1f3f432cf", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "40061d1bf2c0505c6b87be7a3ed05243fc10f6e1af4bac3336db8358bc84d4cc"},
|
||||||
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
|
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
|
||||||
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
|
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
|
||||||
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
|
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
|
||||||
|
|
|
@ -6,3 +6,4 @@ tqdm==4.64.0
|
||||||
openvino==2022.1.0
|
openvino==2022.1.0
|
||||||
huggingface_hub==0.9.0
|
huggingface_hub==0.9.0
|
||||||
scipy==1.9.0
|
scipy==1.9.0
|
||||||
|
ftfy==6.1.1
|
||||||
|
|
|
@ -42,8 +42,8 @@ class StableDiffusion:
|
||||||
self.latent_shape = tuple(self._unet.inputs[0].shape)[1:]
|
self.latent_shape = tuple(self._unet.inputs[0].shape)[1:]
|
||||||
# decoder
|
# decoder
|
||||||
self._vae = self.core.read_model(
|
self._vae = self.core.read_model(
|
||||||
hf_hub_download(repo_id=model, filename="vae.xml"),
|
hf_hub_download(repo_id=model, filename="vae_decoder.xml"),
|
||||||
hf_hub_download(repo_id=model, filename="vae.bin")
|
hf_hub_download(repo_id=model, filename="vae_decoder.bin")
|
||||||
)
|
)
|
||||||
self.vae = self.core.compile_model(self._vae, device)
|
self.vae = self.core.compile_model(self._vae, device)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Diffuser.Repo.Migrations.AddCompletedStepsToPromptRequest do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:prompt_requests) do
|
||||||
|
add :completed_steps, :integer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,10 @@
|
||||||
|
defmodule Diffuser.Repo.Migrations.AddQueueTimestampsToPromptRequest do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:prompt_requests) do
|
||||||
|
add :began_at, :naive_datetime
|
||||||
|
add :ended_at, :naive_datetime
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule Diffuser.Repo.Migrations.AddUserTable do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table(:users) do
|
||||||
|
add :ip_address, :string
|
||||||
|
add :username, :string
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,13 @@
|
||||||
|
defmodule Diffuser.Repo.Migrations.CreateVotes do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table(:votes) do
|
||||||
|
add :prompt_request_id, references(:prompt_requests, on_delete: :nothing)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create index(:votes, [:prompt_request_id])
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Diffuser.Repo.Migrations.AddUserToVotes do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:votes) do
|
||||||
|
add :user_id, references(:users)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Diffuser.Repo.Migrations.AddUserToPromptRequests do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:prompt_requests) do
|
||||||
|
add :user_id, references(:users)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Diffuser.Repo.Migrations.AddCodeToUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add :code, :string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Diffuser.Repo.Migrations.AddCodeToPromptRequests do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:prompt_requests) do
|
||||||
|
add :code, :string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
|
||||||
|
#
|
||||||
|
# To ban all spiders from the entire site uncomment the next two lines:
|
||||||
|
# User-agent: *
|
||||||
|
# Disallow: /
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,130 @@
|
||||||
|
defmodule Diffuser.AccountsTest do
|
||||||
|
use Diffuser.DataCase
|
||||||
|
|
||||||
|
alias Diffuser.Accounts
|
||||||
|
|
||||||
|
describe "users" do
|
||||||
|
alias Diffuser.Accounts.User
|
||||||
|
|
||||||
|
import Diffuser.AccountsFixtures
|
||||||
|
|
||||||
|
@invalid_attrs %{}
|
||||||
|
|
||||||
|
test "list_users/0 returns all users" do
|
||||||
|
user = user_fixture()
|
||||||
|
assert Accounts.list_users() == [user]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "get_user!/1 returns the user with given id" do
|
||||||
|
user = user_fixture()
|
||||||
|
assert Accounts.get_user!(user.id) == user
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create_user/1 with valid data creates a user" do
|
||||||
|
valid_attrs = %{}
|
||||||
|
|
||||||
|
assert {:ok, %User{} = user} = Accounts.create_user(valid_attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create_user/1 with invalid data returns error changeset" do
|
||||||
|
assert {:error, %Ecto.Changeset{}} = Accounts.create_user(@invalid_attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update_user/2 with valid data updates the user" do
|
||||||
|
user = user_fixture()
|
||||||
|
update_attrs = %{}
|
||||||
|
|
||||||
|
assert {:ok, %User{} = user} = Accounts.update_user(user, update_attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update_user/2 with invalid data returns error changeset" do
|
||||||
|
user = user_fixture()
|
||||||
|
assert {:error, %Ecto.Changeset{}} = Accounts.update_user(user, @invalid_attrs)
|
||||||
|
assert user == Accounts.get_user!(user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "delete_user/1 deletes the user" do
|
||||||
|
user = user_fixture()
|
||||||
|
assert {:ok, %User{}} = Accounts.delete_user(user)
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Accounts.get_user!(user.id) end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "change_user/1 returns a user changeset" do
|
||||||
|
user = user_fixture()
|
||||||
|
assert %Ecto.Changeset{} = Accounts.change_user(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "votes" do
|
||||||
|
alias Diffuser.Accounts.Vote
|
||||||
|
|
||||||
|
import Diffuser.AccountsFixtures
|
||||||
|
|
||||||
|
@invalid_attrs %{}
|
||||||
|
|
||||||
|
test "list_votes/0 returns all votes" do
|
||||||
|
vote = vote_fixture()
|
||||||
|
assert Accounts.list_votes() == [vote]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "get_vote!/1 returns the vote with given id" do
|
||||||
|
vote = vote_fixture()
|
||||||
|
assert Accounts.get_vote!(vote.id) == vote
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create_vote/1 with valid data creates a vote" do
|
||||||
|
valid_attrs = %{}
|
||||||
|
|
||||||
|
assert {:ok, %Vote{} = vote} = Accounts.create_vote(valid_attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create_vote/1 with invalid data returns error changeset" do
|
||||||
|
assert {:error, %Ecto.Changeset{}} = Accounts.create_vote(@invalid_attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update_vote/2 with valid data updates the vote" do
|
||||||
|
vote = vote_fixture()
|
||||||
|
update_attrs = %{}
|
||||||
|
|
||||||
|
assert {:ok, %Vote{} = vote} = Accounts.update_vote(vote, update_attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update_vote/2 with invalid data returns error changeset" do
|
||||||
|
vote = vote_fixture()
|
||||||
|
assert {:error, %Ecto.Changeset{}} = Accounts.update_vote(vote, @invalid_attrs)
|
||||||
|
assert vote == Accounts.get_vote!(vote.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "delete_vote/1 deletes the vote" do
|
||||||
|
vote = vote_fixture()
|
||||||
|
assert {:ok, %Vote{}} = Accounts.delete_vote(vote)
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Accounts.get_vote!(vote.id) end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "change_vote/1 returns a vote changeset" do
|
||||||
|
vote = vote_fixture()
|
||||||
|
assert %Ecto.Changeset{} = Accounts.change_vote(vote)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "upvote/2" do
|
||||||
|
alias Diffuser.Accounts.Vote
|
||||||
|
|
||||||
|
import Diffuser.AccountsFixtures
|
||||||
|
import Diffuser.GeneratorFixtures
|
||||||
|
|
||||||
|
test "creates upvote and associates it to specific prompt" do
|
||||||
|
%{id: pr_id} = pr = prompt_request_fixture()
|
||||||
|
|
||||||
|
assert {:ok, %Vote{prompt_request_id: ^pr_id}} = Accounts.upvote(pr)
|
||||||
|
|
||||||
|
%{votes: votes} = Diffuser.Generator.get_prompt_request!(pr_id) |> Repo.preload(:votes)
|
||||||
|
assert votes |> length() == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "creates upvote and associates it to current user" do
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,6 +3,7 @@ defmodule DiffuserWeb.PromptRequestLiveTest do
|
||||||
|
|
||||||
import Phoenix.LiveViewTest
|
import Phoenix.LiveViewTest
|
||||||
import Diffuser.GeneratorFixtures
|
import Diffuser.GeneratorFixtures
|
||||||
|
alias DiffuserWeb.Plugs.UserAuth
|
||||||
|
|
||||||
@create_attrs %{prompt: "some prompt"}
|
@create_attrs %{prompt: "some prompt"}
|
||||||
@update_attrs %{prompt: "some updated prompt"}
|
@update_attrs %{prompt: "some updated prompt"}
|
||||||
|
@ -26,8 +27,8 @@ defmodule DiffuserWeb.PromptRequestLiveTest do
|
||||||
test "saves new prompt_request", %{conn: conn} do
|
test "saves new prompt_request", %{conn: conn} do
|
||||||
{:ok, index_live, _html} = live(conn, Routes.prompt_request_index_path(conn, :index))
|
{:ok, index_live, _html} = live(conn, Routes.prompt_request_index_path(conn, :index))
|
||||||
|
|
||||||
assert index_live |> element("a", "New Prompt request") |> render_click() =~
|
assert index_live |> element("a", "New Prompt") |> render_click() =~
|
||||||
"New Prompt request"
|
"New Prompt"
|
||||||
|
|
||||||
assert_patch(index_live, Routes.prompt_request_index_path(conn, :new))
|
assert_patch(index_live, Routes.prompt_request_index_path(conn, :new))
|
||||||
|
|
||||||
|
@ -39,16 +40,18 @@ defmodule DiffuserWeb.PromptRequestLiveTest do
|
||||||
index_live
|
index_live
|
||||||
|> form("#prompt_request-form", prompt_request: @create_attrs)
|
|> form("#prompt_request-form", prompt_request: @create_attrs)
|
||||||
|> render_submit()
|
|> render_submit()
|
||||||
|> follow_redirect(conn, Routes.prompt_request_index_path(conn, :index))
|
|> follow_redirect(conn, "#{Routes.prompt_request_index_path(conn, :index)}?page=1")
|
||||||
|
|
||||||
assert html =~ "Prompt request created successfully"
|
|
||||||
assert html =~ "some prompt"
|
assert html =~ "some prompt"
|
||||||
|
# assert html =~ user.id
|
||||||
end
|
end
|
||||||
|
|
||||||
test "updates prompt_request in listing", %{conn: conn, prompt_request: prompt_request} do
|
test "updates prompt_request in listing", %{conn: conn, prompt_request: prompt_request} do
|
||||||
{:ok, index_live, _html} = live(conn, Routes.prompt_request_index_path(conn, :index))
|
{:ok, index_live, _html} = live(conn, Routes.prompt_request_index_path(conn, :index))
|
||||||
|
|
||||||
assert index_live |> element("#prompt_request-#{prompt_request.id} a", "Edit") |> render_click() =~
|
assert index_live
|
||||||
|
|> element("#prompt_request-#{prompt_request.id} a", "Edit")
|
||||||
|
|> render_click() =~
|
||||||
"Edit Prompt request"
|
"Edit Prompt request"
|
||||||
|
|
||||||
assert_patch(index_live, Routes.prompt_request_index_path(conn, :edit, prompt_request))
|
assert_patch(index_live, Routes.prompt_request_index_path(conn, :edit, prompt_request))
|
||||||
|
@ -70,7 +73,10 @@ defmodule DiffuserWeb.PromptRequestLiveTest do
|
||||||
test "deletes prompt_request in listing", %{conn: conn, prompt_request: prompt_request} do
|
test "deletes prompt_request in listing", %{conn: conn, prompt_request: prompt_request} do
|
||||||
{:ok, index_live, _html} = live(conn, Routes.prompt_request_index_path(conn, :index))
|
{:ok, index_live, _html} = live(conn, Routes.prompt_request_index_path(conn, :index))
|
||||||
|
|
||||||
assert index_live |> element("#prompt_request-#{prompt_request.id} a", "Delete") |> render_click()
|
assert index_live
|
||||||
|
|> element("#prompt_request-#{prompt_request.id} a", "Delete")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
refute has_element?(index_live, "#prompt_request-#{prompt_request.id}")
|
refute has_element?(index_live, "#prompt_request-#{prompt_request.id}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -79,14 +85,16 @@ defmodule DiffuserWeb.PromptRequestLiveTest do
|
||||||
setup [:create_prompt_request]
|
setup [:create_prompt_request]
|
||||||
|
|
||||||
test "displays prompt_request", %{conn: conn, prompt_request: prompt_request} do
|
test "displays prompt_request", %{conn: conn, prompt_request: prompt_request} do
|
||||||
{:ok, _show_live, html} = live(conn, Routes.prompt_request_show_path(conn, :show, prompt_request))
|
{:ok, _show_live, html} =
|
||||||
|
live(conn, Routes.prompt_request_show_path(conn, :show, prompt_request))
|
||||||
|
|
||||||
assert html =~ "Show Prompt request"
|
assert html =~ "Show Prompt request"
|
||||||
assert html =~ prompt_request.prompt
|
assert html =~ prompt_request.prompt
|
||||||
end
|
end
|
||||||
|
|
||||||
test "updates prompt_request within modal", %{conn: conn, prompt_request: prompt_request} do
|
test "updates prompt_request within modal", %{conn: conn, prompt_request: prompt_request} do
|
||||||
{:ok, show_live, _html} = live(conn, Routes.prompt_request_show_path(conn, :show, prompt_request))
|
{:ok, show_live, _html} =
|
||||||
|
live(conn, Routes.prompt_request_show_path(conn, :show, prompt_request))
|
||||||
|
|
||||||
assert show_live |> element("a", "Edit") |> render_click() =~
|
assert show_live |> element("a", "Edit") |> render_click() =~
|
||||||
"Edit Prompt request"
|
"Edit Prompt request"
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
defmodule Diffuser.AccountsFixtures do
|
||||||
|
@moduledoc """
|
||||||
|
This module defines test helpers for creating
|
||||||
|
entities via the `Diffuser.Accounts` context.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Generate a user.
|
||||||
|
"""
|
||||||
|
def user_fixture(attrs \\ %{}) do
|
||||||
|
{:ok, user} =
|
||||||
|
attrs
|
||||||
|
|> Enum.into(%{
|
||||||
|
|
||||||
|
})
|
||||||
|
|> Diffuser.Accounts.create_user()
|
||||||
|
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Generate a vote.
|
||||||
|
"""
|
||||||
|
def vote_fixture(attrs \\ %{}) do
|
||||||
|
{:ok, vote} =
|
||||||
|
attrs
|
||||||
|
|> Enum.into(%{
|
||||||
|
|
||||||
|
})
|
||||||
|
|> Diffuser.Accounts.create_vote()
|
||||||
|
|
||||||
|
vote
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue