Initial commit

This commit is contained in:
2020-12-12 18:38:31 +01:00
commit f877b78f33
117 changed files with 23104 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
defmodule ConfientWeb.UserSocket do
use Phoenix.Socket
## Channels
# channel "room:*", ConfientWeb.RoomChannel
# Socket params are passed from the client and can
# be used to verify and authenticate a user. After
# verification, you can put default assigns into
# the socket that will be set for all channels, ie
#
# {:ok, assign(socket, :user_id, verified_user_id)}
#
# To deny connection, return `:error`.
#
# See `Phoenix.Token` documentation for examples in
# performing token verification on connect.
@impl true
def connect(_params, socket, _connect_info) do
{:ok, socket}
end
# Socket id's are topics that allow you to identify all sockets for a given user:
#
# def id(socket), do: "user_socket:#{socket.assigns.user_id}"
#
# Would allow you to broadcast a "disconnect" event and terminate
# all active sockets and channels for a given user:
#
# ConfientWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
#
# Returning `nil` makes this socket anonymous.
@impl true
def id(_socket), do: nil
end

View File

@@ -0,0 +1,124 @@
defmodule ConfientWeb.AssignmentController do
use ConfientWeb, :controller
require Logger
import Phoenix.HTML
alias Confient.Works
alias Confient.Works.Assignment
def index(conn, _params) do
assignments = Works.list_assignments()
render(conn, "index.html", assignments: assignments)
end
def new(conn, _params) do
changeset = Works.change_assignment(%Assignment{})
classes = Confient.School.list_classes() |> Enum.map(&{&1.name, &1.id})
render(conn, "new.html", changeset: changeset, classes: classes)
end
def create(conn, %{"assignment" => assignment_params}) do
IO.inspect(assignment_params)
case Works.create_assignment(assignment_params) do
{:ok, assignment} ->
conn
|> put_flash(:info, "Devoir créé avec succès.")
|> redirect(to: Routes.assignment_path(conn, :show, assignment))
{:error, %Ecto.Changeset{} = changeset} ->
classes = Confient.School.list_classes() |> Enum.map(&{&1.name, &1.id})
render(conn, "new.html", changeset: changeset, classes: classes)
end
end
def show(conn, %{"id" => id}) do
assignment = Works.get_full_assignment!(id)
missing = Works.get_missing_works_from_student_in_assignement(id, assignment.class_id)
render(conn, "show.html", assignment: assignment, missing: missing)
end
def edit(conn, %{"id" => id}) do
classes = Confient.School.list_classes() |> Enum.map(&{&1.name, &1.id})
assignment = Works.get_assignment!(id)
changeset = Works.change_assignment(assignment)
render(conn, "edit.html", assignment: assignment, changeset: changeset, classes: classes)
end
def update(conn, %{"id" => id, "assignment" => assignment_params}) do
assignment = Works.get_assignment!(id)
case Works.update_assignment(assignment, assignment_params) do
{:ok, assignment} ->
conn
|> put_flash(:info, "Devoir mis à jour avec succès.")
|> redirect(to: Routes.assignment_path(conn, :show, assignment))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "edit.html", assignment: assignment, changeset: changeset)
end
end
def delete(conn, %{"id" => id}) do
assignment = Works.get_assignment!(id)
{:ok, _assignment} = Works.delete_assignment(assignment)
File.rm_rf(Confient.Deposit.get_upload_dir(assignment.class.name, assignment.slug))
conn
|> put_flash(:info, "Devoir supprimé avec succès.")
|> redirect(to: Routes.assignment_path(conn, :index))
end
def archive(conn, %{"id" => assignment_id}) do
case Works.get_full_assignment!(assignment_id) do
nil ->
conn |> put_flash(:error, "Devoir inexistant") |> redirect(to: "/")
assignment ->
base = Application.get_env(:confiant, :base_upload_dir, "/tmp/confient/uploads")
files =
Enum.map(assignment.students_works, fn v -> v.path end)
|> Enum.map(&String.to_charlist/1)
archive_name = "#{assignment.class.name}_#{assignment.slug}.zip"
archive_sub = "#{assignment.class.name}/#{assignment.slug}"
archive_path = "#{base}/#{archive_sub}/#{archive_name}"
case :zip.create(
String.to_charlist(archive_path),
files,
[{:cwd, "#{base}/#{archive_sub}"}]
) do
{:ok, _fname} ->
conn
|> put_flash(
:info,
raw(
"Archive créé est disponible <a href=#{
Path.join([
Application.fetch_env!(:confient, :domain),
"uploads",
assignment.class.name,
assignment.slug,
archive_name
])
}>#{archive_name}</href>"
)
)
|> redirect(to: Routes.assignment_path(conn, :show, assignment))
{:error, reason} ->
Logger.error(reason)
conn
|> put_flash(:error, "Error lors de la création de l'archive")
|> redirect(to: Routes.assignment_path(conn, :show, assignment))
end
end
end
end

View File

@@ -0,0 +1,65 @@
defmodule ConfientWeb.ClassController do
use ConfientWeb, :controller
alias Confient.School
alias Confient.School.Class
def index(conn, _params) do
classes = School.list_classes()
render(conn, "index.html", classes: classes)
end
def new(conn, _params) do
changeset = School.change_class(%Class{})
render(conn, "new.html", changeset: changeset)
end
def create(conn, %{"class" => class_params}) do
case School.create_class(class_params) do
{:ok, class} ->
conn
|> put_flash(:info, "Classe créé avec succès.")
|> redirect(to: Routes.class_path(conn, :show, class))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
def show(conn, %{"id" => id}) do
class = School.get_class!(id)
render(conn, "show.html", class: class)
end
def edit(conn, %{"id" => id}) do
class = School.get_class!(id)
changeset = School.change_class(class)
render(conn, "edit.html", class: class, changeset: changeset)
end
def update(conn, %{"id" => id, "class" => class_params}) do
class = School.get_class!(id)
case School.update_class(class, class_params) do
{:ok, class} ->
conn
|> put_flash(:info, "Classe mise à jour avec succès.")
|> redirect(to: Routes.class_path(conn, :show, class))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "edit.html", class: class, changeset: changeset)
end
end
def delete(conn, %{"id" => id}) do
class = School.get_class!(id)
{:ok, _class} = School.delete_class(class)
dir = Application.fetch_env!(:confient, :upload_dir)
File.rm_rf("#{dir}/#{class.name}")
conn
|> put_flash(:info, "Classe supprimée.")
|> redirect(to: Routes.class_path(conn, :index))
end
end

View File

@@ -0,0 +1,166 @@
defmodule ConfientWeb.DepositController do
use ConfientWeb, :controller
import Phoenix.HTML
def index(conn, _params) do
works = Confient.Deposit.list_works()
render(conn, "index.html", works: works)
end
def form(conn, params = %{"id" => id}) do
case Confient.School.get_class(id) do
{:error, :no_class} ->
conn
|> put_flash(
:error,
"La classe demandée n'existe pas."
)
|> redirect(to: "/")
{:ok, class} ->
next_assignments = Confient.Works.get_next_assignments(class)
if length(next_assignments) > 0 && length(class.students) do
changeset = Confient.Student.Work.changeset(%Confient.Student.Work{}, %{})
student =
with {:ok, id} <- Map.fetch(params, "student_id"),
{val, _} <- Integer.parse(id) do
Enum.find(class.students, Enum.at(class.students, 0), &(&1.id == val))
else
:error -> Enum.at(class.students, 0)
end
assignment =
with {:ok, id} <- Map.fetch(params, "assignment_id"),
{val, _} <- Integer.parse(id) do
Enum.find(next_assignments, Enum.at(next_assignments, 0), &(&1.id == val))
else
:error -> Enum.at(next_assignments, 0)
end
render(conn, "form.html",
assignment_id: assignment.id,
student_id: student.id,
changeset: changeset,
class: class,
assignments: next_assignments
)
else
conn
|> put_flash(
:info,
"Aucun devoir à rendre pour la classe #{class.name}"
)
|> redirect(to: "/")
end
end
end
def deposit(conn, %{
"id" => id,
"work" => %{"assignment_id" => assignment_id, "student_id" => student_id, "file" => work}
}) do
with {:ok, class} <- Confient.School.get_class(id),
{:ok, student} <- Confient.School.get_student(student_id),
{:ok, assignment} <- Confient.Works.get_assignment(assignment_id),
:ok <- Confient.Deposit.verify_date(assignment.due),
{:ok, ext} <- Confient.Deposit.verify_file(work) do
dir =
Confient.Deposit.get_upload_dir(class.name, assignment.slug)
|> Confient.Deposit.ensure_upload_dir()
fname = Confient.Deposit.gen_filename(class.name, assignment.slug, student, ext)
case File.copy(work.path, "#{dir}/#{fname}") do
{:ok, _} ->
Confient.Works.create_or_update_work(%{
path: fname,
student_id: student.id,
assignment_id: assignment.id
})
conn
|> put_flash(
:info,
raw("Fichier <strong>#{work.filename}</strong> déposé avec succès<br>
Elève : #{student.lastname} #{student.firstname}<br>
Devoir : #{assignment.title}<br>
Classe : #{class.name}")
)
|> redirect(to: "/")
{:error, _} ->
conn
|> put_flash(
:error,
"Erreur lors du téléversement du fichier."
)
|> redirect(
to:
Routes.deposit_path(conn, :form, id,
student_id: student.id,
assignment_id: assignment.id
)
)
end
else
{:error, :date_too_late} ->
conn
|> put_flash(
:error,
"La date de rendue pour le devoir sélectionné est dépassée."
)
|> redirect(to: Routes.deposit_path(conn, :form, id))
{:error, :no_class} ->
conn
|> put_flash(
:error,
"La classe demandé n'existe pas."
)
|> redirect(to: "/")
{:error, :no_student} ->
conn
|> put_flash(
:error,
"L'étudiant demandé n'existe pas."
)
|> redirect(to: "/")
{:error, :no_assignment} ->
conn
|> put_flash(
:error,
"Le devoir demandé n'existe pas."
)
|> redirect(to: "/")
{:error, :invalid_file_type} ->
{:ok, student} = Confient.School.get_student(student_id)
{:ok, assignment} = Confient.Works.get_assignment(assignment_id)
conn
|> put_flash(
:error,
raw("Format de fichier invalide pour le fichier <strong>#{work.filename}</strong><br>
Elève : #{student.lastname} #{student.firstname}<br>
Devoir : #{assignment.title}")
)
|> redirect(
to:
Routes.deposit_path(conn, :form, id,
student_id: student.id,
assignment_id: assignment.id
)
)
end
end
def deposit(conn, %{"id" => id}),
do:
conn
|> put_flash(:error, "Aucun fichier spécifié")
|> redirect(to: Routes.deposit_path(conn, :form, id))
end

View File

@@ -0,0 +1,9 @@
defmodule ConfientWeb.ErrorController do
use ConfientWeb, :controller
def notfound(conn, _params) do
conn
|> put_flash(:error, "Fichier inexistant")
|> redirect(to: "/")
end
end

View File

@@ -0,0 +1,10 @@
defmodule ConfientWeb.PageController do
use ConfientWeb, :controller
alias Confient.School
def index(conn, _params) do
classes = School.list_classes()
render(conn, "index.html", classes: classes)
end
end

View File

@@ -0,0 +1,33 @@
defmodule ConfientWeb.SessionController do
use ConfientWeb, :controller
alias Confient.Account.Auth
import Phoenix.HTML
def new(conn, _params) do
render(conn, "new.html")
end
def create(conn, %{"session" => auth}) do
case Auth.login(auth) do
{:ok, user} ->
conn
|> put_session(:current_user_id, user.id)
|> put_flash(:info, raw("Utilisateur <strong>#{user.username}</strong> connecté."))
|> redirect(to: "/")
:error ->
conn
|> put_flash(:error, "Il y a un problème avec la combinaison utilisateur/mot de passe")
|> redirect(to: Routes.session_path(conn, :new))
end
end
def delete(conn, _params) do
conn
|> delete_session(:current_user_id)
|> delete_session(:current_user)
|> put_flash(:info, "Déconnecté.")
|> redirect(to: "/")
end
end

View File

@@ -0,0 +1,99 @@
defmodule ConfientWeb.StudentController do
use ConfientWeb, :controller
alias Confient.School
alias Confient.School.Student
def index(conn, _params) do
students = School.list_students()
render(conn, "index.html", students: students)
end
def new(conn, _params) do
changeset = School.change_student(%Student{})
classes = School.list_classes() |> Enum.map(&{&1.name, &1.id})
render(conn, "new.html",
changeset: changeset,
classes: classes
)
end
def bulkform(conn, _params) do
classes = School.list_classes() |> Enum.map(&{&1.name, &1.id})
render(conn, "bulkform.html",
classes: classes,
token: get_csrf_token()
)
end
def create(conn, %{"student" => student_params}) do
case School.create_student(student_params) do
{:ok, student} ->
conn
|> put_flash(:info, "Elève créé avec succès.")
|> redirect(to: Routes.student_path(conn, :show, student))
{:error, %Ecto.Changeset{} = changeset} ->
classes = Confient.School.list_classes() |> Enum.map(&{&1.name, &1.id})
render(conn, "new.html", changeset: changeset, classes: classes)
end
end
def bulkcreate(conn, %{"file" => %{"list" => ""}}) do
conn
|> put_flash(:error, "Aucun fichier CSV.")
|> redirect(to: Routes.student_path(conn, :bulkform))
end
def bulkcreate(conn, %{"file" => %{"list" => file}, "class" => %{"class_id" => class_id}}) do
class = School.get_class!(class_id)
{:ok, students} = CSV.parse(file.path)
for {lastname, firstname} <- students do
unless School.student_exists?(lastname, firstname, class) do
School.create_student(class, %{"lastname" => lastname, "firstname" => firstname})
end
end
conn
|> put_flash(:info, "Elèves créés avec succès.")
|> redirect(to: Routes.student_path(conn, :bulkform))
end
def show(conn, %{"id" => id}) do
student = School.get_full_student!(id)
render(conn, "show.html", student: student)
end
def edit(conn, %{"id" => id}) do
student = School.get_student!(id)
changeset = School.change_student(student)
classes = School.list_classes() |> Enum.map(&{&1.name, &1.id})
render(conn, "edit.html", student: student, changeset: changeset, classes: classes)
end
def update(conn, %{"id" => id, "student" => student_params}) do
student = School.get_student!(id)
case School.update_student(student, student_params) do
{:ok, student} ->
conn
|> put_flash(:info, "Elève mis à jour avec succès.")
|> redirect(to: Routes.student_path(conn, :show, student))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "edit.html", student: student, changeset: changeset)
end
end
def delete(conn, %{"id" => id}) do
student = School.get_student!(id)
{:ok, _student} = School.delete_student(student)
conn
|> put_flash(:info, "Elève supprimé.")
|> redirect(to: Routes.student_path(conn, :index))
end
end

View File

@@ -0,0 +1,6 @@
defmodule ConfientWeb.UserController do
use ConfientWeb, :controller
# alias Confient.Account
# alias Confient.Account.User
end

View File

@@ -0,0 +1,54 @@
defmodule ConfientWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :confient
# The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with.
# Set :encryption_salt if you would also like to encrypt it.
@session_options [
store: :cookie,
key: "_confient_key",
signing_salt: "kUFTx1vs"
]
socket "/socket", ConfientWeb.UserSocket,
websocket: true,
longpoll: false
socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]]
# Serve at "/" the static files from "priv/static" directory.
#
# You should set gzip to true if you are running phx.digest
# when deploying your static files in production.
plug Plug.Static,
at: "/",
from: :confient,
gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt)
# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
plug Phoenix.CodeReloader
plug Phoenix.Ecto.CheckRepoStatus, otp_app: :confient
end
plug Phoenix.LiveDashboard.RequestLogger,
param_key: "request_logger",
cookie_key: "request_logger"
plug Plug.RequestId
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Phoenix.json_library()
plug Plug.MethodOverride
plug Plug.Head
plug Plug.Session, @session_options
plug ConfientWeb.Router
end

View File

@@ -0,0 +1,24 @@
defmodule ConfientWeb.Gettext do
@moduledoc """
A module providing Internationalization with a gettext-based API.
By using [Gettext](https://hexdocs.pm/gettext),
your module gains a set of macros for translations, for example:
import ConfientWeb.Gettext
# Simple translation
gettext("Here is the string to translate")
# Plural translation
ngettext("Here is the string to translate",
"Here are the strings to translate",
3)
# Domain-based translation
dgettext("errors", "Here is the error message to translate")
See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
"""
use Gettext, otp_app: :confient
end

View File

@@ -0,0 +1,40 @@
defmodule ConfientWeb.Plugs.Auth do
import Plug.Conn
import Phoenix.Controller
alias Confient.Account
def init(args), do: args
def call(conn, _args) do
if uid = Plug.Conn.get_session(conn, :current_user_id) do
user = Account.get_user!(uid)
conn
|> assign(:current_user, user)
else
conn
|> redirect(to: "/login")
|> halt()
end
end
end
defmodule ConfientWeb.Plugs.InjectUser do
import Plug.Conn
alias Confient.Account
def init(args), do: args
def call(conn, _args) do
if uid = Plug.Conn.get_session(conn, :current_user_id) do
user = Account.get_user!(uid)
conn
|> assign(:current_user, user)
else
conn
end
end
end

View File

@@ -0,0 +1,82 @@
defmodule ConfientWeb.Router do
use ConfientWeb, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
pipeline :api do
plug :accepts, ["json"]
end
pipeline :uploads do
plug :fetch_session
plug :fetch_flash
plug Plug.Static,
at: "/uploads/",
from: Application.get_env(:confiant, :base_upload_dir, "/tmp/confient/uploads")
end
scope "/", ConfientWeb do
scope "/uploads" do
pipe_through [:uploads, ConfientWeb.Plugs.Auth]
get "/*path", ErrorController, :notfound
end
pipe_through :browser
scope "/" do
pipe_through ConfientWeb.Plugs.Auth
resources "/classes", ClassController
resources "/students", StudentController
get "/students-bulk-create", StudentController, :bulkform
post "/students-bulk-create", StudentController, :bulkcreate
resources "/assignments", AssignmentController
get "/assignements/:id/archive", AssignmentController, :archive
get "/deposits/", DepositController, :index
delete "/logout", SessionController, :delete
end
scope "/" do
pipe_through ConfientWeb.Plugs.InjectUser
get "/", PageController, :index
end
get "/login", SessionController, :new
post "/login", SessionController, :create
get "/deposit/:id", DepositController, :form
post "/deposit/:id", DepositController, :deposit
end
# Other scopes may use custom stacks.
# scope "/api", ConfientWeb do
# pipe_through :api
# end
# Enables LiveDashboard only for development
#
# If you want to use the LiveDashboard in production, you should put
# it behind authentication and allow only admins to access it.
# If your application does not have an admins-only section yet,
# you can use Plug.BasicAuth to set up some basic authentication
# as long as you are also using SSL (which you should anyway).
if Mix.env() in [:dev, :test] do
import Phoenix.LiveDashboard.Router
scope "/" do
pipe_through :browser
live_dashboard "/dashboard", metrics: ConfientWeb.Telemetry
end
end
end

View File

@@ -0,0 +1,55 @@
defmodule ConfientWeb.Telemetry do
use Supervisor
import Telemetry.Metrics
def start_link(arg) do
Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
end
@impl true
def init(_arg) do
children = [
# Telemetry poller will execute the given period measurements
# every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics
{:telemetry_poller, measurements: periodic_measurements(), period: 10_000}
# Add reporters as children of your supervision tree.
# {Telemetry.Metrics.ConsoleReporter, metrics: metrics()}
]
Supervisor.init(children, strategy: :one_for_one)
end
def metrics do
[
# Phoenix Metrics
summary("phoenix.endpoint.stop.duration",
unit: {:native, :millisecond}
),
summary("phoenix.router_dispatch.stop.duration",
tags: [:route],
unit: {:native, :millisecond}
),
# Database Metrics
summary("confient.repo.query.total_time", unit: {:native, :millisecond}),
summary("confient.repo.query.decode_time", unit: {:native, :millisecond}),
summary("confient.repo.query.query_time", unit: {:native, :millisecond}),
summary("confient.repo.query.queue_time", unit: {:native, :millisecond}),
summary("confient.repo.query.idle_time", unit: {:native, :millisecond}),
# VM Metrics
summary("vm.memory.total", unit: {:byte, :kilobyte}),
summary("vm.total_run_queue_lengths.total"),
summary("vm.total_run_queue_lengths.cpu"),
summary("vm.total_run_queue_lengths.io")
]
end
defp periodic_measurements do
[
# A module, function and arguments to be invoked periodically.
# This function must call :telemetry.execute/3 and a metric must be added above.
# {ConfientWeb, :count_users, []}
]
end
end

View File

@@ -0,0 +1,7 @@
<h1>Editer un devoir</h1>
<span><%= link "Retour", to: Routes.assignment_path(@conn, :index) %></span>
<hr>
<%= render "form.html", Map.put(assigns, :action, Routes.assignment_path(@conn, :update, @assignment)) %>

View File

@@ -0,0 +1,26 @@
<%= form_for @changeset, @action, fn f -> %>
<%= if @changeset.action do %>
<div class="alert alert-danger">
<p>Oops, quelque chose c'est mal passé, merci de vérifier le formulaire</p>
</div>
<% end %>
<%= label f, "Classe" %>
<%= select f, :class_id, @classes %>
<%= label f, "Titre" %>
<%= text_input f, :title %>
<%= error_tag f, :title %>
<%= label f, "Dénomination" %>
<%= text_input f, :slug %>
<%= error_tag f, :slug %>
<%= label f, "Echéance" %>
<%= date_select f, :due %>
<%= error_tag f, :due %>
<div>
<%= submit "Sauvegarder" %>
</div>
<% end %>

View File

@@ -0,0 +1,38 @@
<h1>Liste des devoirs</h1>
<span><%= link "Ajouter un devoir", to: Routes.assignment_path(@conn, :new) %></span>
<hr>
<table>
<thead>
<tr>
<th>Titre</th>
<th>Dénominateur</th>
<th>Echéance</th>
<th>Classe</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<%= for assignment <- @assignments do %>
<tr>
<td><%= assignment.title %></td>
<td><%= assignment.slug %></td>
<td><%= assignment.due %></td>
<%= if assignment.class do %>
<td><%= link assignment.class.name, to: Routes.class_path(@conn, :show, assignment.class.id) %></td>
<% else %>
<td></td>
<% end %>
<td>
<span><%= link "Voir", to: Routes.assignment_path(@conn, :show, assignment) %></span>
<span><%= link "Editer", to: Routes.assignment_path(@conn, :edit, assignment) %></span>
<span><%= link "Supprimer", to: Routes.assignment_path(@conn, :delete, assignment), method: :delete, data: [confirm: "Vous êtes sur ?"] %></span>
</td>
</tr>
<% end %>
</tbody>
</table>

View File

@@ -0,0 +1,7 @@
<h1>Nouveau devoir</h1>
<span><%= link "Retour", to: Routes.assignment_path(@conn, :index) %></span>
<hr>
<%= render "form.html", Map.put(assigns, :action, Routes.assignment_path(@conn, :create)) %>

View File

@@ -0,0 +1,67 @@
<h1>Devoir : <%= @assignment.title %> | Classe : <%= @assignment.class.name %></h1>
<span><%= link "Editer", to: Routes.assignment_path(@conn, :edit, @assignment) %></span>
<span><%= link "Retour", to: Routes.assignment_path(@conn, :index) %></span>
<hr>
<h2>Informations générales</h2>
<ul>
<li>
<strong>Classe:</strong>
<%= @assignment.class.name %>
</li>
<li>
<strong>Echéance:</strong>
<%= @assignment.due %>
</li>
<li>
<strong>Dénominateur:</strong>
<%= @assignment.slug %>
</li>
</ul>
<hr>
<h2>Liste des rendus pour ce devoir</h2>
<%= if length(@assignment.students_works) > 0 do %>
<%= link "Générer une archive de tous les rendus", to: Routes.assignment_path(@conn, :archive, @assignment.id), target: "_blank" %>
<table>
<thead>
<tr>
<th>Elève</th>
<th>Date</th>
<th>Action</th>
<tr>
</thead>
<tbody>
<%= for work <- @assignment.students_works do %>
<tr>
<td><%= link "#{work.student.lastname} #{work.student.firstname}", to: Routes.student_path(@conn, :show, work.student.id) %></td>
<td><%= work.inserted_at %></td>
<td><%= link "Télécharger", to: Path.join([Application.fetch_env!(:confient, :domain), "uploads", @assignment.class.name, @assignment.slug, work.path]), target: "_blank" %></td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<p>Aucun rendu</p>
<% end %>
<%= if length(@assignment.students_works) > 0 do %>
<h2>Liste des élèves n'ayant pas rendu ce devoir</h2>
<ul>
<%= for stud <- @missing do %>
<li><%= link "#{stud.lastname} #{stud.firstname}", to: Routes.student_path(@conn, :show, stud.id) %></li>
<% end %>
</ul>
<% end %>

View File

@@ -0,0 +1,7 @@
<h1>Editer une classe</h1>
<span><%= link "Retour", to: Routes.class_path(@conn, :index) %></span>
<hr>
<%= render "form.html", Map.put(assigns, :action, Routes.class_path(@conn, :update, @class)) %>

View File

@@ -0,0 +1,15 @@
<%= form_for @changeset, @action, [multipart: true], fn f -> %>
<%= if @changeset.action do %>
<div class="alert alert-danger">
<p>Oops, quelque chose c'est mal passé, merci de vérifier le formulaire</p>
</div>
<% end %>
<%= label f, "Dénomination" %>
<%= text_input f, :name %>
<%= error_tag f, :name %>
<div>
<%= submit "Sauvegarder" %>
</div>
<% end %>

View File

@@ -0,0 +1,29 @@
<h1>Liste des classes</h1>
<span><%= link "Ajouter une classe", to: Routes.class_path(@conn, :new) %></span>
<hr>
<table>
<thead>
<tr>
<th>Nom</th>
<th>Nombre d'élèves</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<%= for class <- @classes do %>
<tr>
<td><%= class.name %></td>
<td><%= length(class.students) %></td>
<td>
<span><%= link "Voir", to: Routes.class_path(@conn, :show, class) %></span>
<span><%= link "Editer", to: Routes.class_path(@conn, :edit, class) %></span>
<span><%= link "Supprimer", to: Routes.class_path(@conn, :delete, class), method: :delete, data: [confirm: "Vous êtes sur ?"] %></span>
</td>
</tr>
<% end %>
</tbody>
</table>

View File

@@ -0,0 +1,7 @@
<h1>Nouvelle classe</h1>
<span><%= link "Retour", to: Routes.class_path(@conn, :index) %></span>
<hr>
<%= render "form.html", Map.put(assigns, :action, Routes.class_path(@conn, :create)) %>

View File

@@ -0,0 +1,14 @@
<h1><%= @class.name %></h1>
<span><%= link "Editer", to: Routes.class_path(@conn, :edit, @class) %></span>
<span><%= link "Retour", to: Routes.class_path(@conn, :index) %></span>
<hr>
<h2>Liste des élèves</h2>
<ul>
<%= for student <- @class.students do %>
<li><%= link "#{student.lastname} - #{student.firstname}", to: Routes.student_path(@conn, :show, student) %></li>
<% end %>
</ul>

View File

@@ -0,0 +1,23 @@
<h1>Rendu des devoirs - <%= @class.name %></h1>
<%= if @changeset.action do %>
<div class="alert alert-danger">
<p>Oops, quelque chose c'est mal passé, merci de vérifier le formulaire</p>
</div>
<% end %>
<%= form_for @changeset, Routes.deposit_path(@conn, :deposit, @class.id), [multipart: true], fn f -> %>
<%= label f, "Elève" %>
<%= select f, :student_id, Enum.map(@class.students, &{"#{&1.lastname} #{&1.firstname}", &1.id}), selected: @student_id %>
<%= label f, "Devoir" %>
<%= select f, :assignment_id, Enum.map(@assignments, &{"#{&1.title} - #{&1.due}", &1.id}), selected: @assignment_id %>
<%= label f, "Fichier (formats acceptés : pdf, rtf, odt, doc, docx)" %>
<%= file_input f, :file %>
<div>
<%= submit "Envoyer" %>
</div>
<% end %>

View File

@@ -0,0 +1,24 @@
<h1>Liste des rendus</h1>
<table>
<thead>
<tr>
<th>Elève</th>
<th>Classe</th>
<th>Devoir</th>
<th>Validé</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<%= for work <- @works do %>
<tr>
<td><%= link "#{work.student.lastname} #{work.student.firstname}", to: Routes.student_path(@conn, :show, work.student.id) %></td>
<td><%= link work.student.class.name, to: Routes.class_path(@conn, :show, work.student.class.id) %></td>
<td><%= link "#{work.assignment.title} | #{work.assignment.slug}", to: Routes.assignment_path(@conn, :show, work.assignment.id) %></td>
<td><%= work.inserted_at %></td>
<td><%= link "Télécharger", to: Path.join([Application.fetch_env!(:confient, :domain), "uploads", work.student.class.name, work.assignment.slug, work.path]), target: "_blank" %></td>
</tr>
<% end %>
</tbody>
</table>

View File

@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Confi-ENT | Rendus de devois</title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</head>
<body>
<header>
<section class="container">
<%= if signed_in?(@conn) do %>
<%= link "#{String.upcase(get_username(@conn))} (Deconnexion)", to: Routes.session_path(@conn, :delete), method: :delete %>
<% else %>
<%= link "Accès Enseignant", to: Routes.session_path(@conn, :new) %>
<% end %>
<a href="/">
<img src="/images/logo.png" alt="">
</a>
</section>
<%= if signed_in?(@conn) do %>
<div class="container">
<div class="navbar">
<ul>
<li><%= link "Classes", to: Routes.class_path(@conn, :index) %></li>
<li><%= link "Elèves", to: Routes.student_path(@conn, :index) %></li>
<li><%= link "Devoirs", to: Routes.assignment_path(@conn, :index) %></li>
<li><%= link "Rendus", to: Routes.deposit_path(@conn, :index) %></li>
</ul>
</div>
</div>
<% end %>
</header>
<main role="main" class="container main">
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<%= @inner_content %>
</main>
<footer>
<a href="https://www.phoenixframework.org/">🔥🐦</a><a href="https://git.athelas-conseils.fr/papey/confient">👨💻</a>
</footer>
</body>
</html>

View File

@@ -0,0 +1,15 @@
<section class="hero">
<h1>Rendre un devoir</h1>
<hr>
<%= if length(@classes) > 0 do %>
<p>Sélectionner une classe</p>
<ul>
<%= for class <- @classes do %>
<li style="font-size: 2rem;"><%= link class.name, to: Routes.deposit_path(@conn, :form, class.id) %></li>
<% end %>
</ul>
<% else %>
<p>Il n'y a actuellement aucune classe dans la base de données</p>
<% end %>
<hr>
</section>

View File

@@ -0,0 +1,9 @@
<h1>Connexion à l'espace enseignant</h1>
<%= form_for @conn, Routes.session_path(@conn, :new), [as: :session], fn f -> %>
<%= label f, "Identifiant" %>
<%= text_input f, :username, placeholder: "Utilisateur" %>
<%= label f, "Mot de passe" %>
<%= password_input f, :password %>
<%= submit "Se connecter" %>
<% end %>

View File

@@ -0,0 +1,17 @@
<h1>Ajouter un lot d'élèves</h1>
<form action="<%= Routes.student_path(@conn, :bulkcreate) %>" method="post" enctype="multipart/form-data">
<input type="hidden" value="<%= @token %>" name="_csrf_token"/>
<%= label :class, "Classe" %>
<%= select :class, :class_id, @classes %>
<%= label :class, "Fichier" %>
<%= file_input :file, :list %>
<div>
<%= submit "Ajouter" %>
</div>
</form>
<span><%= link "Retour", to: Routes.student_path(@conn, :index) %></span>

View File

@@ -0,0 +1,7 @@
<h1>Editer un élève</h1>
<span><%= link "Retour", to: Routes.student_path(@conn, :index) %></span>
<hr>
<%= render "form.html", Map.put(assigns, :action, Routes.student_path(@conn, :update, @student)) %>

View File

@@ -0,0 +1,22 @@
<%= form_for @changeset, @action, fn f -> %>
<%= if @changeset.action do %>
<div class="alert alert-danger">
<p>Oops, quelque chose c'est mal passé, merci de vérifier le formulaire</p>
</div>
<% end %>
<%= label f, "Nom" %>
<%= text_input f, :lastname %>
<%= error_tag f, :lastname %>
<%= label f, "Prénom" %>
<%= text_input f, :firstname %>
<%= error_tag f, :firstname %>
<%= label f, "Classe" %>
<%= select f, :class_id, @classes %>
<div>
<%= submit "Sauvegarder" %>
</div>
<% end %>

View File

@@ -0,0 +1,32 @@
<h1>Liste des élèves</h1>
<span><%= link "Ajouter un élève", to: Routes.student_path(@conn, :new) %></span> |
<span><%= link "Ajouter un lot d'élèves", to: Routes.student_path(@conn, :bulkform) %></span>
<hr>
<table>
<thead>
<tr>
<th>Prénom</th>
<th>Nom</th>
<th>Classe</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<%= for student <- @students do %>
<tr>
<td><%= student.lastname %></td>
<td><%= student.firstname %></td>
<td><%= link student.class.name, to: Routes.class_path(@conn, :show, student.class.id) %></td>
<td>
<span><%= link "Voir", to: Routes.student_path(@conn, :show, student) %></span>
<span><%= link "Editer", to: Routes.student_path(@conn, :edit, student) %></span>
<span><%= link "Supprimer", to: Routes.student_path(@conn, :delete, student), method: :delete, data: [confirm: "Vous êtes sur ?"] %></span>
</td>
</tr>
<% end %>
</tbody>
</table>

View File

@@ -0,0 +1,5 @@
<h1>Ajouter un nouvel élève</h1>
<%= render "form.html", Map.put(assigns, :action, Routes.student_path(@conn, :create)) %>
<span><%= link "Retour", to: Routes.student_path(@conn, :index) %></span>

View File

@@ -0,0 +1,52 @@
<h1><%= @student.lastname %> - <%= @student.firstname %></h1>
<span><%= link "Editer", to: Routes.student_path(@conn, :edit, @student) %></span>
<span><%= link "Retour", to: Routes.student_path(@conn, :index) %></span>
<hr>
<h2>Informations générales</h2>
<ul>
<li>
<strong>Nom:</strong>
<%= @student.lastname %>
</li>
<li>
<strong>Prénom:</strong>
<%= @student.firstname %>
</li>
<li>
<strong>Classe:</strong>
<%= @student.class.name %>
</li>
</ul>
<h2>Rendus</h2>
<%= if length(@student.students_works) > 0 do %>
<table>
<thead>
<tr>
<th>Titre</th>
<th>Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<%= for work <- @student.students_works do %>
<tr>
<td><%= work.assignment.title %></td>
<td><%= work.inserted_at %></td>
<td><%= link "Télécharger", to: Path.join([ConfientWeb.Endpoint.url(), "uploads", @student.class.name, work.assignment.slug, work.path]), target: "_blank" %></td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<p>Aucun devoir rendu</p>
<% end %>

View File

@@ -0,0 +1,3 @@
defmodule ConfientWeb.AssignmentView do
use ConfientWeb, :view
end

View File

@@ -0,0 +1,3 @@
defmodule ConfientWeb.ClassView do
use ConfientWeb, :view
end

View File

@@ -0,0 +1,3 @@
defmodule ConfientWeb.DepositView do
use ConfientWeb, :view
end

View File

@@ -0,0 +1,47 @@
defmodule ConfientWeb.ErrorHelpers do
@moduledoc """
Conveniences for translating and building error messages.
"""
use Phoenix.HTML
@doc """
Generates tag for inlined form input errors.
"""
def error_tag(form, field) do
Enum.map(Keyword.get_values(form.errors, field), fn error ->
content_tag(:span, translate_error(error),
class: "invalid-feedback",
phx_feedback_for: input_id(form, field)
)
end)
end
@doc """
Translates an error message using gettext.
"""
def translate_error({msg, opts}) do
# When using gettext, we typically pass the strings we want
# to translate as a static argument:
#
# # Translate "is invalid" in the "errors" domain
# dgettext("errors", "is invalid")
#
# # Translate the number of files with plural rules
# dngettext("errors", "1 file", "%{count} files", count)
#
# Because the error messages we show in our forms and APIs
# are defined inside Ecto, we need to translate them dynamically.
# This requires us to call the Gettext module passing our gettext
# backend as first argument.
#
# Note we use the "errors" domain, which means translations
# should be written to the errors.po file. The :count option is
# set by Ecto and indicates we should also apply plural rules.
if count = opts[:count] do
Gettext.dngettext(ConfientWeb.Gettext, "errors", msg, msg, count, opts)
else
Gettext.dgettext(ConfientWeb.Gettext, "errors", msg, opts)
end
end
end

View File

@@ -0,0 +1,16 @@
defmodule ConfientWeb.ErrorView do
use ConfientWeb, :view
# If you want to customize a particular status code
# for a certain format, you may uncomment below.
# def render("500.html", _assigns) do
# "Internal Server Error"
# end
# By default, Phoenix returns the status message from
# the template name. For example, "404.html" becomes
# "Not Found".
def template_not_found(template, _assigns) do
Phoenix.Controller.status_message_from_template(template)
end
end

View File

@@ -0,0 +1,3 @@
defmodule ConfientWeb.LayoutView do
use ConfientWeb, :view
end

View File

@@ -0,0 +1,3 @@
defmodule ConfientWeb.PageView do
use ConfientWeb, :view
end

View File

@@ -0,0 +1,3 @@
defmodule ConfientWeb.SessionView do
use ConfientWeb, :view
end

View File

@@ -0,0 +1,3 @@
defmodule ConfientWeb.StudentView do
use ConfientWeb, :view
end

View File

@@ -0,0 +1,3 @@
defmodule ConfientWeb.UserView do
use ConfientWeb, :view
end