From fe60264546fbeb5548b9b1857cdb59b604d01621 Mon Sep 17 00:00:00 2001
From: Felix Chadwick-Smith <chadwick-smithff@cardiff.ac.uk>
Date: Wed, 10 Jan 2024 16:08:06 +0000
Subject: [PATCH] removed Flask_WTF

---
 app.py                                        |   9 +-
 requirements.txt                              |   1 -
 .../flask_wtf-1.2.0.dist-info/INSTALLER       |   1 -
 .../flask_wtf-1.2.0.dist-info/METADATA        |  72 ----
 .../flask_wtf-1.2.0.dist-info/RECORD          |  26 --
 .../flask_wtf-1.2.0.dist-info/REQUESTED       |   0
 .../flask_wtf-1.2.0.dist-info/WHEEL           |   4 -
 .../licenses/LICENSE.rst                      |  28 --
 venv/Lib/site-packages/flask_wtf/__init__.py  |   8 -
 .../__pycache__/__init__.cpython-311.pyc      | Bin 532 -> 0 bytes
 .../__pycache__/_compat.cpython-311.pyc       | Bin 698 -> 0 bytes
 .../__pycache__/csrf.cpython-311.pyc          | Bin 15040 -> 0 bytes
 .../__pycache__/file.cpython-311.pyc          | Bin 8532 -> 0 bytes
 .../__pycache__/form.cpython-311.pyc          | Bin 7261 -> 0 bytes
 .../__pycache__/i18n.cpython-311.pyc          | Bin 2060 -> 0 bytes
 venv/Lib/site-packages/flask_wtf/_compat.py   |  11 -
 venv/Lib/site-packages/flask_wtf/csrf.py      | 329 ------------------
 venv/Lib/site-packages/flask_wtf/file.py      | 151 --------
 venv/Lib/site-packages/flask_wtf/form.py      | 127 -------
 venv/Lib/site-packages/flask_wtf/i18n.py      |  47 ---
 .../flask_wtf/recaptcha/__init__.py           |   3 -
 .../__pycache__/__init__.cpython-311.pyc      | Bin 380 -> 0 bytes
 .../__pycache__/fields.cpython-311.pyc        | Bin 1134 -> 0 bytes
 .../__pycache__/validators.cpython-311.pyc    | Bin 3739 -> 0 bytes
 .../__pycache__/widgets.cpython-311.pyc       | Bin 2875 -> 0 bytes
 .../flask_wtf/recaptcha/fields.py             |  17 -
 .../flask_wtf/recaptcha/validators.py         |  75 ----
 .../flask_wtf/recaptcha/widgets.py            |  43 ---
 28 files changed, 3 insertions(+), 949 deletions(-)
 delete mode 100644 venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/INSTALLER
 delete mode 100644 venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/METADATA
 delete mode 100644 venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/RECORD
 delete mode 100644 venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/REQUESTED
 delete mode 100644 venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/WHEEL
 delete mode 100644 venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/licenses/LICENSE.rst
 delete mode 100644 venv/Lib/site-packages/flask_wtf/__init__.py
 delete mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/__init__.cpython-311.pyc
 delete mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/_compat.cpython-311.pyc
 delete mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/csrf.cpython-311.pyc
 delete mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/file.cpython-311.pyc
 delete mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/form.cpython-311.pyc
 delete mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/i18n.cpython-311.pyc
 delete mode 100644 venv/Lib/site-packages/flask_wtf/_compat.py
 delete mode 100644 venv/Lib/site-packages/flask_wtf/csrf.py
 delete mode 100644 venv/Lib/site-packages/flask_wtf/file.py
 delete mode 100644 venv/Lib/site-packages/flask_wtf/form.py
 delete mode 100644 venv/Lib/site-packages/flask_wtf/i18n.py
 delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__init__.py
 delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/__init__.cpython-311.pyc
 delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/fields.cpython-311.pyc
 delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/validators.cpython-311.pyc
 delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/widgets.cpython-311.pyc
 delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/fields.py
 delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/validators.py
 delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/widgets.py

diff --git a/app.py b/app.py
index d0a5a1a..f31300f 100644
--- a/app.py
+++ b/app.py
@@ -2,14 +2,12 @@ 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,10 +16,9 @@ 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')
+class AddProjectForm():
+    # Remove FlaskForm-related code
+    pass
 
 @app.route('/')
 def home():
diff --git a/requirements.txt b/requirements.txt
index b7475d8..65df85b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,7 +4,6 @@ click==8.1.7
 colorama==0.4.6
 Flask==3.0.0
 Flask-SQLAlchemy==3.1.1
-Flask-WTF==1.2.0
 gevent==23.9.1
 greenlet==3.0.3
 gunicorn==21.2.0
diff --git a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/INSTALLER b/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/INSTALLER
deleted file mode 100644
index a1b589e..0000000
--- a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/METADATA b/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/METADATA
deleted file mode 100644
index cb81ed4..0000000
--- a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/METADATA
+++ /dev/null
@@ -1,72 +0,0 @@
-Metadata-Version: 2.1
-Name: Flask-WTF
-Version: 1.2.0
-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.0.dist-info/RECORD b/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/RECORD
deleted file mode 100644
index c0e003b..0000000
--- a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/RECORD
+++ /dev/null
@@ -1,26 +0,0 @@
-flask_wtf-1.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-flask_wtf-1.2.0.dist-info/METADATA,sha256=DzMrNjYSRoyVUkYdVzRXCc0AOriWm1nM4HzNyd8VSMo,3373
-flask_wtf-1.2.0.dist-info/RECORD,,
-flask_wtf-1.2.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-flask_wtf-1.2.0.dist-info/WHEEL,sha256=9QBuHhg6FNW7lppboF2vKVbCGTVzsFykgRQjjlajrhA,87
-flask_wtf-1.2.0.dist-info/licenses/LICENSE.rst,sha256=1fGQNkUVeMs27u8EyZ6_fXyi5w3PBDY2UZvEIOFafGI,1475
-flask_wtf/__init__.py,sha256=ObJw25Z-Uq2HgLhMKhfKk-BVr2hBXxBdEx_fyV4geBA,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=CKr7kAgGqmKyC47gPuYoR7EHVSgl06xKtkyMmEPsXhs,4759
-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.0.dist-info/REQUESTED b/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/REQUESTED
deleted file mode 100644
index e69de29..0000000
diff --git a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/WHEEL b/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/WHEEL
deleted file mode 100644
index ba1a8af..0000000
--- a/venv/Lib/site-packages/flask_wtf-1.2.0.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.0.dist-info/licenses/LICENSE.rst b/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/licenses/LICENSE.rst
deleted file mode 100644
index 63c3617..0000000
--- a/venv/Lib/site-packages/flask_wtf-1.2.0.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 3f884c0..0000000
--- 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.0"
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 cc492fb7f408d036f111ed881bee398192c91b20..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 532
zcmY+AKTE?v7{>3K|1GU9iXeiELI-PG(Luze5)eeCMT8JIVsfdW`BN@w#ZG<;H&^jX
z_!%-u=p;C~3EevRUT6z>@8RCx^Bla#-FvlKMo?RyZ}bcL4>!vyEnwCKcts9!u#W<4
zV9dGXmjb~Mau$F^L(Ez9r9d|1oF!nzP;!=mRYL_<49!t{sIL8D7+lBGN~hP=Z&{R3
zI{~Zdz7-Gjhy`H8Mc2C2wuXuQXhB$88Qr741IgMdx%ZqtO(s&S(QY)a>*c9v$IN8{
zTs0M47|KLICULK^<xnQ`P~n@(Pb!3rDT}=*B;*6lAVTw-&Z&79Lu%UX=5gz&ePUij
z_9&oX5}TKS)u*vJrs3GU@*d3COX$JSvIlwW@)wb(#62X$3%!Jp#_)Nro&}LJ^65ES
zhxz<7;t6bj0>(H)J84lF+D?ng&}LdxhIZ4UGPIW#m7)E#sPEdwONB31gX{kRgW8h^

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 d3b114711695270b6f6b23c27085d8d5a9e4ac65..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 698
zcmZWnJ#P~+7=G<bj%$^+0t7)!FlOMRtzbwH3sDggWC2tuI>|C;Cmg*m>`S8{h8y?+
zY~2`(5c~#y!y$EuyfU#}l@6#A_FXTi>TCIVKm7Rpu>5sxZ56cK`1ytZ)%v$_YH6Ms
z3!#kzV8BoSL%{G982$vz;^@!<_^x*)LyUpLyQR+>93NuM(7O_(Z)--Q_F0o-X8*W!
zJ`d8BUP8zDV`u}fCL4{;oW;5}4s@;|Vlc!dAFG=?0hN2ZuXg%85!|DxA4j`XM1C|L
zbS&i%648(oqE-nBV>Stl-6G`uga*~gH&~bor-LV5_qF6gy583P2b<el58Zz3O+p@}
z(tQ@vF_-Q>kM`Z?{u@{NDZi6YZ?6oGOb=w5j_wgJ4ilQ*O=iMOT!Jy=hK6jp<_#z7
zi<O$8Y#L1IOe)(SN3q}^F6&&e2<69R<_=blo{o=WF;jLmNvEltX*H`<S1vw4SzWcH
zA0`1G`9aEsTCa3{O*p29vJvwuPMt?p>c`@$cFohvOhaKIgyyi3m8-B3S}EWn<hQqv
zP59*hnFHv0<{ZJb&u{Zv-Cxi<f?f{2IkZi*J#Vz~_GaFFQCJw^W6*G7pg0d@hyMUg
C*0H<*

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 3fd39c21ef0bbb6810a3f32664d89d8534bea2f1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 15040
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

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 d747ca899480b04119d65dc0245885758a6377a7..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 8532
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

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 434265db1bc141bd6e9e00db52dc7ece9afde4a3..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 7261
zcmcf`TWl29_0GP%UVDw#VDt2ZKwwF-ykeBNJZkK9aKLMdO@Nit*<oka9@yP+?#yCv
zOyWqc3KFfHiimzBmD?r+L@MPYk@C~3ks7t1v(ZXfjf8|$MN0lmNhHKq&$+X+yW=%!
zqg1_~J={6x-aGd^?>UzbT3UhxTGwBGCl|wn{1ad7=B;!d{0llah(shVOLAP2<FN07
zzANcseK+*oNjK|zvYwnb>1BOy)|c}q{jBeUejpiOeSfwo7fc3OKag$Cg_0rGZ_2jh
zT9d7;AIyexZOJz1H%p=Hs$6@rog+>7){>3nI+7h6$+tX7<kedU#jmv{JI;{2XPR7e
zpCQxSB$2{@<dSP#MDDx^`@dszMUq`E@*<Ji=83dQ4&8JE4SrTju9e#7NphXkI!dCE
zZx9*f%;t1~QaP^+;?xwp`l<YCLDqEaYO<y&Y96}2VUeCMAbnN8nmeoHWofvO)s+Dy
zjU(HKa=C&op3Tbj*l=1*Ps)-oMb#;p>X%@kXHca%__ujL&WNzUu&j&tdPI>VIX{%2
zD(K&E*1lSW!K|pA#|izTCkA0uNT*_6%ZfVeNlP5`z&O)={N$MFIX*HvW_pkJjrAWf
zeFO1h@v(Rs*|S?!*)Id;m!NK$BsqyBT@sgcGl984U~bv7OkiHwm*K9E3@3T!o05LX
z3*rk%O_G0}OEyUXcne-37rA6J2sCImRX03ypLL(rrsTAeQPR9tIGYA;G+xc{r^b*c
zjqhcc-qh>UdIk|OgfXP0V!<E}Z79R1QizpAT}-8TMdS0T&R15IrDu435^&Q>Hp>?@
zneP>Ko%W_OD7RQek8ITxTTaYNj~Sw5Wi1us`S``Dtddsr>?M8*cU$}VfrOfuVGFv-
zmYSmS1+W?R{TRbI<n5{m{=xh*dCTskNA@OtvR~rn$uiRo02cJH4BG@)cLO#ESWg4C
znQ17*H0GNRBwLtv(A@oxTC3WLq1C0)%<P7ygl_WSFn}9GxAE}rx$3w@o2xo1fv_RD
zZUW^_l?q>TC(Jfbvw(&x#1nmokHrVZZo4Ud!}O)qd`6it-4n8Y8(cz@vl$J)!aq9N
z-#dO%lc_eI-m|M`_uf7G#s}1NAqS$?#)op^gshE&+%JqDQ_hZSiY`ApC8p0aG*h|&
zrnHlp(bOf=lh$ZvCgNl}hR7}OF>M2YIr3FxQz^3b%SiN#NYvP||5K?L=`Th4jZpss
zpp%uQgkJPRrR0z)%7L{8$+;3{q$;`5c>js`m~b?HChB3bqw8T7#hsf$O-@r;7tYI<
z=te+T5~SToYepsoYrxF96$DVBZO3^yW4Rm-eZIcbcd8gUU5cDGLZ|5yFlJ>@{O=5=
z#`fS){Mdky=o^lkLB@%$o|p4<9c-e3)i4{7QRJ*7<i(skvuP!EYxHN3<&{)_=&u`!
zkwhtyFhYs{Grwa)!*Su*(D2Zh($)$*eh;I%l9Pq3l2i1R8LiQGJ)Y5yVq~xs88kwJ
z7Nb>bgelcXuo*n^2AOr$+iii+U+8tCoso(bbevsj=c?oAR=vr_60-6b2i88VNXB&&
zc5w$|18bws+0}70wDSCA<9+rzl-D60vYTFA2cliT#yWXhz7L6vf^0^~Z7vaQGQC1y
zL08QdLD2*_=|DTWEKwAO>CNETLjBnHU}Q4=FzB47<|%&43_uWMuTeMM1PHo^VeLat
zLn*>ZX!&)^G!>@=n>MpynW;Od&%s3PH2`y@9PWI##|ZQA+>P*W9lCyKiTG9xa(6p7
zUBA3UysM$xz3Y>0A3ytf=ifFMyA#Fkky7`F(KT{+E&tAjCE{iyo_c52=z1Da-AjaH
zsNJ7zEA2U4+%r(xGf?b~m%8IdSNvY*`UU-7Wc`&9%e<-w18k;hUf1E}ZSIE#(d9>t
z_Jod-tu&{~7VC`B9zn<~w_KGv%aN@Sbw}6G?&lNLqburL9)7)A0dhLC4b@klmQge;
zhc250fh9VEP*LU_Qzq?&fivC9RJeRO46oV&01xNL-Ib3j&_&C>yPX@}n!Y~$W#`T>
zI(L5LDR%ahI(v+^p06U^MyQ+VAsRLV0*jG?U<L&tr%Hva4E>NGyjl>m7(E4nLI8rp
zg+*1iZHnGuhAf1d*X4^kMR%q=f~^Rm2r#hHeF%^-@CA$Ow4BWf!flTJ1h5)<@=q;W
z_Fxi1I|0EX<?jH1NBgh5`pGN7a_6Qip?ghTOD?WyfCD}O-glGW+H2hvln-98dxlZZ
z)KdXvQ%4weq@D^WoBAT7zF1EMlrPi-hF=96JWc!(`R@ReHth$7()Ff~a%_g*gM$Fj
z^j$=+MmDl6>#Jp566EN!oWjkz-s67FVe!O_l=i?Xw@K7RUw{s3R>>>6AV8wP!U&R0
z-}z~gPH2|w(H$U?RIG0h-e%fs)@RQ@0(k9d0HBO=pzBxOtNv^LQlQHS^uV*Y9l))O
z^-_hFo7eFItX1+!+zU8)9nI;azza8XwIAvkMUo*bPs%)oH>?+|D9S4t#8MfPe=*OC
zX<fM>^OfWtFcQnYfiX5YC+m}{gkw`FEQ(U8XRvqD?qG$4IF$n0DwI-CZQyilQI6ud
z*^p&~>I3`Cnwr?^@ke2%hVC;*z6+AJjaT2e_J$GI#H5pOm;wfM{5`M|$yu@pFOAJ9
zDs?rCs}p2x{4DojLsW~pK&&FvMZGAJ-qZy#t3VYcr+76F)r}=pOV%K)s<>;S7{kg6
zWzK7pD%H~k9Y<FxqF6oO+oPxkwpPsk%=V~LtY#a=4N<7jRjPsI4gm{hz@7pKX1Pp^
zXh8O<*KUsx)O_W5tv~u2sV88vwgbQ%SqhN0jxWRf7h%2_-dqZAHo~VC^$#xp;j%IK
z^5W&<wo|2Tr;Na<Mia)<8PApnvjDJUW<?b@AGqwQH)qMUvQDsMb3uwQ%S~}tiSx`E
zSmx|bs`CvT#3LR8ah#Cz^&YZK<0sjj0nc(<P*ol@EAwg4d8K+rgYL7)s7R=~n3Ktp
zJrj&T(XnvW(08g%&`_68D-xVbTCDDpA_YY^BI`b773-LYYfvYP;IR|nsd?}<G&s0n
zvA56A_O|s&QLla(Cs|xUEe`8}?dep>PvG{k9><EmZXw1wY($@g{nssnCmC8eFehJB
zG}yUyoqz&~#rO+QU&0MadQ!|!$Wm`Fzbm$TZ*2q&N~nBR1tR4!r9#=ly$lm^Mdp%n
zq2?(t!FCpAi~NzX;bVNy5B=!qd1b1?Wxa4vlKE}AEtjgS;H&jBkY`k*sPmGF;~D3*
zO**PKADHxBwC(o*yv-dY9H#nYfV?tvnX9+%Sx!e!tTbn_N>|k<&R4<GCE~n3x(o-`
zo9mqG)ye|Bnrg%02VbdOAZXmR_$>D_xxsPd+Dmxg(HLwY>NSIkrsOqU%%^434YpzW
z!3Oct$MjkDNJBW#ZTc*)Fu4p1Dvr#{Y_YT`SYu|Mu7&doK^G@t`?D(el6ElW7@ULU
zwG4oVUz4vPzLZz5Un2gNE(R93yOE8*72l5(BU|B80TPx~JMVUNFM1ZmTOGx&Sg9+v
z(04D=vGBtCFWq|e!;61jU)&NeZHX_uQ0~}pZ%yZd-}+k$+bgoL^oR>OwC91(GworD
zZ|m-r$a2j&<48rC9W`lk^k*<X>M~uiUGyY$HB@ya=ss%NKOyJki&OOA%(m~BPj%ol
zuCX0}B^skU0ne@7@4FZKe(!tFXT8@$#vO!xG)L2OPF3=D;yeZziiaapT*^KPf;Iij
zbgZ<i(>Mirf$U{zM*vbs(g?omAZ=?Fq#Md_6l*shwS3g_Y39@XKRm@<!=+urfGu_>
zN&wd+N^26u@JJ~<Vg#&m#xlha-H%(u%$#CAXZi&}Qqzz>V~WR;Ke`ipg9z}Tqr(Ws
z5%eKA3_!!<+{%aPYD6OFLc+%YfCu|;$>sG1pi%?S`ji(k(ME9Pwfl(uwnjS6ktd!4
zBlPZjJn(tFP|Q_9tH*m9(%}Xe>h>N7{C|c_rHqX<a8PZS?BMUgaRAPI&=Fuj9(SrK
zj@n*NJ68=vI@(L6UBGOZn|brcXONhB{)p@`>M=cMRW)l>{XT0Yvvu_3VYt>2hT~&L
zMg~S{9JXzV2yfl2ToV({8TeIhr66?-wY}}Xdf?gtqxrzQr{4MbpI$dM9xyxyEO%KC
zR|<H)Z55;#3#<w)m}CD#p@|vUa;VN}nVz4K3llNCJ%?afNb3b!xr?waA6lzJ3c8Zj
zSY2oOE7$Q9>tZuxU(N$ZA~S9?Y@;f90gJg-Imn7fkFxvuJgbAv=AqmaB#&`QRZ2x%
z&!?U}C#QAGIhf6}gxe{YM}hrTb$XCyVG>$|Rsgu<<~XiQHX6>SOx7CCXC+MItR90n
zo-)~IIG-}vVK|>M=`x&8nQSqfPnm2poX<VN8_s9R<L0``p3O$X^RVo12Z!Y#cG;jD
z>6|-;e<*;ihYue@THF4dv1%_obFJk->lNSCz_q}fcgZJk9D3^G|5@<dx2WGb2MxHr
e+(8Hh4M1xL@qgy=^)-{vn*DvN-Jf-E0RIi|uffs)

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 d4ee7b4c91f2cf66961434de6bc21c4f859e9c8c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 2060
zcmZ`(&2Jk;6rWkI?ag|lwAkPzZHcL>5>uKlAcTOBqE@OZsuW1!U|Fm--c90WckRxM
zU7BtyA9`?6fnHDoq9~^{kV^E(iNC=?matZe6bW(Y%~cAAo_I6PI<`XJ&b)o^&DWdX
zy!RgV_NoY||M#D)Z)Al2;z~fE3))+O-zqYZiEZTI48~lRY{>~_LP8FKEN5gP%XZj_
zWFkTi+fhf!C?H2NJ!bR{%BT{$fK25MGJ7^8gtnl!C=-*A725!1D=?vqW~y^29ectp
zY3xU7saPx!24XB%BE)i;ZWN1rgjm-~7Ud)AqQ!K(kTYxxv|h)e)L68rUNqQJTJp6k
z#Bix?uzbO#zQ#JVT>D@U!q7v|_x=U8iWu;KsAS?f0D!ffN9bx>E@L9bSu~iBx>lPZ
z6K}xITYPsxw~&V_7nl<0up2*J(0U}0I{&QiK<kE#9evTrA~xkm;ln3)-c0WaR(lA5
zkE|G<TC7A|nqo^<Dpvr=b1Ve_GwVy1>cnnDO#z)N6mO&!NWn=h*alrbJ8#TecItYb
zEv0C|u~JUK%rE3~E$~}QX8o|3<tMJQg#vM?-np$G-M7M1M$Cd^<lVGNI68z!r>}w^
zmP^H3M!_$GF+w=7grn)pcEK<Se+a^HBpR?ZCj6EZ14}*s=-hPnGini<%}tEGGyc}Z
zWcGX^S8^b<X!fGRBPUz7+;a9(em+a{%sN{%a?8RjuBd~5qxtbEccgg3KdSTC>-3+*
z;S`=>)B{#Q|ENfdHxUje8h!C9YetaL_uX>+l{afq-h}b*_~3(gYp14vJHH*DsmEt3
zmv*D^TJ+dX^3{6s%%91zKayiR+Q8k*_bxxU`n~<5z4pc9i_H+~AAgFZe)Sn=RSmWt
ze*5caT{*QIOV;G%9-NPIoCU-I;IuagY!z83gH4oy*o8C=nG$f>3_%pg0?S-Gz+pB=
znD@Pp#Zne*1zLAF-jY12B6-+Nc+z^uESR`Y9^L@^R(lK%#ItEh0Ou>E{a8zfe3@GI
zf*+xb<lRLdGs+!+m?JO2-{+6dgK{Ntr6Z$9_N^yj3Vj_|1?^}jwzZ+UHdMupfx)$b
zYPnk8#J{S)sE;SNho|eq(=}z9Bw%hXsDB_IlminKC<kqS58YVy<cjJ+k}nIy2){k{
zq&eitFG9eRyij*y0t(XX6MjelMNi2Mun3cW81NMeK$L8Q_>rPrB8Kf_m+-fwJU;-L
z02?J(x$<%sIDz>zUo#D?3!UAVR(-EAlv<zpp}bbUKXrd<bF!w5@Ae(}_U2bN*QVA#
z-~42|Z?xVwT2n>^V6*A4ujsmKIF_#as;)!2muxO;x_+%>*sT$A42~lFNq$CRop?bJ
zWP-iM-I);XRKO+vH6<zheiOBs5($qr(P7rwj;H=?x|hV@FN%R57P%@)hA19>td+c?
z@`%#lYx{*}geS8o8GcmP4cpdr5sNMVc;z4*uLH-QD<8D(`5`$21N_bCBVf%C#<+n7
zYTd1YPSm>F|IImBLtU+bPS?8IuKGqL+KeP{e-pLYNDKomhuKqZdv-8z9344c_3qxj
Lce|qTPu%7|PW|9T

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 50973e0..0000000
--- 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 06afa0c..0000000
--- 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 5646600..0000000
--- a/venv/Lib/site-packages/flask_wtf/file.py
+++ /dev/null
@@ -1,151 +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):
-        if not isinstance(field.data, list):
-            field.data = [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):
-        if not isinstance(field.data, list):
-            field.data = [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):
-        if not isinstance(field.data, list):
-            field.data = [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())
-            print(f, file_size, self.max_size, self.min_size)
-            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 c7f52e0..0000000
--- 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 1cc0e9c..0000000
--- 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 3100d37..0000000
--- 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 fe79a9aef7d9d7147a94eab5c4c4a48536547539..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 380
zcmZ3^%ge<81QPq^rDg%?#~=<2FhLogwSbK23@HpLj5!Rsj8Tk?AU0DDQ!aB9Gnmbs
z!;;Gy#R_Dz1T$!|zGMVy&}6*D7nGWuSWuFjk?5A0nv(();e?9-Is9-=cxFm^YKfmF
z+by;<kjmmBW}peTxXKc9GE)*u@{5X#SU>{o<zR)yMXW&HN`}uslHpgRvsFxJacWU<
zOtO)IsiCovS&U15a%paAUP*C`XKrG8YH>_iYF=54Pi9g~ab`)XZb4#lHdr_<C$Ts?
zzPuz&zX;}1{rLFIyv&mLc)fzkUmP~M`6;D2sdhy?K$}3mDs}`CAD9^#8E-HsT|h-Q
T7!)s{q8kh<7qFosP@n?<B|U4n

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 eba47cb335209d9abc36f31be5779b684315a9cc..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1134
zcmb7C&ubGw6rS15W;bcmq7quDXoXV9!M0Y~Ls1cxN@<CpmckMkx4V<L?dFFwn?@x<
z4h18qc#(RN3biUd`49LvMCc*Rso=?5q#mRv-|TjSK`*|Y{r0_`H*dc8&3s6uk_c@0
z%Uk*xBlJxMk!i=y;SxAo$UzSFP!XFLBS&#mPbsRV8u&3UR@6)le64`ZxPopXC%y?0
z#k(p(d+>&4>W+Q~WsL7K%wVC-yVP^O!_G3&YpzqEJ_k2(o7z^zx9?kGpz8+7c33II
zsPN%6h@S#UicIVvQ&A9A*J4rXE;xHpfK1H+l1{9N^4L)~6Q<ro53y+gF^%bvmKDg3
zD$7+;h(34^?*Y8VTd1y727F2S*m~<1@DteI|D!_h{F)N2tK+(oS6ozAckoLoD*_HU
zn^9RDQiR5<70QG`h+A@fLNc0&aq8uT=2@%M6UhzBa~;bsGcMxmHH#IvNDyLs7UzU;
zNqZEmakO%MF1O4n<2ieJYUaYl>Dk<kvRy6GlFxGsMXNx0ZiALKa!c-Nj=MfRU$N}<
z;CCL1t&^IcpJdUAOy=dG@rlYK)(=}e9_l@yiHPnXO!7H^Uk%hwpKYb5-Y>jc+*#bd
z+)TEO;TPI7{kgtx48KWlCtJo0jF0*7+r>jzjWP;Lz$cqYNGVEIkrE=3gcQq8)sucd
zArGpS7tS!*wixVkS_$ryh=+Ge40hWfl(8}kS`evY)oki{pfhGCpa_>Ie3hL946a20
zk_o>H08K?tJ2lo^*-wpqO^xrT#vAjUI2syh-0H*>ZMK8{1K24joXr?w00b|yBA*DQ
zX7VltA_Uzrscd*fbV4jiIpFpQ1qA}eN5-X4pDXMPWXo1~4WOf9jN53m)jJMQy45?{
h=v=FJ9Q2K}lBXMbCvITq>u-3q{6qfz`H!i9*l%xz82tbM

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 1fd4e8df320b2b9c464813d4b27e912dd20f570a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 3739
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

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 6a613bad6fb737df45ae0e6d900e6d7b2bdaea14..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 2875
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
M-hcm(zz)Oz03GkH&j0`b

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 e91fd09..0000000
--- 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 c5cafb3..0000000
--- 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 bfae830..0000000
--- 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)
-- 
GitLab