Initial commit
This commit is contained in:
		
							
								
								
									
										10
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					deps
 | 
				
			||||||
 | 
					assets/node_modules
 | 
				
			||||||
 | 
					Dockerfile
 | 
				
			||||||
 | 
					.env
 | 
				
			||||||
 | 
					dev-compose.yml
 | 
				
			||||||
 | 
					docker-compose.yml
 | 
				
			||||||
 | 
					rel
 | 
				
			||||||
 | 
					.vscode
 | 
				
			||||||
 | 
					.elixir_ls
 | 
				
			||||||
 | 
					._build
 | 
				
			||||||
							
								
								
									
										5
									
								
								.formatter.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.formatter.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					[
 | 
				
			||||||
 | 
					  import_deps: [:ecto, :phoenix],
 | 
				
			||||||
 | 
					  inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs}"],
 | 
				
			||||||
 | 
					  subdirectories: ["priv/*/migrations"]
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										38
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					# The directory Mix will write compiled artifacts to.
 | 
				
			||||||
 | 
					/_build/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If you run "mix test --cover", coverage assets end up here.
 | 
				
			||||||
 | 
					/cover/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# The directory Mix downloads your dependencies sources to.
 | 
				
			||||||
 | 
					/deps/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Where 3rd-party dependencies like ExDoc output generated docs.
 | 
				
			||||||
 | 
					/doc/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Ignore .fetch files in case you like to edit your project deps locally.
 | 
				
			||||||
 | 
					/.fetch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If the VM crashes, it generates a dump, let's ignore it too.
 | 
				
			||||||
 | 
					erl_crash.dump
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Also ignore archive artifacts (built via "mix archive.build").
 | 
				
			||||||
 | 
					*.ez
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Ignore package tarball (built via "mix hex.build").
 | 
				
			||||||
 | 
					confient-*.tar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If NPM crashes, it generates a log, let's ignore it too.
 | 
				
			||||||
 | 
					npm-debug.log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# The directory NPM downloads your dependencies sources to.
 | 
				
			||||||
 | 
					/assets/node_modules/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Since we are building assets from assets/,
 | 
				
			||||||
 | 
					# we ignore priv/static. You may want to comment
 | 
				
			||||||
 | 
					# this depending on your deployment strategy.
 | 
				
			||||||
 | 
					/priv/static/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.vscode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					rel
 | 
				
			||||||
							
								
								
									
										82
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					FROM elixir:1.11 AS build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# install build dependencies
 | 
				
			||||||
 | 
					RUN set -eux \
 | 
				
			||||||
 | 
					    &&  apt-get update \
 | 
				
			||||||
 | 
					    &&  apt-get install -y --no-install-recommends \
 | 
				
			||||||
 | 
					    apt-transport-https \
 | 
				
			||||||
 | 
					    ca-certificates \
 | 
				
			||||||
 | 
					    curl \
 | 
				
			||||||
 | 
					    &&  curl -sS https://deb.nodesource.com/gpgkey/nodesource.gpg.key | gpg --dearmor > /etc/apt/trusted.gpg.d/nodesource.gpg \
 | 
				
			||||||
 | 
					    &&  echo "deb https://deb.nodesource.com/node_14.x stretch main" > /etc/apt/sources.list.d/nodesource.list \
 | 
				
			||||||
 | 
					    &&  apt-get update \
 | 
				
			||||||
 | 
					    &&  apt-get install -y --no-install-recommends \
 | 
				
			||||||
 | 
					    nodejs \
 | 
				
			||||||
 | 
					    &&  apt-get clean \
 | 
				
			||||||
 | 
					    &&  rm -rf /var/lib/apt/lists/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# prepare build dir
 | 
				
			||||||
 | 
					WORKDIR /opt/confient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# install hex + rebar
 | 
				
			||||||
 | 
					RUN mix local.hex --force && \
 | 
				
			||||||
 | 
					    mix local.rebar --force
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# set build ENV
 | 
				
			||||||
 | 
					ENV MIX_ENV=prod
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# install mix dependencies
 | 
				
			||||||
 | 
					COPY mix.exs mix.lock ./
 | 
				
			||||||
 | 
					COPY config config
 | 
				
			||||||
 | 
					RUN mix do deps.get, deps.compile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# build assets
 | 
				
			||||||
 | 
					COPY assets/package.json assets/package-lock.json ./assets/
 | 
				
			||||||
 | 
					RUN npm --prefix ./assets ci --progress=false --no-audit --loglevel=error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY priv priv
 | 
				
			||||||
 | 
					COPY assets assets
 | 
				
			||||||
 | 
					RUN npm run --prefix ./assets deploy
 | 
				
			||||||
 | 
					RUN mix phx.digest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# compile and build release
 | 
				
			||||||
 | 
					COPY lib lib
 | 
				
			||||||
 | 
					# uncomment COPY if rel/ exists
 | 
				
			||||||
 | 
					# COPY rel rel
 | 
				
			||||||
 | 
					RUN mix do compile, release
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# prepare release image
 | 
				
			||||||
 | 
					FROM elixir:1.11 AS app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN set -eux \
 | 
				
			||||||
 | 
					    &&  apt-get update \
 | 
				
			||||||
 | 
					    &&  apt-get install -y --no-install-recommends \
 | 
				
			||||||
 | 
					    postgresql-client \
 | 
				
			||||||
 | 
					    &&  apt-get clean \
 | 
				
			||||||
 | 
					    &&  rm -rf /var/lib/apt/lists/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN useradd -ms /bin/bash confient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN mkdir -p /opt/confient /srv/confient/uploads
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WORKDIR /opt/confient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN chown confient:confient /opt/confient /srv/confient/uploads
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					USER confient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY --from=build --chown=confient:confient /opt/confient/_build/prod/rel/confient ./
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY entrypoint.sh /opt/confient/entrypoint.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENV HOME=/opt/confient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# image-spec annotations using labels
 | 
				
			||||||
 | 
					# https://github.com/opencontainers/image-spec/blob/master/annotations.md
 | 
				
			||||||
 | 
					LABEL org.opencontainers.image.source="https://git.athelas-conseils.fr/papey/confient"
 | 
				
			||||||
 | 
					LABEL org.opencontainers.image.authors="Wilfried OLLIVIER"
 | 
				
			||||||
 | 
					LABEL org.opencontainers.image.title="Confient"
 | 
				
			||||||
 | 
					LABEL org.opencontainers.image.description="Confient runtime"
 | 
				
			||||||
 | 
					LABEL org.opencontainers.image.licences="Unlicense"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CMD ["./entrypoint.sh"]
 | 
				
			||||||
							
								
								
									
										24
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					This is free and unencumbered software released into the public domain.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Anyone is free to copy, modify, publish, use, compile, sell, or
 | 
				
			||||||
 | 
					distribute this software, either in source code form or as a compiled
 | 
				
			||||||
 | 
					binary, for any purpose, commercial or non-commercial, and by any
 | 
				
			||||||
 | 
					means.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In jurisdictions that recognize copyright laws, the author or authors
 | 
				
			||||||
 | 
					of this software dedicate any and all copyright interest in the
 | 
				
			||||||
 | 
					software to the public domain. We make this dedication for the benefit
 | 
				
			||||||
 | 
					of the public at large and to the detriment of our heirs and
 | 
				
			||||||
 | 
					successors. We intend this dedication to be an overt act of
 | 
				
			||||||
 | 
					relinquishment in perpetuity of all present and future rights to this
 | 
				
			||||||
 | 
					software under copyright law.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
				
			||||||
 | 
					EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
				
			||||||
 | 
					MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 | 
				
			||||||
 | 
					IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 | 
				
			||||||
 | 
					OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 | 
				
			||||||
 | 
					ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 | 
				
			||||||
 | 
					OTHER DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For more information, please refer to <http://unlicense.org/>
 | 
				
			||||||
							
								
								
									
										62
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					# Confient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A tiny [Phoenix](https://www.phoenixframework.org/) web app for teacher to
 | 
				
			||||||
 | 
					collect students works
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Getting Started
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Prerequisites
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [Elixir](https://elixir-lang.org/)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Installing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Get Confient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##### From source
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Clone this repo and run
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					mix deps.get
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To download all the deps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					cd assets && npm install
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To get Node.js deps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This apps needs a [PostgreSQL](https://www.postgresql.org/) data base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					As a startup you can une the one in `compose/dev`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					cd compose/dev && docker-compose up
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When DB is up, run the migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					mix ecto.migrate
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Then start the server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					mix phx.server
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Visit [`localhost:4000`](http://localhost:4000) from your browser : 🎉
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you need details on how to run it in production, see [Phoenix deployment guides](https://hexdocs.pm/phoenix/deployment.html).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Authors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Wilfried OLLIVIER - Main author - [papey](https://git.athelas-conseils.fr/papey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[LICENSE](LICENSE) file for details
 | 
				
			||||||
							
								
								
									
										5
									
								
								assets/.babelrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								assets/.babelrc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "presets": [
 | 
				
			||||||
 | 
					        "@babel/preset-env"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										122
									
								
								assets/css/app.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								assets/css/app.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,122 @@
 | 
				
			|||||||
 | 
					/* This file is for your main application css. */
 | 
				
			||||||
 | 
					@import "./phoenix.css";
 | 
				
			||||||
 | 
					@import "./colors.scss";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Alerts and form errors */
 | 
				
			||||||
 | 
					.alert {
 | 
				
			||||||
 | 
					  padding: 15px;
 | 
				
			||||||
 | 
					  margin-bottom: 20px;
 | 
				
			||||||
 | 
					  border: 1px solid transparent;
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.alert-info {
 | 
				
			||||||
 | 
					  color: #31708f;
 | 
				
			||||||
 | 
					  background-color: #d9edf7;
 | 
				
			||||||
 | 
					  border-color: #bce8f1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.alert-warning {
 | 
				
			||||||
 | 
					  color: #8a6d3b;
 | 
				
			||||||
 | 
					  background-color: #fcf8e3;
 | 
				
			||||||
 | 
					  border-color: #faebcc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.alert-danger {
 | 
				
			||||||
 | 
					  color: #a94442;
 | 
				
			||||||
 | 
					  background-color: #f2dede;
 | 
				
			||||||
 | 
					  border-color: #ebccd1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.alert p {
 | 
				
			||||||
 | 
					  margin-bottom: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.alert:empty {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.invalid-feedback {
 | 
				
			||||||
 | 
					  color: #a94442;
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  margin: -1rem 0 2rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.navbar ul {
 | 
				
			||||||
 | 
					  border-radius: 3px;
 | 
				
			||||||
 | 
					  list-style-type: none;
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  background-color: $lblack;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.navbar li {
 | 
				
			||||||
 | 
					  float: left;
 | 
				
			||||||
 | 
					  margin-bottom: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.navbar li a {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  color: white;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  background-color: $lblack;
 | 
				
			||||||
 | 
					  padding: 1.5rem;
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.navbar li a:hover {
 | 
				
			||||||
 | 
					  background-color: $hover;
 | 
				
			||||||
 | 
					  border-top: 0.5rem solid #eaeaea;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.sign-in {
 | 
				
			||||||
 | 
					  margin-bottom: 2rem;
 | 
				
			||||||
 | 
					  color: $link;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hero {
 | 
				
			||||||
 | 
					  padding: 10rem;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  border-radius: 3px;
 | 
				
			||||||
 | 
					  color: black;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					a {
 | 
				
			||||||
 | 
					  color: $link;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.button,
 | 
				
			||||||
 | 
					button,
 | 
				
			||||||
 | 
					input[type="button"],
 | 
				
			||||||
 | 
					input[type="reset"],
 | 
				
			||||||
 | 
					input[type="submit"] {
 | 
				
			||||||
 | 
					  background-color: $blue;
 | 
				
			||||||
 | 
					  border: 0.1rem solid $blue;
 | 
				
			||||||
 | 
					  border-radius: 0.4rem;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					  font-size: 1.1rem;
 | 
				
			||||||
 | 
					  font-weight: 700;
 | 
				
			||||||
 | 
					  height: 3.8rem;
 | 
				
			||||||
 | 
					  letter-spacing: 0.1rem;
 | 
				
			||||||
 | 
					  line-height: 3.8rem;
 | 
				
			||||||
 | 
					  padding: 0 3rem;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					  text-transform: uppercase;
 | 
				
			||||||
 | 
					  white-space: nowrap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					footer {
 | 
				
			||||||
 | 
					  background-color: #fdfdfd;
 | 
				
			||||||
 | 
					  border-top: 0.5rem solid #eaeaea;
 | 
				
			||||||
 | 
					  padding: 1rem;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  position: fixed;
 | 
				
			||||||
 | 
					  bottom: 0;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					html {
 | 
				
			||||||
 | 
					  min-height: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.main {
 | 
				
			||||||
 | 
					  padding-bottom: 4rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								assets/css/colors.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								assets/css/colors.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					/* Colorscheme */
 | 
				
			||||||
 | 
					$blue: #4682b4;
 | 
				
			||||||
 | 
					$lblue: #269ccc;
 | 
				
			||||||
 | 
					$gray: #696969;
 | 
				
			||||||
 | 
					$lblack: #333;
 | 
				
			||||||
 | 
					$lgray: #d3d3d3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$link: $blue;
 | 
				
			||||||
 | 
					$bg: $gray;
 | 
				
			||||||
 | 
					$hover: $blue;
 | 
				
			||||||
 | 
					$hero-bg: $lgray;
 | 
				
			||||||
							
								
								
									
										635
									
								
								assets/css/phoenix.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										635
									
								
								assets/css/phoenix.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,635 @@
 | 
				
			|||||||
 | 
					/* Includes some default style for the starter application.
 | 
				
			||||||
 | 
					 * This can be safely deleted to start fresh.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Milligram v1.3.0 https://milligram.github.io
 | 
				
			||||||
 | 
					 * Copyright (c) 2017 CJ Patoilo Licensed under the MIT license
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*,
 | 
				
			||||||
 | 
					*:after,
 | 
				
			||||||
 | 
					*:before {
 | 
				
			||||||
 | 
					  box-sizing: inherit;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					html {
 | 
				
			||||||
 | 
					  height: 100vh;
 | 
				
			||||||
 | 
					  box-sizing: border-box;
 | 
				
			||||||
 | 
					  font-size: 62.5%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					body {
 | 
				
			||||||
 | 
					  min-height: 100%;
 | 
				
			||||||
 | 
					  color: #000000;
 | 
				
			||||||
 | 
					  font-family: "Helvetica", "Arial", sans-serif;
 | 
				
			||||||
 | 
					  font-size: 1.6em;
 | 
				
			||||||
 | 
					  font-weight: 300;
 | 
				
			||||||
 | 
					  line-height: 1.6;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					blockquote {
 | 
				
			||||||
 | 
					  border-left: 0.3rem solid #d1d1d1;
 | 
				
			||||||
 | 
					  margin-left: 0;
 | 
				
			||||||
 | 
					  margin-right: 0;
 | 
				
			||||||
 | 
					  padding: 1rem 1.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					blockquote *:last-child {
 | 
				
			||||||
 | 
					  margin-bottom: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button,
 | 
				
			||||||
 | 
					button,
 | 
				
			||||||
 | 
					input[type="button"],
 | 
				
			||||||
 | 
					input[type="reset"],
 | 
				
			||||||
 | 
					input[type="submit"] {
 | 
				
			||||||
 | 
					  background-color: #0069d9;
 | 
				
			||||||
 | 
					  border: 0.1rem solid #0069d9;
 | 
				
			||||||
 | 
					  border-radius: 0.4rem;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					  font-size: 1.1rem;
 | 
				
			||||||
 | 
					  font-weight: 700;
 | 
				
			||||||
 | 
					  height: 3.8rem;
 | 
				
			||||||
 | 
					  letter-spacing: 0.1rem;
 | 
				
			||||||
 | 
					  line-height: 3.8rem;
 | 
				
			||||||
 | 
					  padding: 0 3rem;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					  text-transform: uppercase;
 | 
				
			||||||
 | 
					  white-space: nowrap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button:focus,
 | 
				
			||||||
 | 
					.button:hover,
 | 
				
			||||||
 | 
					button:focus,
 | 
				
			||||||
 | 
					button:hover,
 | 
				
			||||||
 | 
					input[type="button"]:focus,
 | 
				
			||||||
 | 
					input[type="button"]:hover,
 | 
				
			||||||
 | 
					input[type="reset"]:focus,
 | 
				
			||||||
 | 
					input[type="reset"]:hover,
 | 
				
			||||||
 | 
					input[type="submit"]:focus,
 | 
				
			||||||
 | 
					input[type="submit"]:hover {
 | 
				
			||||||
 | 
					  background-color: #606c76;
 | 
				
			||||||
 | 
					  border-color: #606c76;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  outline: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button[disabled],
 | 
				
			||||||
 | 
					button[disabled],
 | 
				
			||||||
 | 
					input[type="button"][disabled],
 | 
				
			||||||
 | 
					input[type="reset"][disabled],
 | 
				
			||||||
 | 
					input[type="submit"][disabled] {
 | 
				
			||||||
 | 
					  cursor: default;
 | 
				
			||||||
 | 
					  opacity: 0.5;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button[disabled]:focus,
 | 
				
			||||||
 | 
					.button[disabled]:hover,
 | 
				
			||||||
 | 
					button[disabled]:focus,
 | 
				
			||||||
 | 
					button[disabled]:hover,
 | 
				
			||||||
 | 
					input[type="button"][disabled]:focus,
 | 
				
			||||||
 | 
					input[type="button"][disabled]:hover,
 | 
				
			||||||
 | 
					input[type="reset"][disabled]:focus,
 | 
				
			||||||
 | 
					input[type="reset"][disabled]:hover,
 | 
				
			||||||
 | 
					input[type="submit"][disabled]:focus,
 | 
				
			||||||
 | 
					input[type="submit"][disabled]:hover {
 | 
				
			||||||
 | 
					  background-color: #0069d9;
 | 
				
			||||||
 | 
					  border-color: #0069d9;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button.button-outline,
 | 
				
			||||||
 | 
					button.button-outline,
 | 
				
			||||||
 | 
					input[type="button"].button-outline,
 | 
				
			||||||
 | 
					input[type="reset"].button-outline,
 | 
				
			||||||
 | 
					input[type="submit"].button-outline {
 | 
				
			||||||
 | 
					  background-color: transparent;
 | 
				
			||||||
 | 
					  color: #0069d9;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button.button-outline:focus,
 | 
				
			||||||
 | 
					.button.button-outline:hover,
 | 
				
			||||||
 | 
					button.button-outline:focus,
 | 
				
			||||||
 | 
					button.button-outline:hover,
 | 
				
			||||||
 | 
					input[type="button"].button-outline:focus,
 | 
				
			||||||
 | 
					input[type="button"].button-outline:hover,
 | 
				
			||||||
 | 
					input[type="reset"].button-outline:focus,
 | 
				
			||||||
 | 
					input[type="reset"].button-outline:hover,
 | 
				
			||||||
 | 
					input[type="submit"].button-outline:focus,
 | 
				
			||||||
 | 
					input[type="submit"].button-outline:hover {
 | 
				
			||||||
 | 
					  background-color: transparent;
 | 
				
			||||||
 | 
					  border-color: #606c76;
 | 
				
			||||||
 | 
					  color: #606c76;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button.button-outline[disabled]:focus,
 | 
				
			||||||
 | 
					.button.button-outline[disabled]:hover,
 | 
				
			||||||
 | 
					button.button-outline[disabled]:focus,
 | 
				
			||||||
 | 
					button.button-outline[disabled]:hover,
 | 
				
			||||||
 | 
					input[type="button"].button-outline[disabled]:focus,
 | 
				
			||||||
 | 
					input[type="button"].button-outline[disabled]:hover,
 | 
				
			||||||
 | 
					input[type="reset"].button-outline[disabled]:focus,
 | 
				
			||||||
 | 
					input[type="reset"].button-outline[disabled]:hover,
 | 
				
			||||||
 | 
					input[type="submit"].button-outline[disabled]:focus,
 | 
				
			||||||
 | 
					input[type="submit"].button-outline[disabled]:hover {
 | 
				
			||||||
 | 
					  border-color: inherit;
 | 
				
			||||||
 | 
					  color: #0069d9;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button.button-clear,
 | 
				
			||||||
 | 
					button.button-clear,
 | 
				
			||||||
 | 
					input[type="button"].button-clear,
 | 
				
			||||||
 | 
					input[type="reset"].button-clear,
 | 
				
			||||||
 | 
					input[type="submit"].button-clear {
 | 
				
			||||||
 | 
					  background-color: transparent;
 | 
				
			||||||
 | 
					  border-color: transparent;
 | 
				
			||||||
 | 
					  color: #0069d9;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button.button-clear:focus,
 | 
				
			||||||
 | 
					.button.button-clear:hover,
 | 
				
			||||||
 | 
					button.button-clear:focus,
 | 
				
			||||||
 | 
					button.button-clear:hover,
 | 
				
			||||||
 | 
					input[type="button"].button-clear:focus,
 | 
				
			||||||
 | 
					input[type="button"].button-clear:hover,
 | 
				
			||||||
 | 
					input[type="reset"].button-clear:focus,
 | 
				
			||||||
 | 
					input[type="reset"].button-clear:hover,
 | 
				
			||||||
 | 
					input[type="submit"].button-clear:focus,
 | 
				
			||||||
 | 
					input[type="submit"].button-clear:hover {
 | 
				
			||||||
 | 
					  background-color: transparent;
 | 
				
			||||||
 | 
					  border-color: transparent;
 | 
				
			||||||
 | 
					  color: #606c76;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button.button-clear[disabled]:focus,
 | 
				
			||||||
 | 
					.button.button-clear[disabled]:hover,
 | 
				
			||||||
 | 
					button.button-clear[disabled]:focus,
 | 
				
			||||||
 | 
					button.button-clear[disabled]:hover,
 | 
				
			||||||
 | 
					input[type="button"].button-clear[disabled]:focus,
 | 
				
			||||||
 | 
					input[type="button"].button-clear[disabled]:hover,
 | 
				
			||||||
 | 
					input[type="reset"].button-clear[disabled]:focus,
 | 
				
			||||||
 | 
					input[type="reset"].button-clear[disabled]:hover,
 | 
				
			||||||
 | 
					input[type="submit"].button-clear[disabled]:focus,
 | 
				
			||||||
 | 
					input[type="submit"].button-clear[disabled]:hover {
 | 
				
			||||||
 | 
					  color: #0069d9;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					code {
 | 
				
			||||||
 | 
					  background: #f4f5f6;
 | 
				
			||||||
 | 
					  border-radius: 0.4rem;
 | 
				
			||||||
 | 
					  font-size: 86%;
 | 
				
			||||||
 | 
					  margin: 0 0.2rem;
 | 
				
			||||||
 | 
					  padding: 0.2rem 0.5rem;
 | 
				
			||||||
 | 
					  white-space: nowrap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					pre {
 | 
				
			||||||
 | 
					  background: #f4f5f6;
 | 
				
			||||||
 | 
					  border-left: 0.3rem solid #0069d9;
 | 
				
			||||||
 | 
					  overflow-y: hidden;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					pre > code {
 | 
				
			||||||
 | 
					  border-radius: 0;
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  padding: 1rem 1.5rem;
 | 
				
			||||||
 | 
					  white-space: pre;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					hr {
 | 
				
			||||||
 | 
					  border: 0;
 | 
				
			||||||
 | 
					  border-top: 0.1rem solid #f4f5f6;
 | 
				
			||||||
 | 
					  margin: 3rem 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					input[type="email"],
 | 
				
			||||||
 | 
					input[type="number"],
 | 
				
			||||||
 | 
					input[type="password"],
 | 
				
			||||||
 | 
					input[type="search"],
 | 
				
			||||||
 | 
					input[type="tel"],
 | 
				
			||||||
 | 
					input[type="text"],
 | 
				
			||||||
 | 
					input[type="url"],
 | 
				
			||||||
 | 
					textarea,
 | 
				
			||||||
 | 
					select {
 | 
				
			||||||
 | 
					  -webkit-appearance: none;
 | 
				
			||||||
 | 
					  -moz-appearance: none;
 | 
				
			||||||
 | 
					  appearance: none;
 | 
				
			||||||
 | 
					  background-color: transparent;
 | 
				
			||||||
 | 
					  border: 0.1rem solid #d1d1d1;
 | 
				
			||||||
 | 
					  border-radius: 0.4rem;
 | 
				
			||||||
 | 
					  box-shadow: none;
 | 
				
			||||||
 | 
					  box-sizing: inherit;
 | 
				
			||||||
 | 
					  height: 3.8rem;
 | 
				
			||||||
 | 
					  padding: 0.6rem 1rem;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					input[type="email"]:focus,
 | 
				
			||||||
 | 
					input[type="number"]:focus,
 | 
				
			||||||
 | 
					input[type="password"]:focus,
 | 
				
			||||||
 | 
					input[type="search"]:focus,
 | 
				
			||||||
 | 
					input[type="tel"]:focus,
 | 
				
			||||||
 | 
					input[type="text"]:focus,
 | 
				
			||||||
 | 
					input[type="url"]:focus,
 | 
				
			||||||
 | 
					textarea:focus,
 | 
				
			||||||
 | 
					select:focus {
 | 
				
			||||||
 | 
					  border-color: #0069d9;
 | 
				
			||||||
 | 
					  outline: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					select {
 | 
				
			||||||
 | 
					  background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 29 14" width="29"><path fill="%23d1d1d1" d="M9.37727 3.625l5.08154 6.93523L19.54036 3.625"/></svg>')
 | 
				
			||||||
 | 
					    center right no-repeat;
 | 
				
			||||||
 | 
					  padding-right: 3rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					select:focus {
 | 
				
			||||||
 | 
					  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 29 14" width="29"><path fill="%230069d9" d="M9.37727 3.625l5.08154 6.93523L19.54036 3.625"/></svg>');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					textarea {
 | 
				
			||||||
 | 
					  min-height: 6.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					label,
 | 
				
			||||||
 | 
					legend {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  font-size: 1.6rem;
 | 
				
			||||||
 | 
					  font-weight: 700;
 | 
				
			||||||
 | 
					  margin-bottom: 0.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					fieldset {
 | 
				
			||||||
 | 
					  border-width: 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					input[type="checkbox"],
 | 
				
			||||||
 | 
					input[type="radio"] {
 | 
				
			||||||
 | 
					  display: inline;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.label-inline {
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					  font-weight: normal;
 | 
				
			||||||
 | 
					  margin-left: 0.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row.row-no-padding {
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row.row-no-padding > .column {
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row.row-wrap {
 | 
				
			||||||
 | 
					  flex-wrap: wrap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row.row-top {
 | 
				
			||||||
 | 
					  align-items: flex-start;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row.row-bottom {
 | 
				
			||||||
 | 
					  align-items: flex-end;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row.row-center {
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row.row-stretch {
 | 
				
			||||||
 | 
					  align-items: stretch;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row.row-baseline {
 | 
				
			||||||
 | 
					  align-items: baseline;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  flex: 1 1 auto;
 | 
				
			||||||
 | 
					  margin-left: 0;
 | 
				
			||||||
 | 
					  max-width: 100%;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-offset-10 {
 | 
				
			||||||
 | 
					  margin-left: 10%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-offset-20 {
 | 
				
			||||||
 | 
					  margin-left: 20%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-offset-25 {
 | 
				
			||||||
 | 
					  margin-left: 25%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-offset-33,
 | 
				
			||||||
 | 
					.row .column.column-offset-34 {
 | 
				
			||||||
 | 
					  margin-left: 33.3333%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-offset-50 {
 | 
				
			||||||
 | 
					  margin-left: 50%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-offset-66,
 | 
				
			||||||
 | 
					.row .column.column-offset-67 {
 | 
				
			||||||
 | 
					  margin-left: 66.6666%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-offset-75 {
 | 
				
			||||||
 | 
					  margin-left: 75%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-offset-80 {
 | 
				
			||||||
 | 
					  margin-left: 80%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-offset-90 {
 | 
				
			||||||
 | 
					  margin-left: 90%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-10 {
 | 
				
			||||||
 | 
					  flex: 0 0 10%;
 | 
				
			||||||
 | 
					  max-width: 10%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-20 {
 | 
				
			||||||
 | 
					  flex: 0 0 20%;
 | 
				
			||||||
 | 
					  max-width: 20%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-25 {
 | 
				
			||||||
 | 
					  flex: 0 0 25%;
 | 
				
			||||||
 | 
					  max-width: 25%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-33,
 | 
				
			||||||
 | 
					.row .column.column-34 {
 | 
				
			||||||
 | 
					  flex: 0 0 33.3333%;
 | 
				
			||||||
 | 
					  max-width: 33.3333%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-40 {
 | 
				
			||||||
 | 
					  flex: 0 0 40%;
 | 
				
			||||||
 | 
					  max-width: 40%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-50 {
 | 
				
			||||||
 | 
					  flex: 0 0 50%;
 | 
				
			||||||
 | 
					  max-width: 50%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-60 {
 | 
				
			||||||
 | 
					  flex: 0 0 60%;
 | 
				
			||||||
 | 
					  max-width: 60%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-66,
 | 
				
			||||||
 | 
					.row .column.column-67 {
 | 
				
			||||||
 | 
					  flex: 0 0 66.6666%;
 | 
				
			||||||
 | 
					  max-width: 66.6666%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-75 {
 | 
				
			||||||
 | 
					  flex: 0 0 75%;
 | 
				
			||||||
 | 
					  max-width: 75%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-80 {
 | 
				
			||||||
 | 
					  flex: 0 0 80%;
 | 
				
			||||||
 | 
					  max-width: 80%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column.column-90 {
 | 
				
			||||||
 | 
					  flex: 0 0 90%;
 | 
				
			||||||
 | 
					  max-width: 90%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column .column-top {
 | 
				
			||||||
 | 
					  align-self: flex-start;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column .column-bottom {
 | 
				
			||||||
 | 
					  align-self: flex-end;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.row .column .column-center {
 | 
				
			||||||
 | 
					  -ms-grid-row-align: center;
 | 
				
			||||||
 | 
					  align-self: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					@media (min-width: 40rem) {
 | 
				
			||||||
 | 
					  .row {
 | 
				
			||||||
 | 
					    flex-direction: row;
 | 
				
			||||||
 | 
					    margin-left: -1rem;
 | 
				
			||||||
 | 
					    width: calc(100% + 2rem);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .row .column {
 | 
				
			||||||
 | 
					    margin-bottom: inherit;
 | 
				
			||||||
 | 
					    padding: 0 1rem;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					a {
 | 
				
			||||||
 | 
					  color: #0069d9;
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					a:focus,
 | 
				
			||||||
 | 
					a:hover {
 | 
				
			||||||
 | 
					  color: #606c76;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					dl,
 | 
				
			||||||
 | 
					ol,
 | 
				
			||||||
 | 
					ul {
 | 
				
			||||||
 | 
					  list-style: none;
 | 
				
			||||||
 | 
					  margin-top: 0;
 | 
				
			||||||
 | 
					  padding-left: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					dl dl,
 | 
				
			||||||
 | 
					dl ol,
 | 
				
			||||||
 | 
					dl ul,
 | 
				
			||||||
 | 
					ol dl,
 | 
				
			||||||
 | 
					ol ol,
 | 
				
			||||||
 | 
					ol ul,
 | 
				
			||||||
 | 
					ul dl,
 | 
				
			||||||
 | 
					ul ol,
 | 
				
			||||||
 | 
					ul ul {
 | 
				
			||||||
 | 
					  font-size: 90%;
 | 
				
			||||||
 | 
					  margin: 1.5rem 0 1.5rem 3rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					ol {
 | 
				
			||||||
 | 
					  list-style: decimal inside;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					ul {
 | 
				
			||||||
 | 
					  list-style: circle inside;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button,
 | 
				
			||||||
 | 
					button,
 | 
				
			||||||
 | 
					dd,
 | 
				
			||||||
 | 
					dt,
 | 
				
			||||||
 | 
					li {
 | 
				
			||||||
 | 
					  margin-bottom: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					fieldset,
 | 
				
			||||||
 | 
					input,
 | 
				
			||||||
 | 
					select,
 | 
				
			||||||
 | 
					textarea {
 | 
				
			||||||
 | 
					  margin-bottom: 1.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					blockquote,
 | 
				
			||||||
 | 
					dl,
 | 
				
			||||||
 | 
					figure,
 | 
				
			||||||
 | 
					form,
 | 
				
			||||||
 | 
					ol,
 | 
				
			||||||
 | 
					p,
 | 
				
			||||||
 | 
					pre,
 | 
				
			||||||
 | 
					table,
 | 
				
			||||||
 | 
					ul {
 | 
				
			||||||
 | 
					  margin-bottom: 2.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					table {
 | 
				
			||||||
 | 
					  border-spacing: 0;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					td,
 | 
				
			||||||
 | 
					th {
 | 
				
			||||||
 | 
					  border-bottom: 0.1rem solid #e1e1e1;
 | 
				
			||||||
 | 
					  padding: 1.2rem 1.5rem;
 | 
				
			||||||
 | 
					  text-align: left;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					td:first-child,
 | 
				
			||||||
 | 
					th:first-child {
 | 
				
			||||||
 | 
					  padding-left: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					td:last-child,
 | 
				
			||||||
 | 
					th:last-child {
 | 
				
			||||||
 | 
					  padding-right: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					b,
 | 
				
			||||||
 | 
					strong {
 | 
				
			||||||
 | 
					  font-weight: bold;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					p {
 | 
				
			||||||
 | 
					  margin-top: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					h1,
 | 
				
			||||||
 | 
					h2,
 | 
				
			||||||
 | 
					h3,
 | 
				
			||||||
 | 
					h4,
 | 
				
			||||||
 | 
					h5,
 | 
				
			||||||
 | 
					h6 {
 | 
				
			||||||
 | 
					  font-weight: 300;
 | 
				
			||||||
 | 
					  letter-spacing: -0.1rem;
 | 
				
			||||||
 | 
					  margin-bottom: 2rem;
 | 
				
			||||||
 | 
					  margin-top: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					h1 {
 | 
				
			||||||
 | 
					  font-size: 4.6rem;
 | 
				
			||||||
 | 
					  line-height: 1.2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					h2 {
 | 
				
			||||||
 | 
					  font-size: 3.6rem;
 | 
				
			||||||
 | 
					  line-height: 1.25;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					h3 {
 | 
				
			||||||
 | 
					  font-size: 2.8rem;
 | 
				
			||||||
 | 
					  line-height: 1.3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					h4 {
 | 
				
			||||||
 | 
					  font-size: 2.2rem;
 | 
				
			||||||
 | 
					  letter-spacing: -0.08rem;
 | 
				
			||||||
 | 
					  line-height: 1.35;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					h5 {
 | 
				
			||||||
 | 
					  font-size: 1.8rem;
 | 
				
			||||||
 | 
					  letter-spacing: -0.05rem;
 | 
				
			||||||
 | 
					  line-height: 1.5;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					h6 {
 | 
				
			||||||
 | 
					  font-size: 1.6rem;
 | 
				
			||||||
 | 
					  letter-spacing: 0;
 | 
				
			||||||
 | 
					  line-height: 1.4;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					img {
 | 
				
			||||||
 | 
					  max-width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.clearfix:after {
 | 
				
			||||||
 | 
					  clear: both;
 | 
				
			||||||
 | 
					  content: " ";
 | 
				
			||||||
 | 
					  display: table;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.float-left {
 | 
				
			||||||
 | 
					  float: left;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.float-right {
 | 
				
			||||||
 | 
					  float: right;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* General style */
 | 
				
			||||||
 | 
					h1 {
 | 
				
			||||||
 | 
					  font-size: 3.6rem;
 | 
				
			||||||
 | 
					  line-height: 1.25;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					h2 {
 | 
				
			||||||
 | 
					  font-size: 2.8rem;
 | 
				
			||||||
 | 
					  line-height: 1.3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					h3 {
 | 
				
			||||||
 | 
					  font-size: 2.2rem;
 | 
				
			||||||
 | 
					  letter-spacing: -0.08rem;
 | 
				
			||||||
 | 
					  line-height: 1.35;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					h4 {
 | 
				
			||||||
 | 
					  font-size: 1.8rem;
 | 
				
			||||||
 | 
					  letter-spacing: -0.05rem;
 | 
				
			||||||
 | 
					  line-height: 1.5;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					h5 {
 | 
				
			||||||
 | 
					  font-size: 1.6rem;
 | 
				
			||||||
 | 
					  letter-spacing: 0;
 | 
				
			||||||
 | 
					  line-height: 1.4;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					h6 {
 | 
				
			||||||
 | 
					  font-size: 1.4rem;
 | 
				
			||||||
 | 
					  letter-spacing: 0;
 | 
				
			||||||
 | 
					  line-height: 1.2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					pre {
 | 
				
			||||||
 | 
					  padding: 1em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.container {
 | 
				
			||||||
 | 
					  margin: 0 auto;
 | 
				
			||||||
 | 
					  max-width: 80rem;
 | 
				
			||||||
 | 
					  padding: 0 2rem;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					select {
 | 
				
			||||||
 | 
					  width: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Phoenix promo and logo */
 | 
				
			||||||
 | 
					.phx-hero {
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  border-bottom: 1px solid #e3e3e3;
 | 
				
			||||||
 | 
					  background: #eee;
 | 
				
			||||||
 | 
					  border-radius: 6px;
 | 
				
			||||||
 | 
					  padding: 3em 3em 1em;
 | 
				
			||||||
 | 
					  margin-bottom: 3rem;
 | 
				
			||||||
 | 
					  font-weight: 200;
 | 
				
			||||||
 | 
					  font-size: 120%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.phx-hero input {
 | 
				
			||||||
 | 
					  background: #ffffff;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.phx-logo {
 | 
				
			||||||
 | 
					  min-width: 300px;
 | 
				
			||||||
 | 
					  margin: 1rem;
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.phx-logo img {
 | 
				
			||||||
 | 
					  width: auto;
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Headers */
 | 
				
			||||||
 | 
					header {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  background: #fdfdfd;
 | 
				
			||||||
 | 
					  border-bottom: 0.5rem solid #eaeaea;
 | 
				
			||||||
 | 
					  margin-bottom: 2rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					header section {
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  justify-content: space-between;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					header section :first-child {
 | 
				
			||||||
 | 
					  order: 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					header section :last-child {
 | 
				
			||||||
 | 
					  order: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					header nav ul,
 | 
				
			||||||
 | 
					header nav li {
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  text-align: right;
 | 
				
			||||||
 | 
					  white-space: nowrap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					header nav ul {
 | 
				
			||||||
 | 
					  margin: 1rem;
 | 
				
			||||||
 | 
					  margin-top: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					header nav a {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media (min-width: 40rem) {
 | 
				
			||||||
 | 
					  /* Small devices (landscape phones, 576px and up) */
 | 
				
			||||||
 | 
					  header section {
 | 
				
			||||||
 | 
					    flex-direction: row;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  header nav ul {
 | 
				
			||||||
 | 
					    margin: 1rem;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .phx-logo {
 | 
				
			||||||
 | 
					    flex-basis: 527px;
 | 
				
			||||||
 | 
					    margin: 2rem 1rem;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								assets/js/app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								assets/js/app.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					// We need to import the CSS so that webpack will load it.
 | 
				
			||||||
 | 
					// The MiniCssExtractPlugin is used to separate it out into
 | 
				
			||||||
 | 
					// its own CSS file.
 | 
				
			||||||
 | 
					import "../css/app.scss"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// webpack automatically bundles all modules in your
 | 
				
			||||||
 | 
					// entry points. Those entry points can be configured
 | 
				
			||||||
 | 
					// in "webpack.config.js".
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Import deps with the dep name or local files with a relative path, for example:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     import {Socket} from "phoenix"
 | 
				
			||||||
 | 
					//     import socket from "./socket"
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					import "phoenix_html"
 | 
				
			||||||
							
								
								
									
										63
									
								
								assets/js/socket.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								assets/js/socket.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					// NOTE: The contents of this file will only be executed if
 | 
				
			||||||
 | 
					// you uncomment its entry in "assets/js/app.js".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// To use Phoenix channels, the first step is to import Socket,
 | 
				
			||||||
 | 
					// and connect at the socket path in "lib/web/endpoint.ex".
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Pass the token on params as below. Or remove it
 | 
				
			||||||
 | 
					// from the params if you are not using authentication.
 | 
				
			||||||
 | 
					import {Socket} from "phoenix"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let socket = new Socket("/socket", {params: {token: window.userToken}})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// When you connect, you'll often need to authenticate the client.
 | 
				
			||||||
 | 
					// For example, imagine you have an authentication plug, `MyAuth`,
 | 
				
			||||||
 | 
					// which authenticates the session and assigns a `:current_user`.
 | 
				
			||||||
 | 
					// If the current user exists you can assign the user's token in
 | 
				
			||||||
 | 
					// the connection for use in the layout.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// In your "lib/web/router.ex":
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     pipeline :browser do
 | 
				
			||||||
 | 
					//       ...
 | 
				
			||||||
 | 
					//       plug MyAuth
 | 
				
			||||||
 | 
					//       plug :put_user_token
 | 
				
			||||||
 | 
					//     end
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     defp put_user_token(conn, _) do
 | 
				
			||||||
 | 
					//       if current_user = conn.assigns[:current_user] do
 | 
				
			||||||
 | 
					//         token = Phoenix.Token.sign(conn, "user socket", current_user.id)
 | 
				
			||||||
 | 
					//         assign(conn, :user_token, token)
 | 
				
			||||||
 | 
					//       else
 | 
				
			||||||
 | 
					//         conn
 | 
				
			||||||
 | 
					//       end
 | 
				
			||||||
 | 
					//     end
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Now you need to pass this token to JavaScript. You can do so
 | 
				
			||||||
 | 
					// inside a script tag in "lib/web/templates/layout/app.html.eex":
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     <script>window.userToken = "<%= assigns[:user_token] %>";</script>
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You will need to verify the user token in the "connect/3" function
 | 
				
			||||||
 | 
					// in "lib/web/channels/user_socket.ex":
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     def connect(%{"token" => token}, socket, _connect_info) do
 | 
				
			||||||
 | 
					//       # max_age: 1209600 is equivalent to two weeks in seconds
 | 
				
			||||||
 | 
					//       case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do
 | 
				
			||||||
 | 
					//         {:ok, user_id} ->
 | 
				
			||||||
 | 
					//           {:ok, assign(socket, :user, user_id)}
 | 
				
			||||||
 | 
					//         {:error, reason} ->
 | 
				
			||||||
 | 
					//           :error
 | 
				
			||||||
 | 
					//       end
 | 
				
			||||||
 | 
					//     end
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Finally, connect to the socket:
 | 
				
			||||||
 | 
					socket.connect()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Now that you are connected, you can join channels with a topic:
 | 
				
			||||||
 | 
					let channel = socket.channel("topic:subtopic", {})
 | 
				
			||||||
 | 
					channel.join()
 | 
				
			||||||
 | 
					  .receive("ok", resp => { console.log("Joined successfully", resp) })
 | 
				
			||||||
 | 
					  .receive("error", resp => { console.log("Unable to join", resp) })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default socket
 | 
				
			||||||
							
								
								
									
										18129
									
								
								assets/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										18129
									
								
								assets/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										28
									
								
								assets/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								assets/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "repository": {},
 | 
				
			||||||
 | 
					  "description": " ",
 | 
				
			||||||
 | 
					  "license": "MIT",
 | 
				
			||||||
 | 
					  "scripts": {
 | 
				
			||||||
 | 
					    "deploy": "webpack --mode production",
 | 
				
			||||||
 | 
					    "watch": "webpack --mode development --watch"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "phoenix": "file:../deps/phoenix",
 | 
				
			||||||
 | 
					    "phoenix_html": "file:../deps/phoenix_html"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "devDependencies": {
 | 
				
			||||||
 | 
					    "@babel/core": "^7.0.0",
 | 
				
			||||||
 | 
					    "@babel/preset-env": "^7.0.0",
 | 
				
			||||||
 | 
					    "babel-loader": "^8.0.0",
 | 
				
			||||||
 | 
					    "copy-webpack-plugin": "^5.1.1",
 | 
				
			||||||
 | 
					    "css-loader": "^3.4.2",
 | 
				
			||||||
 | 
					    "hard-source-webpack-plugin": "^0.13.1",
 | 
				
			||||||
 | 
					    "mini-css-extract-plugin": "^0.9.0",
 | 
				
			||||||
 | 
					    "node-sass": "^4.13.1",
 | 
				
			||||||
 | 
					    "optimize-css-assets-webpack-plugin": "^5.0.1",
 | 
				
			||||||
 | 
					    "sass-loader": "^8.0.2",
 | 
				
			||||||
 | 
					    "terser-webpack-plugin": "^2.3.2",
 | 
				
			||||||
 | 
					    "webpack": "4.41.5",
 | 
				
			||||||
 | 
					    "webpack-cli": "^3.3.2"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								assets/static/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/static/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/static/images/confient.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/static/images/confient.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 9.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/static/images/phoenix.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/static/images/phoenix.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 14 KiB  | 
							
								
								
									
										5
									
								
								assets/static/robots.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								assets/static/robots.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					# See http://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: /
 | 
				
			||||||
							
								
								
									
										53
									
								
								assets/webpack.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								assets/webpack.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					const path = require('path');
 | 
				
			||||||
 | 
					const glob = require('glob');
 | 
				
			||||||
 | 
					const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
 | 
				
			||||||
 | 
					const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 | 
				
			||||||
 | 
					const TerserPlugin = require('terser-webpack-plugin');
 | 
				
			||||||
 | 
					const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
 | 
				
			||||||
 | 
					const CopyWebpackPlugin = require('copy-webpack-plugin');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (env, options) => {
 | 
				
			||||||
 | 
					  const devMode = options.mode !== 'production';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    optimization: {
 | 
				
			||||||
 | 
					      minimizer: [
 | 
				
			||||||
 | 
					        new TerserPlugin({ cache: true, parallel: true, sourceMap: devMode }),
 | 
				
			||||||
 | 
					        new OptimizeCSSAssetsPlugin({})
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    entry: {
 | 
				
			||||||
 | 
					      'app': glob.sync('./vendor/**/*.js').concat(['./js/app.js'])
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    output: {
 | 
				
			||||||
 | 
					      filename: '[name].js',
 | 
				
			||||||
 | 
					      path: path.resolve(__dirname, '../priv/static/js'),
 | 
				
			||||||
 | 
					      publicPath: '/js/'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    devtool: devMode ? 'eval-cheap-module-source-map' : undefined,
 | 
				
			||||||
 | 
					    module: {
 | 
				
			||||||
 | 
					      rules: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          test: /\.js$/,
 | 
				
			||||||
 | 
					          exclude: /node_modules/,
 | 
				
			||||||
 | 
					          use: {
 | 
				
			||||||
 | 
					            loader: 'babel-loader'
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          test: /\.[s]?css$/,
 | 
				
			||||||
 | 
					          use: [
 | 
				
			||||||
 | 
					            MiniCssExtractPlugin.loader,
 | 
				
			||||||
 | 
					            'css-loader',
 | 
				
			||||||
 | 
					            'sass-loader',
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    plugins: [
 | 
				
			||||||
 | 
					      new MiniCssExtractPlugin({ filename: '../css/app.css' }),
 | 
				
			||||||
 | 
					      new CopyWebpackPlugin([{ from: 'static/', to: '../' }])
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    .concat(devMode ? [new HardSourceWebpackPlugin()] : [])
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										3
									
								
								compose/dev/.env
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								compose/dev/.env
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					POSTGRES_USER=postgres
 | 
				
			||||||
 | 
					POSTGRES_PASSWORD=postgres
 | 
				
			||||||
 | 
					POSTGRES_DB=confient_dev
 | 
				
			||||||
							
								
								
									
										11
									
								
								compose/dev/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								compose/dev/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					version: "3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  postgres:
 | 
				
			||||||
 | 
					    image: postgres:latest
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - 127.0.0.1:5432:5432
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      - POSTGRES_USER=$POSTGRES_USER
 | 
				
			||||||
 | 
					      - POSTGRES_DB=$POSTGRES_DB
 | 
				
			||||||
 | 
					      - POSTGRES_PASSWORD=$POSTGRES_PASSWORD
 | 
				
			||||||
							
								
								
									
										7
									
								
								compose/release/.env
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								compose/release/.env
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					POSTGRES_USER=confient
 | 
				
			||||||
 | 
					POSTGRES_DB=confient
 | 
				
			||||||
 | 
					POSTGRES_PASSWORD=password
 | 
				
			||||||
 | 
					CONFIENT_DATA=/tmp/confient/uploads
 | 
				
			||||||
 | 
					POSTGRES_DATA=/tmp/confient/pg
 | 
				
			||||||
 | 
					# gen one with mix phx.gen.secret
 | 
				
			||||||
 | 
					SECRET_KEY_BASE=aQFwnczmCcy1JOQ/opboa+YSETybKWCELSCjI7i7atIBnLesYJ2dVYVgouIi7SYs
 | 
				
			||||||
							
								
								
									
										26
									
								
								compose/release/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								compose/release/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					version: '3'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  postgres:
 | 
				
			||||||
 | 
					    image: postgres:latest
 | 
				
			||||||
 | 
					    volumes:
 | 
				
			||||||
 | 
					      - $POSTGRES_DATA:/var/lib/postgres
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      - POSTGRES_USER=$POSTGRES_USER
 | 
				
			||||||
 | 
					      - POSTGRES_DB=$POSTGRES_DB
 | 
				
			||||||
 | 
					      - POSTGRES_PASSWORD=$POSTGRES_PASSWORD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  confient:
 | 
				
			||||||
 | 
					    image: papey/confient:latest
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - 127.0.0.1:4000:4000
 | 
				
			||||||
 | 
					    volumes:
 | 
				
			||||||
 | 
					      - $CONFIENT_DATA:/srv/confient/uploads
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      - POSTGRES_HOST=postgres
 | 
				
			||||||
 | 
					      - POSTGRES_USER=$POSTGRES_USER
 | 
				
			||||||
 | 
					      - DATABASE_URL=ecto://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres/$POSTGRES_DB
 | 
				
			||||||
 | 
					      - SECRET_KEY_BASE=$SECRET_KEY_BASE
 | 
				
			||||||
 | 
					      - CONFIENT_UPLOAD_DIR=/srv/confient/uploads
 | 
				
			||||||
 | 
					    depends_on:
 | 
				
			||||||
 | 
					      - postgres
 | 
				
			||||||
							
								
								
									
										36
									
								
								config/config.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								config/config.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					# This file is responsible for configuring your application
 | 
				
			||||||
 | 
					# and its dependencies with the aid of the Mix.Config module.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This configuration file is loaded before any dependency and
 | 
				
			||||||
 | 
					# is restricted to this project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# General application configuration
 | 
				
			||||||
 | 
					use Mix.Config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config :confient,
 | 
				
			||||||
 | 
					  ecto_repos: [Confient.Repo],
 | 
				
			||||||
 | 
					  upload_dir: System.get_env("CONFIENT_UPLOAD_DIR", "/srv/confient/uploads"),
 | 
				
			||||||
 | 
					  timezone: System.get_env("CONFIENT_TIMEZONE", "Europe/Paris"),
 | 
				
			||||||
 | 
					  domain: System.get_env("CONFIENT_DOMAIN", "http://localhost:4000")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Configures the endpoint
 | 
				
			||||||
 | 
					config :confient, ConfientWeb.Endpoint,
 | 
				
			||||||
 | 
					  url: [host: "localhost"],
 | 
				
			||||||
 | 
					  secret_key_base: "rTob+2honR0wBwOdVleXuVzLPI/5r71ALpLYwXff0fUeV6zinIKcaVvjLh/ZxG5i",
 | 
				
			||||||
 | 
					  render_errors: [view: ConfientWeb.ErrorView, accepts: ~w(html json), layout: false],
 | 
				
			||||||
 | 
					  pubsub_server: Confient.PubSub,
 | 
				
			||||||
 | 
					  live_view: [signing_salt: "+X6U/dHw"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Configures Elixir's Logger
 | 
				
			||||||
 | 
					config :logger, :console,
 | 
				
			||||||
 | 
					  format: "$time $metadata[$level] $message\n",
 | 
				
			||||||
 | 
					  metadata: [:request_id]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Use Jason for JSON parsing in Phoenix
 | 
				
			||||||
 | 
					config :phoenix, :json_library, Jason
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Import environment specific config. This must remain at the bottom
 | 
				
			||||||
 | 
					# of this file so it overrides the configuration defined above.
 | 
				
			||||||
 | 
					import_config "#{Mix.env()}.exs"
 | 
				
			||||||
							
								
								
									
										76
									
								
								config/dev.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								config/dev.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					use Mix.Config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Configure your database
 | 
				
			||||||
 | 
					config :confient, Confient.Repo,
 | 
				
			||||||
 | 
					  username: "postgres",
 | 
				
			||||||
 | 
					  password: "postgres",
 | 
				
			||||||
 | 
					  database: "confient_dev",
 | 
				
			||||||
 | 
					  hostname: "localhost",
 | 
				
			||||||
 | 
					  show_sensitive_data_on_connection_error: true,
 | 
				
			||||||
 | 
					  pool_size: 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# For development, we disable any cache and enable
 | 
				
			||||||
 | 
					# debugging and code reloading.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# The watchers configuration can be used to run external
 | 
				
			||||||
 | 
					# watchers to your application. For example, we use it
 | 
				
			||||||
 | 
					# with webpack to recompile .js and .css sources.
 | 
				
			||||||
 | 
					config :confient, ConfientWeb.Endpoint,
 | 
				
			||||||
 | 
					  http: [port: 4000],
 | 
				
			||||||
 | 
					  debug_errors: true,
 | 
				
			||||||
 | 
					  code_reloader: true,
 | 
				
			||||||
 | 
					  check_origin: false,
 | 
				
			||||||
 | 
					  watchers: [
 | 
				
			||||||
 | 
					    node: [
 | 
				
			||||||
 | 
					      "node_modules/webpack/bin/webpack.js",
 | 
				
			||||||
 | 
					      "--mode",
 | 
				
			||||||
 | 
					      "development",
 | 
				
			||||||
 | 
					      "--watch-stdin",
 | 
				
			||||||
 | 
					      cd: Path.expand("../assets", __DIR__)
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ## SSL Support
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# In order to use HTTPS in development, a self-signed
 | 
				
			||||||
 | 
					# certificate can be generated by running the following
 | 
				
			||||||
 | 
					# Mix task:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#     mix phx.gen.cert
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Note that this task requires Erlang/OTP 20 or later.
 | 
				
			||||||
 | 
					# Run `mix help phx.gen.cert` for more information.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# The `http:` config above can be replaced with:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#     https: [
 | 
				
			||||||
 | 
					#       port: 4001,
 | 
				
			||||||
 | 
					#       cipher_suite: :strong,
 | 
				
			||||||
 | 
					#       keyfile: "priv/cert/selfsigned_key.pem",
 | 
				
			||||||
 | 
					#       certfile: "priv/cert/selfsigned.pem"
 | 
				
			||||||
 | 
					#     ],
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# If desired, both `http:` and `https:` keys can be
 | 
				
			||||||
 | 
					# configured to run both http and https servers on
 | 
				
			||||||
 | 
					# different ports.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Watch static and templates for browser reloading.
 | 
				
			||||||
 | 
					config :confient, ConfientWeb.Endpoint,
 | 
				
			||||||
 | 
					  live_reload: [
 | 
				
			||||||
 | 
					    patterns: [
 | 
				
			||||||
 | 
					      ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
 | 
				
			||||||
 | 
					      ~r"priv/gettext/.*(po)$",
 | 
				
			||||||
 | 
					      ~r"lib/confient_web/(live|views)/.*(ex)$",
 | 
				
			||||||
 | 
					      ~r"lib/confient_web/templates/.*(eex)$"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Do not include metadata nor timestamps in development logs
 | 
				
			||||||
 | 
					config :logger, :console, format: "[$level] $message\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Set a higher stacktrace during development. Avoid configuring such
 | 
				
			||||||
 | 
					# in production as building large stacktraces may be expensive.
 | 
				
			||||||
 | 
					config :phoenix, :stacktrace_depth, 20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Initialize plugs at runtime for faster development compilation
 | 
				
			||||||
 | 
					config :phoenix, :plug_init_mode, :runtime
 | 
				
			||||||
							
								
								
									
										55
									
								
								config/prod.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								config/prod.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					use Mix.Config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# For production, don't forget to configure the url host
 | 
				
			||||||
 | 
					# to something meaningful, Phoenix uses this information
 | 
				
			||||||
 | 
					# when generating URLs.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Note we also include the path to a cache manifest
 | 
				
			||||||
 | 
					# containing the digested version of static files. This
 | 
				
			||||||
 | 
					# manifest is generated by the `mix phx.digest` task,
 | 
				
			||||||
 | 
					# which you should run after static files are built and
 | 
				
			||||||
 | 
					# before starting your production server.
 | 
				
			||||||
 | 
					config :confient, ConfientWeb.Endpoint,
 | 
				
			||||||
 | 
					  url: [host: "example.com", port: 80],
 | 
				
			||||||
 | 
					  cache_static_manifest: "priv/static/cache_manifest.json"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Do not print debug messages in production
 | 
				
			||||||
 | 
					config :logger, level: :info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ## SSL Support
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# To get SSL working, you will need to add the `https` key
 | 
				
			||||||
 | 
					# to the previous section and set your `:url` port to 443:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#     config :confient, ConfientWeb.Endpoint,
 | 
				
			||||||
 | 
					#       ...
 | 
				
			||||||
 | 
					#       url: [host: "example.com", port: 443],
 | 
				
			||||||
 | 
					#       https: [
 | 
				
			||||||
 | 
					#         port: 443,
 | 
				
			||||||
 | 
					#         cipher_suite: :strong,
 | 
				
			||||||
 | 
					#         keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
 | 
				
			||||||
 | 
					#         certfile: System.get_env("SOME_APP_SSL_CERT_PATH"),
 | 
				
			||||||
 | 
					#         transport_options: [socket_opts: [:inet6]]
 | 
				
			||||||
 | 
					#       ]
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# The `cipher_suite` is set to `:strong` to support only the
 | 
				
			||||||
 | 
					# latest and more secure SSL ciphers. This means old browsers
 | 
				
			||||||
 | 
					# and clients may not be supported. You can set it to
 | 
				
			||||||
 | 
					# `:compatible` for wider support.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# `:keyfile` and `:certfile` expect an absolute path to the key
 | 
				
			||||||
 | 
					# and cert in disk or a relative path inside priv, for example
 | 
				
			||||||
 | 
					# "priv/ssl/server.key". For all supported SSL configuration
 | 
				
			||||||
 | 
					# options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# We also recommend setting `force_ssl` in your endpoint, ensuring
 | 
				
			||||||
 | 
					# no data is ever sent via http, always redirecting to https:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#     config :confient, ConfientWeb.Endpoint,
 | 
				
			||||||
 | 
					#       force_ssl: [hsts: true]
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Check `Plug.SSL` for all available options in `force_ssl`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Finally import the config/prod.secret.exs which loads secrets
 | 
				
			||||||
 | 
					# and configuration from environment variables.
 | 
				
			||||||
 | 
					# import_config "prod.secret.exs"
 | 
				
			||||||
							
								
								
									
										46
									
								
								config/releases.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								config/releases.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					# In this file, we load production configuration and secrets
 | 
				
			||||||
 | 
					# from environment variables. You can also hardcode secrets,
 | 
				
			||||||
 | 
					# although such is generally not recommended and you have to
 | 
				
			||||||
 | 
					# remember to add this file to your .gitignore.
 | 
				
			||||||
 | 
					import Config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					database_url =
 | 
				
			||||||
 | 
					  System.get_env("DATABASE_URL") ||
 | 
				
			||||||
 | 
					    raise """
 | 
				
			||||||
 | 
					    environment variableDATABASE_URL=$DATABASE_URL DATABASE_URL is missing.
 | 
				
			||||||
 | 
					    For example: ecto://USER:PASS@HOST/DATABASE
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config :confient, Confient.Repo,
 | 
				
			||||||
 | 
					  # ssl: true,
 | 
				
			||||||
 | 
					  url: database_url,
 | 
				
			||||||
 | 
					  pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					secret_key_base =
 | 
				
			||||||
 | 
					  System.get_env("SECRET_KEY_BASE") ||
 | 
				
			||||||
 | 
					    raise """
 | 
				
			||||||
 | 
					    environment variable SECRET_KEY_BASE is missing.
 | 
				
			||||||
 | 
					    You can generate one by calling: mix phx.gen.secret
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config :confient, ConfientWeb.Endpoint,
 | 
				
			||||||
 | 
					  http: [
 | 
				
			||||||
 | 
					    port: String.to_integer(System.get_env("PORT") || "4000"),
 | 
				
			||||||
 | 
					    transport_options: [socket_opts: [:inet6]]
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  secret_key_base: secret_key_base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config :confient,
 | 
				
			||||||
 | 
					  base_upload_dir: System.get_env("CONFIENT_BASE_UPLOAD_DIR", "/srv/confient/uploads"),
 | 
				
			||||||
 | 
					  timezone: System.get_env("CONFIENT_TIMEZONE", "Europe/Paris"),
 | 
				
			||||||
 | 
					  domain: System.get_env("CONFIENT_DOMAIN", "http://localhost:4000")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ## Using releases (Elixir v1.9+)
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# If you are doing OTP releases, you need to instruct Phoenix
 | 
				
			||||||
 | 
					# to start each relevant endpoint:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					config :confient, ConfientWeb.Endpoint, server: true
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Then you can assemble a release by calling `mix release`.
 | 
				
			||||||
 | 
					# See `mix help release` for more information.
 | 
				
			||||||
							
								
								
									
										22
									
								
								config/test.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								config/test.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					use Mix.Config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Configure your database
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# The MIX_TEST_PARTITION environment variable can be used
 | 
				
			||||||
 | 
					# to provide built-in test partitioning in CI environment.
 | 
				
			||||||
 | 
					# Run `mix help test` for more information.
 | 
				
			||||||
 | 
					config :confient, Confient.Repo,
 | 
				
			||||||
 | 
					  username: "postgres",
 | 
				
			||||||
 | 
					  password: "postgres",
 | 
				
			||||||
 | 
					  database: "confient_test#{System.get_env("MIX_TEST_PARTITION")}",
 | 
				
			||||||
 | 
					  hostname: "localhost",
 | 
				
			||||||
 | 
					  pool: Ecto.Adapters.SQL.Sandbox
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# We don't run a server during test. If one is required,
 | 
				
			||||||
 | 
					# you can enable the server option below.
 | 
				
			||||||
 | 
					config :confient, ConfientWeb.Endpoint,
 | 
				
			||||||
 | 
					  http: [port: 4002],
 | 
				
			||||||
 | 
					  server: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Print only warnings and errors during test
 | 
				
			||||||
 | 
					config :logger, level: :warn
 | 
				
			||||||
							
								
								
									
										14
									
								
								entrypoint.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										14
									
								
								entrypoint.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					#!/bin/sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# wait until PG is ready
 | 
				
			||||||
 | 
					while ! pg_isready -q -h $POSTGRES_HOST -p 5432 -U $POSTGRES_USER
 | 
				
			||||||
 | 
					do
 | 
				
			||||||
 | 
					    echo "$(date) - waiting for database to start"
 | 
				
			||||||
 | 
					    sleep 1
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# run migrations
 | 
				
			||||||
 | 
					./bin/confient eval Confient.Release.migrate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# start the app
 | 
				
			||||||
 | 
					./bin/confient start
 | 
				
			||||||
							
								
								
									
										9
									
								
								lib/confient.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/confient.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					defmodule Confient do
 | 
				
			||||||
 | 
					  @moduledoc """
 | 
				
			||||||
 | 
					  Confient keeps the contexts that define your domain
 | 
				
			||||||
 | 
					  and business logic.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Contexts are also responsible for managing your data, regardless
 | 
				
			||||||
 | 
					  if it comes from the database, an external API or others.
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										113
									
								
								lib/confient/account.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								lib/confient/account.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Account do
 | 
				
			||||||
 | 
					  @moduledoc """
 | 
				
			||||||
 | 
					  The Account context.
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  import Ecto.Query, warn: false
 | 
				
			||||||
 | 
					  alias Confient.Repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  alias Confient.Account.User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def get_user_by_username(username) do
 | 
				
			||||||
 | 
					    query =
 | 
				
			||||||
 | 
					      from u in User,
 | 
				
			||||||
 | 
					        where: u.username == ^username,
 | 
				
			||||||
 | 
					        select: u
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Repo.one(query)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @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
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										31
									
								
								lib/confient/account/auth.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								lib/confient/account/auth.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Account.Auth do
 | 
				
			||||||
 | 
					  def login(%{"username" => username, "password" => password}) do
 | 
				
			||||||
 | 
					    case Confient.Account.get_user_by_username(username) do
 | 
				
			||||||
 | 
					      nil ->
 | 
				
			||||||
 | 
					        :error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      u ->
 | 
				
			||||||
 | 
					        if auth(u, password) do
 | 
				
			||||||
 | 
					          {:ok, u}
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          :error
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  defp auth(user, _password) when user == nil, do: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  defp auth(user, password) do
 | 
				
			||||||
 | 
					    case Confient.Account.Encryption.validate_password(user, password) do
 | 
				
			||||||
 | 
					      {:ok, _u} -> true
 | 
				
			||||||
 | 
					      {:error, _} -> false
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def signed_in?(conn), do: conn.assigns[:current_user]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def get_username(conn) do
 | 
				
			||||||
 | 
					    user = conn.assigns[:current_user]
 | 
				
			||||||
 | 
					    user.username
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										8
									
								
								lib/confient/account/encryption.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								lib/confient/account/encryption.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Account.Encryption do
 | 
				
			||||||
 | 
					  alias Comeonin.Bcrypt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def hash_password(password), do: Bcrypt.hashpwsalt(password)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def validate_password(%Confient.Account.User{} = user, password),
 | 
				
			||||||
 | 
					    do: Bcrypt.check_pass(user, password)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										39
									
								
								lib/confient/account/user.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								lib/confient/account/user.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Account.User do
 | 
				
			||||||
 | 
					  use Ecto.Schema
 | 
				
			||||||
 | 
					  import Ecto.Changeset
 | 
				
			||||||
 | 
					  alias Confient.Account.Encryption
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  schema "users" do
 | 
				
			||||||
 | 
					    field :encrypted_password, :string
 | 
				
			||||||
 | 
					    field :username, :string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    field :password, :string, virtual: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    timestamps()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc false
 | 
				
			||||||
 | 
					  def changeset(user, attrs) do
 | 
				
			||||||
 | 
					    user
 | 
				
			||||||
 | 
					    |> cast(attrs, [:username, :encrypted_password, :password])
 | 
				
			||||||
 | 
					    |> validate_length(:password, min: 6)
 | 
				
			||||||
 | 
					    |> validate_format(:username, ~r/^[a-z0-9][a-z0-9]+[a-z0-9]$/i)
 | 
				
			||||||
 | 
					    |> validate_length(:username, min: 3)
 | 
				
			||||||
 | 
					    |> unique_constraint(:username)
 | 
				
			||||||
 | 
					    |> update_change(:username, &String.downcase/1)
 | 
				
			||||||
 | 
					    |> encrypt()
 | 
				
			||||||
 | 
					    |> validate_required([:username, :encrypted_password])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  defp encrypt(changeset) do
 | 
				
			||||||
 | 
					    IO.inspect(get_change(changeset, :password))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case get_change(changeset, :password) do
 | 
				
			||||||
 | 
					      nil ->
 | 
				
			||||||
 | 
					        changeset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pass ->
 | 
				
			||||||
 | 
					        put_change(changeset, :encrypted_password, Encryption.hash_password(pass))
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										34
									
								
								lib/confient/application.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								lib/confient/application.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Application do
 | 
				
			||||||
 | 
					  # See https://hexdocs.pm/elixir/Application.html
 | 
				
			||||||
 | 
					  # for more information on OTP Applications
 | 
				
			||||||
 | 
					  @moduledoc false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  use Application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def start(_type, _args) do
 | 
				
			||||||
 | 
					    children = [
 | 
				
			||||||
 | 
					      # Start the Ecto repository
 | 
				
			||||||
 | 
					      Confient.Repo,
 | 
				
			||||||
 | 
					      # Start the Telemetry supervisor
 | 
				
			||||||
 | 
					      ConfientWeb.Telemetry,
 | 
				
			||||||
 | 
					      # Start the PubSub system
 | 
				
			||||||
 | 
					      {Phoenix.PubSub, name: Confient.PubSub},
 | 
				
			||||||
 | 
					      # Start the Endpoint (http/https)
 | 
				
			||||||
 | 
					      ConfientWeb.Endpoint
 | 
				
			||||||
 | 
					      # Start a worker by calling: Confient.Worker.start_link(arg)
 | 
				
			||||||
 | 
					      # {Confient.Worker, arg}
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # See https://hexdocs.pm/elixir/Supervisor.html
 | 
				
			||||||
 | 
					    # for other strategies and supported options
 | 
				
			||||||
 | 
					    opts = [strategy: :one_for_one, name: Confient.Supervisor]
 | 
				
			||||||
 | 
					    Supervisor.start_link(children, opts)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Tell Phoenix to update the endpoint configuration
 | 
				
			||||||
 | 
					  # whenever the application is updated.
 | 
				
			||||||
 | 
					  def config_change(changed, _new, removed) do
 | 
				
			||||||
 | 
					    ConfientWeb.Endpoint.config_change(changed, removed)
 | 
				
			||||||
 | 
					    :ok
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										24
									
								
								lib/confient/release.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								lib/confient/release.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Release do
 | 
				
			||||||
 | 
					  @app :confient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def migrate do
 | 
				
			||||||
 | 
					    load_app()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for repo <- repos() do
 | 
				
			||||||
 | 
					      {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def rollback(repo, version) do
 | 
				
			||||||
 | 
					    load_app()
 | 
				
			||||||
 | 
					    {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  defp repos do
 | 
				
			||||||
 | 
					    Application.fetch_env!(@app, :ecto_repos)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  defp load_app do
 | 
				
			||||||
 | 
					    Application.load(@app)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										5
									
								
								lib/confient/repo.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								lib/confient/repo.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Repo do
 | 
				
			||||||
 | 
					  use Ecto.Repo,
 | 
				
			||||||
 | 
					    otp_app: :confient,
 | 
				
			||||||
 | 
					    adapter: Ecto.Adapters.Postgres
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										236
									
								
								lib/confient/school.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								lib/confient/school.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,236 @@
 | 
				
			|||||||
 | 
					defmodule Confient.School do
 | 
				
			||||||
 | 
					  @moduledoc """
 | 
				
			||||||
 | 
					  The School context.
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  import Ecto.Query, warn: false
 | 
				
			||||||
 | 
					  alias Confient.Repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  alias Confient.School.Class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Returns the list of classes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> list_classes()
 | 
				
			||||||
 | 
					      [%Class{}, ...]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def list_classes do
 | 
				
			||||||
 | 
					    Repo.all(Class) |> Repo.preload(:students)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Gets a single class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Raises `Ecto.NoResultsError` if the Class does not exist.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> get_class!(123)
 | 
				
			||||||
 | 
					      %Class{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> get_class!(456)
 | 
				
			||||||
 | 
					      ** (Ecto.NoResultsError)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def get_class!(id), do: Repo.get!(Class, id) |> Repo.preload(:students)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def get_class(id) do
 | 
				
			||||||
 | 
					    case Repo.get(Class, id) |> Repo.preload(:students) |> Repo.preload(:assignments) do
 | 
				
			||||||
 | 
					      nil -> {:error, :no_class}
 | 
				
			||||||
 | 
					      class -> {:ok, class}
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Creates a class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> create_class(%{field: value})
 | 
				
			||||||
 | 
					      {:ok, %Class{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> create_class(%{field: bad_value})
 | 
				
			||||||
 | 
					      {:error, %Ecto.Changeset{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def create_class(attrs \\ %{}) do
 | 
				
			||||||
 | 
					    %Class{}
 | 
				
			||||||
 | 
					    |> Class.changeset(attrs)
 | 
				
			||||||
 | 
					    |> Repo.insert()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Updates a class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> update_class(class, %{field: new_value})
 | 
				
			||||||
 | 
					      {:ok, %Class{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> update_class(class, %{field: bad_value})
 | 
				
			||||||
 | 
					      {:error, %Ecto.Changeset{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def update_class(%Class{} = class, attrs) do
 | 
				
			||||||
 | 
					    class
 | 
				
			||||||
 | 
					    |> Class.changeset(attrs)
 | 
				
			||||||
 | 
					    |> Repo.update()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Deletes a class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> delete_class(class)
 | 
				
			||||||
 | 
					      {:ok, %Class{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> delete_class(class)
 | 
				
			||||||
 | 
					      {:error, %Ecto.Changeset{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def delete_class(%Class{} = class) do
 | 
				
			||||||
 | 
					    Repo.delete(class)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Returns an `%Ecto.Changeset{}` for tracking class changes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> change_class(class)
 | 
				
			||||||
 | 
					      %Ecto.Changeset{data: %Class{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def change_class(%Class{} = class, attrs \\ %{}) do
 | 
				
			||||||
 | 
					    Class.changeset(class, attrs)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  alias Confient.School.Student
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Returns the list of students.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> list_students()
 | 
				
			||||||
 | 
					      [%Student{}, ...]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def list_students do
 | 
				
			||||||
 | 
					    Repo.all(Student) |> Repo.preload(:class)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Gets a single student.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Raises `Ecto.NoResultsError` if the Student does not exist.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> get_student!(123)
 | 
				
			||||||
 | 
					      %Student{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> get_student!(456)
 | 
				
			||||||
 | 
					      ** (Ecto.NoResultsError)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def get_student!(id), do: Repo.get!(Student, id) |> Repo.preload(:class)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def get_full_student!(id) do
 | 
				
			||||||
 | 
					    Repo.one(
 | 
				
			||||||
 | 
					      from s in Student,
 | 
				
			||||||
 | 
					        where: s.id == ^id,
 | 
				
			||||||
 | 
					        preload: [{:students_works, :assignment}, :class],
 | 
				
			||||||
 | 
					        select: s
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def get_student(id) do
 | 
				
			||||||
 | 
					    case Repo.get(Student, id) |> Repo.preload(:class) do
 | 
				
			||||||
 | 
					      nil -> {:error, :no_student}
 | 
				
			||||||
 | 
					      student -> {:ok, student}
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def student_exists?(lastname, firstname, %Class{id: class_id}) do
 | 
				
			||||||
 | 
					    Repo.exists?(
 | 
				
			||||||
 | 
					      from s in Student,
 | 
				
			||||||
 | 
					        where: s.lastname == ^lastname and s.firstname == ^firstname and s.class_id == ^class_id
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Creates a student.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> create_student(%{field: value})
 | 
				
			||||||
 | 
					      {:ok, %Student{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> create_student(%{field: bad_value})
 | 
				
			||||||
 | 
					      {:error, %Ecto.Changeset{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def create_student(attrs \\ %{}) do
 | 
				
			||||||
 | 
					    %Student{}
 | 
				
			||||||
 | 
					    |> Student.changeset(attrs)
 | 
				
			||||||
 | 
					    |> Repo.insert()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def create_student(%Class{id: class_id}, attrs) do
 | 
				
			||||||
 | 
					    %Student{class_id: class_id}
 | 
				
			||||||
 | 
					    |> Student.changeset(attrs)
 | 
				
			||||||
 | 
					    |> Repo.insert()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Updates a student.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> update_student(student, %{field: new_value})
 | 
				
			||||||
 | 
					      {:ok, %Student{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> update_student(student, %{field: bad_value})
 | 
				
			||||||
 | 
					      {:error, %Ecto.Changeset{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def update_student(%Student{} = student, attrs) do
 | 
				
			||||||
 | 
					    student
 | 
				
			||||||
 | 
					    |> Student.changeset(attrs)
 | 
				
			||||||
 | 
					    |> Repo.update()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Deletes a student.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> delete_student(student)
 | 
				
			||||||
 | 
					      {:ok, %Student{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> delete_student(student)
 | 
				
			||||||
 | 
					      {:error, %Ecto.Changeset{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def delete_student(%Student{} = student) do
 | 
				
			||||||
 | 
					    Repo.delete(student)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Returns an `%Ecto.Changeset{}` for tracking student changes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> change_student(student)
 | 
				
			||||||
 | 
					      %Ecto.Changeset{data: %Student{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def change_student(%Student{} = student, attrs \\ %{}) do
 | 
				
			||||||
 | 
					    Student.changeset(student, attrs)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										22
									
								
								lib/confient/school/class.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								lib/confient/school/class.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					defmodule Confient.School.Class do
 | 
				
			||||||
 | 
					  use Ecto.Schema
 | 
				
			||||||
 | 
					  import Ecto.Changeset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  schema "classes" do
 | 
				
			||||||
 | 
					    field :name, :string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    has_many :students, Confient.School.Student
 | 
				
			||||||
 | 
					    has_many :assignments, Confient.Works.Assignment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    timestamps()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc false
 | 
				
			||||||
 | 
					  def changeset(class, attrs) do
 | 
				
			||||||
 | 
					    class
 | 
				
			||||||
 | 
					    |> cast(attrs, [:name])
 | 
				
			||||||
 | 
					    |> unique_constraint(:name, message: "Existe déjà")
 | 
				
			||||||
 | 
					    |> update_change(:name, &String.upcase/1)
 | 
				
			||||||
 | 
					    |> validate_required([:name], message: "Le champ ne peut être vide")
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										25
									
								
								lib/confient/school/student.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								lib/confient/school/student.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					defmodule Confient.School.Student do
 | 
				
			||||||
 | 
					  use Ecto.Schema
 | 
				
			||||||
 | 
					  import Ecto.Changeset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  schema "students" do
 | 
				
			||||||
 | 
					    field :firstname, :string
 | 
				
			||||||
 | 
					    field :lastname, :string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    belongs_to :class, Confient.School.Class
 | 
				
			||||||
 | 
					    has_many :students_works, Confient.Student.Work
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    timestamps()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc false
 | 
				
			||||||
 | 
					  def changeset(student, attrs) do
 | 
				
			||||||
 | 
					    student
 | 
				
			||||||
 | 
					    |> cast(attrs, [:lastname, :firstname, :class_id])
 | 
				
			||||||
 | 
					    |> unique_constraint(:students_lastname_firstname_class_id_index, message: "Existe déjà")
 | 
				
			||||||
 | 
					    |> assoc_constraint(:class)
 | 
				
			||||||
 | 
					    |> update_change(:lastname, fn e -> String.upcase(e) end)
 | 
				
			||||||
 | 
					    |> update_change(:firstname, fn e -> String.downcase(e) |> String.capitalize() end)
 | 
				
			||||||
 | 
					    |> validate_required([:lastname, :firstname, :class_id], message: "Le champ ne peut être vide")
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										29
									
								
								lib/confient/student/work.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								lib/confient/student/work.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Student.Work do
 | 
				
			||||||
 | 
					  use Ecto.Schema
 | 
				
			||||||
 | 
					  import Ecto.Changeset
 | 
				
			||||||
 | 
					  alias Confient.Repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  schema "students_works" do
 | 
				
			||||||
 | 
					    field :path, :string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    belongs_to :student, Confient.School.Student
 | 
				
			||||||
 | 
					    belongs_to :assignment, Confient.Works.Assignment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    timestamps()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc false
 | 
				
			||||||
 | 
					  def changeset(work, attrs) do
 | 
				
			||||||
 | 
					    work
 | 
				
			||||||
 | 
					    |> cast(attrs, [:path, :student_id, :assignment_id])
 | 
				
			||||||
 | 
					    |> assoc_constraint(:student)
 | 
				
			||||||
 | 
					    |> assoc_constraint(:assignment)
 | 
				
			||||||
 | 
					    |> validate_required([:path, :student_id, :assignment_id])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def create_work!(attrs \\ %{}) do
 | 
				
			||||||
 | 
					    %Confient.Student.Work{}
 | 
				
			||||||
 | 
					    |> changeset(attrs)
 | 
				
			||||||
 | 
					    |> Repo.insert!()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										163
									
								
								lib/confient/works.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								lib/confient/works.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,163 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Works do
 | 
				
			||||||
 | 
					  @moduledoc """
 | 
				
			||||||
 | 
					  The Works context.
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  import Ecto.Query, warn: false
 | 
				
			||||||
 | 
					  alias Confient.Repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  alias Confient.Works.Assignment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Returns the list of assignments.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> list_assignments()
 | 
				
			||||||
 | 
					      [%Assignment{}, ...]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def list_assignments do
 | 
				
			||||||
 | 
					    Repo.all(Assignment) |> Repo.preload(:class)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def get_next_assignments(%Confient.School.Class{id: id}) do
 | 
				
			||||||
 | 
					    date = DateTime.now!(Application.fetch_env!(:confient, :timezone)) |> DateTime.to_date()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    query =
 | 
				
			||||||
 | 
					      from a in Assignment,
 | 
				
			||||||
 | 
					        where: a.id == ^id,
 | 
				
			||||||
 | 
					        where: a.due >= ^date,
 | 
				
			||||||
 | 
					        select: a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Repo.all(query)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Gets a single assignment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Raises `Ecto.NoResultsError` if the Assignment does not exist.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> get_assignment!(123)
 | 
				
			||||||
 | 
					      %Assignment{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> get_assignment!(456)
 | 
				
			||||||
 | 
					      ** (Ecto.NoResultsError)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def get_assignment!(id), do: Repo.get!(Assignment, id) |> Repo.preload(:class)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def get_full_assignment!(id) do
 | 
				
			||||||
 | 
					    Repo.one!(
 | 
				
			||||||
 | 
					      from a in Assignment,
 | 
				
			||||||
 | 
					        where: a.id == ^id,
 | 
				
			||||||
 | 
					        preload: [{:students_works, :student}, :class],
 | 
				
			||||||
 | 
					        select: a
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def get_missing_works_from_student_in_assignement(id, class_id) do
 | 
				
			||||||
 | 
					    squery =
 | 
				
			||||||
 | 
					      from a in Assignment,
 | 
				
			||||||
 | 
					        where: a.id == ^id,
 | 
				
			||||||
 | 
					        left_join: w in Confient.Student.Work,
 | 
				
			||||||
 | 
					        select: %{id: w.student_id}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    query =
 | 
				
			||||||
 | 
					      from studs in subquery(squery),
 | 
				
			||||||
 | 
					        right_join: s in Confient.School.Student,
 | 
				
			||||||
 | 
					        on: studs.id == s.id,
 | 
				
			||||||
 | 
					        where: is_nil(studs.id) and s.class_id == ^class_id,
 | 
				
			||||||
 | 
					        select: s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Repo.all(query)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def get_assignment(id) do
 | 
				
			||||||
 | 
					    case Repo.get(Assignment, id) |> Repo.preload(:class) do
 | 
				
			||||||
 | 
					      nil -> {:error, :no_assignment}
 | 
				
			||||||
 | 
					      assignment -> {:ok, assignment}
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Creates a assignment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> create_assignment(%{field: value})
 | 
				
			||||||
 | 
					      {:ok, %Assignment{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> create_assignment(%{field: bad_value})
 | 
				
			||||||
 | 
					      {:error, %Ecto.Changeset{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def create_assignment(attrs \\ %{}) do
 | 
				
			||||||
 | 
					    %Assignment{}
 | 
				
			||||||
 | 
					    |> Assignment.changeset(attrs)
 | 
				
			||||||
 | 
					    |> Repo.insert()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Updates a assignment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> update_assignment(assignment, %{field: new_value})
 | 
				
			||||||
 | 
					      {:ok, %Assignment{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> update_assignment(assignment, %{field: bad_value})
 | 
				
			||||||
 | 
					      {:error, %Ecto.Changeset{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def update_assignment(%Assignment{} = assignment, attrs) do
 | 
				
			||||||
 | 
					    assignment
 | 
				
			||||||
 | 
					    |> Assignment.changeset(attrs)
 | 
				
			||||||
 | 
					    |> Repo.update()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Deletes a assignment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> delete_assignment(assignment)
 | 
				
			||||||
 | 
					      {:ok, %Assignment{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> delete_assignment(assignment)
 | 
				
			||||||
 | 
					      {:error, %Ecto.Changeset{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def delete_assignment(%Assignment{} = assignment) do
 | 
				
			||||||
 | 
					    Repo.delete(assignment)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  Returns an `%Ecto.Changeset{}` for tracking assignment changes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      iex> change_assignment(assignment)
 | 
				
			||||||
 | 
					      %Ecto.Changeset{data: %Assignment{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  def change_assignment(%Assignment{} = assignment, attrs \\ %{}) do
 | 
				
			||||||
 | 
					    Assignment.changeset(assignment, attrs)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def create_or_update_work(attrs = %{path: _path, student_id: sid, assignment_id: aid} \\ %{}) do
 | 
				
			||||||
 | 
					    work =
 | 
				
			||||||
 | 
					      case Repo.one(
 | 
				
			||||||
 | 
					             from w in Confient.Student.Work,
 | 
				
			||||||
 | 
					               where: w.assignment_id == ^aid and w.student_id == ^sid,
 | 
				
			||||||
 | 
					               select: w
 | 
				
			||||||
 | 
					           ) do
 | 
				
			||||||
 | 
					        nil -> %Confient.Student.Work{}
 | 
				
			||||||
 | 
					        data -> data
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    work |> Confient.Student.Work.changeset(attrs) |> Repo.insert_or_update()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										26
									
								
								lib/confient/works/assignment.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								lib/confient/works/assignment.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Works.Assignment do
 | 
				
			||||||
 | 
					  use Ecto.Schema
 | 
				
			||||||
 | 
					  import Ecto.Changeset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  schema "assignments" do
 | 
				
			||||||
 | 
					    field :due, :date
 | 
				
			||||||
 | 
					    field :title, :string
 | 
				
			||||||
 | 
					    field :slug, :string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    belongs_to :class, Confient.School.Class
 | 
				
			||||||
 | 
					    has_many :students_works, Confient.Student.Work
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    timestamps()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc false
 | 
				
			||||||
 | 
					  def changeset(assignment, attrs) do
 | 
				
			||||||
 | 
					    assignment
 | 
				
			||||||
 | 
					    |> cast(attrs, [:title, :due, :slug, :class_id])
 | 
				
			||||||
 | 
					    |> unique_constraint(:assignments_title_class_id_index, message: "Existe déjà")
 | 
				
			||||||
 | 
					    |> validate_length(:slug, min: 3, max: 5, message: "Doit être compris en 3 et 5 caractères")
 | 
				
			||||||
 | 
					    |> assoc_constraint(:class)
 | 
				
			||||||
 | 
					    |> update_change(:slug, fn e -> String.downcase(e) end)
 | 
				
			||||||
 | 
					    |> validate_required([:title, :due, :slug, :class_id], message: "Le champ ne peut être vide")
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										83
									
								
								lib/confient_web.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								lib/confient_web.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					defmodule ConfientWeb do
 | 
				
			||||||
 | 
					  @moduledoc """
 | 
				
			||||||
 | 
					  The entrypoint for defining your web interface, such
 | 
				
			||||||
 | 
					  as controllers, views, channels and so on.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  This can be used in your application as:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      use ConfientWeb, :controller
 | 
				
			||||||
 | 
					      use ConfientWeb, :view
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  The definitions below will be executed for every view,
 | 
				
			||||||
 | 
					  controller, etc, so keep them short and clean, focused
 | 
				
			||||||
 | 
					  on imports, uses and aliases.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Do NOT define functions inside the quoted expressions
 | 
				
			||||||
 | 
					  below. Instead, define any helper function in modules
 | 
				
			||||||
 | 
					  and import those modules here.
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def controller do
 | 
				
			||||||
 | 
					    quote do
 | 
				
			||||||
 | 
					      use Phoenix.Controller, namespace: ConfientWeb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      import Plug.Conn
 | 
				
			||||||
 | 
					      import ConfientWeb.Gettext
 | 
				
			||||||
 | 
					      alias ConfientWeb.Router.Helpers, as: Routes
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def view do
 | 
				
			||||||
 | 
					    quote do
 | 
				
			||||||
 | 
					      use Phoenix.View,
 | 
				
			||||||
 | 
					        root: "lib/confient_web/templates",
 | 
				
			||||||
 | 
					        namespace: ConfientWeb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # Import convenience functions from controllers
 | 
				
			||||||
 | 
					      import Phoenix.Controller,
 | 
				
			||||||
 | 
					        only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      import Confient.Account.Auth, only: [signed_in?: 1, get_username: 1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # Include shared imports and aliases for view
 | 
				
			||||||
 | 
					      unquote(view_helpers())
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def router do
 | 
				
			||||||
 | 
					    quote do
 | 
				
			||||||
 | 
					      use Phoenix.Router
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      import Plug.Conn
 | 
				
			||||||
 | 
					      import Phoenix.Controller
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def channel do
 | 
				
			||||||
 | 
					    quote do
 | 
				
			||||||
 | 
					      use Phoenix.Channel
 | 
				
			||||||
 | 
					      import ConfientWeb.Gettext
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  defp view_helpers do
 | 
				
			||||||
 | 
					    quote do
 | 
				
			||||||
 | 
					      # Use all HTML functionality (forms, tags, etc)
 | 
				
			||||||
 | 
					      use Phoenix.HTML
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # Import basic rendering functionality (render, render_layout, etc)
 | 
				
			||||||
 | 
					      import Phoenix.View
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      import ConfientWeb.ErrorHelpers
 | 
				
			||||||
 | 
					      import ConfientWeb.Gettext
 | 
				
			||||||
 | 
					      alias ConfientWeb.Router.Helpers, as: Routes
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @doc """
 | 
				
			||||||
 | 
					  When used, dispatch to the appropriate controller/view/etc.
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  defmacro __using__(which) when is_atom(which) do
 | 
				
			||||||
 | 
					    apply(__MODULE__, which, [])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										35
									
								
								lib/confient_web/channels/user_socket.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								lib/confient_web/channels/user_socket.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										124
									
								
								lib/confient_web/controllers/assignment_controller.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								lib/confient_web/controllers/assignment_controller.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										65
									
								
								lib/confient_web/controllers/class_controller.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								lib/confient_web/controllers/class_controller.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										166
									
								
								lib/confient_web/controllers/deposit_controller.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								lib/confient_web/controllers/deposit_controller.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										9
									
								
								lib/confient_web/controllers/error_controller.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/confient_web/controllers/error_controller.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										10
									
								
								lib/confient_web/controllers/page_controller.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								lib/confient_web/controllers/page_controller.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										33
									
								
								lib/confient_web/controllers/session_controller.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								lib/confient_web/controllers/session_controller.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										99
									
								
								lib/confient_web/controllers/student_controller.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								lib/confient_web/controllers/student_controller.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										6
									
								
								lib/confient_web/controllers/user_controller.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								lib/confient_web/controllers/user_controller.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					defmodule ConfientWeb.UserController do
 | 
				
			||||||
 | 
					  use ConfientWeb, :controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # alias Confient.Account
 | 
				
			||||||
 | 
					  # alias Confient.Account.User
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										54
									
								
								lib/confient_web/endpoint.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								lib/confient_web/endpoint.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										24
									
								
								lib/confient_web/gettext.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								lib/confient_web/gettext.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										40
									
								
								lib/confient_web/plugs/auth.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								lib/confient_web/plugs/auth.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										82
									
								
								lib/confient_web/router.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								lib/confient_web/router.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										55
									
								
								lib/confient_web/telemetry.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								lib/confient_web/telemetry.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										7
									
								
								lib/confient_web/templates/assignment/edit.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								lib/confient_web/templates/assignment/edit.html.eex
									
									
									
									
									
										Normal 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)) %>
 | 
				
			||||||
							
								
								
									
										26
									
								
								lib/confient_web/templates/assignment/form.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								lib/confient_web/templates/assignment/form.html.eex
									
									
									
									
									
										Normal 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 %>
 | 
				
			||||||
							
								
								
									
										38
									
								
								lib/confient_web/templates/assignment/index.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								lib/confient_web/templates/assignment/index.html.eex
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
							
								
								
									
										7
									
								
								lib/confient_web/templates/assignment/new.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								lib/confient_web/templates/assignment/new.html.eex
									
									
									
									
									
										Normal 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)) %>
 | 
				
			||||||
							
								
								
									
										67
									
								
								lib/confient_web/templates/assignment/show.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								lib/confient_web/templates/assignment/show.html.eex
									
									
									
									
									
										Normal 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 %>
 | 
				
			||||||
							
								
								
									
										7
									
								
								lib/confient_web/templates/class/edit.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								lib/confient_web/templates/class/edit.html.eex
									
									
									
									
									
										Normal 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)) %>
 | 
				
			||||||
							
								
								
									
										15
									
								
								lib/confient_web/templates/class/form.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								lib/confient_web/templates/class/form.html.eex
									
									
									
									
									
										Normal 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 %>
 | 
				
			||||||
							
								
								
									
										29
									
								
								lib/confient_web/templates/class/index.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								lib/confient_web/templates/class/index.html.eex
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
							
								
								
									
										7
									
								
								lib/confient_web/templates/class/new.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								lib/confient_web/templates/class/new.html.eex
									
									
									
									
									
										Normal 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)) %>
 | 
				
			||||||
							
								
								
									
										14
									
								
								lib/confient_web/templates/class/show.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								lib/confient_web/templates/class/show.html.eex
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
							
								
								
									
										23
									
								
								lib/confient_web/templates/deposit/form.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								lib/confient_web/templates/deposit/form.html.eex
									
									
									
									
									
										Normal 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 %>
 | 
				
			||||||
							
								
								
									
										24
									
								
								lib/confient_web/templates/deposit/index.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								lib/confient_web/templates/deposit/index.html.eex
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
							
								
								
									
										46
									
								
								lib/confient_web/templates/layout/app.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								lib/confient_web/templates/layout/app.html.eex
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
							
								
								
									
										15
									
								
								lib/confient_web/templates/page/index.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								lib/confient_web/templates/page/index.html.eex
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
							
								
								
									
										9
									
								
								lib/confient_web/templates/session/new.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/confient_web/templates/session/new.html.eex
									
									
									
									
									
										Normal 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 %>
 | 
				
			||||||
							
								
								
									
										17
									
								
								lib/confient_web/templates/student/bulkform.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								lib/confient_web/templates/student/bulkform.html.eex
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
							
								
								
									
										7
									
								
								lib/confient_web/templates/student/edit.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								lib/confient_web/templates/student/edit.html.eex
									
									
									
									
									
										Normal 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)) %>
 | 
				
			||||||
							
								
								
									
										22
									
								
								lib/confient_web/templates/student/form.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								lib/confient_web/templates/student/form.html.eex
									
									
									
									
									
										Normal 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 %>
 | 
				
			||||||
							
								
								
									
										32
									
								
								lib/confient_web/templates/student/index.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								lib/confient_web/templates/student/index.html.eex
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
							
								
								
									
										5
									
								
								lib/confient_web/templates/student/new.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								lib/confient_web/templates/student/new.html.eex
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
							
								
								
									
										52
									
								
								lib/confient_web/templates/student/show.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								lib/confient_web/templates/student/show.html.eex
									
									
									
									
									
										Normal 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 %>
 | 
				
			||||||
							
								
								
									
										3
									
								
								lib/confient_web/views/assignment_view.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/confient_web/views/assignment_view.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					defmodule ConfientWeb.AssignmentView do
 | 
				
			||||||
 | 
					  use ConfientWeb, :view
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										3
									
								
								lib/confient_web/views/class_view.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/confient_web/views/class_view.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					defmodule ConfientWeb.ClassView do
 | 
				
			||||||
 | 
					  use ConfientWeb, :view
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										3
									
								
								lib/confient_web/views/deposit_view.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/confient_web/views/deposit_view.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					defmodule ConfientWeb.DepositView do
 | 
				
			||||||
 | 
					  use ConfientWeb, :view
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										47
									
								
								lib/confient_web/views/error_helpers.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								lib/confient_web/views/error_helpers.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										16
									
								
								lib/confient_web/views/error_view.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								lib/confient_web/views/error_view.ex
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										3
									
								
								lib/confient_web/views/layout_view.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/confient_web/views/layout_view.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					defmodule ConfientWeb.LayoutView do
 | 
				
			||||||
 | 
					  use ConfientWeb, :view
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										3
									
								
								lib/confient_web/views/page_view.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/confient_web/views/page_view.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					defmodule ConfientWeb.PageView do
 | 
				
			||||||
 | 
					  use ConfientWeb, :view
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										3
									
								
								lib/confient_web/views/session_view.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/confient_web/views/session_view.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					defmodule ConfientWeb.SessionView do
 | 
				
			||||||
 | 
					  use ConfientWeb, :view
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										3
									
								
								lib/confient_web/views/student_view.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/confient_web/views/student_view.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					defmodule ConfientWeb.StudentView do
 | 
				
			||||||
 | 
					  use ConfientWeb, :view
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										3
									
								
								lib/confient_web/views/user_view.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/confient_web/views/user_view.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					defmodule ConfientWeb.UserView do
 | 
				
			||||||
 | 
					  use ConfientWeb, :view
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										58
									
								
								lib/deposit.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								lib/deposit.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Deposit do
 | 
				
			||||||
 | 
					  alias Confient.Student.Work
 | 
				
			||||||
 | 
					  alias Confient.Repo
 | 
				
			||||||
 | 
					  import Ecto.Query
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def list_works(), do: Repo.all(from w in Work, preload: [{:student, :class}, :assignment])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def verify_date(due) do
 | 
				
			||||||
 | 
					    date = DateTime.now!(Application.fetch_env!(:confient, :timezone)) |> DateTime.to_date()
 | 
				
			||||||
 | 
					    if Timex.before?(date, due), do: :ok, else: {:error, :date_too_late}
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def verify_file(file) do
 | 
				
			||||||
 | 
					    supported = [
 | 
				
			||||||
 | 
					      "application/pdf",
 | 
				
			||||||
 | 
					      "application/rtf",
 | 
				
			||||||
 | 
					      "application/vnd.oasis.opendocument.text",
 | 
				
			||||||
 | 
					      "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
 | 
				
			||||||
 | 
					      "application/msword"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if Enum.member?(supported, file.content_type) do
 | 
				
			||||||
 | 
					      {:ok, Path.extname(file.filename)}
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      {:error, :invalid_file_type}
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def get_upload_dir(class_name, assignment_slug) do
 | 
				
			||||||
 | 
					    dir = Application.fetch_env!(:confient, :upload_dir)
 | 
				
			||||||
 | 
					    "#{dir}/#{class_name}/#{assignment_slug}"
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def ensure_upload_dir(dir) do
 | 
				
			||||||
 | 
					    File.mkdir_p!(dir)
 | 
				
			||||||
 | 
					    dir
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def gen_filename(
 | 
				
			||||||
 | 
					        class_name,
 | 
				
			||||||
 | 
					        assignment_slug,
 | 
				
			||||||
 | 
					        %Confient.School.Student{
 | 
				
			||||||
 | 
					          firstname: fname,
 | 
				
			||||||
 | 
					          lastname: lname
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        ext
 | 
				
			||||||
 | 
					      ) do
 | 
				
			||||||
 | 
					    Enum.join(
 | 
				
			||||||
 | 
					      [
 | 
				
			||||||
 | 
					        class_name,
 | 
				
			||||||
 | 
					        assignment_slug,
 | 
				
			||||||
 | 
					        "#{String.downcase(String.at(fname, 0) <> String.replace(lname, " ", "-"))}",
 | 
				
			||||||
 | 
					        String.replace(DateTime.to_iso8601(DateTime.now!("Europe/Paris")), ":", "-")
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "_"
 | 
				
			||||||
 | 
					    ) <> ext
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										20
									
								
								lib/helpers/csv.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								lib/helpers/csv.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					defmodule CSV do
 | 
				
			||||||
 | 
					  @moduledoc """
 | 
				
			||||||
 | 
					  Helper to interact with CSV
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def parse(path) do
 | 
				
			||||||
 | 
					    content = File.read!(path)
 | 
				
			||||||
 | 
					    [_ | parts] = String.replace(content, "\r", "") |> String.split("\n") |> Enum.filter(fn v -> v != "" end)
 | 
				
			||||||
 | 
					    names = Enum.map(parts, fn v ->
 | 
				
			||||||
 | 
					      [name | _ ] = String.split(v, ";")
 | 
				
			||||||
 | 
					      String.trim(name, "\"")
 | 
				
			||||||
 | 
					    end)
 | 
				
			||||||
 | 
					    students = Enum.map(names, fn v ->
 | 
				
			||||||
 | 
					      IO.inspect(v)
 | 
				
			||||||
 | 
					      [_, lastname, firstname] = Regex.run(~r/(^[A-Z -]{2,}) (.+)/, v)
 | 
				
			||||||
 | 
					      {lastname, firstname}
 | 
				
			||||||
 | 
					    end)
 | 
				
			||||||
 | 
					    {:ok, students}
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										69
									
								
								mix.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								mix.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					defmodule Confient.MixProject do
 | 
				
			||||||
 | 
					  use Mix.Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def project do
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      app: :confient,
 | 
				
			||||||
 | 
					      version: "0.1.0",
 | 
				
			||||||
 | 
					      elixir: "~> 1.7",
 | 
				
			||||||
 | 
					      elixirc_paths: elixirc_paths(Mix.env()),
 | 
				
			||||||
 | 
					      compilers: [:phoenix, :gettext] ++ Mix.compilers(),
 | 
				
			||||||
 | 
					      start_permanent: Mix.env() == :prod,
 | 
				
			||||||
 | 
					      aliases: aliases(),
 | 
				
			||||||
 | 
					      deps: deps()
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Configuration for the OTP application.
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # Type `mix help compile.app` for more information.
 | 
				
			||||||
 | 
					  def application do
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      mod: {Confient.Application, []},
 | 
				
			||||||
 | 
					      extra_applications: [:logger, :runtime_tools]
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Specifies which paths to compile per environment.
 | 
				
			||||||
 | 
					  defp elixirc_paths(:test), do: ["lib", "test/support"]
 | 
				
			||||||
 | 
					  defp elixirc_paths(_), do: ["lib"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Specifies your project dependencies.
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # Type `mix help deps` for examples and options.
 | 
				
			||||||
 | 
					  defp deps do
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      {:phoenix, "~> 1.5.7"},
 | 
				
			||||||
 | 
					      {:phoenix_ecto, "~> 4.1"},
 | 
				
			||||||
 | 
					      {:ecto_sql, "~> 3.4"},
 | 
				
			||||||
 | 
					      {:postgrex, ">= 0.0.0"},
 | 
				
			||||||
 | 
					      {:phoenix_html, "~> 2.11"},
 | 
				
			||||||
 | 
					      {:phoenix_live_reload, "~> 1.2", only: :dev},
 | 
				
			||||||
 | 
					      {:phoenix_live_dashboard, "~> 0.4"},
 | 
				
			||||||
 | 
					      {:telemetry_metrics, "~> 0.4"},
 | 
				
			||||||
 | 
					      {:telemetry_poller, "~> 0.4"},
 | 
				
			||||||
 | 
					      {:gettext, "~> 0.11"},
 | 
				
			||||||
 | 
					      {:jason, "~> 1.0"},
 | 
				
			||||||
 | 
					      {:plug_cowboy, "~> 2.0"},
 | 
				
			||||||
 | 
					      {:comeonin, "~> 4.0"},
 | 
				
			||||||
 | 
					      {:bcrypt_elixir, "~> 1.0"},
 | 
				
			||||||
 | 
					      {:timex, "~> 3.0"},
 | 
				
			||||||
 | 
					      {:tzdata, "~> 1.0.5"}
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Aliases are shortcuts or tasks specific to the current project.
 | 
				
			||||||
 | 
					  # For example, to install project dependencies and perform other setup tasks, run:
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  #     $ mix setup
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # See the documentation for `Mix` for more info on aliases.
 | 
				
			||||||
 | 
					  defp aliases do
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      setup: ["deps.get", "ecto.setup", "cmd npm install --prefix assets"],
 | 
				
			||||||
 | 
					      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
 | 
				
			||||||
 | 
					      "ecto.reset": ["ecto.drop", "ecto.setup"],
 | 
				
			||||||
 | 
					      test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"]
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										43
									
								
								mix.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								mix.lock
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					%{
 | 
				
			||||||
 | 
					  "bcrypt_elixir": {:hex, :bcrypt_elixir, "1.1.1", "6b5560e47a02196ce5f0ab3f1d8265db79a23868c137e973b27afef928ed8006", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "10f658be786bd2daaadcd45cc5b598da01d5bbc313da4d0e3efb2d6a511d896d"},
 | 
				
			||||||
 | 
					  "certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
 | 
				
			||||||
 | 
					  "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
 | 
				
			||||||
 | 
					  "comeonin": {:hex, :comeonin, "4.1.2", "3eb5620fd8e35508991664b4c2b04dd41e52f1620b36957be837c1d7784b7592", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm", "d8700a0ca4dbb616c22c9b3f6dd539d88deaafec3efe66869d6370c9a559b3e9"},
 | 
				
			||||||
 | 
					  "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
 | 
				
			||||||
 | 
					  "cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"},
 | 
				
			||||||
 | 
					  "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.1", "ebd1a1d7aff97f27c66654e78ece187abdc646992714164380d8a041eda16754", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a6efd3366130eab84ca372cbd4a7d3c3a97bdfcfb4911233b035d117063f0af"},
 | 
				
			||||||
 | 
					  "cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"},
 | 
				
			||||||
 | 
					  "db_connection": {:hex, :db_connection, "2.3.0", "d56ef906956a37959bcb385704fc04035f4f43c0f560dd23e00740daf8028c49", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "dcc082b8f723de9a630451b49fdbd7a59b065c4b38176fb147aaf773574d4520"},
 | 
				
			||||||
 | 
					  "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
 | 
				
			||||||
 | 
					  "ecto": {:hex, :ecto, "3.5.5", "48219a991bb86daba6e38a1e64f8cea540cded58950ff38fbc8163e062281a07", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "98dd0e5e1de7f45beca6130d13116eae675db59adfa055fb79612406acf6f6f1"},
 | 
				
			||||||
 | 
					  "ecto_sql": {:hex, :ecto_sql, "3.5.3", "1964df0305538364b97cc4661a2bd2b6c89d803e66e5655e4e55ff1571943efd", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.5.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d2f53592432ce17d3978feb8f43e8dc0705e288b0890caf06d449785f018061c"},
 | 
				
			||||||
 | 
					  "elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
 | 
				
			||||||
 | 
					  "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
 | 
				
			||||||
 | 
					  "gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
 | 
				
			||||||
 | 
					  "hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
 | 
				
			||||||
 | 
					  "idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
 | 
				
			||||||
 | 
					  "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
 | 
				
			||||||
 | 
					  "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
 | 
				
			||||||
 | 
					  "mime": {:hex, :mime, "1.5.0", "203ef35ef3389aae6d361918bf3f952fa17a09e8e43b5aa592b93eba05d0fb8d", [:mix], [], "hexpm", "55a94c0f552249fc1a3dd9cd2d3ab9de9d3c89b559c2bd01121f824834f24746"},
 | 
				
			||||||
 | 
					  "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
 | 
				
			||||||
 | 
					  "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
 | 
				
			||||||
 | 
					  "phoenix": {:hex, :phoenix, "1.5.7", "2923bb3af924f184459fe4fa4b100bd25fa6468e69b2803dfae82698269aa5e0", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "774cd64417c5a3788414fdbb2be2eb9bcd0c048d9e6ad11a0c1fd67b7c0d0978"},
 | 
				
			||||||
 | 
					  "phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"},
 | 
				
			||||||
 | 
					  "phoenix_html": {:hex, :phoenix_html, "2.14.2", "b8a3899a72050f3f48a36430da507dd99caf0ac2d06c77529b1646964f3d563e", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "58061c8dfd25da5df1ea0ca47c972f161beb6c875cd293917045b92ffe1bf617"},
 | 
				
			||||||
 | 
					  "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.4.0", "87990e68b60213d7487e65814046f9a2bed4a67886c943270125913499b3e5c3", [:mix], [{:ecto_psql_extras, "~> 0.4.1 or ~> 0.5", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.14.1 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.15.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.4.0 or ~> 0.5.0 or ~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "8d52149e58188e9e4497cc0d8900ab94d9b66f96998ec38c47c7a4f8f4f50e57"},
 | 
				
			||||||
 | 
					  "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.0", "f35f61c3f959c9a01b36defaa1f0624edd55b87e236b606664a556d6f72fd2e7", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "02c1007ae393f2b76ec61c1a869b1e617179877984678babde131d716f95b582"},
 | 
				
			||||||
 | 
					  "phoenix_live_view": {:hex, :phoenix_live_view, "0.15.0", "1bb9b0c230a047cddc32b1708ec4cad4f91aaab0b4688c09702fcf78d7e2df94", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 0.5", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d42ad9b5fe9e874df3a12ea0a806e3f650db99774d597285c07df43f33cab486"},
 | 
				
			||||||
 | 
					  "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
 | 
				
			||||||
 | 
					  "plug": {:hex, :plug, "1.11.0", "f17217525597628298998bc3baed9f8ea1fa3f1160aa9871aee6df47a6e4d38e", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2d9c633f0499f9dc5c2fd069161af4e2e7756890b81adcbb2ceaa074e8308876"},
 | 
				
			||||||
 | 
					  "plug_cowboy": {:hex, :plug_cowboy, "2.4.1", "779ba386c0915027f22e14a48919a9545714f849505fa15af2631a0d298abf0f", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d72113b6dff7b37a7d9b2a5b68892808e3a9a752f2bf7e503240945385b70507"},
 | 
				
			||||||
 | 
					  "plug_crypto": {:hex, :plug_crypto, "1.2.0", "1cb20793aa63a6c619dd18bb33d7a3aa94818e5fd39ad357051a67f26dfa2df6", [:mix], [], "hexpm", "a48b538ae8bf381ffac344520755f3007cc10bd8e90b240af98ea29b69683fc2"},
 | 
				
			||||||
 | 
					  "postgrex": {:hex, :postgrex, "0.15.7", "724410acd48abac529d0faa6c2a379fb8ae2088e31247687b16cacc0e0883372", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "88310c010ff047cecd73d5ceca1d99205e4b1ab1b9abfdab7e00f5c9d20ef8f9"},
 | 
				
			||||||
 | 
					  "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
 | 
				
			||||||
 | 
					  "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
 | 
				
			||||||
 | 
					  "telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
 | 
				
			||||||
 | 
					  "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.0", "da9d49ee7e6bb1c259d36ce6539cd45ae14d81247a2b0c90edf55e2b50507f7b", [:mix], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5cfe67ad464b243835512aa44321cee91faed6ea868d7fb761d7016e02915c3d"},
 | 
				
			||||||
 | 
					  "telemetry_poller": {:hex, :telemetry_poller, "0.5.1", "21071cc2e536810bac5628b935521ff3e28f0303e770951158c73eaaa01e962a", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4cab72069210bc6e7a080cec9afffad1b33370149ed5d379b81c7c5f0c663fd4"},
 | 
				
			||||||
 | 
					  "timex": {:hex, :timex, "3.6.2", "845cdeb6119e2fef10751c0b247b6c59d86d78554c83f78db612e3290f819bc2", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "26030b46199d02a590be61c2394b37ea25a3664c02fafbeca0b24c972025d47a"},
 | 
				
			||||||
 | 
					  "tzdata": {:hex, :tzdata, "1.0.5", "69f1ee029a49afa04ad77801febaf69385f3d3e3d1e4b56b9469025677b89a28", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "55519aa2a99e5d2095c1e61cc74c9be69688f8ab75c27da724eb8279ff402a5a"},
 | 
				
			||||||
 | 
					  "unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										97
									
								
								priv/gettext/en/LC_MESSAGES/errors.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								priv/gettext/en/LC_MESSAGES/errors.po
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					## `msgid`s in this file come from POT (.pot) files.
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## Do not add, change, or remove `msgid`s manually here as
 | 
				
			||||||
 | 
					## they're tied to the ones in the corresponding POT file
 | 
				
			||||||
 | 
					## (with the same domain).
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## Use `mix gettext.extract --merge` or `mix gettext.merge`
 | 
				
			||||||
 | 
					## to merge POT files into PO files.
 | 
				
			||||||
 | 
					msgid ""
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					"Language: en\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.cast/4
 | 
				
			||||||
 | 
					msgid "can't be blank"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.unique_constraint/3
 | 
				
			||||||
 | 
					msgid "has already been taken"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.put_change/3
 | 
				
			||||||
 | 
					msgid "is invalid"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_acceptance/3
 | 
				
			||||||
 | 
					msgid "must be accepted"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_format/3
 | 
				
			||||||
 | 
					msgid "has invalid format"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_subset/3
 | 
				
			||||||
 | 
					msgid "has an invalid entry"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_exclusion/3
 | 
				
			||||||
 | 
					msgid "is reserved"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_confirmation/3
 | 
				
			||||||
 | 
					msgid "does not match confirmation"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.no_assoc_constraint/3
 | 
				
			||||||
 | 
					msgid "is still associated with this entry"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "are still associated with this entry"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_length/3
 | 
				
			||||||
 | 
					msgid "should be %{count} character(s)"
 | 
				
			||||||
 | 
					msgid_plural "should be %{count} character(s)"
 | 
				
			||||||
 | 
					msgstr[0] ""
 | 
				
			||||||
 | 
					msgstr[1] ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "should have %{count} item(s)"
 | 
				
			||||||
 | 
					msgid_plural "should have %{count} item(s)"
 | 
				
			||||||
 | 
					msgstr[0] ""
 | 
				
			||||||
 | 
					msgstr[1] ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "should be at least %{count} character(s)"
 | 
				
			||||||
 | 
					msgid_plural "should be at least %{count} character(s)"
 | 
				
			||||||
 | 
					msgstr[0] ""
 | 
				
			||||||
 | 
					msgstr[1] ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "should have at least %{count} item(s)"
 | 
				
			||||||
 | 
					msgid_plural "should have at least %{count} item(s)"
 | 
				
			||||||
 | 
					msgstr[0] ""
 | 
				
			||||||
 | 
					msgstr[1] ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "should be at most %{count} character(s)"
 | 
				
			||||||
 | 
					msgid_plural "should be at most %{count} character(s)"
 | 
				
			||||||
 | 
					msgstr[0] ""
 | 
				
			||||||
 | 
					msgstr[1] ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "should have at most %{count} item(s)"
 | 
				
			||||||
 | 
					msgid_plural "should have at most %{count} item(s)"
 | 
				
			||||||
 | 
					msgstr[0] ""
 | 
				
			||||||
 | 
					msgstr[1] ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_number/3
 | 
				
			||||||
 | 
					msgid "must be less than %{number}"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "must be greater than %{number}"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "must be less than or equal to %{number}"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "must be greater than or equal to %{number}"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "must be equal to %{number}"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
							
								
								
									
										95
									
								
								priv/gettext/errors.pot
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								priv/gettext/errors.pot
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					## This is a PO Template file.
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## `msgid`s here are often extracted from source code.
 | 
				
			||||||
 | 
					## Add new translations manually only if they're dynamic
 | 
				
			||||||
 | 
					## translations that can't be statically extracted.
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## Run `mix gettext.extract` to bring this file up to
 | 
				
			||||||
 | 
					## date. Leave `msgstr`s empty as changing them here has no
 | 
				
			||||||
 | 
					## effect: edit them in PO (`.po`) files instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.cast/4
 | 
				
			||||||
 | 
					msgid "can't be blank"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.unique_constraint/3
 | 
				
			||||||
 | 
					msgid "has already been taken"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.put_change/3
 | 
				
			||||||
 | 
					msgid "is invalid"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_acceptance/3
 | 
				
			||||||
 | 
					msgid "must be accepted"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_format/3
 | 
				
			||||||
 | 
					msgid "has invalid format"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_subset/3
 | 
				
			||||||
 | 
					msgid "has an invalid entry"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_exclusion/3
 | 
				
			||||||
 | 
					msgid "is reserved"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_confirmation/3
 | 
				
			||||||
 | 
					msgid "does not match confirmation"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.no_assoc_constraint/3
 | 
				
			||||||
 | 
					msgid "is still associated with this entry"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "are still associated with this entry"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_length/3
 | 
				
			||||||
 | 
					msgid "should be %{count} character(s)"
 | 
				
			||||||
 | 
					msgid_plural "should be %{count} character(s)"
 | 
				
			||||||
 | 
					msgstr[0] ""
 | 
				
			||||||
 | 
					msgstr[1] ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "should have %{count} item(s)"
 | 
				
			||||||
 | 
					msgid_plural "should have %{count} item(s)"
 | 
				
			||||||
 | 
					msgstr[0] ""
 | 
				
			||||||
 | 
					msgstr[1] ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "should be at least %{count} character(s)"
 | 
				
			||||||
 | 
					msgid_plural "should be at least %{count} character(s)"
 | 
				
			||||||
 | 
					msgstr[0] ""
 | 
				
			||||||
 | 
					msgstr[1] ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "should have at least %{count} item(s)"
 | 
				
			||||||
 | 
					msgid_plural "should have at least %{count} item(s)"
 | 
				
			||||||
 | 
					msgstr[0] ""
 | 
				
			||||||
 | 
					msgstr[1] ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "should be at most %{count} character(s)"
 | 
				
			||||||
 | 
					msgid_plural "should be at most %{count} character(s)"
 | 
				
			||||||
 | 
					msgstr[0] ""
 | 
				
			||||||
 | 
					msgstr[1] ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "should have at most %{count} item(s)"
 | 
				
			||||||
 | 
					msgid_plural "should have at most %{count} item(s)"
 | 
				
			||||||
 | 
					msgstr[0] ""
 | 
				
			||||||
 | 
					msgstr[1] ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## From Ecto.Changeset.validate_number/3
 | 
				
			||||||
 | 
					msgid "must be less than %{number}"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "must be greater than %{number}"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "must be less than or equal to %{number}"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "must be greater than or equal to %{number}"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "must be equal to %{number}"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
							
								
								
									
										4
									
								
								priv/repo/migrations/.formatter.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								priv/repo/migrations/.formatter.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					[
 | 
				
			||||||
 | 
					  import_deps: [:ecto_sql],
 | 
				
			||||||
 | 
					  inputs: ["*.exs"]
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										14
									
								
								priv/repo/migrations/20201124120633_create_classes.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								priv/repo/migrations/20201124120633_create_classes.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Repo.Migrations.CreateClasses do
 | 
				
			||||||
 | 
					  use Ecto.Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def change do
 | 
				
			||||||
 | 
					    create table(:classes) do
 | 
				
			||||||
 | 
					      add :name, :string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      timestamps()
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    create unique_index(:classes, [:name])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										15
									
								
								priv/repo/migrations/20201128001745_create_students.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								priv/repo/migrations/20201128001745_create_students.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Repo.Migrations.CreateStudents do
 | 
				
			||||||
 | 
					  use Ecto.Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def change do
 | 
				
			||||||
 | 
					    create table(:students) do
 | 
				
			||||||
 | 
					      add :lastname, :string
 | 
				
			||||||
 | 
					      add :firstname, :string
 | 
				
			||||||
 | 
					      add :class_id, references(:classes, on_delete: :delete_all)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      timestamps()
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    create unique_index(:students, [:lastname, :firstname, :class_id])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										16
									
								
								priv/repo/migrations/20201128151154_create_assignments.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								priv/repo/migrations/20201128151154_create_assignments.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Repo.Migrations.CreateAssignments do
 | 
				
			||||||
 | 
					  use Ecto.Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def change do
 | 
				
			||||||
 | 
					    create table(:assignments) do
 | 
				
			||||||
 | 
					      add :title, :string
 | 
				
			||||||
 | 
					      add :slug, :string
 | 
				
			||||||
 | 
					      add :due, :date
 | 
				
			||||||
 | 
					      add :class_id, references(:classes, on_delete: :delete_all)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      timestamps()
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    create unique_index(:assignments, [:title, :class_id])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					defmodule Confient.Repo.Migrations.CreateStudentsWorks do
 | 
				
			||||||
 | 
					  use Ecto.Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def change do
 | 
				
			||||||
 | 
					    create table(:students_works) do
 | 
				
			||||||
 | 
					      add :date, :date
 | 
				
			||||||
 | 
					      add :path, :string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      add :student_id, references(:students, on_delete: :delete_all)
 | 
				
			||||||
 | 
					      add :assignment_id, references(:assignments, on_delete: :delete_all)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      timestamps()
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  create unique_index(:students_works, [:student_id, :assignment_id])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user