Compare commits

...

1 Commits

Author SHA1 Message Date
letteka 95acf9d84e Fixing ruff and mypy issues
CI / black (pull_request) Failing after 42s
CI / ruff (pull_request) Failing after 41s
CI / mypy (pull_request) Failing after 41s
CI / pytest (pull_request) Failing after 41s
2026-05-12 15:33:20 -07:00
3 changed files with 36 additions and 30 deletions
Generated
+3 -3
View File
@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. # This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand.
[[package]] [[package]]
name = "attrs" name = "attrs"
@@ -704,7 +704,7 @@ files = [
[package.dependencies] [package.dependencies]
attrs = ">=22.2.0" attrs = ">=22.2.0"
jsonschema-specifications = ">=2023.03.6" jsonschema-specifications = ">=2023.3.6"
referencing = ">=0.28.4" referencing = ">=0.28.4"
rpds-py = ">=0.25.0" rpds-py = ">=0.25.0"
@@ -2258,7 +2258,7 @@ files = [
{file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
{file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
] ]
markers = {pi = "python_version < \"3.13\""} markers = {pi = "python_version == \"3.12\""}
[[package]] [[package]]
name = "urllib3" name = "urllib3"
+3 -3
View File
@@ -13,14 +13,14 @@ from flask import (
from werkzeug.middleware.proxy_fix import ProxyFix from werkzeug.middleware.proxy_fix import ProxyFix
from src.camera import camera from src.camera import camera
from src.database import db, CameraStatus, CameraEvent from src.database import CameraEvent, CameraStatus, db
logging.basicConfig(level=logging.WARNING) logging.basicConfig(level=logging.WARNING)
def create_app() -> Flask: def create_app() -> Flask:
flask_app = Flask(__name__, template_folder="templates") flask_app = Flask(__name__, template_folder="templates")
flask_app.wsgi_app = ProxyFix( # type: ignore[assignment, method-assign] flask_app.wsgi_app = ProxyFix( # type: ignore[method-assign]
flask_app.wsgi_app, x_for=2, x_proto=2, x_host=2 flask_app.wsgi_app, x_for=2, x_proto=2, x_host=2
) )
flask_app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///birdcam.db" flask_app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///birdcam.db"
@@ -110,7 +110,7 @@ def camera_status() -> tuple[Response, int]:
@app.get("/camera/log") @app.get("/camera/log")
def camera_log() -> tuple: def camera_log() -> tuple[Response, int]:
events = CameraEvent.recent(limit=50) events = CameraEvent.recent(limit=50)
return ( return (
jsonify( jsonify(
+30 -24
View File
@@ -1,22 +1,26 @@
from __future__ import annotations from __future__ import annotations
from datetime import datetime, timezone from datetime import UTC, datetime
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import DeclarativeBase, Mapped
db = SQLAlchemy() db: SQLAlchemy = SQLAlchemy()
class CameraStatus(db.Model): class Base(DeclarativeBase):
pass
class CameraStatus(Base):
__tablename__ = "camera_status" __tablename__ = "camera_status"
id: db.Mapped[int] = db.mapped_column(db.Integer, primary_key=True) id: Mapped[int] = db.mapped_column(db.Integer, primary_key=True)
running: db.Mapped[bool] = db.mapped_column( running: Mapped[bool] = db.mapped_column(db.Boolean, nullable=False, default=False)
db.Boolean, nullable=False, default=False updated_at: Mapped[datetime] = db.mapped_column(
)
updated_at: db.Mapped[datetime] = db.mapped_column(
db.DateTime(timezone=True), db.DateTime(timezone=True),
nullable=False, nullable=False,
default=lambda: datetime.now(timezone.utc), default=lambda: datetime.now(UTC),
) )
@staticmethod @staticmethod
@@ -33,23 +37,23 @@ class CameraStatus(db.Model):
def set_running(running: bool) -> CameraStatus: def set_running(running: bool) -> CameraStatus:
status = CameraStatus.get() status = CameraStatus.get()
status.running = running status.running = running
status.updated_at = datetime.now(timezone.utc) status.updated_at = datetime.now(UTC)
db.session.commit() db.session.commit()
return status return status
class CameraEvent(db.Model): class CameraEvent(Base):
__tablename__ = "camera_events" __tablename__ = "camera_events"
id: db.Mapped[int] = db.mapped_column(db.Integer, primary_key=True) id: Mapped[int] = db.mapped_column(db.Integer, primary_key=True)
action: db.Mapped[str] = db.mapped_column( action: Mapped[str] = db.mapped_column(
db.String(10), nullable=False db.String(10), nullable=False
) # 'start' | 'stop' ) # 'start' | 'stop'
ip_address: db.Mapped[str] = db.mapped_column(db.String(45), nullable=False) ip_address: Mapped[str] = db.mapped_column(db.String(45), nullable=False)
timestamp: db.Mapped[datetime] = db.mapped_column( timestamp: Mapped[datetime] = db.mapped_column(
db.DateTime(timezone=True), db.DateTime(timezone=True),
nullable=False, nullable=False,
default=lambda: datetime.now(timezone.utc), default=lambda: datetime.now(UTC),
) )
@staticmethod @staticmethod
@@ -61,12 +65,14 @@ class CameraEvent(db.Model):
@staticmethod @staticmethod
def recent(limit: int = 50) -> list[CameraEvent]: def recent(limit: int = 50) -> list[CameraEvent]:
return ( return list(
db.session.execute(
db.select(CameraEvent) db.session.execute(
.order_by(CameraEvent.timestamp.desc()) db.select(CameraEvent)
.limit(limit) .order_by(CameraEvent.timestamp.desc())
) .limit(limit)
.scalars() )
.all() .scalars()
.all()
) )