diff --git a/python/typing-models/flask-sqlalchemy-typing/.envrc b/python/typing-models/flask-sqlalchemy-typing/.envrc new file mode 100644 index 0000000..5bf8fc1 --- /dev/null +++ b/python/typing-models/flask-sqlalchemy-typing/.envrc @@ -0,0 +1,3 @@ +source_url "https://raw.githubusercontent.com/cachix/devenv/95f329d49a8a5289d31e0982652f7058a189bfca/direnvrc" "sha256-d+8cBpDfDBj41inrADaJt+bDWhOktwslgoP5YiGJ1v0=" + +use devenv \ No newline at end of file diff --git a/python/typing-models/flask-sqlalchemy-typing/.gitignore b/python/typing-models/flask-sqlalchemy-typing/.gitignore new file mode 100644 index 0000000..4d058db --- /dev/null +++ b/python/typing-models/flask-sqlalchemy-typing/.gitignore @@ -0,0 +1,9 @@ +# Devenv +.devenv* +devenv.local.nix + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml diff --git a/python/typing-models/flask-sqlalchemy-typing/README.md b/python/typing-models/flask-sqlalchemy-typing/README.md new file mode 100644 index 0000000..e723dcf --- /dev/null +++ b/python/typing-models/flask-sqlalchemy-typing/README.md @@ -0,0 +1,11 @@ +# Flask-SQLAlchemy models + +## Requirements + +* `devenv` + +## Start + +``` +devenv shell +``` diff --git a/python/typing-models/flask-sqlalchemy-typing/app/main.py b/python/typing-models/flask-sqlalchemy-typing/app/main.py new file mode 100644 index 0000000..2e59a14 --- /dev/null +++ b/python/typing-models/flask-sqlalchemy-typing/app/main.py @@ -0,0 +1,94 @@ +from datetime import datetime +import sys +from typing import Optional, TypedDict + +from flask import Flask, Response, jsonify, render_template, request + +from flask_sqlalchemy import SQLAlchemy + +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column + +# Web Application + +app = Flask(__name__) + +# Database + +class Base(DeclarativeBase): + pass + +db = SQLAlchemy(model_class=Base) + +app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db" + +db.init_app(app) + + +class NotificationModel(TypedDict): + id: int + description: Optional[str] + email: str + date: datetime + url: Optional[str] + read: bool + + +class Notification(db.Model): + __tablename__ = "notifications" + + id: Mapped[int] = mapped_column(primary_key=True, index=True) + description: Mapped[str] + email: Mapped[str] = mapped_column(nullable=False) + date: Mapped[datetime] = mapped_column(nullable=False) + url: Mapped[str] + read: Mapped[bool] = mapped_column(default=False) + + def to_dict(self) -> NotificationModel: + return { + "id": self.id, + "description": self.description, + "email": self.email, + "date": self.date, + "url": self.url, + "read": self.read, + } + + +def get_all(): + return db.session.query(Notification).all() + + +def get_unread(): + return db.session.query(Notification).filter(Notification.read.is_(False)).all() + +reveal_type(get_all) + +reveal_type(get_unread) + + +@app.route("/", methods=["GET"]) +def root(): + return render_template("root.html") + + +@app.route("/notifications", methods=["GET", "POST"]) +def notifications() -> Response: + if request.method == "POST": + new_notification = Notification( + **dict(request.form, date=datetime.fromisoformat(request.form["date"])) + ) + db.session.add(new_notification) + db.session.commit() + + notifications = get_all() + return jsonify([notification.to_dict() for notification in notifications]) + + +if __name__ == "__main__": + if len(sys.argv) > 1 and sys.argv[1] == 'db': + print("Creating db...") + with app.app_context(): + db.create_all() + sys.exit(0) + + app.run(debug=True) diff --git a/python/typing-models/flask-sqlalchemy-typing/app/templates/root.html b/python/typing-models/flask-sqlalchemy-typing/app/templates/root.html new file mode 100644 index 0000000..787016c --- /dev/null +++ b/python/typing-models/flask-sqlalchemy-typing/app/templates/root.html @@ -0,0 +1,34 @@ + + + + + + Notification Form + + +

Notification Form

+
+ + +

+ + + +

+ + + +

+ + + +

+ + + +

+ + +
+ + diff --git a/python/typing-models/flask-sqlalchemy-typing/devenv.lock b/python/typing-models/flask-sqlalchemy-typing/devenv.lock new file mode 100644 index 0000000..e5233a2 --- /dev/null +++ b/python/typing-models/flask-sqlalchemy-typing/devenv.lock @@ -0,0 +1,160 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1727098005, + "owner": "cachix", + "repo": "devenv", + "rev": "f318d27a4637aff765a378106d82dfded124c3b3", + "treeHash": "c77efa71afb25615542aed9d7e805a3b6216b6a2", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "treeHash": "ca14199cabdfe1a06a7b1654c76ed49100a689f9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1716977621, + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "4267e705586473d3e5c8d50299e71503f16a6fb6", + "treeHash": "6d9f1f7ca0faf1bc2eeb397c78a49623260d3412", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs-python": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1722978926, + "owner": "cachix", + "repo": "nixpkgs-python", + "rev": "7c550bca7e6cf95898e32eb2173efe7ebb447460", + "treeHash": "d9d38ef1b6fc92be18170b74e9889a7ab9174f6e", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "nixpkgs-python", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1726969270, + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "23cbb250f3bf4f516a2d0bf03c51a30900848075", + "treeHash": "f150876866adcc3af432c103db116c2f516f49b1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat_2", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1726745158, + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "4e743a6920eab45e8ba0fbe49dc459f1423a4b74", + "treeHash": "56fbe2a9610b3ad9163a74011131e7624f6b3b81", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs", + "nixpkgs-python": "nixpkgs-python", + "pre-commit-hooks": "pre-commit-hooks" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/python/typing-models/flask-sqlalchemy-typing/devenv.nix b/python/typing-models/flask-sqlalchemy-typing/devenv.nix new file mode 100644 index 0000000..83c02ab --- /dev/null +++ b/python/typing-models/flask-sqlalchemy-typing/devenv.nix @@ -0,0 +1,16 @@ +{ pkgs, lib, config, inputs, ... }: + +{ + packages = [ + pkgs.sqlite + ]; + + languages.python = { + enable = true; + version = "3.12.0"; + venv = { + enable = true; + requirements = "requirements.txt"; + }; + }; +} diff --git a/python/typing-models/flask-sqlalchemy-typing/devenv.yaml b/python/typing-models/flask-sqlalchemy-typing/devenv.yaml new file mode 100644 index 0000000..184b866 --- /dev/null +++ b/python/typing-models/flask-sqlalchemy-typing/devenv.yaml @@ -0,0 +1,8 @@ +inputs: + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling + nixpkgs-python: + url: github:cachix/nixpkgs-python + inputs: + nixpkgs: + follows: nixpkgs diff --git a/python/typing-models/flask-sqlalchemy-typing/instance/project.db b/python/typing-models/flask-sqlalchemy-typing/instance/project.db new file mode 100644 index 0000000..310aa4d Binary files /dev/null and b/python/typing-models/flask-sqlalchemy-typing/instance/project.db differ diff --git a/python/typing-models/flask-sqlalchemy-typing/requirements.txt b/python/typing-models/flask-sqlalchemy-typing/requirements.txt new file mode 100644 index 0000000..f37be4b --- /dev/null +++ b/python/typing-models/flask-sqlalchemy-typing/requirements.txt @@ -0,0 +1,12 @@ +blinker==1.8.2 +click==8.1.7 +Flask==3.0.3 +Flask-SQLAlchemy==3.1.1 +itsdangerous==2.2.0 +Jinja2==3.1.4 +MarkupSafe==2.1.5 +nodeenv==1.9.1 +pyright==1.1.383 +SQLAlchemy==2.0.35 +typing_extensions==4.12.2 +Werkzeug==3.0.4