From 72cfff1b8fc0aa1ed414364af2ef0a4e3c4cb278 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 09:56:25 +0100 Subject: [PATCH 01/61] commit --- app.py | 28 ++++++++++++++++++++++++++-- index.html | 0 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 index.html diff --git a/app.py b/app.py index d82c51f0d..5b600b0a2 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,30 @@ from flask import Flask -app = Flask(__name__) +app = Flask("Studsight") +app.secret_key = "davidneastudsightkey.com" @app.route('/') def hello_world(): - return 'Hello, World!' + return 'Welcome to studsight!' + +@app.route("/login") # Login route +def login(): + pass + +@app.route("/callback") # Callback route +def callback(): + pass# + +@app.route("/logout") # Logout route +def logout(): + pass + +@app.route("/") # Home route +def index(): + return render_template("index.html") + +@app.route("/protected_area") +def protected_area(): + pass + +if __name__ == "__main__": # Run the app + app.run(debug=True) \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 000000000..e69de29bb From ece2e633076c34d5c732c22f2c749a044e387afa Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 10:08:14 +0100 Subject: [PATCH 02/61] Delete index.html --- index.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 index.html diff --git a/index.html b/index.html deleted file mode 100644 index e69de29bb..000000000 From a849bbb638c5153401360ee3147202adc32cb044 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 11:34:33 +0100 Subject: [PATCH 03/61] Update app.py --- app.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/app.py b/app.py index 5b600b0a2..54f6dd367 100644 --- a/app.py +++ b/app.py @@ -1,10 +1,16 @@ -from flask import Flask +from flask import Flask, render_template, session, abort app = Flask("Studsight") app.secret_key = "davidneastudsightkey.com" -@app.route('/') -def hello_world(): - return 'Welcome to studsight!' +def login_is_required(function): + def wrapper(*args, **kwargs): + if "google_id" not in session: + abort(401) # Authorisation required + else: + return function() + + return wrapper + @app.route("/login") # Login route def login(): @@ -20,11 +26,12 @@ def logout(): @app.route("/") # Home route def index(): - return render_template("index.html") + return "hi there...." -@app.route("/protected_area") +@app.route("/protected_area") +@login_is_required def protected_area(): pass if __name__ == "__main__": # Run the app - app.run(debug=True) \ No newline at end of file + app.run(debug=True) From adf1557fe3ab05905d723f532d93304012a83b39 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 11:35:05 +0100 Subject: [PATCH 04/61] Create require.txt --- require.txt | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 require.txt diff --git a/require.txt b/require.txt new file mode 100644 index 000000000..2426ece65 --- /dev/null +++ b/require.txt @@ -0,0 +1,136 @@ +anyio==4.9.0 +argon2-cffi==25.1.0 +argon2-cffi-bindings==21.2.0 +arrow==1.3.0 +asttokens==3.0.0 +async-lru==2.0.5 +attrs==25.3.0 +babel==2.17.0 +beautifulsoup4==4.13.4 +bleach==6.2.0 +blinker==1.9.0 +cachetools==5.5.2 +certifi==2025.7.9 +cffi==1.17.1 +charset-normalizer==3.4.2 +click==8.2.1 +colorama==0.4.6 +comm==0.2.2 +contourpy==1.3.2 +cycler==0.12.1 +debugpy==1.8.14 +decorator==5.2.1 +defusedxml==0.7.1 +executing==2.2.0 +fastjsonschema==2.21.1 +filelock==3.13.1 +Flask==3.1.2 +fonttools==4.58.5 +fqdn==1.5.1 +fsspec==2024.6.1 +gitdb==4.0.12 +GitPython==3.1.44 +google-auth==2.40.3 +google-auth-oauthlib==1.2.2 +gunicorn==23.0.0 +h11==0.16.0 +httpcore==1.0.9 +httpx==0.28.1 +idna==3.10 +ipykernel==6.29.5 +ipython==9.4.0 +ipython_pygments_lexers==1.1.1 +isoduration==20.11.0 +itsdangerous==2.2.0 +jedi==0.19.2 +Jinja2==3.1.6 +joblib==1.5.1 +json5==0.12.0 +jsonpointer==3.0.0 +jsonschema==4.24.0 +jsonschema-specifications==2025.4.1 +jupyter-events==0.12.0 +jupyter-lsp==2.2.5 +jupyter-server-mathjax==0.2.6 +jupyter_client==8.6.3 +jupyter_core==5.8.1 +jupyter_server==2.16.0 +jupyter_server_terminals==0.5.3 +jupyterlab==4.4.4 +jupyterlab_git==0.51.2 +jupyterlab_pygments==0.3.0 +jupyterlab_server==2.27.3 +kiwisolver==1.4.8 +MarkupSafe==3.0.2 +matplotlib==3.10.3 +matplotlib-inline==0.1.7 +mistune==3.1.3 +mpmath==1.3.0 +narwhals==1.46.0 +nbclient==0.10.2 +nbconvert==7.16.6 +nbdime==4.0.2 +nbformat==5.10.4 +nest-asyncio==1.6.0 +networkx==3.3 +notebook_shim==0.2.4 +numpy==2.3.1 +oauthlib==3.3.1 +overrides==7.7.0 +packaging==25.0 +pandas==2.3.1 +pandocfilters==1.5.1 +parso==0.8.4 +pexpect==4.9.0 +pillow==11.3.0 +platformdirs==4.3.8 +plotly==6.2.0 +prometheus_client==0.22.1 +prompt_toolkit==3.0.51 +psutil==7.0.0 +ptyprocess==0.7.0 +pure_eval==0.2.3 +pyasn1==0.6.1 +pyasn1_modules==0.4.2 +pycparser==2.22 +Pygments==2.19.2 +pyparsing==3.2.3 +python-dateutil==2.9.0.post0 +python-json-logger==3.3.0 +pytz==2025.2 +PyYAML==6.0.2 +pyzmq==27.0.0 +referencing==0.36.2 +requests==2.32.4 +requests-oauthlib==2.0.0 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +rpds-py==0.26.0 +rsa==4.9.1 +scikit-learn==1.7.0 +scipy==1.16.0 +seaborn==0.13.2 +Send2Trash==1.8.3 +setuptools==80.9.0 +six==1.17.0 +smmap==5.0.2 +sniffio==1.3.1 +soupsieve==2.7 +stack-data==0.6.3 +sympy==1.13.3 +terminado==0.18.1 +threadpoolctl==3.6.0 +tinycss2==1.4.0 +torch==2.7.1+cpu +tornado==6.5.1 +traitlets==5.14.3 +types-python-dateutil==2.9.0.20250708 +typing_extensions==4.14.1 +tzdata==2025.2 +uri-template==1.3.0 +urllib3==2.5.0 +wcwidth==0.2.13 +webcolors==24.11.1 +webencodings==0.5.1 +websocket-client==1.8.0 +Werkzeug==3.1.3 From aa627eef3b4be79f686d4d9ee81ca8df035a41e4 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 10:39:36 +0000 Subject: [PATCH 05/61] just hi --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 54f6dd367..3752954ca 100644 --- a/app.py +++ b/app.py @@ -26,7 +26,7 @@ def logout(): @app.route("/") # Home route def index(): - return "hi there...." + return "hi." @app.route("/protected_area") @login_is_required From db31637d8d2c69e3c3c6d15b7a22b0b2f8a229cf Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 10:42:41 +0000 Subject: [PATCH 06/61] yo --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 3752954ca..390349d0e 100644 --- a/app.py +++ b/app.py @@ -26,7 +26,7 @@ def logout(): @app.route("/") # Home route def index(): - return "hi." + return "me." @app.route("/protected_area") @login_is_required From 174195b89d2ea84a72502eb89ef239d2daf2812d Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 10:45:52 +0000 Subject: [PATCH 07/61] add html --- app.py | 4 ++-- home.html | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 home.html diff --git a/app.py b/app.py index 390349d0e..36ec4dabf 100644 --- a/app.py +++ b/app.py @@ -25,8 +25,8 @@ def logout(): pass @app.route("/") # Home route -def index(): - return "me." +def home(): + return render_template("home.html") @app.route("/protected_area") @login_is_required diff --git a/home.html b/home.html new file mode 100644 index 000000000..5bb7143e3 --- /dev/null +++ b/home.html @@ -0,0 +1,49 @@ + + + + + StudSight - Home + + + +
+

Welcome to StudSight

+ +
+ + \ No newline at end of file From a3fd91a27673b5b482daa0217eee57e6a8bd7a7b Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 10:50:40 +0000 Subject: [PATCH 08/61] templates --- home.html => templates/home.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename home.html => templates/home.html (100%) diff --git a/home.html b/templates/home.html similarity index 100% rename from home.html rename to templates/home.html From b06a2c1a2f9136c5e2dc7b79d45fb52a625df863 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 10:56:44 +0000 Subject: [PATCH 09/61] add log --- app.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index 36ec4dabf..17b533c26 100644 --- a/app.py +++ b/app.py @@ -1,4 +1,4 @@ -from flask import Flask, render_template, session, abort +from flask import Flask, render_template, session, abort, redirect, url_for app = Flask("Studsight") app.secret_key = "davidneastudsightkey.com" @@ -14,7 +14,8 @@ def wrapper(*args, **kwargs): @app.route("/login") # Login route def login(): - pass + session["google_id"] = "example_google_id" + return redirect(url_for("protected_area")) @app.route("/callback") # Callback route def callback(): @@ -31,7 +32,7 @@ def home(): @app.route("/protected_area") @login_is_required def protected_area(): - pass + return "Welcome to the protected area." if __name__ == "__main__": # Run the app app.run(debug=True) From 2e1e38ef822ed10f778dea874ea60260349bbfe4 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 10:58:13 +0000 Subject: [PATCH 10/61] logn --- templates/home.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/home.html b/templates/home.html index 5bb7143e3..928532e86 100644 --- a/templates/home.html +++ b/templates/home.html @@ -43,7 +43,7 @@

Welcome to StudSight

- +
\ No newline at end of file From 6d361bc8232666601b5ff2f97e9bb41a4e36bcbd Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 11:01:46 +0000 Subject: [PATCH 11/61] url for login --- templates/home.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/home.html b/templates/home.html index 928532e86..daef06790 100644 --- a/templates/home.html +++ b/templates/home.html @@ -43,7 +43,7 @@

Welcome to StudSight

- +
\ No newline at end of file From 679400a234e0b6e23a72fda7d981c0ffa9f79be3 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 11:04:17 +0000 Subject: [PATCH 12/61] red --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 17b533c26..7a31b33db 100644 --- a/app.py +++ b/app.py @@ -15,7 +15,7 @@ def wrapper(*args, **kwargs): @app.route("/login") # Login route def login(): session["google_id"] = "example_google_id" - return redirect(url_for("protected_area")) + return redirect("/protected_area") @app.route("/callback") # Callback route def callback(): From 535ba2cdbd4ba9ab6b5b423e3e5306766f64d81a Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 11:08:26 +0000 Subject: [PATCH 13/61] log out --- app.py | 2 +- templates/protected_area.html | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 templates/protected_area.html diff --git a/app.py b/app.py index 7a31b33db..5ca176134 100644 --- a/app.py +++ b/app.py @@ -32,7 +32,7 @@ def home(): @app.route("/protected_area") @login_is_required def protected_area(): - return "Welcome to the protected area." + return render_template("protected_area.html", email=session["google_id"]) if __name__ == "__main__": # Run the app app.run(debug=True) diff --git a/templates/protected_area.html b/templates/protected_area.html new file mode 100644 index 000000000..088428bb2 --- /dev/null +++ b/templates/protected_area.html @@ -0,0 +1,15 @@ + + + + + + Protected Area + + +
+

You are logged in

+

Welcome to the Protected Area

+ +
+ + \ No newline at end of file From f4e6b5e4e1916de8fbb5f8dceda673524292ebae Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 11:10:10 +0000 Subject: [PATCH 14/61] style sgn out --- templates/protected_area.html | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/templates/protected_area.html b/templates/protected_area.html index 088428bb2..5808588aa 100644 --- a/templates/protected_area.html +++ b/templates/protected_area.html @@ -4,6 +4,40 @@ Protected Area +
From 423263e149bf334d96c3ef277f4498eb2a6c81f6 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 11:12:12 +0000 Subject: [PATCH 15/61] continue --- app.py | 2 +- templates/logout.html | 0 templates/protected_area.html | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 templates/logout.html diff --git a/app.py b/app.py index 5ca176134..965d2fbf2 100644 --- a/app.py +++ b/app.py @@ -23,7 +23,7 @@ def callback(): @app.route("/logout") # Logout route def logout(): - pass + return render_template("logout.html") @app.route("/") # Home route def home(): diff --git a/templates/logout.html b/templates/logout.html new file mode 100644 index 000000000..e69de29bb diff --git a/templates/protected_area.html b/templates/protected_area.html index 5808588aa..bfc12e65a 100644 --- a/templates/protected_area.html +++ b/templates/protected_area.html @@ -43,7 +43,7 @@

You are logged in

Welcome to the Protected Area

- +
\ No newline at end of file From 8e678a3e302f50c418aa2af6dc91dedefd7458f3 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 12:29:03 +0000 Subject: [PATCH 16/61] iso --- app.py | 2 +- templates/logout.html | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index 965d2fbf2..4c885c6d4 100644 --- a/app.py +++ b/app.py @@ -23,7 +23,7 @@ def callback(): @app.route("/logout") # Logout route def logout(): - return render_template("logout.html") + return redirect("/") @app.route("/") # Home route def home(): diff --git a/templates/logout.html b/templates/logout.html index e69de29bb..fcc2cddc7 100644 --- a/templates/logout.html +++ b/templates/logout.html @@ -0,0 +1,10 @@ + + + + + Logged Out + + + + + \ No newline at end of file From cd363adca0531bd236aa76156398263fe5e8bb2f Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 12:33:48 +0000 Subject: [PATCH 17/61] coms --- templates/home.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/home.html b/templates/home.html index daef06790..294836623 100644 --- a/templates/home.html +++ b/templates/home.html @@ -43,7 +43,7 @@

Welcome to StudSight

- +
\ No newline at end of file From 9be3f4a8b41cfcd6868b09182d333f364a196d31 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 12 Sep 2025 12:34:18 +0000 Subject: [PATCH 18/61] sub --- templates/home.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/home.html b/templates/home.html index 294836623..daef06790 100644 --- a/templates/home.html +++ b/templates/home.html @@ -43,7 +43,7 @@

Welcome to StudSight

- +
\ No newline at end of file From c686a673c06b78814521491d0986e17e3ff43562 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 04:50:52 +0000 Subject: [PATCH 19/61] call --- app.py | 3 ++- templates/home.html | 2 +- templates/logout.html | 10 ---------- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/app.py b/app.py index 4c885c6d4..2e3492397 100644 --- a/app.py +++ b/app.py @@ -19,10 +19,11 @@ def login(): @app.route("/callback") # Callback route def callback(): - pass# + return "call" @app.route("/logout") # Logout route def logout(): + session.clear() return redirect("/") @app.route("/") # Home route diff --git a/templates/home.html b/templates/home.html index daef06790..e81592099 100644 --- a/templates/home.html +++ b/templates/home.html @@ -43,7 +43,7 @@

Welcome to StudSight

- +
\ No newline at end of file diff --git a/templates/logout.html b/templates/logout.html index fcc2cddc7..e69de29bb 100644 --- a/templates/logout.html +++ b/templates/logout.html @@ -1,10 +0,0 @@ - - - - - Logged Out - - - - - \ No newline at end of file From 107abb465c3eb3ee8ede92b3761462f902e987ba Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 04:52:29 +0000 Subject: [PATCH 20/61] lll --- templates/home.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/home.html b/templates/home.html index e81592099..daef06790 100644 --- a/templates/home.html +++ b/templates/home.html @@ -43,7 +43,7 @@

Welcome to StudSight

- +
\ No newline at end of file From 6c0a063d2c5a83f21da04bc0ab39383e7188f226 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 04:52:56 +0000 Subject: [PATCH 21/61] pss --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 2e3492397..37f0588b7 100644 --- a/app.py +++ b/app.py @@ -19,7 +19,7 @@ def login(): @app.route("/callback") # Callback route def callback(): - return "call" + pass @app.route("/logout") # Logout route def logout(): From 7014474051097e0bc1faadb7d89ce2d1ce95819a Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 06:12:06 +0100 Subject: [PATCH 22/61] Update app.py --- app.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index 37f0588b7..accbb51e8 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,22 @@ from flask import Flask, render_template, session, abort, redirect, url_for +import pathlib,os +from google_auth_oauthlib.flow import Flow + app = Flask("Studsight") app.secret_key = "davidneastudsightkey.com" +client_secrets_file = os.path.join(pathlib.Path(__file__).parent, "client_secret.json") + + +GOOGLE_CLIENT_ID = "771970138692-gjilmd2o08eitr81o07oiuhfe7m5ardh.apps.googleusercontent.com" + +# Example initialization (update with your actual client secrets file and scopes) +flow = Flow.from_client_secrets_file( + client_secrets_file=client_secrets_file, + scopes=["https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email", "openid"], + redirect_uri="https://nea-studsight.onrender.com/callback" + + ) + def login_is_required(function): def wrapper(*args, **kwargs): @@ -12,28 +28,36 @@ def wrapper(*args, **kwargs): return wrapper + + @app.route("/login") # Login route def login(): - session["google_id"] = "example_google_id" - return redirect("/protected_area") + authorization_url, state = flow.authorization_url() + session["state"] = state + return redirect(authorization_url) + @app.route("/callback") # Callback route def callback(): pass + @app.route("/logout") # Logout route def logout(): session.clear() return redirect("/") + @app.route("/") # Home route def home(): return render_template("home.html") + @app.route("/protected_area") @login_is_required def protected_area(): return render_template("protected_area.html", email=session["google_id"]) + if __name__ == "__main__": # Run the app app.run(debug=True) From 51e405f5b39870d8caf5526708a4dc5876d5829e Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 06:16:52 +0100 Subject: [PATCH 23/61] clien --- client_secret.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 client_secret.json diff --git a/client_secret.json b/client_secret.json new file mode 100644 index 000000000..e597839d8 --- /dev/null +++ b/client_secret.json @@ -0,0 +1,13 @@ +{ + "web": { + "client_id": "771970138692-gjilmd2o08eitr81o07oiuhfe7m5ardh.apps.googleusercontent.com", + "project_id": "nea-studsight", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_secret": "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", + "redirect_uris": [ + "https://nea-studsight.onrender.com/callback" + ] + } +} \ No newline at end of file From 889db392f450c1fa4e0bc1d8fcaab5e8f2d6af46 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 05:35:41 +0000 Subject: [PATCH 24/61] fin --- app.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index accbb51e8..170c49daa 100644 --- a/app.py +++ b/app.py @@ -1,12 +1,16 @@ -from flask import Flask, render_template, session, abort, redirect, url_for +from flask import Flask, render_template, session, abort, redirect, url_for, request import pathlib,os +from googl.oauth2 import id_token from google_auth_oauthlib.flow import Flow +from pip._vendor import cachecontrol +import google.auth.transport.requests app = Flask("Studsight") app.secret_key = "davidneastudsightkey.com" -client_secrets_file = os.path.join(pathlib.Path(__file__).parent, "client_secret.json") +os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1" +client_secrets_file = os.path.join(pathlib.Path(__file__).parent, "client_secret.json") GOOGLE_CLIENT_ID = "771970138692-gjilmd2o08eitr81o07oiuhfe7m5ardh.apps.googleusercontent.com" # Example initialization (update with your actual client secrets file and scopes) @@ -39,7 +43,22 @@ def login(): @app.route("/callback") # Callback route def callback(): - pass + flow.fetch_token(authorization_response=request.url) + + if not session["state"] == request.args["state"]: + abort(500) # State does not match! + + credentials = flow.credentials + request_session = request.session() + cached_session = cachecontrol.CacheControl(request_session) + token_request = google.auth.transport.requests.Request(session=cached_session) + + id_info = id_token.verify_oauth2_token( + id_token=credentials._id_token, + request=token_request, + audience=GOOGLE_CLIENT_ID + ) + return id_info @app.route("/logout") # Logout route From 4dc70f0a5bc5a93df29d1d357d6642a43a9e9d7f Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 05:36:11 +0000 Subject: [PATCH 25/61] fin2 --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 170c49daa..68e19c4e4 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,6 @@ from flask import Flask, render_template, session, abort, redirect, url_for, request import pathlib,os -from googl.oauth2 import id_token +from google.oauth2 import id_token from google_auth_oauthlib.flow import Flow from pip._vendor import cachecontrol import google.auth.transport.requests From 9bc04d40cc1df6bba40bdc918a0309f02971e850 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 05:45:25 +0000 Subject: [PATCH 26/61] rrr --- require.txt | 137 +---------------------------------------------- requirements.txt | 136 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 136 deletions(-) diff --git a/require.txt b/require.txt index 2426ece65..8b1378917 100644 --- a/require.txt +++ b/require.txt @@ -1,136 +1 @@ -anyio==4.9.0 -argon2-cffi==25.1.0 -argon2-cffi-bindings==21.2.0 -arrow==1.3.0 -asttokens==3.0.0 -async-lru==2.0.5 -attrs==25.3.0 -babel==2.17.0 -beautifulsoup4==4.13.4 -bleach==6.2.0 -blinker==1.9.0 -cachetools==5.5.2 -certifi==2025.7.9 -cffi==1.17.1 -charset-normalizer==3.4.2 -click==8.2.1 -colorama==0.4.6 -comm==0.2.2 -contourpy==1.3.2 -cycler==0.12.1 -debugpy==1.8.14 -decorator==5.2.1 -defusedxml==0.7.1 -executing==2.2.0 -fastjsonschema==2.21.1 -filelock==3.13.1 -Flask==3.1.2 -fonttools==4.58.5 -fqdn==1.5.1 -fsspec==2024.6.1 -gitdb==4.0.12 -GitPython==3.1.44 -google-auth==2.40.3 -google-auth-oauthlib==1.2.2 -gunicorn==23.0.0 -h11==0.16.0 -httpcore==1.0.9 -httpx==0.28.1 -idna==3.10 -ipykernel==6.29.5 -ipython==9.4.0 -ipython_pygments_lexers==1.1.1 -isoduration==20.11.0 -itsdangerous==2.2.0 -jedi==0.19.2 -Jinja2==3.1.6 -joblib==1.5.1 -json5==0.12.0 -jsonpointer==3.0.0 -jsonschema==4.24.0 -jsonschema-specifications==2025.4.1 -jupyter-events==0.12.0 -jupyter-lsp==2.2.5 -jupyter-server-mathjax==0.2.6 -jupyter_client==8.6.3 -jupyter_core==5.8.1 -jupyter_server==2.16.0 -jupyter_server_terminals==0.5.3 -jupyterlab==4.4.4 -jupyterlab_git==0.51.2 -jupyterlab_pygments==0.3.0 -jupyterlab_server==2.27.3 -kiwisolver==1.4.8 -MarkupSafe==3.0.2 -matplotlib==3.10.3 -matplotlib-inline==0.1.7 -mistune==3.1.3 -mpmath==1.3.0 -narwhals==1.46.0 -nbclient==0.10.2 -nbconvert==7.16.6 -nbdime==4.0.2 -nbformat==5.10.4 -nest-asyncio==1.6.0 -networkx==3.3 -notebook_shim==0.2.4 -numpy==2.3.1 -oauthlib==3.3.1 -overrides==7.7.0 -packaging==25.0 -pandas==2.3.1 -pandocfilters==1.5.1 -parso==0.8.4 -pexpect==4.9.0 -pillow==11.3.0 -platformdirs==4.3.8 -plotly==6.2.0 -prometheus_client==0.22.1 -prompt_toolkit==3.0.51 -psutil==7.0.0 -ptyprocess==0.7.0 -pure_eval==0.2.3 -pyasn1==0.6.1 -pyasn1_modules==0.4.2 -pycparser==2.22 -Pygments==2.19.2 -pyparsing==3.2.3 -python-dateutil==2.9.0.post0 -python-json-logger==3.3.0 -pytz==2025.2 -PyYAML==6.0.2 -pyzmq==27.0.0 -referencing==0.36.2 -requests==2.32.4 -requests-oauthlib==2.0.0 -rfc3339-validator==0.1.4 -rfc3986-validator==0.1.1 -rpds-py==0.26.0 -rsa==4.9.1 -scikit-learn==1.7.0 -scipy==1.16.0 -seaborn==0.13.2 -Send2Trash==1.8.3 -setuptools==80.9.0 -six==1.17.0 -smmap==5.0.2 -sniffio==1.3.1 -soupsieve==2.7 -stack-data==0.6.3 -sympy==1.13.3 -terminado==0.18.1 -threadpoolctl==3.6.0 -tinycss2==1.4.0 -torch==2.7.1+cpu -tornado==6.5.1 -traitlets==5.14.3 -types-python-dateutil==2.9.0.20250708 -typing_extensions==4.14.1 -tzdata==2025.2 -uri-template==1.3.0 -urllib3==2.5.0 -wcwidth==0.2.13 -webcolors==24.11.1 -webencodings==0.5.1 -websocket-client==1.8.0 -Werkzeug==3.1.3 + diff --git a/requirements.txt b/requirements.txt index 147ddd086..8fcb6c03d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,138 @@ Flask Gunicorn +anyio==4.9.0 +argon2-cffi==25.1.0 +argon2-cffi-bindings==21.2.0 +arrow==1.3.0 +asttokens==3.0.0 +async-lru==2.0.5 +attrs==25.3.0 +babel==2.17.0 +beautifulsoup4==4.13.4 +bleach==6.2.0 +blinker==1.9.0 +cachetools==5.5.2 +certifi==2025.7.9 +cffi==1.17.1 +charset-normalizer==3.4.2 +click==8.2.1 +colorama==0.4.6 +comm==0.2.2 +contourpy==1.3.2 +cycler==0.12.1 +debugpy==1.8.14 +decorator==5.2.1 +defusedxml==0.7.1 +executing==2.2.0 +fastjsonschema==2.21.1 +filelock==3.13.1 +Flask==3.1.2 +fonttools==4.58.5 +fqdn==1.5.1 +fsspec==2024.6.1 +gitdb==4.0.12 +GitPython==3.1.44 +google-auth==2.40.3 +google-auth-oauthlib==1.2.2 +gunicorn==23.0.0 +h11==0.16.0 +httpcore==1.0.9 +httpx==0.28.1 +idna==3.10 +ipykernel==6.29.5 +ipython==9.4.0 +ipython_pygments_lexers==1.1.1 +isoduration==20.11.0 +itsdangerous==2.2.0 +jedi==0.19.2 +Jinja2==3.1.6 +joblib==1.5.1 +json5==0.12.0 +jsonpointer==3.0.0 +jsonschema==4.24.0 +jsonschema-specifications==2025.4.1 +jupyter-events==0.12.0 +jupyter-lsp==2.2.5 +jupyter-server-mathjax==0.2.6 +jupyter_client==8.6.3 +jupyter_core==5.8.1 +jupyter_server==2.16.0 +jupyter_server_terminals==0.5.3 +jupyterlab==4.4.4 +jupyterlab_git==0.51.2 +jupyterlab_pygments==0.3.0 +jupyterlab_server==2.27.3 +kiwisolver==1.4.8 +MarkupSafe==3.0.2 +matplotlib==3.10.3 +matplotlib-inline==0.1.7 +mistune==3.1.3 +mpmath==1.3.0 +narwhals==1.46.0 +nbclient==0.10.2 +nbconvert==7.16.6 +nbdime==4.0.2 +nbformat==5.10.4 +nest-asyncio==1.6.0 +networkx==3.3 +notebook_shim==0.2.4 +numpy==2.3.1 +oauthlib==3.3.1 +overrides==7.7.0 +packaging==25.0 +pandas==2.3.1 +pandocfilters==1.5.1 +parso==0.8.4 +pexpect==4.9.0 +pillow==11.3.0 +platformdirs==4.3.8 +plotly==6.2.0 +prometheus_client==0.22.1 +prompt_toolkit==3.0.51 +psutil==7.0.0 +ptyprocess==0.7.0 +pure_eval==0.2.3 +pyasn1==0.6.1 +pyasn1_modules==0.4.2 +pycparser==2.22 +Pygments==2.19.2 +pyparsing==3.2.3 +python-dateutil==2.9.0.post0 +python-json-logger==3.3.0 +pytz==2025.2 +PyYAML==6.0.2 +pyzmq==27.0.0 +referencing==0.36.2 +requests==2.32.4 +requests-oauthlib==2.0.0 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +rpds-py==0.26.0 +rsa==4.9.1 +scikit-learn==1.7.0 +scipy==1.16.0 +seaborn==0.13.2 +Send2Trash==1.8.3 +setuptools==80.9.0 +six==1.17.0 +smmap==5.0.2 +sniffio==1.3.1 +soupsieve==2.7 +stack-data==0.6.3 +sympy==1.13.3 +terminado==0.18.1 +threadpoolctl==3.6.0 +tinycss2==1.4.0 +torch==2.7.1+cpu +tornado==6.5.1 +traitlets==5.14.3 +types-python-dateutil==2.9.0.20250708 +typing_extensions==4.14.1 +tzdata==2025.2 +uri-template==1.3.0 +urllib3==2.5.0 +wcwidth==0.2.13 +webcolors==24.11.1 +webencodings==0.5.1 +websocket-client==1.8.0 +Werkzeug==3.1.3 From 0b7c81569e9e128e52f2340620f13c39df77696f Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 05:47:41 +0000 Subject: [PATCH 27/61] rid --- require.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 require.txt diff --git a/require.txt b/require.txt deleted file mode 100644 index 8b1378917..000000000 --- a/require.txt +++ /dev/null @@ -1 +0,0 @@ - From 817c9a5f377ed090aa38624f07194b9ca9db62a6 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 05:52:02 +0000 Subject: [PATCH 28/61] req --- requirements.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8fcb6c03d..07a33a5e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,3 @@ -Flask -Gunicorn anyio==4.9.0 argon2-cffi==25.1.0 argon2-cffi-bindings==21.2.0 @@ -32,11 +30,17 @@ fqdn==1.5.1 fsspec==2024.6.1 gitdb==4.0.12 GitPython==3.1.44 +google-api-core==2.25.1 +google-api-python-client==2.181.0 google-auth==2.40.3 +google-auth-httplib2==0.2.0 google-auth-oauthlib==1.2.2 +google-oauth2-tool==0.0.3 +googleapis-common-protos==1.70.0 gunicorn==23.0.0 h11==0.16.0 httpcore==1.0.9 +httplib2==0.31.0 httpx==0.28.1 idna==3.10 ipykernel==6.29.5 @@ -77,6 +81,7 @@ nest-asyncio==1.6.0 networkx==3.3 notebook_shim==0.2.4 numpy==2.3.1 +oauth2client==4.1.3 oauthlib==3.3.1 overrides==7.7.0 packaging==25.0 @@ -89,6 +94,8 @@ platformdirs==4.3.8 plotly==6.2.0 prometheus_client==0.22.1 prompt_toolkit==3.0.51 +proto-plus==1.26.1 +protobuf==6.32.1 psutil==7.0.0 ptyprocess==0.7.0 pure_eval==0.2.3 @@ -130,6 +137,7 @@ types-python-dateutil==2.9.0.20250708 typing_extensions==4.14.1 tzdata==2025.2 uri-template==1.3.0 +uritemplate==4.2.0 urllib3==2.5.0 wcwidth==0.2.13 webcolors==24.11.1 From 91364942804f86f68d7722155b6a6a3fbfc17b94 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 05:58:05 +0000 Subject: [PATCH 29/61] cv --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 07a33a5e3..c2a5ffdb7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -130,7 +130,7 @@ sympy==1.13.3 terminado==0.18.1 threadpoolctl==3.6.0 tinycss2==1.4.0 -torch==2.7.1+cpu +torch==2.2.2+cpu tornado==6.5.1 traitlets==5.14.3 types-python-dateutil==2.9.0.20250708 From df3fe4fe67333bf918a16aebaa0a504d9506bc76 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 06:18:34 +0000 Subject: [PATCH 30/61] require --- requirements.txt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/requirements.txt b/requirements.txt index c2a5ffdb7..dd5ba4c16 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +--extra-index-url https://download.pytorch.org/whl/cpu + anyio==4.9.0 argon2-cffi==25.1.0 argon2-cffi-bindings==21.2.0 @@ -68,7 +70,7 @@ jupyterlab_pygments==0.3.0 jupyterlab_server==2.27.3 kiwisolver==1.4.8 MarkupSafe==3.0.2 -matplotlib==3.10.3 +matplotlib==3.8.4 matplotlib-inline==0.1.7 mistune==3.1.3 mpmath==1.3.0 @@ -80,12 +82,12 @@ nbformat==5.10.4 nest-asyncio==1.6.0 networkx==3.3 notebook_shim==0.2.4 -numpy==2.3.1 +numpy==1.26.4 oauth2client==4.1.3 oauthlib==3.3.1 overrides==7.7.0 packaging==25.0 -pandas==2.3.1 +pandas==2.1.4 pandocfilters==1.5.1 parso==0.8.4 pexpect==4.9.0 @@ -95,7 +97,7 @@ plotly==6.2.0 prometheus_client==0.22.1 prompt_toolkit==3.0.51 proto-plus==1.26.1 -protobuf==6.32.1 +protobuf==4.25.3 psutil==7.0.0 ptyprocess==0.7.0 pure_eval==0.2.3 @@ -116,8 +118,8 @@ rfc3339-validator==0.1.4 rfc3986-validator==0.1.1 rpds-py==0.26.0 rsa==4.9.1 -scikit-learn==1.7.0 -scipy==1.16.0 +scikit-learn==1.3.2 +scipy==1.11.4 seaborn==0.13.2 Send2Trash==1.8.3 setuptools==80.9.0 @@ -130,7 +132,7 @@ sympy==1.13.3 terminado==0.18.1 threadpoolctl==3.6.0 tinycss2==1.4.0 -torch==2.2.2+cpu +torch==2.2.2 tornado==6.5.1 traitlets==5.14.3 types-python-dateutil==2.9.0.20250708 From 4a5cadbfeb3cb11926f5296723389f69a19e349d Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 06:40:47 +0000 Subject: [PATCH 31/61] call --- app.py | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/app.py b/app.py index 68e19c4e4..7a0e8258b 100644 --- a/app.py +++ b/app.py @@ -4,6 +4,7 @@ from google_auth_oauthlib.flow import Flow from pip._vendor import cachecontrol import google.auth.transport.requests +import requests app = Flask("Studsight") app.secret_key = "davidneastudsightkey.com" @@ -41,24 +42,44 @@ def login(): return redirect(authorization_url) -@app.route("/callback") # Callback route -def callback(): - flow.fetch_token(authorization_response=request.url) +# @app.route("/callback") # Callback route +# def callback(): +# flow.fetch_token(authorization_response=request.url) + +# if not session["state"] == request.args["state"]: +# abort(500) # State does not match! + +# credentials = flow.credentials +# request_session = request.session() +# cached_session = cachecontrol.CacheControl(request_session) +# token_request = google.auth.transport.requests.Request(session=cached_session) + +# id_info = id_token.verify_oauth2_token( +# id_token=credentials._id_token, +# request=token_request, +# audience=GOOGLE_CLIENT_ID +# ) +# return id_info - if not session["state"] == request.args["state"]: - abort(500) # State does not match! + +@app.route("/callback") +def callback(): + flow.fetch_token(authorization_response=request.url) + if not session["state"] == request.args["state"]: + abort(500) credentials = flow.credentials - request_session = request.session() + request_session = requests.session() cached_session = cachecontrol.CacheControl(request_session) token_request = google.auth.transport.requests.Request(session=cached_session) - id_info = id_token.verify_oauth2_token( id_token=credentials._id_token, request=token_request, audience=GOOGLE_CLIENT_ID ) - return id_info + session["google_id"] = id_info.get("sub") + session["email"] = id_info.get("email") + return redirect("/protected_area") @app.route("/logout") # Logout route From f316ae45be93b04de2274f12f522be30c4ad373f Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Sat, 13 Sep 2025 06:48:32 +0000 Subject: [PATCH 32/61] req --- app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app.py b/app.py index 7a0e8258b..eeac657ac 100644 --- a/app.py +++ b/app.py @@ -1,3 +1,4 @@ + from flask import Flask, render_template, session, abort, redirect, url_for, request import pathlib,os from google.oauth2 import id_token @@ -6,6 +7,7 @@ import google.auth.transport.requests import requests + app = Flask("Studsight") app.secret_key = "davidneastudsightkey.com" From 74dc1f1cbe27f2f6a7633d193cf5e014f9b10e27 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 09:13:06 +0100 Subject: [PATCH 33/61] Delete requirements.txt --- requirements.txt | 148 ----------------------------------------------- 1 file changed, 148 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index dd5ba4c16..000000000 --- a/requirements.txt +++ /dev/null @@ -1,148 +0,0 @@ ---extra-index-url https://download.pytorch.org/whl/cpu - -anyio==4.9.0 -argon2-cffi==25.1.0 -argon2-cffi-bindings==21.2.0 -arrow==1.3.0 -asttokens==3.0.0 -async-lru==2.0.5 -attrs==25.3.0 -babel==2.17.0 -beautifulsoup4==4.13.4 -bleach==6.2.0 -blinker==1.9.0 -cachetools==5.5.2 -certifi==2025.7.9 -cffi==1.17.1 -charset-normalizer==3.4.2 -click==8.2.1 -colorama==0.4.6 -comm==0.2.2 -contourpy==1.3.2 -cycler==0.12.1 -debugpy==1.8.14 -decorator==5.2.1 -defusedxml==0.7.1 -executing==2.2.0 -fastjsonschema==2.21.1 -filelock==3.13.1 -Flask==3.1.2 -fonttools==4.58.5 -fqdn==1.5.1 -fsspec==2024.6.1 -gitdb==4.0.12 -GitPython==3.1.44 -google-api-core==2.25.1 -google-api-python-client==2.181.0 -google-auth==2.40.3 -google-auth-httplib2==0.2.0 -google-auth-oauthlib==1.2.2 -google-oauth2-tool==0.0.3 -googleapis-common-protos==1.70.0 -gunicorn==23.0.0 -h11==0.16.0 -httpcore==1.0.9 -httplib2==0.31.0 -httpx==0.28.1 -idna==3.10 -ipykernel==6.29.5 -ipython==9.4.0 -ipython_pygments_lexers==1.1.1 -isoduration==20.11.0 -itsdangerous==2.2.0 -jedi==0.19.2 -Jinja2==3.1.6 -joblib==1.5.1 -json5==0.12.0 -jsonpointer==3.0.0 -jsonschema==4.24.0 -jsonschema-specifications==2025.4.1 -jupyter-events==0.12.0 -jupyter-lsp==2.2.5 -jupyter-server-mathjax==0.2.6 -jupyter_client==8.6.3 -jupyter_core==5.8.1 -jupyter_server==2.16.0 -jupyter_server_terminals==0.5.3 -jupyterlab==4.4.4 -jupyterlab_git==0.51.2 -jupyterlab_pygments==0.3.0 -jupyterlab_server==2.27.3 -kiwisolver==1.4.8 -MarkupSafe==3.0.2 -matplotlib==3.8.4 -matplotlib-inline==0.1.7 -mistune==3.1.3 -mpmath==1.3.0 -narwhals==1.46.0 -nbclient==0.10.2 -nbconvert==7.16.6 -nbdime==4.0.2 -nbformat==5.10.4 -nest-asyncio==1.6.0 -networkx==3.3 -notebook_shim==0.2.4 -numpy==1.26.4 -oauth2client==4.1.3 -oauthlib==3.3.1 -overrides==7.7.0 -packaging==25.0 -pandas==2.1.4 -pandocfilters==1.5.1 -parso==0.8.4 -pexpect==4.9.0 -pillow==11.3.0 -platformdirs==4.3.8 -plotly==6.2.0 -prometheus_client==0.22.1 -prompt_toolkit==3.0.51 -proto-plus==1.26.1 -protobuf==4.25.3 -psutil==7.0.0 -ptyprocess==0.7.0 -pure_eval==0.2.3 -pyasn1==0.6.1 -pyasn1_modules==0.4.2 -pycparser==2.22 -Pygments==2.19.2 -pyparsing==3.2.3 -python-dateutil==2.9.0.post0 -python-json-logger==3.3.0 -pytz==2025.2 -PyYAML==6.0.2 -pyzmq==27.0.0 -referencing==0.36.2 -requests==2.32.4 -requests-oauthlib==2.0.0 -rfc3339-validator==0.1.4 -rfc3986-validator==0.1.1 -rpds-py==0.26.0 -rsa==4.9.1 -scikit-learn==1.3.2 -scipy==1.11.4 -seaborn==0.13.2 -Send2Trash==1.8.3 -setuptools==80.9.0 -six==1.17.0 -smmap==5.0.2 -sniffio==1.3.1 -soupsieve==2.7 -stack-data==0.6.3 -sympy==1.13.3 -terminado==0.18.1 -threadpoolctl==3.6.0 -tinycss2==1.4.0 -torch==2.2.2 -tornado==6.5.1 -traitlets==5.14.3 -types-python-dateutil==2.9.0.20250708 -typing_extensions==4.14.1 -tzdata==2025.2 -uri-template==1.3.0 -uritemplate==4.2.0 -urllib3==2.5.0 -wcwidth==0.2.13 -webcolors==24.11.1 -webencodings==0.5.1 -websocket-client==1.8.0 -Werkzeug==3.1.3 From 9219468c9d14ae75c74b6420012a61370d6ed6e1 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 08:16:21 +0000 Subject: [PATCH 34/61] requiire brought --- requirements.txt | 122 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..b7c25418f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,122 @@ +anyio==4.9.0 +argon2-cffi==25.1.0 +argon2-cffi-bindings==21.2.0 +arrow==1.3.0 +asttokens==3.0.0 +async-lru==2.0.5 +attrs==25.3.0 +babel==2.17.0 +beautifulsoup4==4.13.4 +bleach==6.2.0 +certifi==2025.7.9 +cffi==1.17.1 +charset-normalizer==3.4.2 +colorama==0.4.6 +comm==0.2.2 +contourpy==1.3.2 +cycler==0.12.1 +debugpy==1.8.14 +decorator==5.2.1 +defusedxml==0.7.1 +executing==2.2.0 +fastjsonschema==2.21.1 +filelock==3.13.1 +fonttools==4.58.5 +fqdn==1.5.1 +fsspec==2024.6.1 +gitdb==4.0.12 +GitPython==3.1.44 +h11==0.16.0 +httpcore==1.0.9 +httpx==0.28.1 +idna==3.10 +ipykernel==6.29.5 +ipython==9.4.0 +ipython_pygments_lexers==1.1.1 +isoduration==20.11.0 +jedi==0.19.2 +Jinja2==3.1.6 +joblib==1.5.1 +json5==0.12.0 +jsonpointer==3.0.0 +jsonschema==4.24.0 +jsonschema-specifications==2025.4.1 +jupyter-events==0.12.0 +jupyter-lsp==2.2.5 +jupyter-server-mathjax==0.2.6 +jupyter_client==8.6.3 +jupyter_core==5.8.1 +jupyter_server==2.16.0 +jupyter_server_terminals==0.5.3 +jupyterlab==4.4.4 +jupyterlab_git==0.51.2 +jupyterlab_pygments==0.3.0 +jupyterlab_server==2.27.3 +kiwisolver==1.4.8 +MarkupSafe==3.0.2 +matplotlib==3.10.3 +matplotlib-inline==0.1.7 +mistune==3.1.3 +mpmath==1.3.0 +narwhals==1.46.0 +nbclient==0.10.2 +nbconvert==7.16.6 +nbdime==4.0.2 +nbformat==5.10.4 +nest-asyncio==1.6.0 +networkx==3.3 +notebook_shim==0.2.4 +numpy==2.3.1 +overrides==7.7.0 +packaging==25.0 +pandas==2.3.1 +pandocfilters==1.5.1 +parso==0.8.4 +pexpect==4.9.0 +pillow==11.3.0 +platformdirs==4.3.8 +plotly==6.2.0 +prometheus_client==0.22.1 +prompt_toolkit==3.0.51 +psutil==7.0.0 +ptyprocess==0.7.0 +pure_eval==0.2.3 +pycparser==2.22 +Pygments==2.19.2 +pyparsing==3.2.3 +python-dateutil==2.9.0.post0 +python-json-logger==3.3.0 +pytz==2025.2 +PyYAML==6.0.2 +pyzmq==27.0.0 +referencing==0.36.2 +requests==2.32.4 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +rpds-py==0.26.0 +scikit-learn==1.7.0 +scipy==1.16.0 +seaborn==0.13.2 +Send2Trash==1.8.3 +setuptools==80.9.0 +six==1.17.0 +smmap==5.0.2 +sniffio==1.3.1 +soupsieve==2.7 +stack-data==0.6.3 +sympy==1.13.3 +terminado==0.18.1 +threadpoolctl==3.6.0 +tinycss2==1.4.0 +torch==2.7.1+cpu +tornado==6.5.1 +traitlets==5.14.3 +types-python-dateutil==2.9.0.20250708 +typing_extensions==4.14.1 +tzdata==2025.2 +uri-template==1.3.0 +urllib3==2.5.0 +wcwidth==0.2.13 +webcolors==24.11.1 +webencodings==0.5.1 +websocket-client==1.8.0 From 5fac2b4d0946336e0fb63d79bfe4d4fbf8b48fd3 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 08:18:58 +0000 Subject: [PATCH 35/61] upd requ --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index b7c25418f..6b9bb7673 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +Flask +Gunicorn anyio==4.9.0 argon2-cffi==25.1.0 argon2-cffi-bindings==21.2.0 From 3fc4ab7690e42a8e66010b5eaf8578476e33ae67 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 08:22:56 +0000 Subject: [PATCH 36/61] torch out --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6b9bb7673..152b293cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -110,7 +110,6 @@ sympy==1.13.3 terminado==0.18.1 threadpoolctl==3.6.0 tinycss2==1.4.0 -torch==2.7.1+cpu tornado==6.5.1 traitlets==5.14.3 types-python-dateutil==2.9.0.20250708 From 2a34159cce652d30b2f51b2a448130db09457e02 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 08:36:18 +0000 Subject: [PATCH 37/61] due goog --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 152b293cd..23f549896 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ Flask Gunicorn +google-auth-oauthlib anyio==4.9.0 argon2-cffi==25.1.0 argon2-cffi-bindings==21.2.0 From a6366988cd54d02dbf86c065c019c87811853231 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 08:41:59 +0000 Subject: [PATCH 38/61] new req --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements.txt b/requirements.txt index 23f549896..d6ab295ab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,9 @@ Flask Gunicorn +google-api-python-client +google-auth google-auth-oauthlib +google-auth-httplib2 anyio==4.9.0 argon2-cffi==25.1.0 argon2-cffi-bindings==21.2.0 From 7da2c7c36204d108519549fd46f589bccda39f93 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 14:19:17 +0000 Subject: [PATCH 39/61] new homepage --- templates/home.html | 119 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 104 insertions(+), 15 deletions(-) diff --git a/templates/home.html b/templates/home.html index daef06790..b2eba77db 100644 --- a/templates/home.html +++ b/templates/home.html @@ -5,45 +5,134 @@ StudSight - Home + + + + +
+ Classroom graphic

Welcome to StudSight

+
Encouraging teacher collaboration to help Students' learning.
Collaboration & Insight for Modern Classrooms.
+
+
+ 👩‍🏫 + Teacher Collaboration +
Share resources, strategies, and feedback with fellow educators.
+
+
+ 📊 + Student Insights +
Track student progress and identify learning trends with ease.
+
+
+ 🔒 + Secure & Private +
All data is protected with top-tier security and privacy standards.
+
+
\ No newline at end of file From 2dfb2b25ecc1e0f5e8088f3d155662fcdaae8d6d Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 14:33:18 +0000 Subject: [PATCH 40/61] test --- templates/home.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/home.html b/templates/home.html index b2eba77db..c118d302b 100644 --- a/templates/home.html +++ b/templates/home.html @@ -15,7 +15,7 @@ justify-content: center; } .container { - background: rgba(255,255,255,0.95); + background-image: linear-gradient(to right, #7D77FF, #FF9482); padding: 2.5rem 3.5rem; border-radius: 18px; box-shadow: 0 6px 32px rgba(79,140,255,0.10), 0 1.5px 6px rgba(0,0,0,0.06); @@ -114,7 +114,7 @@
Classroom graphic

Welcome to StudSight

-
Encouraging teacher collaboration to help Students' learning.
Collaboration & Insight for Modern Classrooms.
+
Encouraging teacher collaboration to help Students' lear.
Collaboration & Insight for Modern Classrooms.
From 12058e731fbc17de33800ae4aadad43cb793bf61 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 15:18:32 +0000 Subject: [PATCH 41/61] change --- templates/home.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/home.html b/templates/home.html index c118d302b..ee4c7ff65 100644 --- a/templates/home.html +++ b/templates/home.html @@ -15,7 +15,7 @@ justify-content: center; } .container { - background-image: linear-gradient(to right, #7D77FF, #FF9482); + background-image: linear-gradient(to top, #EF3B36, #FFFFFF); padding: 2.5rem 3.5rem; border-radius: 18px; box-shadow: 0 6px 32px rgba(79,140,255,0.10), 0 1.5px 6px rgba(0,0,0,0.06); @@ -30,7 +30,7 @@ letter-spacing: 1px; } .subtitle { - color: #4f8cff; + color: #2255b3; font-size: 1.2rem; margin-bottom: 2rem; } From 574a0b9a6b889a1fa8a154393b15dc50de92f179 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 19:50:36 +0000 Subject: [PATCH 42/61] conc --- app.py | 52 +++++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/app.py b/app.py index eeac657ac..55bf08faf 100644 --- a/app.py +++ b/app.py @@ -44,32 +44,11 @@ def login(): return redirect(authorization_url) -# @app.route("/callback") # Callback route -# def callback(): -# flow.fetch_token(authorization_response=request.url) - -# if not session["state"] == request.args["state"]: -# abort(500) # State does not match! - -# credentials = flow.credentials -# request_session = request.session() -# cached_session = cachecontrol.CacheControl(request_session) -# token_request = google.auth.transport.requests.Request(session=cached_session) - -# id_info = id_token.verify_oauth2_token( -# id_token=credentials._id_token, -# request=token_request, -# audience=GOOGLE_CLIENT_ID -# ) -# return id_info - - - -@app.route("/callback") +@app.route("/callback") # Callback route def callback(): flow.fetch_token(authorization_response=request.url) - if not session["state"] == request.args["state"]: - abort(500) + if not session["state"] == request.args["state"]: + abort(500) # State does not match!+ credentials = flow.credentials request_session = requests.session() cached_session = cachecontrol.CacheControl(request_session) @@ -79,9 +58,28 @@ def callback(): request=token_request, audience=GOOGLE_CLIENT_ID ) - session["google_id"] = id_info.get("sub") - session["email"] = id_info.get("email") - return redirect("/protected_area") + + return id_info + + + +# @app.route("/callback") +# def callback(): +# flow.fetch_token(authorization_response=request.url) +# if not session["state"] == request.args["state"]: +# abort(500) +# credentials = flow.credentials +# request_session = requests.session() +# cached_session = cachecontrol.CacheControl(request_session) +# token_request = google.auth.transport.requests.Request(session=cached_session) +# id_info = id_token.verify_oauth2_token( +# id_token=credentials._id_token, +# request=token_request, +# audience=GOOGLE_CLIENT_ID +# ) +# session["google_id"] = id_info.get("sub") +# session["email"] = id_info.get("email") +# return redirect("/protected_area") @app.route("/logout") # Logout route From a8001eb6a5f4a412a8d57ee517ce2542fc2c3972 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 20:00:27 +0000 Subject: [PATCH 43/61] comm --- app.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index 55bf08faf..a509bc3dd 100644 --- a/app.py +++ b/app.py @@ -59,7 +59,19 @@ def callback(): audience=GOOGLE_CLIENT_ID ) - return id_info + # Optionally, store info in session for later use + session["google_id"] = id_info.get("sub") + session["email"] = id_info.get("email") + # Return account info as JSON for direct feedback + from flask import jsonify + return jsonify({ + "id": id_info.get("sub"), + "email": id_info.get("email"), + "name": id_info.get("name"), + "picture": id_info.get("picture"), + "locale": id_info.get("locale"), + "hd": id_info.get("hd") + }) From 22db5f4e981b07e0d664c7ca436a8537bdfd5dbb Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 20:12:26 +0000 Subject: [PATCH 44/61] bday --- app.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index a509bc3dd..9808c7078 100644 --- a/app.py +++ b/app.py @@ -19,7 +19,14 @@ # Example initialization (update with your actual client secrets file and scopes) flow = Flow.from_client_secrets_file( client_secrets_file=client_secrets_file, - scopes=["https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email", "openid"], + scopes=[ + "https://www.googleapis.com/auth/userinfo.profile", + "https://www.googleapis.com/auth/userinfo.email", + "openid", + "https://www.googleapis.com/auth/user.phonenumbers.read", + "https://www.googleapis.com/auth/user.gender.read", + "https://www.googleapis.com/auth/user.birthday.read" + ], redirect_uri="https://nea-studsight.onrender.com/callback" ) @@ -70,7 +77,10 @@ def callback(): "name": id_info.get("name"), "picture": id_info.get("picture"), "locale": id_info.get("locale"), - "hd": id_info.get("hd") + "hd": id_info.get("hd"), + "phone_number": id_info.get("phone_number"), + "gender": id_info.get("gender"), + "birthday": id_info.get("birthday") }) From 543363b3a5d474e01df7c4b695a8e8cce2c963d1 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 20:20:40 +0000 Subject: [PATCH 45/61] go --- app.py | 25 +++-------- test.txt | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 19 deletions(-) create mode 100644 test.txt diff --git a/app.py b/app.py index 9808c7078..b819cab2e 100644 --- a/app.py +++ b/app.py @@ -54,34 +54,21 @@ def login(): @app.route("/callback") # Callback route def callback(): flow.fetch_token(authorization_response=request.url) + if not session["state"] == request.args["state"]: - abort(500) # State does not match!+ + abort(500) # State does not match! + credentials = flow.credentials - request_session = requests.session() + request_session = request.session() cached_session = cachecontrol.CacheControl(request_session) token_request = google.auth.transport.requests.Request(session=cached_session) + id_info = id_token.verify_oauth2_token( id_token=credentials._id_token, request=token_request, audience=GOOGLE_CLIENT_ID ) - - # Optionally, store info in session for later use - session["google_id"] = id_info.get("sub") - session["email"] = id_info.get("email") - # Return account info as JSON for direct feedback - from flask import jsonify - return jsonify({ - "id": id_info.get("sub"), - "email": id_info.get("email"), - "name": id_info.get("name"), - "picture": id_info.get("picture"), - "locale": id_info.get("locale"), - "hd": id_info.get("hd"), - "phone_number": id_info.get("phone_number"), - "gender": id_info.get("gender"), - "birthday": id_info.get("birthday") - }) + return id_info diff --git a/test.txt b/test.txt new file mode 100644 index 000000000..9808c7078 --- /dev/null +++ b/test.txt @@ -0,0 +1,125 @@ + +from flask import Flask, render_template, session, abort, redirect, url_for, request +import pathlib,os +from google.oauth2 import id_token +from google_auth_oauthlib.flow import Flow +from pip._vendor import cachecontrol +import google.auth.transport.requests +import requests + + +app = Flask("Studsight") +app.secret_key = "davidneastudsightkey.com" + +os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1" + +client_secrets_file = os.path.join(pathlib.Path(__file__).parent, "client_secret.json") +GOOGLE_CLIENT_ID = "771970138692-gjilmd2o08eitr81o07oiuhfe7m5ardh.apps.googleusercontent.com" + +# Example initialization (update with your actual client secrets file and scopes) +flow = Flow.from_client_secrets_file( + client_secrets_file=client_secrets_file, + scopes=[ + "https://www.googleapis.com/auth/userinfo.profile", + "https://www.googleapis.com/auth/userinfo.email", + "openid", + "https://www.googleapis.com/auth/user.phonenumbers.read", + "https://www.googleapis.com/auth/user.gender.read", + "https://www.googleapis.com/auth/user.birthday.read" + ], + redirect_uri="https://nea-studsight.onrender.com/callback" + + ) + + +def login_is_required(function): + def wrapper(*args, **kwargs): + if "google_id" not in session: + abort(401) # Authorisation required + else: + return function() + + return wrapper + + + + +@app.route("/login") # Login route +def login(): + authorization_url, state = flow.authorization_url() + session["state"] = state + return redirect(authorization_url) + + +@app.route("/callback") # Callback route +def callback(): + flow.fetch_token(authorization_response=request.url) + if not session["state"] == request.args["state"]: + abort(500) # State does not match!+ + credentials = flow.credentials + request_session = requests.session() + cached_session = cachecontrol.CacheControl(request_session) + token_request = google.auth.transport.requests.Request(session=cached_session) + id_info = id_token.verify_oauth2_token( + id_token=credentials._id_token, + request=token_request, + audience=GOOGLE_CLIENT_ID + ) + + # Optionally, store info in session for later use + session["google_id"] = id_info.get("sub") + session["email"] = id_info.get("email") + # Return account info as JSON for direct feedback + from flask import jsonify + return jsonify({ + "id": id_info.get("sub"), + "email": id_info.get("email"), + "name": id_info.get("name"), + "picture": id_info.get("picture"), + "locale": id_info.get("locale"), + "hd": id_info.get("hd"), + "phone_number": id_info.get("phone_number"), + "gender": id_info.get("gender"), + "birthday": id_info.get("birthday") + }) + + + +# @app.route("/callback") +# def callback(): +# flow.fetch_token(authorization_response=request.url) +# if not session["state"] == request.args["state"]: +# abort(500) +# credentials = flow.credentials +# request_session = requests.session() +# cached_session = cachecontrol.CacheControl(request_session) +# token_request = google.auth.transport.requests.Request(session=cached_session) +# id_info = id_token.verify_oauth2_token( +# id_token=credentials._id_token, +# request=token_request, +# audience=GOOGLE_CLIENT_ID +# ) +# session["google_id"] = id_info.get("sub") +# session["email"] = id_info.get("email") +# return redirect("/protected_area") + + +@app.route("/logout") # Logout route +def logout(): + session.clear() + return redirect("/") + + +@app.route("/") # Home route +def home(): + return render_template("home.html") + + +@app.route("/protected_area") +@login_is_required +def protected_area(): + return render_template("protected_area.html", email=session["google_id"]) + + +if __name__ == "__main__": # Run the app + app.run(debug=True) From bb6620027bf62c781271da194c0e21da899a6522 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 20:38:17 +0000 Subject: [PATCH 46/61] CHA --- app.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app.py b/app.py index b819cab2e..2845bc987 100644 --- a/app.py +++ b/app.py @@ -22,10 +22,7 @@ scopes=[ "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email", - "openid", - "https://www.googleapis.com/auth/user.phonenumbers.read", - "https://www.googleapis.com/auth/user.gender.read", - "https://www.googleapis.com/auth/user.birthday.read" + "openid" ], redirect_uri="https://nea-studsight.onrender.com/callback" From 480c872f108494094f88986b78387999bf54ed13 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 22:06:11 +0000 Subject: [PATCH 47/61] back --- app.py | 56 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/app.py b/app.py index 2845bc987..8cfc3eefc 100644 --- a/app.py +++ b/app.py @@ -48,44 +48,44 @@ def login(): return redirect(authorization_url) -@app.route("/callback") # Callback route -def callback(): - flow.fetch_token(authorization_response=request.url) - - if not session["state"] == request.args["state"]: - abort(500) # State does not match! - - credentials = flow.credentials - request_session = request.session() - cached_session = cachecontrol.CacheControl(request_session) - token_request = google.auth.transport.requests.Request(session=cached_session) - - id_info = id_token.verify_oauth2_token( - id_token=credentials._id_token, - request=token_request, - audience=GOOGLE_CLIENT_ID - ) - return id_info - - - -# @app.route("/callback") +# @app.route("/callback") # Callback route # def callback(): # flow.fetch_token(authorization_response=request.url) -# if not session["state"] == request.args["state"]: -# abort(500) + +# if not session["state"] == request.args["state"]: +# abort(500) # State does not match! + # credentials = flow.credentials -# request_session = requests.session() +# request_session = request.session() # cached_session = cachecontrol.CacheControl(request_session) # token_request = google.auth.transport.requests.Request(session=cached_session) + # id_info = id_token.verify_oauth2_token( # id_token=credentials._id_token, # request=token_request, # audience=GOOGLE_CLIENT_ID # ) -# session["google_id"] = id_info.get("sub") -# session["email"] = id_info.get("email") -# return redirect("/protected_area") +# return id_info + + + +@app.route("/callback") +def callback(): + flow.fetch_token(authorization_response=request.url) + if not session["state"] == request.args["state"]: + abort(500) + credentials = flow.credentials + request_session = requests.session() + cached_session = cachecontrol.CacheControl(request_session) + token_request = google.auth.transport.requests.Request(session=cached_session) + id_info = id_token.verify_oauth2_token( + id_token=credentials._id_token, + request=token_request, + audience=GOOGLE_CLIENT_ID + ) + session["google_id"] = id_info.get("sub") + session["email"] = id_info.get("email") + return redirect("/protected_area") @app.route("/logout") # Logout route From 1ce01507de4d2b3053f327e9f9727e263edbe716 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Mon, 15 Sep 2025 22:23:59 +0000 Subject: [PATCH 48/61] size --- templates/home.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/home.html b/templates/home.html index ee4c7ff65..eabd8a0c6 100644 --- a/templates/home.html +++ b/templates/home.html @@ -1,7 +1,7 @@ - + StudSight - Home From e8d8764e86d9919a80c0a3006ee0cc930a64cf40 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Tue, 16 Sep 2025 08:11:37 +0000 Subject: [PATCH 53/61] link --- templates/home.html | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/home.html b/templates/home.html index b878d4e4d..54187b4b5 100644 --- a/templates/home.html +++ b/templates/home.html @@ -1,6 +1,7 @@ + StudSight - Home From 155b8891e3bf4c4d2c25afc82a644ccad95a0a98 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Tue, 16 Sep 2025 08:13:53 +0000 Subject: [PATCH 54/61] stat --- templates/home.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/home.html b/templates/home.html index 54187b4b5..49d221750 100644 --- a/templates/home.html +++ b/templates/home.html @@ -1,7 +1,7 @@ - + StudSight - Home From 495cb8e26822f2443706adbbfe80173f325cb42e Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Tue, 16 Sep 2025 08:19:15 +0000 Subject: [PATCH 55/61] dk --- static/home.css | 7 +++++++ templates/home.html | 7 +------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/static/home.css b/static/home.css index 9eddb9fdc..828df2000 100644 --- a/static/home.css +++ b/static/home.css @@ -10,6 +10,13 @@ body { box-sizing: border-box; } +.main-container { + max-width: 1200px; + width: 100%; + margin: 0 auto; + box-sizing: border-box; +} + .container { background-image: linear-gradient(to top, #EF3B36, #FFFFFF); diff --git a/templates/home.html b/templates/home.html index 49d221750..4ebd1f9a4 100644 --- a/templates/home.html +++ b/templates/home.html @@ -2,15 +2,10 @@ - + StudSight - Home - - - - -
Classroom graphic

Welcome to StudSight

From d740b8dcb62039d8f874796b523a7086746d6bce Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Wed, 17 Sep 2025 14:27:24 +0000 Subject: [PATCH 56/61] changes --- .vscode/settings.json | 4 ++++ app.py | 52 +++++++++++++++++++++++++++++++------------ static/home.css | 3 --- templates/home.html | 1 - 4 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..ba2a6c013 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python-envs.defaultEnvManager": "ms-python.python:system", + "python-envs.pythonProjects": [] +} \ No newline at end of file diff --git a/app.py b/app.py index 8cfc3eefc..d41d48c6e 100644 --- a/app.py +++ b/app.py @@ -1,4 +1,4 @@ - +# IMPOPRTS from flask import Flask, render_template, session, abort, redirect, url_for, request import pathlib,os from google.oauth2 import id_token @@ -8,15 +8,18 @@ import requests -app = Flask("Studsight") -app.secret_key = "davidneastudsightkey.com" +app = Flask("Studsight") # Initialize Flask app +app.secret_key = "davidneastudsightkey.com" # Secret key for session management + + +client_secrets_file = os.path.join(pathlib.Path(__file__).parent, "client_secret.json") # Path to client secrets file +GOOGLE_CLIENT_ID = "771970138692-gjilmd2o08eitr81o07oiuhfe7m5ardh.apps.googleusercontent.com" # Your Google Client ID for OAuth 2.0 + -os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1" -client_secrets_file = os.path.join(pathlib.Path(__file__).parent, "client_secret.json") -GOOGLE_CLIENT_ID = "771970138692-gjilmd2o08eitr81o07oiuhfe7m5ardh.apps.googleusercontent.com" # Example initialization (update with your actual client secrets file and scopes) +#links to the google oauth 2.0 server for authentication flow = Flow.from_client_secrets_file( client_secrets_file=client_secrets_file, scopes=[ @@ -24,30 +27,39 @@ "https://www.googleapis.com/auth/userinfo.email", "openid" ], - redirect_uri="https://nea-studsight.onrender.com/callback" + redirect_uri="https://nea-studsight.onrender.com/callback" # Your redirect URI once authentication is complete ) + + +# Decorator to check if user is logged in before accessing certain routes def login_is_required(function): def wrapper(*args, **kwargs): + if "google_id" not in session: - abort(401) # Authorisation required + abort(401) # Authorisation required else: - return function() + return function() # Call the original function return wrapper + + @app.route("/login") # Login route def login(): - authorization_url, state = flow.authorization_url() + authorization_url, state = flow.authorization_url() # Get authorization URL and state reply from Google session["state"] = state return redirect(authorization_url) + + + # @app.route("/callback") # Callback route # def callback(): # flow.fetch_token(authorization_response=request.url) @@ -69,7 +81,9 @@ def login(): -@app.route("/callback") + + +@app.route("/callback") # Callback route to handle Google's response def callback(): flow.fetch_token(authorization_response=request.url) if not session["state"] == request.args["state"]: @@ -88,18 +102,28 @@ def callback(): return redirect("/protected_area") -@app.route("/logout") # Logout route + + + +@app.route("/logout") # Logout route to clear session def logout(): session.clear() return redirect("/") -@app.route("/") # Home route + + + +@app.route("/") # Home route, which is the landing page when the app is accessed def home(): return render_template("home.html") -@app.route("/protected_area") + + + +# Protected area route, accessible only after login. +@app.route("/protected_area") #This is where the people who have access to the app will go after logging in to view the app's main content. @login_is_required def protected_area(): return render_template("protected_area.html", email=session["google_id"]) diff --git a/static/home.css b/static/home.css index 828df2000..2412dcb66 100644 --- a/static/home.css +++ b/static/home.css @@ -1,20 +1,17 @@ body { font-family: 'Segoe UI', Arial, sans-serif; background: linear-gradient(135deg, #e0e7ff 0%, #f4f6fb 100%); - min-height: 100vh; margin: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; - box-sizing: border-box; } .main-container { max-width: 1200px; width: 100%; margin: 0 auto; - box-sizing: border-box; } diff --git a/templates/home.html b/templates/home.html index 4ebd1f9a4..d71a05343 100644 --- a/templates/home.html +++ b/templates/home.html @@ -2,7 +2,6 @@ - StudSight - Home From cde8e3a803c676bbfe448a8d057c8d65ecfce4f1 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 19 Sep 2025 09:58:28 +0100 Subject: [PATCH 57/61] Update home.css --- static/home.css | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/static/home.css b/static/home.css index 2412dcb66..8e830bdc6 100644 --- a/static/home.css +++ b/static/home.css @@ -1,30 +1,32 @@ body { font-family: 'Segoe UI', Arial, sans-serif; background: linear-gradient(135deg, #e0e7ff 0%, #f4f6fb 100%); - margin: 0; + margin-top: 5vh; display: flex; flex-direction: column; align-items: center; justify-content: center; } -.main-container { - max-width: 1200px; - width: 100%; +/* .main-container { + max-width: 1500px; + width: 1500px; margin: 0 auto; -} +} */ .container { - background-image: linear-gradient(to top, #EF3B36, #FFFFFF); + background-image: url('school2.jpg'); + background-size: cover; + background-repeat: no-repeat; padding: 2.5rem 3.5rem; border-radius: 18px; box-shadow: 0 6px 32px rgba(79,140,255,0.10), 0 1.5px 6px rgba(0,0,0,0.06); - text-align: center; + text-align: align; position: relative; z-index: 2; - max-width: 95vw; - width: 420px; + max-width: 85vw; + width: 85vw; } @@ -56,15 +58,15 @@ h1 { transform: translateY(-2px) scale(1.04); } .hero-img { - width: 180px; - max-width: 60vw; + width: max-content; + max-width: 40vw; margin-bottom: 1.5rem; filter: drop-shadow(0 4px 16px rgba(79,140,255,0.12)); } .features { display: flex; flex-wrap: wrap; - justify-content: center; + justify-content: right; gap: 2rem; margin-top: 2.5rem; width: 100%; @@ -74,9 +76,10 @@ h1 { border-radius: 12px; box-shadow: 0 1px 4px rgba(79,140,255,0.07); padding: 1.2rem 1.5rem; - width: 220px; + position: relative; + width: 10vw; max-width: 90vw; - text-align: left; + text-align: right; display: flex; flex-direction: column; align-items: flex-start; From 22447188412605daa124e38ce444600a54ed78eb Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Fri, 19 Sep 2025 09:58:51 +0100 Subject: [PATCH 58/61] Add files via upload --- static/schoolback.jpg | Bin 0 -> 63170 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 static/schoolback.jpg diff --git a/static/schoolback.jpg b/static/schoolback.jpg new file mode 100644 index 0000000000000000000000000000000000000000..482c1927e9e7ced589d2d631cfd116586576008d GIT binary patch literal 63170 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

RX!|tKC(K3;FnZvQ^+w$2 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 Date: Fri, 19 Sep 2025 09:59:05 +0100 Subject: [PATCH 59/61] Add files via upload --- static/school2.jpg | Bin 0 -> 38824 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 static/school2.jpg diff --git a/static/school2.jpg b/static/school2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..14e45aa90aa2a65261ec7af4793b6eaf46d53e9b GIT binary patch literal 38824 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 literal 0 HcmV?d00001 From e0fe0578a903737bb090a6c95ae450d9c63b1aed Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Tue, 23 Sep 2025 13:42:57 +0000 Subject: [PATCH 60/61] requuu --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index d6ab295ab..e7f67afc2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ Flask +SQLAlchemy +PyInputPlus Gunicorn google-api-python-client google-auth From 32bfb62b10f96302aa4902a4650fbb3493d0e1b6 Mon Sep 17 00:00:00 2001 From: jxtdav3 Date: Wed, 24 Sep 2025 15:19:55 +0100 Subject: [PATCH 61/61] Update app.py --- app.py | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/app.py b/app.py index d41d48c6e..b20a58bdf 100644 --- a/app.py +++ b/app.py @@ -7,17 +7,12 @@ import google.auth.transport.requests import requests - app = Flask("Studsight") # Initialize Flask app app.secret_key = "davidneastudsightkey.com" # Secret key for session management - client_secrets_file = os.path.join(pathlib.Path(__file__).parent, "client_secret.json") # Path to client secrets file GOOGLE_CLIENT_ID = "771970138692-gjilmd2o08eitr81o07oiuhfe7m5ardh.apps.googleusercontent.com" # Your Google Client ID for OAuth 2.0 - - - # Example initialization (update with your actual client secrets file and scopes) #links to the google oauth 2.0 server for authentication flow = Flow.from_client_secrets_file( @@ -32,8 +27,6 @@ ) - - # Decorator to check if user is logged in before accessing certain routes def login_is_required(function): def wrapper(*args, **kwargs): @@ -45,11 +38,6 @@ def wrapper(*args, **kwargs): return wrapper - - - - - @app.route("/login") # Login route def login(): authorization_url, state = flow.authorization_url() # Get authorization URL and state reply from Google @@ -57,9 +45,6 @@ def login(): return redirect(authorization_url) - - - # @app.route("/callback") # Callback route # def callback(): # flow.fetch_token(authorization_response=request.url) @@ -81,8 +66,6 @@ def login(): - - @app.route("/callback") # Callback route to handle Google's response def callback(): flow.fetch_token(authorization_response=request.url) @@ -102,26 +85,16 @@ def callback(): return redirect("/protected_area") - - - @app.route("/logout") # Logout route to clear session def logout(): session.clear() return redirect("/") - - - @app.route("/") # Home route, which is the landing page when the app is accessed def home(): return render_template("home.html") - - - - # Protected area route, accessible only after login. @app.route("/protected_area") #This is where the people who have access to the app will go after logging in to view the app's main content. @login_is_required