diff --git a/app.py b/app.py
index d0a5a1ad0166a949335494d9a8c5590c70a9cecb..1f1654cc0d5fcd4e496b73bc783f380b121258a3 100644
--- a/app.py
+++ b/app.py
@@ -2,14 +2,11 @@ import os
 import secrets
 from flask import Flask, render_template, request, redirect, url_for, send_from_directory, abort
 from flask_sqlalchemy import SQLAlchemy
-from flask_wtf import FlaskForm
-from wtforms import StringField, SubmitField
 
 app = Flask(__name__, static_folder='static')
 app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
 app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
 app.config['SECRET_KEY'] = secrets.token_hex(16)
-app.config['WTF_CSRF_ENABLED'] = True
 
 db = SQLAlchemy(app)
 
@@ -18,11 +15,6 @@ class Project(db.Model):
     title = db.Column(db.String(100), nullable=False)
     description = db.Column(db.Text, nullable=False)
 
-class AddProjectForm(FlaskForm):
-    title = StringField('Title')
-    description = StringField('Description')
-    submit = SubmitField('Submit')
-
 @app.route('/')
 def home():
     try:
@@ -54,21 +46,23 @@ def portfolio():
 def contact():
     return render_template('contact.html')
 
-# Updated route for adding a project with Flask-WTF form
+# Updated route for adding a project without Flask-WTF form
 @app.route('/add_project', methods=['GET', 'POST'])
 def add_project():
-    form = AddProjectForm()
+    if request.method == 'POST':
+        # Retrieve form data directly from request
+        title = request.form.get('title')
+        description = request.form.get('description')
 
-    if form.validate_on_submit():
         # Print or log the form data to check if it's received
-        print(f"Received form data - Title: {form.title.data}, Description: {form.description.data}")
+        print(f"Received form data - Title: {title}, Description: {description}")
 
-        new_project = Project(title=form.title.data, description=form.description.data)
+        new_project = Project(title=title, description=description)
         db.session.add(new_project)
         db.session.commit()
         return redirect(url_for('home'))
 
-    return render_template('add_project.html', form=form)
+    return render_template('add_project.html')
 
 # Updated route for serving the 'my-cv.docx' file
 @app.route('/download_cv')
@@ -92,5 +86,3 @@ def download_assessment(filename):
         app.logger.exception(f"Error serving assessment file: {str(e)}")
         abort(500) 
 
-if __name__ == '__main__':
-    app.run(debug=True, port=int(os.environ.get('PORT', 5000)))
\ No newline at end of file
diff --git a/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/INSTALLER b/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/INSTALLER
deleted file mode 100644
index a1b589e38a32041e49332e5e81c2d363dc418d68..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/METADATA b/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/METADATA
deleted file mode 100644
index 92f1ff25658838ae9639052003770899dc3851cb..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/METADATA
+++ /dev/null
@@ -1,72 +0,0 @@
-Metadata-Version: 2.1
-Name: Flask-WTF
-Version: 1.2.1
-Summary: Form rendering, validation, and CSRF protection for Flask with WTForms.
-Project-URL: Documentation, https://flask-wtf.readthedocs.io/
-Project-URL: Changes, https://flask-wtf.readthedocs.io/changes/
-Project-URL: Source Code, https://github.com/wtforms/flask-wtf/
-Project-URL: Issue Tracker, https://github.com/wtforms/flask-wtf/issues/
-Project-URL: Chat, https://discord.gg/pallets
-Maintainer: WTForms
-License: Copyright 2010 WTForms
-        
-        Redistribution and use in source and binary forms, with or without
-        modification, are permitted provided that the following conditions are
-        met:
-        
-        1.  Redistributions of source code must retain the above copyright
-            notice, this list of conditions and the following disclaimer.
-        
-        2.  Redistributions in binary form must reproduce the above copyright
-            notice, this list of conditions and the following disclaimer in the
-            documentation and/or other materials provided with the distribution.
-        
-        3.  Neither the name of the copyright holder nor the names of its
-            contributors may be used to endorse or promote products derived from
-            this software without specific prior written permission.
-        
-        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-        "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-        LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-        PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-        HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-        SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-        TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-        PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-        LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-        NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-        SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-License-File: LICENSE.rst
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Environment :: Web Environment
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: BSD License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
-Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
-Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
-Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
-Requires-Python: >=3.8
-Requires-Dist: flask
-Requires-Dist: itsdangerous
-Requires-Dist: wtforms
-Provides-Extra: email
-Requires-Dist: email-validator; extra == 'email'
-Description-Content-Type: text/x-rst
-
-Flask-WTF
-=========
-
-Simple integration of Flask and WTForms, including CSRF, file upload,
-and reCAPTCHA.
-
-Links
------
-
--   Documentation: https://flask-wtf.readthedocs.io/
--   Changes: https://flask-wtf.readthedocs.io/changes/
--   PyPI Releases: https://pypi.org/project/Flask-WTF/
--   Source Code: https://github.com/wtforms/flask-wtf/
--   Issue Tracker: https://github.com/wtforms/flask-wtf/issues/
--   Chat: https://discord.gg/pallets
diff --git a/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/RECORD b/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/RECORD
deleted file mode 100644
index a7814665f525f142935e652b0eafe2213194eccf..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/RECORD
+++ /dev/null
@@ -1,26 +0,0 @@
-flask_wtf-1.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-flask_wtf-1.2.1.dist-info/METADATA,sha256=9Y5upDJ7WU2m2l4erWImF3HcVSWIZKH3TdX6klYpq4M,3373
-flask_wtf-1.2.1.dist-info/RECORD,,
-flask_wtf-1.2.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-flask_wtf-1.2.1.dist-info/WHEEL,sha256=9QBuHhg6FNW7lppboF2vKVbCGTVzsFykgRQjjlajrhA,87
-flask_wtf-1.2.1.dist-info/licenses/LICENSE.rst,sha256=1fGQNkUVeMs27u8EyZ6_fXyi5w3PBDY2UZvEIOFafGI,1475
-flask_wtf/__init__.py,sha256=x6ydw5SJzsXZgz-Y6IM_95Sy8VufRepvZH1DUIlFoTo,214
-flask_wtf/__pycache__/__init__.cpython-311.pyc,,
-flask_wtf/__pycache__/_compat.cpython-311.pyc,,
-flask_wtf/__pycache__/csrf.cpython-311.pyc,,
-flask_wtf/__pycache__/file.cpython-311.pyc,,
-flask_wtf/__pycache__/form.cpython-311.pyc,,
-flask_wtf/__pycache__/i18n.cpython-311.pyc,,
-flask_wtf/_compat.py,sha256=N3sqC9yzFWY-3MZ7QazX1sidvkO3d5yy4NR6lkp0s94,248
-flask_wtf/csrf.py,sha256=O-fjnWygxxi_FsIU2koua97ZpIhiOJVDHA57dXLpvTA,10171
-flask_wtf/file.py,sha256=AsfkYTCgtqGWySimc_NjeAxg-DtpdcthhqMLrXIDAhU,4706
-flask_wtf/form.py,sha256=TmR7xCrxin2LHp6thn7fq1OeU8aLB7xsZzvv52nH7Ss,4049
-flask_wtf/i18n.py,sha256=TyO8gqt9DocHMSaNhj0KKgxoUrPYs-G1nVW-jns0SOw,1166
-flask_wtf/recaptcha/__init__.py,sha256=m4eNGoU3Q0Wnt_wP8VvOlA0mwWuoMtAcK9pYT7sPFp8,106
-flask_wtf/recaptcha/__pycache__/__init__.cpython-311.pyc,,
-flask_wtf/recaptcha/__pycache__/fields.cpython-311.pyc,,
-flask_wtf/recaptcha/__pycache__/validators.cpython-311.pyc,,
-flask_wtf/recaptcha/__pycache__/widgets.cpython-311.pyc,,
-flask_wtf/recaptcha/fields.py,sha256=M1-RFuUKOsJAzsLm3xaaxuhX2bB9oRqS-HVSN-NpkmI,433
-flask_wtf/recaptcha/validators.py,sha256=3sd1mUQT3Y3D_WJeKwecxUGstnhh_QD-A_dEBJfkf6s,2434
-flask_wtf/recaptcha/widgets.py,sha256=J_XyxAZt3uB15diIMnkXXGII2dmsWCsVsKV3KQYn4Ns,1512
diff --git a/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/REQUESTED b/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/REQUESTED
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/WHEEL b/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/WHEEL
deleted file mode 100644
index ba1a8af28bcccdacebb8c22dfda1537447a1a58a..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/WHEEL
+++ /dev/null
@@ -1,4 +0,0 @@
-Wheel-Version: 1.0
-Generator: hatchling 1.18.0
-Root-Is-Purelib: true
-Tag: py3-none-any
diff --git a/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/licenses/LICENSE.rst b/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/licenses/LICENSE.rst
deleted file mode 100644
index 63c3617a2d7164d30cae358c23eb3f75b5a758a1..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf-1.2.1.dist-info/licenses/LICENSE.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright 2010 WTForms
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-1.  Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
-
-2.  Redistributions in binary form must reproduce the above copyright
-    notice, this list of conditions and the following disclaimer in the
-    documentation and/or other materials provided with the distribution.
-
-3.  Neither the name of the copyright holder nor the names of its
-    contributors may be used to endorse or promote products derived from
-    this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/venv/Lib/site-packages/flask_wtf/__init__.py b/venv/Lib/site-packages/flask_wtf/__init__.py
deleted file mode 100644
index be2649e26d8dfa2cde5457f13b72715135d12b5a..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from .csrf import CSRFProtect
-from .form import FlaskForm
-from .form import Form
-from .recaptcha import Recaptcha
-from .recaptcha import RecaptchaField
-from .recaptcha import RecaptchaWidget
-
-__version__ = "1.2.1"
diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/__init__.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/__init__.cpython-311.pyc
deleted file mode 100644
index 304531c8dd034ce8289de1e379acd11a66bf38cd..0000000000000000000000000000000000000000
Binary files a/venv/Lib/site-packages/flask_wtf/__pycache__/__init__.cpython-311.pyc and /dev/null differ
diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/_compat.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/_compat.cpython-311.pyc
deleted file mode 100644
index beb00be94d82d4c7316bfcf1139bf843f1194cc5..0000000000000000000000000000000000000000
Binary files a/venv/Lib/site-packages/flask_wtf/__pycache__/_compat.cpython-311.pyc and /dev/null differ
diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/csrf.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/csrf.cpython-311.pyc
deleted file mode 100644
index 3a6e9a34361fdea3a9249ed18fe09ac5474f45b6..0000000000000000000000000000000000000000
Binary files a/venv/Lib/site-packages/flask_wtf/__pycache__/csrf.cpython-311.pyc and /dev/null differ
diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/file.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/file.cpython-311.pyc
deleted file mode 100644
index bac198e45ca285c998a6e60eec1a57deacb716eb..0000000000000000000000000000000000000000
Binary files a/venv/Lib/site-packages/flask_wtf/__pycache__/file.cpython-311.pyc and /dev/null differ
diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/form.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/form.cpython-311.pyc
deleted file mode 100644
index 156609e14de7e83103f2a2c776a0a668e0e6b194..0000000000000000000000000000000000000000
Binary files a/venv/Lib/site-packages/flask_wtf/__pycache__/form.cpython-311.pyc and /dev/null differ
diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/i18n.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/i18n.cpython-311.pyc
deleted file mode 100644
index 04d2d7e2cbe3df7f61e7c03d89b95925494f4bec..0000000000000000000000000000000000000000
Binary files a/venv/Lib/site-packages/flask_wtf/__pycache__/i18n.cpython-311.pyc and /dev/null differ
diff --git a/venv/Lib/site-packages/flask_wtf/_compat.py b/venv/Lib/site-packages/flask_wtf/_compat.py
deleted file mode 100644
index 50973e063bbdbd6982fc9501221603efbc2e88f9..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf/_compat.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import warnings
-
-
-class FlaskWTFDeprecationWarning(DeprecationWarning):
-    pass
-
-
-warnings.simplefilter("always", FlaskWTFDeprecationWarning)
-warnings.filterwarnings(
-    "ignore", category=FlaskWTFDeprecationWarning, module="wtforms|flask_wtf"
-)
diff --git a/venv/Lib/site-packages/flask_wtf/csrf.py b/venv/Lib/site-packages/flask_wtf/csrf.py
deleted file mode 100644
index 06afa0cd4ef3670ca3357d47bbecc2baa7e18fe0..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf/csrf.py
+++ /dev/null
@@ -1,329 +0,0 @@
-import hashlib
-import hmac
-import logging
-import os
-from urllib.parse import urlparse
-
-from flask import Blueprint
-from flask import current_app
-from flask import g
-from flask import request
-from flask import session
-from itsdangerous import BadData
-from itsdangerous import SignatureExpired
-from itsdangerous import URLSafeTimedSerializer
-from werkzeug.exceptions import BadRequest
-from wtforms import ValidationError
-from wtforms.csrf.core import CSRF
-
-__all__ = ("generate_csrf", "validate_csrf", "CSRFProtect")
-logger = logging.getLogger(__name__)
-
-
-def generate_csrf(secret_key=None, token_key=None):
-    """Generate a CSRF token. The token is cached for a request, so multiple
-    calls to this function will generate the same token.
-
-    During testing, it might be useful to access the signed token in
-    ``g.csrf_token`` and the raw token in ``session['csrf_token']``.
-
-    :param secret_key: Used to securely sign the token. Default is
-        ``WTF_CSRF_SECRET_KEY`` or ``SECRET_KEY``.
-    :param token_key: Key where token is stored in session for comparison.
-        Default is ``WTF_CSRF_FIELD_NAME`` or ``'csrf_token'``.
-    """
-
-    secret_key = _get_config(
-        secret_key,
-        "WTF_CSRF_SECRET_KEY",
-        current_app.secret_key,
-        message="A secret key is required to use CSRF.",
-    )
-    field_name = _get_config(
-        token_key,
-        "WTF_CSRF_FIELD_NAME",
-        "csrf_token",
-        message="A field name is required to use CSRF.",
-    )
-
-    if field_name not in g:
-        s = URLSafeTimedSerializer(secret_key, salt="wtf-csrf-token")
-
-        if field_name not in session:
-            session[field_name] = hashlib.sha1(os.urandom(64)).hexdigest()
-
-        try:
-            token = s.dumps(session[field_name])
-        except TypeError:
-            session[field_name] = hashlib.sha1(os.urandom(64)).hexdigest()
-            token = s.dumps(session[field_name])
-
-        setattr(g, field_name, token)
-
-    return g.get(field_name)
-
-
-def validate_csrf(data, secret_key=None, time_limit=None, token_key=None):
-    """Check if the given data is a valid CSRF token. This compares the given
-    signed token to the one stored in the session.
-
-    :param data: The signed CSRF token to be checked.
-    :param secret_key: Used to securely sign the token. Default is
-        ``WTF_CSRF_SECRET_KEY`` or ``SECRET_KEY``.
-    :param time_limit: Number of seconds that the token is valid. Default is
-        ``WTF_CSRF_TIME_LIMIT`` or 3600 seconds (60 minutes).
-    :param token_key: Key where token is stored in session for comparison.
-        Default is ``WTF_CSRF_FIELD_NAME`` or ``'csrf_token'``.
-
-    :raises ValidationError: Contains the reason that validation failed.
-
-    .. versionchanged:: 0.14
-        Raises ``ValidationError`` with a specific error message rather than
-        returning ``True`` or ``False``.
-    """
-
-    secret_key = _get_config(
-        secret_key,
-        "WTF_CSRF_SECRET_KEY",
-        current_app.secret_key,
-        message="A secret key is required to use CSRF.",
-    )
-    field_name = _get_config(
-        token_key,
-        "WTF_CSRF_FIELD_NAME",
-        "csrf_token",
-        message="A field name is required to use CSRF.",
-    )
-    time_limit = _get_config(time_limit, "WTF_CSRF_TIME_LIMIT", 3600, required=False)
-
-    if not data:
-        raise ValidationError("The CSRF token is missing.")
-
-    if field_name not in session:
-        raise ValidationError("The CSRF session token is missing.")
-
-    s = URLSafeTimedSerializer(secret_key, salt="wtf-csrf-token")
-
-    try:
-        token = s.loads(data, max_age=time_limit)
-    except SignatureExpired as e:
-        raise ValidationError("The CSRF token has expired.") from e
-    except BadData as e:
-        raise ValidationError("The CSRF token is invalid.") from e
-
-    if not hmac.compare_digest(session[field_name], token):
-        raise ValidationError("The CSRF tokens do not match.")
-
-
-def _get_config(
-    value, config_name, default=None, required=True, message="CSRF is not configured."
-):
-    """Find config value based on provided value, Flask config, and default
-    value.
-
-    :param value: already provided config value
-    :param config_name: Flask ``config`` key
-    :param default: default value if not provided or configured
-    :param required: whether the value must not be ``None``
-    :param message: error message if required config is not found
-    :raises KeyError: if required config is not found
-    """
-
-    if value is None:
-        value = current_app.config.get(config_name, default)
-
-    if required and value is None:
-        raise RuntimeError(message)
-
-    return value
-
-
-class _FlaskFormCSRF(CSRF):
-    def setup_form(self, form):
-        self.meta = form.meta
-        return super().setup_form(form)
-
-    def generate_csrf_token(self, csrf_token_field):
-        return generate_csrf(
-            secret_key=self.meta.csrf_secret, token_key=self.meta.csrf_field_name
-        )
-
-    def validate_csrf_token(self, form, field):
-        if g.get("csrf_valid", False):
-            # already validated by CSRFProtect
-            return
-
-        try:
-            validate_csrf(
-                field.data,
-                self.meta.csrf_secret,
-                self.meta.csrf_time_limit,
-                self.meta.csrf_field_name,
-            )
-        except ValidationError as e:
-            logger.info(e.args[0])
-            raise
-
-
-class CSRFProtect:
-    """Enable CSRF protection globally for a Flask app.
-
-    ::
-
-        app = Flask(__name__)
-        csrf = CSRFProtect(app)
-
-    Checks the ``csrf_token`` field sent with forms, or the ``X-CSRFToken``
-    header sent with JavaScript requests. Render the token in templates using
-    ``{{ csrf_token() }}``.
-
-    See the :ref:`csrf` documentation.
-    """
-
-    def __init__(self, app=None):
-        self._exempt_views = set()
-        self._exempt_blueprints = set()
-
-        if app:
-            self.init_app(app)
-
-    def init_app(self, app):
-        app.extensions["csrf"] = self
-
-        app.config.setdefault("WTF_CSRF_ENABLED", True)
-        app.config.setdefault("WTF_CSRF_CHECK_DEFAULT", True)
-        app.config["WTF_CSRF_METHODS"] = set(
-            app.config.get("WTF_CSRF_METHODS", ["POST", "PUT", "PATCH", "DELETE"])
-        )
-        app.config.setdefault("WTF_CSRF_FIELD_NAME", "csrf_token")
-        app.config.setdefault("WTF_CSRF_HEADERS", ["X-CSRFToken", "X-CSRF-Token"])
-        app.config.setdefault("WTF_CSRF_TIME_LIMIT", 3600)
-        app.config.setdefault("WTF_CSRF_SSL_STRICT", True)
-
-        app.jinja_env.globals["csrf_token"] = generate_csrf
-        app.context_processor(lambda: {"csrf_token": generate_csrf})
-
-        @app.before_request
-        def csrf_protect():
-            if not app.config["WTF_CSRF_ENABLED"]:
-                return
-
-            if not app.config["WTF_CSRF_CHECK_DEFAULT"]:
-                return
-
-            if request.method not in app.config["WTF_CSRF_METHODS"]:
-                return
-
-            if not request.endpoint:
-                return
-
-            if app.blueprints.get(request.blueprint) in self._exempt_blueprints:
-                return
-
-            view = app.view_functions.get(request.endpoint)
-            dest = f"{view.__module__}.{view.__name__}"
-
-            if dest in self._exempt_views:
-                return
-
-            self.protect()
-
-    def _get_csrf_token(self):
-        # find the token in the form data
-        field_name = current_app.config["WTF_CSRF_FIELD_NAME"]
-        base_token = request.form.get(field_name)
-
-        if base_token:
-            return base_token
-
-        # if the form has a prefix, the name will be {prefix}-csrf_token
-        for key in request.form:
-            if key.endswith(field_name):
-                csrf_token = request.form[key]
-
-                if csrf_token:
-                    return csrf_token
-
-        # find the token in the headers
-        for header_name in current_app.config["WTF_CSRF_HEADERS"]:
-            csrf_token = request.headers.get(header_name)
-
-            if csrf_token:
-                return csrf_token
-
-        return None
-
-    def protect(self):
-        if request.method not in current_app.config["WTF_CSRF_METHODS"]:
-            return
-
-        try:
-            validate_csrf(self._get_csrf_token())
-        except ValidationError as e:
-            logger.info(e.args[0])
-            self._error_response(e.args[0])
-
-        if request.is_secure and current_app.config["WTF_CSRF_SSL_STRICT"]:
-            if not request.referrer:
-                self._error_response("The referrer header is missing.")
-
-            good_referrer = f"https://{request.host}/"
-
-            if not same_origin(request.referrer, good_referrer):
-                self._error_response("The referrer does not match the host.")
-
-        g.csrf_valid = True  # mark this request as CSRF valid
-
-    def exempt(self, view):
-        """Mark a view or blueprint to be excluded from CSRF protection.
-
-        ::
-
-            @app.route('/some-view', methods=['POST'])
-            @csrf.exempt
-            def some_view():
-                ...
-
-        ::
-
-            bp = Blueprint(...)
-            csrf.exempt(bp)
-
-        """
-
-        if isinstance(view, Blueprint):
-            self._exempt_blueprints.add(view)
-            return view
-
-        if isinstance(view, str):
-            view_location = view
-        else:
-            view_location = ".".join((view.__module__, view.__name__))
-
-        self._exempt_views.add(view_location)
-        return view
-
-    def _error_response(self, reason):
-        raise CSRFError(reason)
-
-
-class CSRFError(BadRequest):
-    """Raise if the client sends invalid CSRF data with the request.
-
-    Generates a 400 Bad Request response with the failure reason by default.
-    Customize the response by registering a handler with
-    :meth:`flask.Flask.errorhandler`.
-    """
-
-    description = "CSRF validation failed."
-
-
-def same_origin(current_uri, compare_uri):
-    current = urlparse(current_uri)
-    compare = urlparse(compare_uri)
-
-    return (
-        current.scheme == compare.scheme
-        and current.hostname == compare.hostname
-        and current.port == compare.port
-    )
diff --git a/venv/Lib/site-packages/flask_wtf/file.py b/venv/Lib/site-packages/flask_wtf/file.py
deleted file mode 100644
index a720dff8d81911df179e80512caa0056a47be410..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf/file.py
+++ /dev/null
@@ -1,147 +0,0 @@
-from collections import abc
-
-from werkzeug.datastructures import FileStorage
-from wtforms import FileField as _FileField
-from wtforms import MultipleFileField as _MultipleFileField
-from wtforms.validators import DataRequired
-from wtforms.validators import StopValidation
-from wtforms.validators import ValidationError
-
-
-class FileField(_FileField):
-    """Werkzeug-aware subclass of :class:`wtforms.fields.FileField`."""
-
-    def process_formdata(self, valuelist):
-        valuelist = (x for x in valuelist if isinstance(x, FileStorage) and x)
-        data = next(valuelist, None)
-
-        if data is not None:
-            self.data = data
-        else:
-            self.raw_data = ()
-
-
-class MultipleFileField(_MultipleFileField):
-    """Werkzeug-aware subclass of :class:`wtforms.fields.MultipleFileField`.
-
-    .. versionadded:: 1.2.0
-    """
-
-    def process_formdata(self, valuelist):
-        valuelist = (x for x in valuelist if isinstance(x, FileStorage) and x)
-        data = list(valuelist) or None
-
-        if data is not None:
-            self.data = data
-        else:
-            self.raw_data = ()
-
-
-class FileRequired(DataRequired):
-    """Validates that the uploaded files(s) is a Werkzeug
-    :class:`~werkzeug.datastructures.FileStorage` object.
-
-    :param message: error message
-
-    You can also use the synonym ``file_required``.
-    """
-
-    def __call__(self, form, field):
-        field_data = [field.data] if not isinstance(field.data, list) else field.data
-        if not (
-            all(isinstance(x, FileStorage) and x for x in field_data) and field_data
-        ):
-            raise StopValidation(
-                self.message or field.gettext("This field is required.")
-            )
-
-
-file_required = FileRequired
-
-
-class FileAllowed:
-    """Validates that the uploaded file(s) is allowed by a given list of
-    extensions or a Flask-Uploads :class:`~flaskext.uploads.UploadSet`.
-
-    :param upload_set: A list of extensions or an
-        :class:`~flaskext.uploads.UploadSet`
-    :param message: error message
-
-    You can also use the synonym ``file_allowed``.
-    """
-
-    def __init__(self, upload_set, message=None):
-        self.upload_set = upload_set
-        self.message = message
-
-    def __call__(self, form, field):
-        field_data = [field.data] if not isinstance(field.data, list) else field.data
-        if not (
-            all(isinstance(x, FileStorage) and x for x in field_data) and field_data
-        ):
-            return
-
-        filenames = [f.filename.lower() for f in field_data]
-
-        for filename in filenames:
-            if isinstance(self.upload_set, abc.Iterable):
-                if any(filename.endswith("." + x) for x in self.upload_set):
-                    continue
-
-                raise StopValidation(
-                    self.message
-                    or field.gettext(
-                        "File does not have an approved extension: {extensions}"
-                    ).format(extensions=", ".join(self.upload_set))
-                )
-
-            if not self.upload_set.file_allowed(field_data, filename):
-                raise StopValidation(
-                    self.message
-                    or field.gettext("File does not have an approved extension.")
-                )
-
-
-file_allowed = FileAllowed
-
-
-class FileSize:
-    """Validates that the uploaded file(s) is within a minimum and maximum
-    file size (set in bytes).
-
-    :param min_size: minimum allowed file size (in bytes). Defaults to 0 bytes.
-    :param max_size: maximum allowed file size (in bytes).
-    :param message: error message
-
-    You can also use the synonym ``file_size``.
-    """
-
-    def __init__(self, max_size, min_size=0, message=None):
-        self.min_size = min_size
-        self.max_size = max_size
-        self.message = message
-
-    def __call__(self, form, field):
-        field_data = [field.data] if not isinstance(field.data, list) else field.data
-        if not (
-            all(isinstance(x, FileStorage) and x for x in field_data) and field_data
-        ):
-            return
-
-        for f in field_data:
-            file_size = len(f.read())
-            f.seek(0)  # reset cursor position to beginning of file
-
-            if (file_size < self.min_size) or (file_size > self.max_size):
-                # the file is too small or too big => validation failure
-                raise ValidationError(
-                    self.message
-                    or field.gettext(
-                        "File must be between {min_size} and {max_size} bytes.".format(
-                            min_size=self.min_size, max_size=self.max_size
-                        )
-                    )
-                )
-
-
-file_size = FileSize
diff --git a/venv/Lib/site-packages/flask_wtf/form.py b/venv/Lib/site-packages/flask_wtf/form.py
deleted file mode 100644
index c7f52e022c82fe43d6674377e5df040c82d10d79..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf/form.py
+++ /dev/null
@@ -1,127 +0,0 @@
-from flask import current_app
-from flask import request
-from flask import session
-from markupsafe import Markup
-from werkzeug.datastructures import CombinedMultiDict
-from werkzeug.datastructures import ImmutableMultiDict
-from werkzeug.utils import cached_property
-from wtforms import Form
-from wtforms.meta import DefaultMeta
-from wtforms.widgets import HiddenInput
-
-from .csrf import _FlaskFormCSRF
-
-try:
-    from .i18n import translations
-except ImportError:
-    translations = None  # babel not installed
-
-
-SUBMIT_METHODS = {"POST", "PUT", "PATCH", "DELETE"}
-_Auto = object()
-
-
-class FlaskForm(Form):
-    """Flask-specific subclass of WTForms :class:`~wtforms.form.Form`.
-
-    If ``formdata`` is not specified, this will use :attr:`flask.request.form`
-    and :attr:`flask.request.files`.  Explicitly pass ``formdata=None`` to
-    prevent this.
-    """
-
-    class Meta(DefaultMeta):
-        csrf_class = _FlaskFormCSRF
-        csrf_context = session  # not used, provided for custom csrf_class
-
-        @cached_property
-        def csrf(self):
-            return current_app.config.get("WTF_CSRF_ENABLED", True)
-
-        @cached_property
-        def csrf_secret(self):
-            return current_app.config.get("WTF_CSRF_SECRET_KEY", current_app.secret_key)
-
-        @cached_property
-        def csrf_field_name(self):
-            return current_app.config.get("WTF_CSRF_FIELD_NAME", "csrf_token")
-
-        @cached_property
-        def csrf_time_limit(self):
-            return current_app.config.get("WTF_CSRF_TIME_LIMIT", 3600)
-
-        def wrap_formdata(self, form, formdata):
-            if formdata is _Auto:
-                if _is_submitted():
-                    if request.files:
-                        return CombinedMultiDict((request.files, request.form))
-                    elif request.form:
-                        return request.form
-                    elif request.is_json:
-                        return ImmutableMultiDict(request.get_json())
-
-                return None
-
-            return formdata
-
-        def get_translations(self, form):
-            if not current_app.config.get("WTF_I18N_ENABLED", True):
-                return super().get_translations(form)
-
-            return translations
-
-    def __init__(self, formdata=_Auto, **kwargs):
-        super().__init__(formdata=formdata, **kwargs)
-
-    def is_submitted(self):
-        """Consider the form submitted if there is an active request and
-        the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
-        """
-
-        return _is_submitted()
-
-    def validate_on_submit(self, extra_validators=None):
-        """Call :meth:`validate` only if the form is submitted.
-        This is a shortcut for ``form.is_submitted() and form.validate()``.
-        """
-        return self.is_submitted() and self.validate(extra_validators=extra_validators)
-
-    def hidden_tag(self, *fields):
-        """Render the form's hidden fields in one call.
-
-        A field is considered hidden if it uses the
-        :class:`~wtforms.widgets.HiddenInput` widget.
-
-        If ``fields`` are given, only render the given fields that
-        are hidden.  If a string is passed, render the field with that
-        name if it exists.
-
-        .. versionchanged:: 0.13
-
-           No longer wraps inputs in hidden div.
-           This is valid HTML 5.
-
-        .. versionchanged:: 0.13
-
-           Skip passed fields that aren't hidden.
-           Skip passed names that don't exist.
-        """
-
-        def hidden_fields(fields):
-            for f in fields:
-                if isinstance(f, str):
-                    f = getattr(self, f, None)
-
-                if f is None or not isinstance(f.widget, HiddenInput):
-                    continue
-
-                yield f
-
-        return Markup("\n".join(str(f) for f in hidden_fields(fields or self)))
-
-
-def _is_submitted():
-    """Consider the form submitted if there is an active request and
-    the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
-    """
-
-    return bool(request) and request.method in SUBMIT_METHODS
diff --git a/venv/Lib/site-packages/flask_wtf/i18n.py b/venv/Lib/site-packages/flask_wtf/i18n.py
deleted file mode 100644
index 1cc0e9c5a6d2fee8d18c4f46a79ed82f93d132a7..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf/i18n.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from babel import support
-from flask import current_app
-from flask import request
-from flask_babel import get_locale
-from wtforms.i18n import messages_path
-
-__all__ = ("Translations", "translations")
-
-
-def _get_translations():
-    """Returns the correct gettext translations.
-    Copy from flask-babel with some modifications.
-    """
-
-    if not request:
-        return None
-
-    # babel should be in extensions for get_locale
-    if "babel" not in current_app.extensions:
-        return None
-
-    translations = getattr(request, "wtforms_translations", None)
-
-    if translations is None:
-        translations = support.Translations.load(
-            messages_path(), [get_locale()], domain="wtforms"
-        )
-        request.wtforms_translations = translations
-
-    return translations
-
-
-class Translations:
-    def gettext(self, string):
-        t = _get_translations()
-        return string if t is None else t.ugettext(string)
-
-    def ngettext(self, singular, plural, n):
-        t = _get_translations()
-
-        if t is None:
-            return singular if n == 1 else plural
-
-        return t.ungettext(singular, plural, n)
-
-
-translations = Translations()
diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__init__.py b/venv/Lib/site-packages/flask_wtf/recaptcha/__init__.py
deleted file mode 100644
index 3100d37e3389219d98787b585357edbe0d9bcc37..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf/recaptcha/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .fields import RecaptchaField
-from .validators import Recaptcha
-from .widgets import RecaptchaWidget
diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/__init__.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/__init__.cpython-311.pyc
deleted file mode 100644
index ad5312065a905cf14e41f922291566616856c8fb..0000000000000000000000000000000000000000
Binary files a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/__init__.cpython-311.pyc and /dev/null differ
diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/fields.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/fields.cpython-311.pyc
deleted file mode 100644
index 1eb5b704de329b4e160ea7d0eefa8dd48b16104d..0000000000000000000000000000000000000000
Binary files a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/fields.cpython-311.pyc and /dev/null differ
diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/validators.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/validators.cpython-311.pyc
deleted file mode 100644
index b8a1ffd5e1f5b5180ec496e94c61d8d59e9302f6..0000000000000000000000000000000000000000
Binary files a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/validators.cpython-311.pyc and /dev/null differ
diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/widgets.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/widgets.cpython-311.pyc
deleted file mode 100644
index 6f5bb03a28b7e7b55f3ef3a93a63eda13b3d3754..0000000000000000000000000000000000000000
Binary files a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/widgets.cpython-311.pyc and /dev/null differ
diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/fields.py b/venv/Lib/site-packages/flask_wtf/recaptcha/fields.py
deleted file mode 100644
index e91fd092f98c01932a90ffafe68bbf98390ff2ed..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf/recaptcha/fields.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from wtforms.fields import Field
-
-from . import widgets
-from .validators import Recaptcha
-
-__all__ = ["RecaptchaField"]
-
-
-class RecaptchaField(Field):
-    widget = widgets.RecaptchaWidget()
-
-    # error message if recaptcha validation fails
-    recaptcha_error = None
-
-    def __init__(self, label="", validators=None, **kwargs):
-        validators = validators or [Recaptcha()]
-        super().__init__(label, validators, **kwargs)
diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/validators.py b/venv/Lib/site-packages/flask_wtf/recaptcha/validators.py
deleted file mode 100644
index c5cafb3478cd644a199fb731cf5d2c0c440f986c..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf/recaptcha/validators.py
+++ /dev/null
@@ -1,75 +0,0 @@
-import json
-from urllib import request as http
-from urllib.parse import urlencode
-
-from flask import current_app
-from flask import request
-from wtforms import ValidationError
-
-RECAPTCHA_VERIFY_SERVER_DEFAULT = "https://www.google.com/recaptcha/api/siteverify"
-RECAPTCHA_ERROR_CODES = {
-    "missing-input-secret": "The secret parameter is missing.",
-    "invalid-input-secret": "The secret parameter is invalid or malformed.",
-    "missing-input-response": "The response parameter is missing.",
-    "invalid-input-response": "The response parameter is invalid or malformed.",
-}
-
-
-__all__ = ["Recaptcha"]
-
-
-class Recaptcha:
-    """Validates a ReCaptcha."""
-
-    def __init__(self, message=None):
-        if message is None:
-            message = RECAPTCHA_ERROR_CODES["missing-input-response"]
-        self.message = message
-
-    def __call__(self, form, field):
-        if current_app.testing:
-            return True
-
-        if request.is_json:
-            response = request.json.get("g-recaptcha-response", "")
-        else:
-            response = request.form.get("g-recaptcha-response", "")
-        remote_ip = request.remote_addr
-
-        if not response:
-            raise ValidationError(field.gettext(self.message))
-
-        if not self._validate_recaptcha(response, remote_ip):
-            field.recaptcha_error = "incorrect-captcha-sol"
-            raise ValidationError(field.gettext(self.message))
-
-    def _validate_recaptcha(self, response, remote_addr):
-        """Performs the actual validation."""
-        try:
-            private_key = current_app.config["RECAPTCHA_PRIVATE_KEY"]
-        except KeyError:
-            raise RuntimeError("No RECAPTCHA_PRIVATE_KEY config set") from None
-
-        verify_server = current_app.config.get("RECAPTCHA_VERIFY_SERVER")
-        if not verify_server:
-            verify_server = RECAPTCHA_VERIFY_SERVER_DEFAULT
-
-        data = urlencode(
-            {"secret": private_key, "remoteip": remote_addr, "response": response}
-        )
-
-        http_response = http.urlopen(verify_server, data.encode("utf-8"))
-
-        if http_response.code != 200:
-            return False
-
-        json_resp = json.loads(http_response.read())
-
-        if json_resp["success"]:
-            return True
-
-        for error in json_resp.get("error-codes", []):
-            if error in RECAPTCHA_ERROR_CODES:
-                raise ValidationError(RECAPTCHA_ERROR_CODES[error])
-
-        return False
diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/widgets.py b/venv/Lib/site-packages/flask_wtf/recaptcha/widgets.py
deleted file mode 100644
index bfae830bb17f188f2123931847f958590c77e690..0000000000000000000000000000000000000000
--- a/venv/Lib/site-packages/flask_wtf/recaptcha/widgets.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from urllib.parse import urlencode
-
-from flask import current_app
-from markupsafe import Markup
-
-RECAPTCHA_SCRIPT_DEFAULT = "https://www.google.com/recaptcha/api.js"
-RECAPTCHA_DIV_CLASS_DEFAULT = "g-recaptcha"
-RECAPTCHA_TEMPLATE = """
-<script src='%s' async defer></script>
-<div class="%s" %s></div>
-"""
-
-__all__ = ["RecaptchaWidget"]
-
-
-class RecaptchaWidget:
-    def recaptcha_html(self, public_key):
-        html = current_app.config.get("RECAPTCHA_HTML")
-        if html:
-            return Markup(html)
-        params = current_app.config.get("RECAPTCHA_PARAMETERS")
-        script = current_app.config.get("RECAPTCHA_SCRIPT")
-        if not script:
-            script = RECAPTCHA_SCRIPT_DEFAULT
-        if params:
-            script += "?" + urlencode(params)
-        attrs = current_app.config.get("RECAPTCHA_DATA_ATTRS", {})
-        attrs["sitekey"] = public_key
-        snippet = " ".join(f'data-{k}="{attrs[k]}"' for k in attrs)  # noqa: B028, B907
-        div_class = current_app.config.get("RECAPTCHA_DIV_CLASS")
-        if not div_class:
-            div_class = RECAPTCHA_DIV_CLASS_DEFAULT
-        return Markup(RECAPTCHA_TEMPLATE % (script, div_class, snippet))
-
-    def __call__(self, field, error=None, **kwargs):
-        """Returns the recaptcha input HTML."""
-
-        try:
-            public_key = current_app.config["RECAPTCHA_PUBLIC_KEY"]
-        except KeyError:
-            raise RuntimeError("RECAPTCHA_PUBLIC_KEY config not set") from None
-
-        return self.recaptcha_html(public_key)
diff --git a/wsgi.py b/wsgi.py
index 78681907a56f6e8852ba79e6565c4778be08abb6..ab58b4ebdd8b8760b2bbb45528cdda8c27fba07a 100644
--- a/wsgi.py
+++ b/wsgi.py
@@ -1,8 +1,11 @@
 import sys
 import logging
 from app import app as application
+from markupsafe import Markup
+
+sys.modules['flask'].Markup = Markup
 
 logging.basicConfig(stream=sys.stderr)
 
 if __name__ == '__main__':
-    application.run(host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
+    application.run(host='0.0.0.0', port=8080)