From 788e182f295cd48377f0d919717dc6dc9f39baa5 Mon Sep 17 00:00:00 2001
From: theodorsm <theodor@midtlien.com>
Date: Wed, 15 Mar 2023 15:46:52 +0100
Subject: [PATCH 1/3] Resolved merge conflict

---
 README.md                                     |  39 +++++--
 backend/Dockerfile                            |   2 +-
 backend/alembic.ini                           | 110 ++++++++++++++++++
 backend/alembic/README                        |   1 +
 backend/alembic/env.py                        |  76 ++++++++++++
 backend/alembic/script.py.mako                |  24 ++++
 .../versions/aa01c8290072_first_revision.py   |  92 +++++++++++++++
 backend/crud.py                               |  23 ++--
 backend/database.py                           |  11 +-
 backend/main.py                               |  60 ++++++----
 backend/model.py                              |  25 ++--
 requirements.txt => backend/requirements.txt  |   1 +
 docker-compose.yml                            |  15 ++-
 13 files changed, 410 insertions(+), 69 deletions(-)
 create mode 100644 backend/alembic.ini
 create mode 100644 backend/alembic/README
 create mode 100644 backend/alembic/env.py
 create mode 100644 backend/alembic/script.py.mako
 create mode 100644 backend/alembic/versions/aa01c8290072_first_revision.py
 rename requirements.txt => backend/requirements.txt (94%)

diff --git a/README.md b/README.md
index 9488446..f4f88e3 100644
--- a/README.md
+++ b/README.md
@@ -26,25 +26,44 @@ Environment:
 
 Create this .env file in the root folder!
 
-In the `.env` make sure that:
+In the `backend` folder, create`.env` containing the following:
 - `production = false`
 - `SECRET_KEY` is set
 - the client secrets corresponds to your feide instance
-In frontend:
-- create `.env.development` with PUBLIC_API_URL="http://127.0.0.1:8000" => Create this in frontend folder!
+
+In `frontend` folder:
+- create `.env.development` with PUBLIC_API_URL="http://127.0.0.1:8000".
+
+#### Backend:
 
 Running backend:
 ```bash
+cd backend
 python -m venv venv # only do once
 source ./venv/bin/activate
 pip install -r requirements.txt
 
 # Run the backend
-uvicorn backend.main:app --reload
+uvicorn main:app --reload
 
 # Running at 127.0.0.1:8000
 ```
 
+**Migration**: alembic is used for databse migration (kind of git for databases).
+You can read more about alembic [here](https://alembic.sqlalchemy.org/en/latest/tutorial.html), or take a look at this tutorial [video](https://www.youtube.com/watch?v=SdcH6IEi6nE&list=WL&index=6).
+
+When ANY changes is made to the database model file, then a migration has to happen. This is done by the following commands:
+
+```bash
+# Create migration
+alembic revision --autogenerate -m "Revision message, something relevant here"
+
+# Apply latest migration
+alembic upgrade head
+```
+
+#### Frontend:
+
 Running frontend:
 ```bash
 cd frontend
@@ -68,13 +87,14 @@ Requirements:
 
 Environment:
 
-In the `.env` make sure that:
+In the `backend` folder, create`.env` containing the following:
 - `production = true`
 - `SECRET_KEY` is set
 - the client secrets corresponds to your feide instance
 - the following postgres variables is set: `POSTGRES_USER`, `POSTGRES_PASSWORD`,  `POSTGRES_DB`.
-In frontend:
-- create `.env.production` with PUBLIC_API_URL="https://ref.iik.ntnu.no/api" in the frontend directory.
+
+In `frontend` folder:
+- create `.env.production` with PUBLIC_API_URL="https://ref.iik.ntnu.no/api".
 
 Also make sure that there exists a `certs` folder with the ssl privatekey and certificate.
 
@@ -100,8 +120,3 @@ Sometimes the volume of postgres needs to be deleted before running the containe
 ```
 docker volume rm reflect_db-data
 ```
-
-
-## TODO
-
-- [ ] migration of database
diff --git a/backend/Dockerfile b/backend/Dockerfile
index 03a264a..5747dbb 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -14,4 +14,4 @@ EXPOSE 80
 
 WORKDIR /build/
 
-CMD python -m uvicorn backend.main:app --host 0.0.0.0 --port 80
+CMD python -m uvicorn main:app --host 0.0.0.0 --port 80
diff --git a/backend/alembic.ini b/backend/alembic.ini
new file mode 100644
index 0000000..00dd9ea
--- /dev/null
+++ b/backend/alembic.ini
@@ -0,0 +1,110 @@
+# A generic, single database configuration.
+
+[alembic]
+# path to migration scripts
+script_location = alembic
+
+# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
+# Uncomment the line below if you want the files to be prepended with date and time
+# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
+# for all available tokens
+# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
+
+# sys.path path, will be prepended to sys.path if present.
+# defaults to the current working directory.
+prepend_sys_path = .
+
+# timezone to use when rendering the date within the migration file
+# as well as the filename.
+# If specified, requires the python-dateutil library that can be
+# installed by adding `alembic[tz]` to the pip requirements
+# string value is passed to dateutil.tz.gettz()
+# leave blank for localtime
+# timezone =
+
+# max length of characters to apply to the
+# "slug" field
+# truncate_slug_length = 40
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+# set to 'true' to allow .pyc and .pyo files without
+# a source .py file to be detected as revisions in the
+# versions/ directory
+# sourceless = false
+
+# version location specification; This defaults
+# to alembic/versions.  When using multiple version
+# directories, initial revisions must be specified with --version-path.
+# The path separator used here should be the separator specified by "version_path_separator" below.
+# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
+
+# version path separator; As mentioned above, this is the character used to split
+# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
+# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
+# Valid values for version_path_separator are:
+#
+# version_path_separator = :
+# version_path_separator = ;
+# version_path_separator = space
+version_path_separator = os  # Use os.pathsep. Default configuration used for new projects.
+
+# set to 'true' to search source files recursively
+# in each "version_locations" directory
+# new in Alembic version 1.10
+# recursive_version_locations = false
+
+# the output encoding used when revision files
+# are written from script.py.mako
+# output_encoding = utf-8
+
+# sqlalchemy.url = driver://user:pass@localhost/dbname
+
+
+[post_write_hooks]
+# post_write_hooks defines scripts or Python functions that are run
+# on newly generated revision scripts.  See the documentation for further
+# detail and examples
+
+# format using "black" - use the console_scripts runner, against the "black" entrypoint
+# hooks = black
+# black.type = console_scripts
+# black.entrypoint = black
+# black.options = -l 79 REVISION_SCRIPT_FILENAME
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/backend/alembic/README b/backend/alembic/README
new file mode 100644
index 0000000..98e4f9c
--- /dev/null
+++ b/backend/alembic/README
@@ -0,0 +1 @@
+Generic single-database configuration.
\ No newline at end of file
diff --git a/backend/alembic/env.py b/backend/alembic/env.py
new file mode 100644
index 0000000..6d1b469
--- /dev/null
+++ b/backend/alembic/env.py
@@ -0,0 +1,76 @@
+from logging.config import fileConfig
+
+from alembic import context
+from database import DATABASE_URL, Base
+from sqlalchemy import engine_from_config, pool
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+config.set_main_option("sqlalchemy.url", DATABASE_URL)
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+if config.config_file_name is not None:
+    fileConfig(config.config_file_name)
+
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+target_metadata = Base.metadata
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+
+def run_migrations_offline() -> None:
+    """Run migrations in 'offline' mode.
+
+    This configures the context with just a URL
+    and not an Engine, though an Engine is acceptable
+    here as well.  By skipping the Engine creation
+    we don't even need a DBAPI to be available.
+
+    Calls to context.execute() here emit the given string to the
+    script output.
+
+    """
+    url = config.get_main_option("sqlalchemy.url")
+    context.configure(
+        url=url,
+        target_metadata=target_metadata,
+        literal_binds=True,
+        dialect_opts={"paramstyle": "named"},
+    )
+
+    with context.begin_transaction():
+        context.run_migrations()
+
+
+def run_migrations_online() -> None:
+    """Run migrations in 'online' mode.
+
+    In this scenario we need to create an Engine
+    and associate a connection with the context.
+
+    """
+    connectable = engine_from_config(
+        config.get_section(config.config_ini_section, {}),
+        prefix="sqlalchemy.",
+        poolclass=pool.NullPool,
+    )
+
+    with connectable.connect() as connection:
+        context.configure(connection=connection, target_metadata=target_metadata)
+
+        with context.begin_transaction():
+            context.run_migrations()
+
+
+if context.is_offline_mode():
+    run_migrations_offline()
+else:
+    run_migrations_online()
diff --git a/backend/alembic/script.py.mako b/backend/alembic/script.py.mako
new file mode 100644
index 0000000..55df286
--- /dev/null
+++ b/backend/alembic/script.py.mako
@@ -0,0 +1,24 @@
+"""${message}
+
+Revision ID: ${up_revision}
+Revises: ${down_revision | comma,n}
+Create Date: ${create_date}
+
+"""
+from alembic import op
+import sqlalchemy as sa
+${imports if imports else ""}
+
+# revision identifiers, used by Alembic.
+revision = ${repr(up_revision)}
+down_revision = ${repr(down_revision)}
+branch_labels = ${repr(branch_labels)}
+depends_on = ${repr(depends_on)}
+
+
+def upgrade() -> None:
+    ${upgrades if upgrades else "pass"}
+
+
+def downgrade() -> None:
+    ${downgrades if downgrades else "pass"}
diff --git a/backend/alembic/versions/aa01c8290072_first_revision.py b/backend/alembic/versions/aa01c8290072_first_revision.py
new file mode 100644
index 0000000..f228de5
--- /dev/null
+++ b/backend/alembic/versions/aa01c8290072_first_revision.py
@@ -0,0 +1,92 @@
+"""First revision
+
+Revision ID: aa01c8290072
+Revises: 
+Create Date: 2023-03-10 14:33:14.535079
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'aa01c8290072'
+down_revision = None
+branch_labels = None
+depends_on = None
+
+
+def upgrade() -> None:
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_table('reflections')
+    op.drop_table('users')
+    op.drop_table('questions')
+    op.drop_table('course_question')
+    op.drop_table('units')
+    op.drop_table('enrollment')
+    op.drop_table('courses')
+    # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.create_table('courses',
+    sa.Column('name', sa.VARCHAR(), nullable=True),
+    sa.Column('id', sa.VARCHAR(), nullable=False),
+    sa.Column('semester', sa.VARCHAR(), nullable=True),
+    sa.Column('responsible', sa.VARCHAR(), nullable=True),
+    sa.Column('website', sa.VARCHAR(), nullable=True),
+    sa.PrimaryKeyConstraint('id')
+    )
+    op.create_table('enrollment',
+    sa.Column('user_email', sa.VARCHAR(), nullable=False),
+    sa.Column('course_id', sa.VARCHAR(), nullable=False),
+    sa.Column('role', sa.VARCHAR(length=18), nullable=False),
+    sa.ForeignKeyConstraint(['course_id'], ['courses.id'], ),
+    sa.ForeignKeyConstraint(['user_email'], ['users.email'], ),
+    sa.PrimaryKeyConstraint('user_email', 'course_id', 'role')
+    )
+    op.create_table('units',
+    sa.Column('id', sa.INTEGER(), nullable=False),
+    sa.Column('seq_no', sa.INTEGER(), nullable=True),
+    sa.Column('title', sa.VARCHAR(), nullable=True),
+    sa.Column('date_available', sa.DATE(), nullable=True),
+    sa.Column('course_id', sa.VARCHAR(), nullable=True),
+    sa.ForeignKeyConstraint(['course_id'], ['courses.id'], ),
+    sa.PrimaryKeyConstraint('id')
+    )
+    op.create_table('course_question',
+    sa.Column('course_id', sa.VARCHAR(), nullable=False),
+    sa.Column('question_id', sa.INTEGER(), nullable=False),
+    sa.ForeignKeyConstraint(['course_id'], ['courses.id'], ),
+    sa.ForeignKeyConstraint(['question_id'], ['questions.id'], ),
+    sa.PrimaryKeyConstraint('course_id', 'question_id')
+    )
+    op.create_table('questions',
+    sa.Column('id', sa.INTEGER(), nullable=False),
+    sa.Column('question', sa.VARCHAR(), nullable=True),
+    sa.Column('comment', sa.VARCHAR(), nullable=True),
+    sa.PrimaryKeyConstraint('id')
+    )
+    op.create_table('users',
+    sa.Column('email', sa.VARCHAR(), nullable=False),
+    sa.PrimaryKeyConstraint('email'),
+    sa.UniqueConstraint('email')
+    )
+    op.create_table('reflections',
+    sa.Column('id', sa.INTEGER(), nullable=False),
+    sa.Column('body', sa.VARCHAR(), nullable=True),
+    sa.Column('timestamp', sa.DATE(), nullable=True),
+    sa.Column('category', sa.VARCHAR(), nullable=True),
+    sa.Column('is_interesting', sa.BOOLEAN(), nullable=True),
+    sa.Column('is_problematic', sa.BOOLEAN(), nullable=True),
+    sa.Column('is_sorted', sa.BOOLEAN(), nullable=True),
+    sa.Column('user_id', sa.VARCHAR(), nullable=True),
+    sa.Column('unit_id', sa.INTEGER(), nullable=True),
+    sa.Column('question_id', sa.INTEGER(), nullable=True),
+    sa.ForeignKeyConstraint(['question_id'], ['questions.id'], ),
+    sa.ForeignKeyConstraint(['unit_id'], ['units.id'], ),
+    sa.ForeignKeyConstraint(['user_id'], ['users.email'], ),
+    sa.PrimaryKeyConstraint('id')
+    )
+    # ### end Alembic commands ###
diff --git a/backend/crud.py b/backend/crud.py
index 5675888..31cee47 100644
--- a/backend/crud.py
+++ b/backend/crud.py
@@ -1,9 +1,9 @@
 from datetime import datetime
 
+import model
+import schemas
 from sqlalchemy.orm import Session
 
-from . import model, schemas
-
 
 
 
@@ -19,14 +19,17 @@ def create_user(db: Session, user_email: str):
     db.refresh(db_user)
     return db_user
 
-def create_enrollment(db: Session, user_email: str, course_id: str, role: str ):
-    
-    #getting student
+
+def create_enrollment(db: Session, user_email: str, course_id: str, role: str):
+
+    # getting student
     db_user = get_user(db, user_email=user_email)
-    #getting course
+    # getting course
     db_course = get_course(db, course_id=course_id)
-    #Creating enrollment
-    db_enrollment = model.Enrollment(user_email=user_email, course_id=course_id, role=role)
+    # Creating enrollment
+    db_enrollment = model.Enrollment(
+        user_email=user_email, course_id=course_id, role=role
+    )
     print(db_enrollment)
     print("Creating enrollment")
     db.add(db_enrollment)
@@ -46,7 +49,6 @@ def create_question(db: Session, question: str, comment: str):
     return db_obj
 
 
-
 def create_course(db: Session, course: schemas.CourseCreate):
     db_course = model.Course(**course)
     questions = [
@@ -69,6 +71,7 @@ def create_course(db: Session, course: schemas.CourseCreate):
     db.refresh(db_course)
     return db_course
 
+
 # def enroll_user_to_course(db: Session, course_id: str, user_email: str):
 #     db_course = get_course(db, course_id=course_id)
 #     db_user: schemas.User = get_user(db, user_email = user_email)
@@ -138,4 +141,4 @@ def get_number_of_unit_questions(db: Session, unit_id: int ):
 
 #mulig man må bruke id, fordi seq no ikke er unikt
 #def get_unit(db: Session, id: int):
-   # return db.query(model.Unit).filter(model.Unit.id == id).first()
\ No newline at end of file
+   # return db.query(model.Unit).filter(model.Unit.id == id).first()
diff --git a/backend/database.py b/backend/database.py
index 17e63a0..a1f1ad9 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -1,20 +1,23 @@
-import databases
+# import databases
 from sqlalchemy import create_engine
 from sqlalchemy.ext.declarative import declarative_base
 from sqlalchemy.orm import sessionmaker
 from starlette.config import Config
+from starlette.datastructures import Secret
 
 config = Config(".env")
+postgres_user = str(config("POSTGRES_USER", cast=Secret))
+postgres_pass = str(config("POSTGRES_PASSWORD", cast=Secret))
 
 if config("production", cast=bool, default=False):
     DATABASE_URL = (
-        "postgresql://postgres:thissentenceissupersecure@reflect_db_1:5432/reflect"
+        f"postgresql://{postgres_user}:{postgres_pass}@reflect_db_1:5432/reflect"
     )
-    database = databases.Database(DATABASE_URL)
+    # database = databases.Database(DATABASE_URL)
     engine = create_engine(DATABASE_URL)
 else:
     DATABASE_URL = "sqlite:///./reflect.db"
-    database = databases.Database(DATABASE_URL)
+    # database = databases.Database(DATABASE_URL)
     engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
 
 
diff --git a/backend/main.py b/backend/main.py
index f8b547b..d3160c1 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -7,22 +7,23 @@ from typing import List, Optional
 import requests
 from requests.structures import CaseInsensitiveDict
 
+import crud
+import model
 import motor.motor_asyncio
+import schemas
 from authlib.integrations.starlette_client import OAuth, OAuthError
+from database import SessionLocal, engine
 from fastapi import Depends, FastAPI, Form, HTTPException, Request
 from fastapi.middleware.cors import CORSMiddleware
 from fastapi.responses import HTMLResponse
 from fastapi.staticfiles import StaticFiles
 from fastapi.templating import Jinja2Templates
-from sqlalchemy.orm import Session
 from sqlalchemy.exc import IntegrityError
+from sqlalchemy.orm import Session
 from starlette.config import Config
 from starlette.datastructures import Secret
 from starlette.middleware.sessions import SessionMiddleware
-from starlette.responses import RedirectResponse,Response
-
-from . import crud, model, schemas
-from .database import SessionLocal, engine
+from starlette.responses import RedirectResponse, Response
 
 if os.path.exists("./reflect.db"):
     os.remove("./reflect.db")
@@ -48,14 +49,14 @@ app.add_middleware(
     allow_methods=["*"],
 )
 
-templates = Jinja2Templates(directory="backend/templates")
-app.mount("/static", StaticFiles(directory="backend/static"), name="static")
+templates = Jinja2Templates(directory="templates")
+app.mount("/static", StaticFiles(directory="static"), name="static")
 
 if config("production", cast=bool, default=False):
     REDIRECT_URI = "https://ref2.iik.ntnu.no/auth"
     BASE_URL = "https://ref2.iik.ntnu.no"
 else:
-    REDIRECT_URI = "https://localhost/auth"
+    REDIRECT_URI = "http://localhost/auth"
     BASE_URL = "http://127.0.0.1:5173"
 
 course_id: str = "TDT4100"
@@ -102,7 +103,9 @@ def start_db():
     print("init database")
     db = SessionLocal()
 
-    course = crud.create_course(db, course={"name": course_name, "id": course_id, "semester": semester})
+    course = crud.create_course(
+        db, course={"name": course_name, "id": course_id, "semester": semester}
+    )
     user = crud.create_user(db, user_email="thomas@test.no")
     user0 = crud.create_user(db, user_email="eivind@test.no")
     user1 = crud.create_user(db, user_email="jørgen@test.no")
@@ -159,7 +162,9 @@ def start_db():
     # }
     # crud.create_reflection(db=db, reflection=ref)
 
-    enrollment = crud.create_enrollment(course_id="TDT4100", db=db, role="student", user_email="thomas@test.no")
+    enrollment = crud.create_enrollment(
+        course_id="TDT4100", db=db, role="student", user_email="thomas@test.no"
+    )
     db.commit()
     db.close()
 
@@ -167,7 +172,8 @@ def start_db():
 @app.get("/login")
 async def login(request: Request):
     return await oauth.feide.authorize_redirect(request, REDIRECT_URI)
-    
+
+
 @app.get("/auth")
 async def auth(request: Request, db: Session = Depends(get_db)):
     try:
@@ -218,13 +224,16 @@ async def course(request: Request, db: Session = Depends(get_db)):
     print(course)
     return course
 
+
 @app.get("/getcourse", response_model=schemas.Course)
-async def course(request: Request, course_id: str = 'ttm4175', db: Session = Depends(get_db)):
+async def course(
+    request: Request, course_id: str = "ttm4175", db: Session = Depends(get_db)
+):
     if not is_logged_in(request):
         raise HTTPException(401, detail="You are not logged in ")
 
     course = crud.get_course(db, course_id=course_id)
-    if(course is None):
+    if course is None:
         raise HTTPException(404, detail="Course not found")
 
     return course
@@ -238,24 +247,31 @@ async def user(request: Request, db: Session = Depends(get_db)):
     user = request.session.get("user")
     email: str = user.get("eduPersonPrincipalName")
     user = crud.get_user(db, user_email=email)
-    if(user == None):
-        request.session.pop('user')
+    if user == None:
+        request.session.pop("user")
         raise HTTPException(404, detail="User not found")
-    
+
     return user
 
+
 # enroll user in course
 @app.post("/enroll_user", response_model=schemas.EnrollmentBase)
-async def enroll(request: Request, ref: schemas.EnrollmentCreate,  db: Session = Depends(get_db)):
+async def enroll(
+    request: Request, ref: schemas.EnrollmentCreate, db: Session = Depends(get_db)
+):
 
     if not is_logged_in(request):
         raise HTTPException(401, detail="You are not logged in ")
 
-    try: 
-        return crud.create_enrollment(db, role=ref.role, course_id=ref.course_id, user_email=ref.user_email)
+    try:
+        return crud.create_enrollment(
+            db, role=ref.role, course_id=ref.course_id, user_email=ref.user_email
+        )
     except IntegrityError:
-        raise HTTPException(409, detail="User already enrolled with this course and role")
-    
+        raise HTTPException(
+            409, detail="User already enrolled with this course and role"
+        )
+
 
 @app.post("/create_course", response_model=schemas.Course)
 async def create_reflection(
@@ -265,7 +281,7 @@ async def create_reflection(
         raise HTTPException(401, detail="You are not logged in")
     try:
         return crud.create_course(db, course=ref.dict())
-    
+
     except IntegrityError:
         raise HTTPException(409, detail="Course already exists")
 
diff --git a/backend/model.py b/backend/model.py
index 7172463..e842d6d 100644
--- a/backend/model.py
+++ b/backend/model.py
@@ -1,10 +1,9 @@
-from sqlalchemy import Boolean, Column, Date, ForeignKey, Integer, String, Enum
+from database import Base
+from sqlalchemy import Boolean, Column, Date, Enum, ForeignKey, Integer, String
 from sqlalchemy.orm import relationship
 from sqlalchemy.schema import ForeignKeyConstraint, Table
 
-from .database import Base
-
-#Course question (specify a relationship between a course and a question)
+# Course question (specify a relationship between a course and a question)
 course_question = Table(
     "course_question",
     Base.metadata,
@@ -12,7 +11,8 @@ course_question = Table(
     Column("question_id", ForeignKey("questions.id"), primary_key=True),
 )
 
-enum_values = Enum('lecturer', 'teaching assistant', 'student')
+enum_values = Enum("lecturer", "teaching assistant", "student")
+
 
 class Enrollment(Base):
     __tablename__ = "enrollment"
@@ -24,12 +24,11 @@ class Enrollment(Base):
     user = relationship("User", back_populates="enrollments")
     course = relationship("Course", back_populates="users")
 
+
 class User(Base):
     __tablename__ = "users"
-    email = Column(String, unique=True, primary_key = True)
-    enrollments = relationship(
-        "Enrollment", back_populates="user"
-    )
+    email = Column(String, unique=True, primary_key=True)
+    enrollments = relationship("Enrollment", back_populates="user")
     reflections = relationship("Reflection", back_populates="user")
 
 
@@ -42,11 +41,9 @@ class Course(Base):
     semester = Column(String)
     responsible = Column(String, default="")
     website = Column(String, default="")
-    
+
     units = relationship("Unit", back_populates="course")
-    users = relationship(
-        "Enrollment",back_populates="course"
-    )
+    users = relationship("Enrollment", back_populates="course")
     questions = relationship(
         "Question", secondary=course_question, back_populates="courses"
     )
@@ -89,4 +86,4 @@ class Reflection(Base):
     user = relationship("User", back_populates="reflections")
     unit_id = Column(Integer, ForeignKey("units.id"))
     unit = relationship("Unit", back_populates="reflections")
-    question_id = Column(Integer, ForeignKey("questions.id"))
\ No newline at end of file
+    question_id = Column(Integer, ForeignKey("questions.id"))
diff --git a/requirements.txt b/backend/requirements.txt
similarity index 94%
rename from requirements.txt
rename to backend/requirements.txt
index e2f985d..fd9e313 100644
--- a/requirements.txt
+++ b/backend/requirements.txt
@@ -14,3 +14,4 @@ databases
 aiosqlite
 asyncpg
 psycopg2
+alembic
diff --git a/docker-compose.yml b/docker-compose.yml
index 5afb791..08f8517 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -21,15 +21,17 @@ services:
     volumes:
       - db-data:/var/lib/postgresql/data/pgdata
     env_file:
-      - .env
+      - ./backend/.env
     environment:
       - PGDATA=/var/lib/postgresql/data/pgdata
   frontend:
-    image: reflect-frontend
+    image:
+      reflect-frontend
       # network_mode: "host"
     build:
       context: ./frontend
-      dockerfile: Dockerfile
+      dockerfile:
+        Dockerfile
         #    ports:
         #      - 5173:3000
     labels:
@@ -40,10 +42,11 @@ services:
       - "traefik.port=3000"
   backend:
     image: reflect
-      #network_mode: "host"
+    #network_mode: "host"
     build:
-      context: .
-      dockerfile: backend/Dockerfile
+      context: ./backend
+      dockerfile:
+        Dockerfile
         #    ports:
         #      - 8000:80
     env_file:
-- 
GitLab


From 95e4627b1e2a64a916a9b1f0233ae5e0f94200a1 Mon Sep 17 00:00:00 2001
From: theodorsm <theodor@midtlien.com>
Date: Wed, 15 Mar 2023 15:48:19 +0100
Subject: [PATCH 2/3] Resolved merge conflict

---
 README.md                  |    4 +-
 assistant-categorize.html  |  200 -----
 backend/database.py        |    2 +-
 backend/main.py            |   28 +-
 backend/model.py           |    2 +-
 docker-compose.yml         |    6 +-
 frontend/Dockerfile        |   12 +
 frontend/package-lock.json |  226 +++++
 frontend/package.json      |    1 +
 frontend/svelte.config.js  |    2 +-
 package-lock.json          | 1599 ------------------------------------
 package.json               |    7 -
 traefik.yaml               |    4 +-
 13 files changed, 265 insertions(+), 1828 deletions(-)
 delete mode 100644 assistant-categorize.html
 create mode 100644 frontend/Dockerfile
 delete mode 100644 package-lock.json
 delete mode 100644 package.json

diff --git a/README.md b/README.md
index f4f88e3..bd3c539 100644
--- a/README.md
+++ b/README.md
@@ -24,8 +24,6 @@ Environment:
 
 `.env` is not commited to this repo because of security, so this file has to be created.
 
-Create this .env file in the root folder!
-
 In the `backend` folder, create`.env` containing the following:
 - `production = false`
 - `SECRET_KEY` is set
@@ -92,6 +90,8 @@ In the `backend` folder, create`.env` containing the following:
 - `SECRET_KEY` is set
 - the client secrets corresponds to your feide instance
 - the following postgres variables is set: `POSTGRES_USER`, `POSTGRES_PASSWORD`,  `POSTGRES_DB`.
+- REDIRECT_URI = "https://ref2.iik.ntnu.no/auth"
+- BASE_URL = "https://ref2.iik.tnun.no"
 
 In `frontend` folder:
 - create `.env.production` with PUBLIC_API_URL="https://ref.iik.ntnu.no/api".
diff --git a/assistant-categorize.html b/assistant-categorize.html
deleted file mode 100644
index c438490..0000000
--- a/assistant-categorize.html
+++ /dev/null
@@ -1,200 +0,0 @@
-{% extends "base.html" %}
-{% block body %}
-<main class="flex-shrink-0">
-    <div class="container">
-
-        <div class="card mt-5 mb-5" style="width: 100%;">
-            <div class="card-header">
-                <div class="fs-5 fw-bold">
-                    Problems with state machines
-                </div>
-            </div>
-            <div class="list-group list-group-flush fst-italic">
-                <a href="#" class="list-group-item list-group-item-action" aria-current="true">
-                    The current link item
-                </a>
-                <a href="#" class="list-group-item list-group-item-action list-group-item-success">A second link item,
-                    flagged as interesting.</a>
-                <a href="#" class="list-group-item list-group-item-action muted">
-                    This is a text with a reflection of a user. I don't understand anything of what
-                    the lecture said.
-                </a>
-                <a href="#" class="list-group-item list-group-item-action list-group-item-danger">A fourth link item,
-                    flagged as problematic.</a>
-            </div>
-        </div>
-
-
-        <form class="mt-5 mb-5" action="store" method="post">
-            <div class="card mt-5 mb-5" style="width: 100%;">
-                <div class="card-header">
-                    Reflection 3 / 54
-                </div>
-                <ul class="list-group list-group-flush">
-                    <li class="list-group-item p-4">
-                        <p class="text-muted">
-                            <b>Clearest Insight:</b> What did you learn in this unit? What was your clearest insight, or
-                            your best learing
-                            achievement?
-                        </p>
-                        <textarea disabled placeholder="Write one or two sentences..."
-                            class="form-control bg-light border-1 disabled" id="best" name="best"
-                            rows="5">This is a text with a reflection of a user. I don't understand anything of what the lecture said. This is a text with a reflection of a user. I don't understand anything of what the lecture said.</textarea>
-                    </li>
-
-                    <li class="list-group-item p-4">
-
-                        <!--<p class="text-muted">Assign the reflection to a category.</p>-->
-
-                        <div class="row g-3 align-items-center flex-nowrap">
-                            <div class="col-auto">
-                                <input class="form-check-input" type="radio" name="category" id="flexRadioDefault2"
-                                    value="category_new" checked>
-                            </div>
-                            <div class="col-auto" style="width:80%">
-                                <input type="text" id="inputPassword6" class="form-control" name="new_category_text"
-                                    aria-describedby="passwordHelpInline" disabled value="Default Label (unassigned)">
-                            </div>
-                        </div>
-
-                        <div class="row g-3 align-items-center flex-nowrap mt-1">
-                            <div class="col-auto">
-                                <input class="form-check-input" type="radio" name="category" id="flexRadioDefault2"
-                                    value="category_new">
-                            </div>
-                            <div class="col-auto" style="width:80%">
-                                <input type="text" id="inputPassword6" class="form-control" name="new_category_text"
-                                    aria-describedby="passwordHelpInline" disabled
-                                    value="State machines and deferring elements">
-                            </div>
-                        </div>
-
-                        <div class="row g-3 align-items-center flex-nowrap mt-1">
-                            <div class="col-auto">
-                                <input class="form-check-input" type="radio" name="category" id="flexRadioDefault2"
-                                    value="category_new">
-                            </div>
-                            <div class="col-auto" style="width:80%">
-                                <input type="text" id="inputPassword6" class="form-control" name="new_category_text"
-                                    aria-describedby="passwordHelpInline" disabled value="Sequence diagrams">
-                            </div>
-                        </div>
-
-                        <div class="row g-3 align-items-center flex-nowrap mt-1">
-                            <div class="col-auto">
-                                <input class="form-check-input" type="radio" name="category" id="flexRadioDefault2"
-                                    value="category_new">
-                            </div>
-                            <div class="col-auto" style="width:80%">
-                                <input type="text" id="inputPassword6" class="form-control" name="new_category_text"
-                                    aria-describedby="passwordHelpInline" disabled
-                                    value="Could not install the software">
-                            </div>
-                        </div>
-
-                        <div class="row g-3 align-items-center flex-nowrap mt-1">
-                            <div class="col-auto">
-                                <input class="form-check-input" type="radio" name="category" id="flexRadioDefault2"
-                                    value="category_new">
-                            </div>
-                            <div class="col-auto" style="width:80%">
-                                <input type="text" id="inputPassword6" class="form-control" name="new_category_text"
-                                    aria-describedby="passwordHelpInline" placeholder="Create new...">
-                            </div>
-                        </div>
-                    </li>
-
-                    <li class="list-group-item p-4">
-                        <div class="form-check">
-                            <input class="form-check-input" type="checkbox" id="interesting" name="interesting">
-                            <label class="form-check-label" for="interesting">
-                                Flag as interesting or representative
-                            </label>
-                        </div>
-                        <div class="form-check">
-                            <input class="form-check-input" type="checkbox" id="problematic" name="problematic">
-                            <label class="form-check-label" for="problematic">
-                                Flag as problematic
-                            </label>
-                        </div>
-
-                    </li>
-
-
-
-                </ul>
-                <div class="card-footer p-4">
-                    <button type="submit" class="btn btn-primary shadow-sm end-0 position-sticky">Save</button>
-                    <button type="submit" class="btn btn-link end-0 position-sticky">Cancel</button>
-                </div>
-            </div>
-        </form>
-
-
-
-
-
-
-        <div class="list-group mt-5 mb-5">
-
-            <div class="list-group-item list-group-item-action">
-                <div class="d-flex w-100 justify-content-between">
-                    <h5 class="mb-1">Unit 1: State Machines</h5>
-                    <small class="text-muted">54 reflections</small>
-                </div>
-                <div class="d-flex w-100 justify-content-between">
-                    <a href="#">Læringsprestasjon</a>
-                    <small class="text-muted">3 unassigned</small>
-                </div>
-                <div class="d-flex w-100 justify-content-between">
-                    <a href="#">Vanskeligst</a>
-                    <small class="text-muted">3 unassigned</small>
-                </div>
-            </div>
-            <div class="list-group-item list-group-item-action">
-                <div class="d-flex w-100 justify-content-between">
-                    <h5 class="mb-1">Unit 2: State Machines</h5>
-                    <small class="text-muted">54 reflections</small>
-                </div>
-                <div class="d-flex w-100 justify-content-between">
-                    <a href="#">Læringsprestasjon</a>
-                    <small class="text-muted">3 unassigned</small>
-                </div>
-                <div class="d-flex w-100 justify-content-between">
-                    <a href="#">Vanskeligst</a>
-                    <small class="text-muted">3 unassigned</small>
-                </div>
-            </div>
-            <div class="list-group-item list-group-item-action">
-                <div class="d-flex w-100 justify-content-between">
-                    <h5 class="mb-1">Unit 3: State Machines</h5>
-                    <small class="text-muted">54 reflections</small>
-                </div>
-                <div class="d-flex w-100 justify-content-between">
-                    <a href="#">Læringsprestasjon</a>
-                    <small class="text-muted">3 unassigned</small>
-                </div>
-                <div class="d-flex w-100 justify-content-between">
-                    <a href="#">Vanskeligst</a>
-                    <small class="text-muted">3 unassigned</small>
-                </div>
-            </div>
-            <div class="list-group-item list-group-item-action">
-                <div class="d-flex w-100 justify-content-between">
-                    <h5 class="mb-1">Unit 4: State Machines</h5>
-                    <small class="text-muted">54 reflections</small>
-                </div>
-                <div class="d-flex w-100 justify-content-between">
-                    <a href="#">Læringsprestasjon</a>
-                    <small class="text-muted">3 unassigned</small>
-                </div>
-                <div class="d-flex w-100 justify-content-between">
-                    <a href="#">Vanskeligst</a>
-                    <small class="text-muted">3 unassigned</small>
-                </div>
-            </div>
-        </div>
-    </div>
-
-</main>
-{% endblock %}
\ No newline at end of file
diff --git a/backend/database.py b/backend/database.py
index a1f1ad9..bc14bba 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -11,7 +11,7 @@ postgres_pass = str(config("POSTGRES_PASSWORD", cast=Secret))
 
 if config("production", cast=bool, default=False):
     DATABASE_URL = (
-        f"postgresql://{postgres_user}:{postgres_pass}@reflect_db_1:5432/reflect"
+        f"postgresql://{postgres_user}:{postgres_pass}@ntnu_reflect_db_1:5432/reflect"
     )
     # database = databases.Database(DATABASE_URL)
     engine = create_engine(DATABASE_URL)
diff --git a/backend/main.py b/backend/main.py
index d3160c1..c1b5ef3 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -53,8 +53,8 @@ templates = Jinja2Templates(directory="templates")
 app.mount("/static", StaticFiles(directory="static"), name="static")
 
 if config("production", cast=bool, default=False):
-    REDIRECT_URI = "https://ref2.iik.ntnu.no/auth"
-    BASE_URL = "https://ref2.iik.ntnu.no"
+    REDIRECT_URI = config("REDIRECT_URI", cast=str)
+    BASE_URL = config("BASE_URL", cast=str)
 else:
     REDIRECT_URI = "http://localhost/auth"
     BASE_URL = "http://127.0.0.1:5173"
@@ -209,9 +209,12 @@ async def create_reflection(
     if not is_logged_in(request):
         raise HTTPException(401, detail="You are not logged in")
     number_of_questions = crud.get_number_of_unit_questions(db, ref.unit_id)
-    if len(crud.get_reflections(db, ref.user_id, ref.unit_id)) > number_of_questions - 1:
-         raise HTTPException(403, detail="You have already reflected this unit")
-        
+    if (
+        len(crud.get_reflections(db, ref.user_id, ref.unit_id))
+        > number_of_questions - 1
+    ):
+        raise HTTPException(403, detail="You have already reflected this unit")
+
     return crud.create_reflection(db, reflection=ref.dict())
 
 
@@ -285,21 +288,22 @@ async def create_reflection(
     except IntegrityError:
         raise HTTPException(409, detail="Course already exists")
 
+
 @app.post("/create_unit", response_model=schemas.Unit)
 async def create_unit(
     request: Request, ref: schemas.UnitCreate, db: Session = Depends(get_db)
 ):
     if not is_logged_in(request):
         raise HTTPException(401, detail="You are not logged in")
-    
+
     try:
         seq_number = int(ref.seq_no)
         return crud.create_unit(
-                db=db,
-                title=ref.title,
-                date_available=ref.date_available,
-                seq_no=seq_number,
-                course_id=ref.course_id,
-            )
+            db=db,
+            title=ref.title,
+            date_available=ref.date_available,
+            seq_no=seq_number,
+            course_id=ref.course_id,
+        )
     except IntegrityError:
         raise HTTPException(409, detail="unit already exists")
diff --git a/backend/model.py b/backend/model.py
index e842d6d..2dfe5d9 100644
--- a/backend/model.py
+++ b/backend/model.py
@@ -11,7 +11,7 @@ course_question = Table(
     Column("question_id", ForeignKey("questions.id"), primary_key=True),
 )
 
-enum_values = Enum("lecturer", "teaching assistant", "student")
+enum_values = Enum("lecturer", "teaching assistant", "student", name="enrollment_roles")
 
 
 class Enrollment(Base):
diff --git a/docker-compose.yml b/docker-compose.yml
index 08f8517..5aa8601 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -36,7 +36,7 @@ services:
         #      - 5173:3000
     labels:
       - "traefik.enable=true"
-      - "traefik.http.routers.frontend.rule=Host(`ref.iik.ntnu.no`)"
+      - "traefik.http.routers.frontend.rule=Host(`ref2.iik.ntnu.no`)"
       - "traefik.http.routers.frontend.tls=true"
       - "traefik.http.routers.frontend.entrypoints=websecure"
       - "traefik.port=3000"
@@ -50,10 +50,10 @@ services:
         #    ports:
         #      - 8000:80
     env_file:
-      - .env
+      - ./backend/.env
     labels:
       - "traefik.enable=true"
-      - "traefik.http.routers.backend.rule=Host(`ref.iik.ntnu.no`) && PathPrefix(`/api`) || Host(`ref.iik.ntnu.no`) && PathPrefix(`/auth`) "
+      - "traefik.http.routers.backend.rule=Host(`ref2.iik.ntnu.no`) && PathPrefix(`/api`) || Host(`ref2.iik.ntnu.no`) && PathPrefix(`/auth`) "
       - "traefik.http.routers.backend.tls=true"
       - "traefik.http.routers.backend.entrypoints=websecure"
       - "traefik.http.middlewares.backend-strip.stripprefix.prefixes=/api"
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
new file mode 100644
index 0000000..e2f43f0
--- /dev/null
+++ b/frontend/Dockerfile
@@ -0,0 +1,12 @@
+FROM node:16-alpine
+
+WORKDIR /app
+COPY . .
+RUN npm install
+RUN npm run build
+
+CMD ["node", "build"]
+# CMD ["npm", "run", "dev", "--" , "--host", "3000"]
+# CMD ["npm", "run", "dev", "--", "--host"]
+
+EXPOSE 3000
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index b569e88..4428caa 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -19,6 +19,7 @@
 				"@rgossiaux/svelte-headlessui": "^1.0.2",
 				"@skeletonlabs/skeleton": "^0.124.2",
 				"@sveltejs/adapter-auto": "^2.0.0",
+				"@sveltejs/adapter-node": "^1.2.2",
 				"@sveltejs/kit": "^1.5.0",
 				"@typescript-eslint/eslint-plugin": "^5.45.0",
 				"@typescript-eslint/parser": "^5.45.0",
@@ -553,6 +554,150 @@
 				"svelte": "^3.44.0"
 			}
 		},
+		"node_modules/@rollup/plugin-commonjs": {
+			"version": "24.0.1",
+			"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.1.tgz",
+			"integrity": "sha512-15LsiWRZk4eOGqvrJyu3z3DaBu5BhXIMeWnijSRvd8irrrg9SHpQ1pH+BUK4H6Z9wL9yOxZJMTLU+Au86XHxow==",
+			"dev": true,
+			"dependencies": {
+				"@rollup/pluginutils": "^5.0.1",
+				"commondir": "^1.0.1",
+				"estree-walker": "^2.0.2",
+				"glob": "^8.0.3",
+				"is-reference": "1.2.1",
+				"magic-string": "^0.27.0"
+			},
+			"engines": {
+				"node": ">=14.0.0"
+			},
+			"peerDependencies": {
+				"rollup": "^2.68.0||^3.0.0"
+			},
+			"peerDependenciesMeta": {
+				"rollup": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+			"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0"
+			}
+		},
+		"node_modules/@rollup/plugin-commonjs/node_modules/glob": {
+			"version": "8.1.0",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+			"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+			"dev": true,
+			"dependencies": {
+				"fs.realpath": "^1.0.0",
+				"inflight": "^1.0.4",
+				"inherits": "2",
+				"minimatch": "^5.0.1",
+				"once": "^1.3.0"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/@rollup/plugin-commonjs/node_modules/magic-string": {
+			"version": "0.27.0",
+			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
+			"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
+			"dev": true,
+			"dependencies": {
+				"@jridgewell/sourcemap-codec": "^1.4.13"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@rollup/plugin-commonjs/node_modules/minimatch": {
+			"version": "5.1.6",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+			"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/@rollup/plugin-json": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.0.0.tgz",
+			"integrity": "sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==",
+			"dev": true,
+			"dependencies": {
+				"@rollup/pluginutils": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=14.0.0"
+			},
+			"peerDependencies": {
+				"rollup": "^1.20.0||^2.0.0||^3.0.0"
+			},
+			"peerDependenciesMeta": {
+				"rollup": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@rollup/plugin-node-resolve": {
+			"version": "15.0.1",
+			"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz",
+			"integrity": "sha512-ReY88T7JhJjeRVbfCyNj+NXAG3IIsVMsX9b5/9jC98dRP8/yxlZdz7mHZbHk5zHr24wZZICS5AcXsFZAXYUQEg==",
+			"dev": true,
+			"dependencies": {
+				"@rollup/pluginutils": "^5.0.1",
+				"@types/resolve": "1.20.2",
+				"deepmerge": "^4.2.2",
+				"is-builtin-module": "^3.2.0",
+				"is-module": "^1.0.0",
+				"resolve": "^1.22.1"
+			},
+			"engines": {
+				"node": ">=14.0.0"
+			},
+			"peerDependencies": {
+				"rollup": "^2.78.0||^3.0.0"
+			},
+			"peerDependenciesMeta": {
+				"rollup": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@rollup/pluginutils": {
+			"version": "5.0.2",
+			"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz",
+			"integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==",
+			"dev": true,
+			"dependencies": {
+				"@types/estree": "^1.0.0",
+				"estree-walker": "^2.0.2",
+				"picomatch": "^2.3.1"
+			},
+			"engines": {
+				"node": ">=14.0.0"
+			},
+			"peerDependencies": {
+				"rollup": "^1.20.0||^2.0.0||^3.0.0"
+			},
+			"peerDependenciesMeta": {
+				"rollup": {
+					"optional": true
+				}
+			}
+		},
 		"node_modules/@skeletonlabs/skeleton": {
 			"version": "0.124.2",
 			"resolved": "https://registry.npmjs.org/@skeletonlabs/skeleton/-/skeleton-0.124.2.tgz",
@@ -574,6 +719,21 @@
 				"@sveltejs/kit": "^1.0.0"
 			}
 		},
+		"node_modules/@sveltejs/adapter-node": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-1.2.2.tgz",
+			"integrity": "sha512-HA+QPRve97ZAQfZ0wkHjtoSR9ilyYZV82T7zRgzm2WJ0rIpAgyRYf6K7KNwiCq23KKkN0hZHY5elNyCnVz8zrA==",
+			"dev": true,
+			"dependencies": {
+				"@rollup/plugin-commonjs": "^24.0.0",
+				"@rollup/plugin-json": "^6.0.0",
+				"@rollup/plugin-node-resolve": "^15.0.1",
+				"rollup": "^3.7.0"
+			},
+			"peerDependencies": {
+				"@sveltejs/kit": "^1.0.0"
+			}
+		},
 		"node_modules/@sveltejs/kit": {
 			"version": "1.7.1",
 			"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.7.1.tgz",
@@ -660,6 +820,12 @@
 			"integrity": "sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==",
 			"dev": true
 		},
+		"node_modules/@types/estree": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
+			"integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==",
+			"dev": true
+		},
 		"node_modules/@types/json-schema": {
 			"version": "7.0.11",
 			"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@@ -678,6 +844,12 @@
 			"integrity": "sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==",
 			"dev": true
 		},
+		"node_modules/@types/resolve": {
+			"version": "1.20.2",
+			"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
+			"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
+			"dev": true
+		},
 		"node_modules/@types/sass": {
 			"version": "1.43.1",
 			"resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.43.1.tgz",
@@ -1124,6 +1296,18 @@
 				"node": "*"
 			}
 		},
+		"node_modules/builtin-modules": {
+			"version": "3.3.0",
+			"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
+			"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
 		"node_modules/busboy": {
 			"version": "1.6.0",
 			"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -1275,6 +1459,12 @@
 			"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
 			"dev": true
 		},
+		"node_modules/commondir": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+			"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+			"dev": true
+		},
 		"node_modules/concat-map": {
 			"version": "0.0.1",
 			"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -1730,6 +1920,12 @@
 				"node": ">=4.0"
 			}
 		},
+		"node_modules/estree-walker": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+			"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+			"dev": true
+		},
 		"node_modules/esutils": {
 			"version": "2.0.3",
 			"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -2114,6 +2310,21 @@
 				"node": ">=8"
 			}
 		},
+		"node_modules/is-builtin-module": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
+			"integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
+			"dev": true,
+			"dependencies": {
+				"builtin-modules": "^3.3.0"
+			},
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
 		"node_modules/is-core-module": {
 			"version": "2.11.0",
 			"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
@@ -2147,6 +2358,12 @@
 				"node": ">=0.10.0"
 			}
 		},
+		"node_modules/is-module": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
+			"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
+			"dev": true
+		},
 		"node_modules/is-number": {
 			"version": "7.0.0",
 			"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -2165,6 +2382,15 @@
 				"node": ">=8"
 			}
 		},
+		"node_modules/is-reference": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
+			"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
+			"dev": true,
+			"dependencies": {
+				"@types/estree": "*"
+			}
+		},
 		"node_modules/isexe": {
 			"version": "2.0.0",
 			"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 2b0f28f..e5cc464 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -19,6 +19,7 @@
 		"@rgossiaux/svelte-headlessui": "^1.0.2",
 		"@skeletonlabs/skeleton": "^0.124.2",
 		"@sveltejs/adapter-auto": "^2.0.0",
+		"@sveltejs/adapter-node": "^1.2.2",
 		"@sveltejs/kit": "^1.5.0",
 		"@typescript-eslint/eslint-plugin": "^5.45.0",
 		"@typescript-eslint/parser": "^5.45.0",
diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js
index 0508916..42320d0 100644
--- a/frontend/svelte.config.js
+++ b/frontend/svelte.config.js
@@ -1,4 +1,4 @@
-import adapter from '@sveltejs/adapter-auto';
+import adapter from '@sveltejs/adapter-node';
 import { vitePreprocess } from '@sveltejs/kit/vite';
 
 /** @type {import('@sveltejs/kit').Config} */
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index 7e8b16b..0000000
--- a/package-lock.json
+++ /dev/null
@@ -1,1599 +0,0 @@
-{
-  "name": "NTNU_reflect",
-  "lockfileVersion": 2,
-  "requires": true,
-  "packages": {
-    "": {
-      "devDependencies": {
-        "@tailwindcss/forms": "^0.5.3",
-        "prettier": "^2.8.4",
-        "prettier-plugin-tailwindcss": "^0.2.3"
-      }
-    },
-    "node_modules/@nodelib/fs.scandir": {
-      "version": "2.1.5",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
-      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "@nodelib/fs.stat": "2.0.5",
-        "run-parallel": "^1.1.9"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@nodelib/fs.stat": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
-      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@nodelib/fs.walk": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
-      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "@nodelib/fs.scandir": "2.1.5",
-        "fastq": "^1.6.0"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@tailwindcss/forms": {
-      "version": "0.5.3",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.3.tgz",
-      "integrity": "sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==",
-      "dev": true,
-      "dependencies": {
-        "mini-svg-data-uri": "^1.2.3"
-      },
-      "peerDependencies": {
-        "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1"
-      }
-    },
-    "node_modules/acorn": {
-      "version": "7.4.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
-      "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
-      "dev": true,
-      "peer": true,
-      "bin": {
-        "acorn": "bin/acorn"
-      },
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/acorn-node": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
-      "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "acorn": "^7.0.0",
-        "acorn-walk": "^7.0.0",
-        "xtend": "^4.0.2"
-      }
-    },
-    "node_modules/acorn-walk": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
-      "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/anymatch": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
-      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "normalize-path": "^3.0.0",
-        "picomatch": "^2.0.4"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/arg": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
-      "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
-      "dev": true,
-      "peer": true
-    },
-    "node_modules/binary-extensions": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
-      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "fill-range": "^7.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/camelcase-css": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
-      "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/chokidar": {
-      "version": "3.5.3",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
-      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://paulmillr.com/funding/"
-        }
-      ],
-      "peer": true,
-      "dependencies": {
-        "anymatch": "~3.1.2",
-        "braces": "~3.0.2",
-        "glob-parent": "~5.1.2",
-        "is-binary-path": "~2.1.0",
-        "is-glob": "~4.0.1",
-        "normalize-path": "~3.0.0",
-        "readdirp": "~3.6.0"
-      },
-      "engines": {
-        "node": ">= 8.10.0"
-      },
-      "optionalDependencies": {
-        "fsevents": "~2.3.2"
-      }
-    },
-    "node_modules/chokidar/node_modules/glob-parent": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "is-glob": "^4.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/color-name": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "dev": true,
-      "peer": true
-    },
-    "node_modules/cssesc": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
-      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
-      "dev": true,
-      "peer": true,
-      "bin": {
-        "cssesc": "bin/cssesc"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/defined": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz",
-      "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==",
-      "dev": true,
-      "peer": true,
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/detective": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz",
-      "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "acorn-node": "^1.8.2",
-        "defined": "^1.0.0",
-        "minimist": "^1.2.6"
-      },
-      "bin": {
-        "detective": "bin/detective.js"
-      },
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
-    "node_modules/didyoumean": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
-      "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
-      "dev": true,
-      "peer": true
-    },
-    "node_modules/dlv": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
-      "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
-      "dev": true,
-      "peer": true
-    },
-    "node_modules/fast-glob": {
-      "version": "3.2.12",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
-      "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "@nodelib/fs.stat": "^2.0.2",
-        "@nodelib/fs.walk": "^1.2.3",
-        "glob-parent": "^5.1.2",
-        "merge2": "^1.3.0",
-        "micromatch": "^4.0.4"
-      },
-      "engines": {
-        "node": ">=8.6.0"
-      }
-    },
-    "node_modules/fast-glob/node_modules/glob-parent": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "is-glob": "^4.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/fastq": {
-      "version": "1.15.0",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
-      "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "reusify": "^1.0.4"
-      }
-    },
-    "node_modules/fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "to-regex-range": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/fsevents": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
-      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
-      "dev": true,
-      "hasInstallScript": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "peer": true,
-      "engines": {
-        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
-      }
-    },
-    "node_modules/function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true,
-      "peer": true
-    },
-    "node_modules/glob-parent": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
-      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "is-glob": "^4.0.3"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      }
-    },
-    "node_modules/has": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "function-bind": "^1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.4.0"
-      }
-    },
-    "node_modules/is-binary-path": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
-      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "binary-extensions": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-core-module": {
-      "version": "2.11.0",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
-      "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "has": "^1.0.3"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-glob": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
-      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "is-extglob": "^2.1.1"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-number": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
-      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">=0.12.0"
-      }
-    },
-    "node_modules/lilconfig": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
-      "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/merge2": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
-      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/micromatch": {
-      "version": "4.0.5",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
-      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "braces": "^3.0.2",
-        "picomatch": "^2.3.1"
-      },
-      "engines": {
-        "node": ">=8.6"
-      }
-    },
-    "node_modules/mini-svg-data-uri": {
-      "version": "1.4.4",
-      "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
-      "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
-      "dev": true,
-      "bin": {
-        "mini-svg-data-uri": "cli.js"
-      }
-    },
-    "node_modules/minimist": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
-      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
-      "dev": true,
-      "peer": true,
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/nanoid": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
-      "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
-      "dev": true,
-      "peer": true,
-      "bin": {
-        "nanoid": "bin/nanoid.cjs"
-      },
-      "engines": {
-        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
-      }
-    },
-    "node_modules/normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/object-hash": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
-      "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/path-parse": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
-      "dev": true,
-      "peer": true
-    },
-    "node_modules/picocolors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
-      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
-      "dev": true,
-      "peer": true
-    },
-    "node_modules/picomatch": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
-      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">=8.6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/jonschlinkert"
-      }
-    },
-    "node_modules/pify": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-      "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/postcss": {
-      "version": "8.4.21",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
-      "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/postcss/"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/postcss"
-        }
-      ],
-      "peer": true,
-      "dependencies": {
-        "nanoid": "^3.3.4",
-        "picocolors": "^1.0.0",
-        "source-map-js": "^1.0.2"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14"
-      }
-    },
-    "node_modules/postcss-import": {
-      "version": "14.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz",
-      "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "postcss-value-parser": "^4.0.0",
-        "read-cache": "^1.0.0",
-        "resolve": "^1.1.7"
-      },
-      "engines": {
-        "node": ">=10.0.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.0.0"
-      }
-    },
-    "node_modules/postcss-js": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
-      "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "camelcase-css": "^2.0.1"
-      },
-      "engines": {
-        "node": "^12 || ^14 || >= 16"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/postcss/"
-      },
-      "peerDependencies": {
-        "postcss": "^8.4.21"
-      }
-    },
-    "node_modules/postcss-load-config": {
-      "version": "3.1.4",
-      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
-      "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "lilconfig": "^2.0.5",
-        "yaml": "^1.10.2"
-      },
-      "engines": {
-        "node": ">= 10"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/postcss/"
-      },
-      "peerDependencies": {
-        "postcss": ">=8.0.9",
-        "ts-node": ">=9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "postcss": {
-          "optional": true
-        },
-        "ts-node": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/postcss-nested": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz",
-      "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "postcss-selector-parser": "^6.0.10"
-      },
-      "engines": {
-        "node": ">=12.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/postcss/"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.14"
-      }
-    },
-    "node_modules/postcss-selector-parser": {
-      "version": "6.0.11",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz",
-      "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/postcss-value-parser": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
-      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
-      "dev": true,
-      "peer": true
-    },
-    "node_modules/prettier": {
-      "version": "2.8.4",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
-      "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
-      "dev": true,
-      "bin": {
-        "prettier": "bin-prettier.js"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      },
-      "funding": {
-        "url": "https://github.com/prettier/prettier?sponsor=1"
-      }
-    },
-    "node_modules/prettier-plugin-tailwindcss": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.2.3.tgz",
-      "integrity": "sha512-s2N5Dh7Ao5KTV1mao5ZBnn8EKtUcDPJEkGViZIjI0Ij9TTI5zgTz4IHOxW33jOdjHKa8CSjM88scelUiC5TNRQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=12.17.0"
-      },
-      "peerDependencies": {
-        "@ianvs/prettier-plugin-sort-imports": "*",
-        "@prettier/plugin-php": "*",
-        "@prettier/plugin-pug": "*",
-        "@shopify/prettier-plugin-liquid": "*",
-        "@shufo/prettier-plugin-blade": "*",
-        "@trivago/prettier-plugin-sort-imports": "*",
-        "prettier": ">=2.2.0",
-        "prettier-plugin-astro": "*",
-        "prettier-plugin-css-order": "*",
-        "prettier-plugin-import-sort": "*",
-        "prettier-plugin-jsdoc": "*",
-        "prettier-plugin-organize-attributes": "*",
-        "prettier-plugin-organize-imports": "*",
-        "prettier-plugin-style-order": "*",
-        "prettier-plugin-svelte": "*",
-        "prettier-plugin-twig-melody": "*"
-      },
-      "peerDependenciesMeta": {
-        "@ianvs/prettier-plugin-sort-imports": {
-          "optional": true
-        },
-        "@prettier/plugin-php": {
-          "optional": true
-        },
-        "@prettier/plugin-pug": {
-          "optional": true
-        },
-        "@shopify/prettier-plugin-liquid": {
-          "optional": true
-        },
-        "@shufo/prettier-plugin-blade": {
-          "optional": true
-        },
-        "@trivago/prettier-plugin-sort-imports": {
-          "optional": true
-        },
-        "prettier-plugin-astro": {
-          "optional": true
-        },
-        "prettier-plugin-css-order": {
-          "optional": true
-        },
-        "prettier-plugin-import-sort": {
-          "optional": true
-        },
-        "prettier-plugin-jsdoc": {
-          "optional": true
-        },
-        "prettier-plugin-organize-attributes": {
-          "optional": true
-        },
-        "prettier-plugin-organize-imports": {
-          "optional": true
-        },
-        "prettier-plugin-style-order": {
-          "optional": true
-        },
-        "prettier-plugin-svelte": {
-          "optional": true
-        },
-        "prettier-plugin-twig-melody": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/queue-microtask": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
-      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "peer": true
-    },
-    "node_modules/quick-lru": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
-      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/read-cache": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
-      "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "pify": "^2.3.0"
-      }
-    },
-    "node_modules/readdirp": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
-      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "picomatch": "^2.2.1"
-      },
-      "engines": {
-        "node": ">=8.10.0"
-      }
-    },
-    "node_modules/resolve": {
-      "version": "1.22.1",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
-      "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "is-core-module": "^2.9.0",
-        "path-parse": "^1.0.7",
-        "supports-preserve-symlinks-flag": "^1.0.0"
-      },
-      "bin": {
-        "resolve": "bin/resolve"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/reusify": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
-      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "iojs": ">=1.0.0",
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/run-parallel": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
-      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "peer": true,
-      "dependencies": {
-        "queue-microtask": "^1.2.2"
-      }
-    },
-    "node_modules/source-map-js": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
-      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/supports-preserve-symlinks-flag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
-      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/tailwindcss": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.7.tgz",
-      "integrity": "sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "arg": "^5.0.2",
-        "chokidar": "^3.5.3",
-        "color-name": "^1.1.4",
-        "detective": "^5.2.1",
-        "didyoumean": "^1.2.2",
-        "dlv": "^1.1.3",
-        "fast-glob": "^3.2.12",
-        "glob-parent": "^6.0.2",
-        "is-glob": "^4.0.3",
-        "lilconfig": "^2.0.6",
-        "micromatch": "^4.0.5",
-        "normalize-path": "^3.0.0",
-        "object-hash": "^3.0.0",
-        "picocolors": "^1.0.0",
-        "postcss": "^8.0.9",
-        "postcss-import": "^14.1.0",
-        "postcss-js": "^4.0.0",
-        "postcss-load-config": "^3.1.4",
-        "postcss-nested": "6.0.0",
-        "postcss-selector-parser": "^6.0.11",
-        "postcss-value-parser": "^4.2.0",
-        "quick-lru": "^5.1.1",
-        "resolve": "^1.22.1"
-      },
-      "bin": {
-        "tailwind": "lib/cli.js",
-        "tailwindcss": "lib/cli.js"
-      },
-      "engines": {
-        "node": ">=12.13.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.0.9"
-      }
-    },
-    "node_modules/to-regex-range": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "is-number": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=8.0"
-      }
-    },
-    "node_modules/util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
-      "dev": true,
-      "peer": true
-    },
-    "node_modules/xtend": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
-      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">=0.4"
-      }
-    },
-    "node_modules/yaml": {
-      "version": "1.10.2",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
-      "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
-      "dev": true,
-      "peer": true,
-      "engines": {
-        "node": ">= 6"
-      }
-    }
-  },
-  "dependencies": {
-    "@nodelib/fs.scandir": {
-      "version": "2.1.5",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
-      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "@nodelib/fs.stat": "2.0.5",
-        "run-parallel": "^1.1.9"
-      }
-    },
-    "@nodelib/fs.stat": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
-      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
-      "dev": true,
-      "peer": true
-    },
-    "@nodelib/fs.walk": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
-      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "@nodelib/fs.scandir": "2.1.5",
-        "fastq": "^1.6.0"
-      }
-    },
-    "@tailwindcss/forms": {
-      "version": "0.5.3",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.3.tgz",
-      "integrity": "sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==",
-      "dev": true,
-      "requires": {
-        "mini-svg-data-uri": "^1.2.3"
-      }
-    },
-    "acorn": {
-      "version": "7.4.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
-      "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
-      "dev": true,
-      "peer": true
-    },
-    "acorn-node": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
-      "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "acorn": "^7.0.0",
-        "acorn-walk": "^7.0.0",
-        "xtend": "^4.0.2"
-      }
-    },
-    "acorn-walk": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
-      "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
-      "dev": true,
-      "peer": true
-    },
-    "anymatch": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
-      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "normalize-path": "^3.0.0",
-        "picomatch": "^2.0.4"
-      }
-    },
-    "arg": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
-      "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
-      "dev": true,
-      "peer": true
-    },
-    "binary-extensions": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
-      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
-      "dev": true,
-      "peer": true
-    },
-    "braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "fill-range": "^7.0.1"
-      }
-    },
-    "camelcase-css": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
-      "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
-      "dev": true,
-      "peer": true
-    },
-    "chokidar": {
-      "version": "3.5.3",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
-      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "anymatch": "~3.1.2",
-        "braces": "~3.0.2",
-        "fsevents": "~2.3.2",
-        "glob-parent": "~5.1.2",
-        "is-binary-path": "~2.1.0",
-        "is-glob": "~4.0.1",
-        "normalize-path": "~3.0.0",
-        "readdirp": "~3.6.0"
-      },
-      "dependencies": {
-        "glob-parent": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-          "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-          "dev": true,
-          "peer": true,
-          "requires": {
-            "is-glob": "^4.0.1"
-          }
-        }
-      }
-    },
-    "color-name": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "dev": true,
-      "peer": true
-    },
-    "cssesc": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
-      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
-      "dev": true,
-      "peer": true
-    },
-    "defined": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz",
-      "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==",
-      "dev": true,
-      "peer": true
-    },
-    "detective": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz",
-      "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "acorn-node": "^1.8.2",
-        "defined": "^1.0.0",
-        "minimist": "^1.2.6"
-      }
-    },
-    "didyoumean": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
-      "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
-      "dev": true,
-      "peer": true
-    },
-    "dlv": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
-      "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
-      "dev": true,
-      "peer": true
-    },
-    "fast-glob": {
-      "version": "3.2.12",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
-      "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "@nodelib/fs.stat": "^2.0.2",
-        "@nodelib/fs.walk": "^1.2.3",
-        "glob-parent": "^5.1.2",
-        "merge2": "^1.3.0",
-        "micromatch": "^4.0.4"
-      },
-      "dependencies": {
-        "glob-parent": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-          "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-          "dev": true,
-          "peer": true,
-          "requires": {
-            "is-glob": "^4.0.1"
-          }
-        }
-      }
-    },
-    "fastq": {
-      "version": "1.15.0",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
-      "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "reusify": "^1.0.4"
-      }
-    },
-    "fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "to-regex-range": "^5.0.1"
-      }
-    },
-    "fsevents": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
-      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
-      "dev": true,
-      "optional": true,
-      "peer": true
-    },
-    "function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true,
-      "peer": true
-    },
-    "glob-parent": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
-      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "is-glob": "^4.0.3"
-      }
-    },
-    "has": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "function-bind": "^1.1.1"
-      }
-    },
-    "is-binary-path": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
-      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "binary-extensions": "^2.0.0"
-      }
-    },
-    "is-core-module": {
-      "version": "2.11.0",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
-      "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "has": "^1.0.3"
-      }
-    },
-    "is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
-      "dev": true,
-      "peer": true
-    },
-    "is-glob": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
-      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "is-extglob": "^2.1.1"
-      }
-    },
-    "is-number": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
-      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true,
-      "peer": true
-    },
-    "lilconfig": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
-      "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
-      "dev": true,
-      "peer": true
-    },
-    "merge2": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
-      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
-      "dev": true,
-      "peer": true
-    },
-    "micromatch": {
-      "version": "4.0.5",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
-      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "braces": "^3.0.2",
-        "picomatch": "^2.3.1"
-      }
-    },
-    "mini-svg-data-uri": {
-      "version": "1.4.4",
-      "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
-      "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
-      "dev": true
-    },
-    "minimist": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
-      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
-      "dev": true,
-      "peer": true
-    },
-    "nanoid": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
-      "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
-      "dev": true,
-      "peer": true
-    },
-    "normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "dev": true,
-      "peer": true
-    },
-    "object-hash": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
-      "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
-      "dev": true,
-      "peer": true
-    },
-    "path-parse": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
-      "dev": true,
-      "peer": true
-    },
-    "picocolors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
-      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
-      "dev": true,
-      "peer": true
-    },
-    "picomatch": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
-      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-      "dev": true,
-      "peer": true
-    },
-    "pify": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-      "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
-      "dev": true,
-      "peer": true
-    },
-    "postcss": {
-      "version": "8.4.21",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
-      "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "nanoid": "^3.3.4",
-        "picocolors": "^1.0.0",
-        "source-map-js": "^1.0.2"
-      }
-    },
-    "postcss-import": {
-      "version": "14.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz",
-      "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "postcss-value-parser": "^4.0.0",
-        "read-cache": "^1.0.0",
-        "resolve": "^1.1.7"
-      }
-    },
-    "postcss-js": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
-      "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "camelcase-css": "^2.0.1"
-      }
-    },
-    "postcss-load-config": {
-      "version": "3.1.4",
-      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
-      "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "lilconfig": "^2.0.5",
-        "yaml": "^1.10.2"
-      }
-    },
-    "postcss-nested": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz",
-      "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "postcss-selector-parser": "^6.0.10"
-      }
-    },
-    "postcss-selector-parser": {
-      "version": "6.0.11",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz",
-      "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
-      }
-    },
-    "postcss-value-parser": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
-      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
-      "dev": true,
-      "peer": true
-    },
-    "prettier": {
-      "version": "2.8.4",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
-      "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
-      "dev": true
-    },
-    "prettier-plugin-tailwindcss": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.2.3.tgz",
-      "integrity": "sha512-s2N5Dh7Ao5KTV1mao5ZBnn8EKtUcDPJEkGViZIjI0Ij9TTI5zgTz4IHOxW33jOdjHKa8CSjM88scelUiC5TNRQ==",
-      "dev": true,
-      "requires": {}
-    },
-    "queue-microtask": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
-      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
-      "dev": true,
-      "peer": true
-    },
-    "quick-lru": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
-      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
-      "dev": true,
-      "peer": true
-    },
-    "read-cache": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
-      "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "pify": "^2.3.0"
-      }
-    },
-    "readdirp": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
-      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "picomatch": "^2.2.1"
-      }
-    },
-    "resolve": {
-      "version": "1.22.1",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
-      "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "is-core-module": "^2.9.0",
-        "path-parse": "^1.0.7",
-        "supports-preserve-symlinks-flag": "^1.0.0"
-      }
-    },
-    "reusify": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
-      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
-      "dev": true,
-      "peer": true
-    },
-    "run-parallel": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
-      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "queue-microtask": "^1.2.2"
-      }
-    },
-    "source-map-js": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
-      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
-      "dev": true,
-      "peer": true
-    },
-    "supports-preserve-symlinks-flag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
-      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
-      "dev": true,
-      "peer": true
-    },
-    "tailwindcss": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.7.tgz",
-      "integrity": "sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "arg": "^5.0.2",
-        "chokidar": "^3.5.3",
-        "color-name": "^1.1.4",
-        "detective": "^5.2.1",
-        "didyoumean": "^1.2.2",
-        "dlv": "^1.1.3",
-        "fast-glob": "^3.2.12",
-        "glob-parent": "^6.0.2",
-        "is-glob": "^4.0.3",
-        "lilconfig": "^2.0.6",
-        "micromatch": "^4.0.5",
-        "normalize-path": "^3.0.0",
-        "object-hash": "^3.0.0",
-        "picocolors": "^1.0.0",
-        "postcss": "^8.0.9",
-        "postcss-import": "^14.1.0",
-        "postcss-js": "^4.0.0",
-        "postcss-load-config": "^3.1.4",
-        "postcss-nested": "6.0.0",
-        "postcss-selector-parser": "^6.0.11",
-        "postcss-value-parser": "^4.2.0",
-        "quick-lru": "^5.1.1",
-        "resolve": "^1.22.1"
-      }
-    },
-    "to-regex-range": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "is-number": "^7.0.0"
-      }
-    },
-    "util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
-      "dev": true,
-      "peer": true
-    },
-    "xtend": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
-      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
-      "dev": true,
-      "peer": true
-    },
-    "yaml": {
-      "version": "1.10.2",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
-      "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
-      "dev": true,
-      "peer": true
-    }
-  }
-}
diff --git a/package.json b/package.json
deleted file mode 100644
index c8b9ed3..0000000
--- a/package.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "devDependencies": {
-    "@tailwindcss/forms": "^0.5.3",
-    "prettier": "^2.8.4",
-    "prettier-plugin-tailwindcss": "^0.2.3"
-  }
-}
diff --git a/traefik.yaml b/traefik.yaml
index 32367b7..0726d13 100644
--- a/traefik.yaml
+++ b/traefik.yaml
@@ -45,8 +45,8 @@ entryPoints:
 
 tls:
   certificates:
-    - certFile: /certs/ref_iik_ntnu_no.pem
-      keyFile: /certs/PRIV_NONPASS.key
+    - certFile: /certs/ref2_iik_ntnu_no.pem
+      keyFile: /certs/ref2PRIVATEKEY_nopass.key
 # when testing certs, enable this so traefik doesn't use 
 # it's own self signed. By default if it can't find a matching
 # cert, it'll just create it's own which will cause cert warnings
-- 
GitLab


From d8fdf1756ff6929e93efdc1c1900da6830e4c853 Mon Sep 17 00:00:00 2001
From: theodorsm <theodor@midtlien.com>
Date: Wed, 15 Mar 2023 18:07:16 +0100
Subject: [PATCH 3/3] Fix migrations

---
 backend/alembic.ini                           |  2 +-
 backend/alembic/env.py                        |  9 +-
 backend/alembic/versions/9b1fabdaa344_init.py | 92 +++++++++++++++++++
 .../versions/aa01c8290072_first_revision.py   | 92 -------------------
 4 files changed, 100 insertions(+), 95 deletions(-)
 create mode 100644 backend/alembic/versions/9b1fabdaa344_init.py
 delete mode 100644 backend/alembic/versions/aa01c8290072_first_revision.py

diff --git a/backend/alembic.ini b/backend/alembic.ini
index 00dd9ea..42cab18 100644
--- a/backend/alembic.ini
+++ b/backend/alembic.ini
@@ -60,7 +60,7 @@ version_path_separator = os  # Use os.pathsep. Default configuration used for ne
 # are written from script.py.mako
 # output_encoding = utf-8
 
-# sqlalchemy.url = driver://user:pass@localhost/dbname
+sqlalchemy.url = sqlite:///./reflect.db
 
 
 [post_write_hooks]
diff --git a/backend/alembic/env.py b/backend/alembic/env.py
index 6d1b469..c00e201 100644
--- a/backend/alembic/env.py
+++ b/backend/alembic/env.py
@@ -1,8 +1,11 @@
 from logging.config import fileConfig
 
+from sqlalchemy import engine_from_config
+from sqlalchemy import pool
+
+from database import DATABASE_URL
+
 from alembic import context
-from database import DATABASE_URL, Base
-from sqlalchemy import engine_from_config, pool
 
 # this is the Alembic Config object, which provides
 # access to the values within the .ini file in use.
@@ -18,6 +21,8 @@ if config.config_file_name is not None:
 # for 'autogenerate' support
 # from myapp import mymodel
 # target_metadata = mymodel.Base.metadata
+from model import Base
+
 target_metadata = Base.metadata
 
 # other values from the config, defined by the needs of env.py,
diff --git a/backend/alembic/versions/9b1fabdaa344_init.py b/backend/alembic/versions/9b1fabdaa344_init.py
new file mode 100644
index 0000000..465fc4d
--- /dev/null
+++ b/backend/alembic/versions/9b1fabdaa344_init.py
@@ -0,0 +1,92 @@
+"""Init
+
+Revision ID: 9b1fabdaa344
+Revises: 
+Create Date: 2023-03-15 18:04:12.205139
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '9b1fabdaa344'
+down_revision = None
+branch_labels = None
+depends_on = None
+
+
+def upgrade() -> None:
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.create_table('courses',
+    sa.Column('name', sa.String(), nullable=True),
+    sa.Column('id', sa.String(), nullable=False),
+    sa.Column('semester', sa.String(), nullable=True),
+    sa.Column('responsible', sa.String(), nullable=True),
+    sa.Column('website', sa.String(), nullable=True),
+    sa.PrimaryKeyConstraint('id')
+    )
+    op.create_table('questions',
+    sa.Column('id', sa.Integer(), nullable=False),
+    sa.Column('question', sa.String(), nullable=True),
+    sa.Column('comment', sa.String(), nullable=True),
+    sa.PrimaryKeyConstraint('id')
+    )
+    op.create_table('users',
+    sa.Column('email', sa.String(), nullable=False),
+    sa.PrimaryKeyConstraint('email'),
+    sa.UniqueConstraint('email')
+    )
+    op.create_table('course_question',
+    sa.Column('course_id', sa.String(), nullable=False),
+    sa.Column('question_id', sa.Integer(), nullable=False),
+    sa.ForeignKeyConstraint(['course_id'], ['courses.id'], ),
+    sa.ForeignKeyConstraint(['question_id'], ['questions.id'], ),
+    sa.PrimaryKeyConstraint('course_id', 'question_id')
+    )
+    op.create_table('enrollment',
+    sa.Column('user_email', sa.String(), nullable=False),
+    sa.Column('course_id', sa.String(), nullable=False),
+    sa.Column('role', sa.Enum('lecturer', 'teaching assistant', 'student', name='enrollment_roles'), nullable=False),
+    sa.ForeignKeyConstraint(['course_id'], ['courses.id'], ),
+    sa.ForeignKeyConstraint(['user_email'], ['users.email'], ),
+    sa.PrimaryKeyConstraint('user_email', 'course_id', 'role')
+    )
+    op.create_table('units',
+    sa.Column('id', sa.Integer(), nullable=False),
+    sa.Column('seq_no', sa.Integer(), nullable=True),
+    sa.Column('title', sa.String(), nullable=True),
+    sa.Column('date_available', sa.Date(), nullable=True),
+    sa.Column('course_id', sa.String(), nullable=True),
+    sa.ForeignKeyConstraint(['course_id'], ['courses.id'], ),
+    sa.PrimaryKeyConstraint('id')
+    )
+    op.create_table('reflections',
+    sa.Column('id', sa.Integer(), nullable=False),
+    sa.Column('body', sa.String(), nullable=True),
+    sa.Column('timestamp', sa.Date(), nullable=True),
+    sa.Column('category', sa.String(), nullable=True),
+    sa.Column('is_interesting', sa.Boolean(), nullable=True),
+    sa.Column('is_problematic', sa.Boolean(), nullable=True),
+    sa.Column('is_sorted', sa.Boolean(), nullable=True),
+    sa.Column('user_id', sa.String(), nullable=True),
+    sa.Column('unit_id', sa.Integer(), nullable=True),
+    sa.Column('question_id', sa.Integer(), nullable=True),
+    sa.ForeignKeyConstraint(['question_id'], ['questions.id'], ),
+    sa.ForeignKeyConstraint(['unit_id'], ['units.id'], ),
+    sa.ForeignKeyConstraint(['user_id'], ['users.email'], ),
+    sa.PrimaryKeyConstraint('id')
+    )
+    # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_table('reflections')
+    op.drop_table('units')
+    op.drop_table('enrollment')
+    op.drop_table('course_question')
+    op.drop_table('users')
+    op.drop_table('questions')
+    op.drop_table('courses')
+    # ### end Alembic commands ###
diff --git a/backend/alembic/versions/aa01c8290072_first_revision.py b/backend/alembic/versions/aa01c8290072_first_revision.py
deleted file mode 100644
index f228de5..0000000
--- a/backend/alembic/versions/aa01c8290072_first_revision.py
+++ /dev/null
@@ -1,92 +0,0 @@
-"""First revision
-
-Revision ID: aa01c8290072
-Revises: 
-Create Date: 2023-03-10 14:33:14.535079
-
-"""
-from alembic import op
-import sqlalchemy as sa
-
-
-# revision identifiers, used by Alembic.
-revision = 'aa01c8290072'
-down_revision = None
-branch_labels = None
-depends_on = None
-
-
-def upgrade() -> None:
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.drop_table('reflections')
-    op.drop_table('users')
-    op.drop_table('questions')
-    op.drop_table('course_question')
-    op.drop_table('units')
-    op.drop_table('enrollment')
-    op.drop_table('courses')
-    # ### end Alembic commands ###
-
-
-def downgrade() -> None:
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.create_table('courses',
-    sa.Column('name', sa.VARCHAR(), nullable=True),
-    sa.Column('id', sa.VARCHAR(), nullable=False),
-    sa.Column('semester', sa.VARCHAR(), nullable=True),
-    sa.Column('responsible', sa.VARCHAR(), nullable=True),
-    sa.Column('website', sa.VARCHAR(), nullable=True),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('enrollment',
-    sa.Column('user_email', sa.VARCHAR(), nullable=False),
-    sa.Column('course_id', sa.VARCHAR(), nullable=False),
-    sa.Column('role', sa.VARCHAR(length=18), nullable=False),
-    sa.ForeignKeyConstraint(['course_id'], ['courses.id'], ),
-    sa.ForeignKeyConstraint(['user_email'], ['users.email'], ),
-    sa.PrimaryKeyConstraint('user_email', 'course_id', 'role')
-    )
-    op.create_table('units',
-    sa.Column('id', sa.INTEGER(), nullable=False),
-    sa.Column('seq_no', sa.INTEGER(), nullable=True),
-    sa.Column('title', sa.VARCHAR(), nullable=True),
-    sa.Column('date_available', sa.DATE(), nullable=True),
-    sa.Column('course_id', sa.VARCHAR(), nullable=True),
-    sa.ForeignKeyConstraint(['course_id'], ['courses.id'], ),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('course_question',
-    sa.Column('course_id', sa.VARCHAR(), nullable=False),
-    sa.Column('question_id', sa.INTEGER(), nullable=False),
-    sa.ForeignKeyConstraint(['course_id'], ['courses.id'], ),
-    sa.ForeignKeyConstraint(['question_id'], ['questions.id'], ),
-    sa.PrimaryKeyConstraint('course_id', 'question_id')
-    )
-    op.create_table('questions',
-    sa.Column('id', sa.INTEGER(), nullable=False),
-    sa.Column('question', sa.VARCHAR(), nullable=True),
-    sa.Column('comment', sa.VARCHAR(), nullable=True),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('users',
-    sa.Column('email', sa.VARCHAR(), nullable=False),
-    sa.PrimaryKeyConstraint('email'),
-    sa.UniqueConstraint('email')
-    )
-    op.create_table('reflections',
-    sa.Column('id', sa.INTEGER(), nullable=False),
-    sa.Column('body', sa.VARCHAR(), nullable=True),
-    sa.Column('timestamp', sa.DATE(), nullable=True),
-    sa.Column('category', sa.VARCHAR(), nullable=True),
-    sa.Column('is_interesting', sa.BOOLEAN(), nullable=True),
-    sa.Column('is_problematic', sa.BOOLEAN(), nullable=True),
-    sa.Column('is_sorted', sa.BOOLEAN(), nullable=True),
-    sa.Column('user_id', sa.VARCHAR(), nullable=True),
-    sa.Column('unit_id', sa.INTEGER(), nullable=True),
-    sa.Column('question_id', sa.INTEGER(), nullable=True),
-    sa.ForeignKeyConstraint(['question_id'], ['questions.id'], ),
-    sa.ForeignKeyConstraint(['unit_id'], ['units.id'], ),
-    sa.ForeignKeyConstraint(['user_id'], ['users.email'], ),
-    sa.PrimaryKeyConstraint('id')
-    )
-    # ### end Alembic commands ###
-- 
GitLab