refined grafana and prometheus
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from . import slides_bp
|
||||
from flask import current_app, abort, redirect, url_for, request, send_from_directory
|
||||
from app.utils import get_cached_deck_info, token_required
|
||||
from app.models import Deck, db, Course
|
||||
from app.models import Deck, db, Course, PageView
|
||||
import os
|
||||
import flask_login
|
||||
import re
|
||||
@@ -115,6 +115,13 @@ def serve_deck(course_folder, deck, token, path="index.html"):
|
||||
current_app.logger.info(
|
||||
f"User {token.name} accessed deck {deck_info['deck_name']}"
|
||||
)
|
||||
try:
|
||||
pv = PageView(deck_id=deck_info["deck_id"], token_id=token.id)
|
||||
db.session.add(pv)
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
current_app.logger.error(f"Failed to log page view: {e}")
|
||||
return send_from_directory(deck_path, path)
|
||||
|
||||
log_404("File missing within deck", deck, course_folder, path, token)
|
||||
|
||||
119
app/models.py
119
app/models.py
@@ -6,20 +6,29 @@ from typing import List, Optional
|
||||
import pytz
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask import current_app
|
||||
from sqlalchemy import Boolean, Column, DateTime, String, TypeDecorator, ForeignKey, UniqueConstraint
|
||||
from sqlalchemy import (
|
||||
Boolean,
|
||||
Column,
|
||||
DateTime,
|
||||
String,
|
||||
TypeDecorator,
|
||||
ForeignKey,
|
||||
UniqueConstraint,
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
db : SQLAlchemy = SQLAlchemy()
|
||||
|
||||
db: SQLAlchemy = SQLAlchemy()
|
||||
|
||||
deck_password = db.Table(
|
||||
"deck_password",
|
||||
Column("deck_id", db.Integer, db.ForeignKey("deck.id"), primary_key=True),
|
||||
Column("password_id", db.Integer, db.ForeignKey("password.id"), primary_key=True)
|
||||
Column("password_id", db.Integer, db.ForeignKey("password.id"), primary_key=True),
|
||||
)
|
||||
|
||||
deck_token = db.Table(
|
||||
"deck_token",
|
||||
Column("deck_id", db.Integer, db.ForeignKey("deck.id"), primary_key=True),
|
||||
Column("token_id", db.Integer, db.ForeignKey("token.id"), primary_key=True)
|
||||
Column("token_id", db.Integer, db.ForeignKey("token.id"), primary_key=True),
|
||||
)
|
||||
|
||||
|
||||
@@ -38,22 +47,29 @@ class UTCDateTime(TypeDecorator):
|
||||
value = value.replace(tzinfo=pytz.utc)
|
||||
return value
|
||||
|
||||
|
||||
class Password(db.Model):
|
||||
__tablename__ = "password"
|
||||
|
||||
id : Mapped[int] = mapped_column(primary_key=True)
|
||||
value : Mapped[str] = mapped_column(String(60))
|
||||
expires_at: Mapped[Optional[datetime]] = mapped_column(UTCDateTime()) # Optional expiration
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
value: Mapped[str] = mapped_column(String(60))
|
||||
expires_at: Mapped[Optional[datetime]] = mapped_column(
|
||||
UTCDateTime()
|
||||
) # Optional expiration
|
||||
|
||||
decks : Mapped[List[Deck]] = relationship("Deck", secondary=deck_password, back_populates='passwords')
|
||||
decks: Mapped[List[Deck]] = relationship(
|
||||
"Deck", secondary=deck_password, back_populates="passwords"
|
||||
)
|
||||
|
||||
def __init__(self, value: str, expires_at: datetime|None) -> None:
|
||||
def __init__(self, value: str, expires_at: datetime | None) -> None:
|
||||
self.value = value
|
||||
self.expires_at = expires_at
|
||||
|
||||
def is_expired(self) -> bool:
|
||||
"""Check if the password is expired"""
|
||||
return self.expires_at is not None and datetime.now(timezone.utc) > self.expires_at
|
||||
return (
|
||||
self.expires_at is not None and datetime.now(timezone.utc) > self.expires_at
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Password(value={self.value}, expires_at={self.expires_at})>"
|
||||
@@ -62,59 +78,78 @@ class Password(db.Model):
|
||||
class Deck(db.Model):
|
||||
__tablename__ = "deck"
|
||||
|
||||
__table_args__ = (
|
||||
UniqueConstraint('course_id', 'deck', name='_course_deck_uc'),
|
||||
)
|
||||
__table_args__ = (UniqueConstraint("course_id", "deck", name="_course_deck_uc"),)
|
||||
|
||||
id : Mapped[int] = mapped_column(primary_key=True)
|
||||
name : Mapped[str] = mapped_column(String(60), unique=True, nullable=False)
|
||||
deck : Mapped[str] = mapped_column(String(60), nullable=False)
|
||||
description : Mapped[str] = mapped_column(String(60))
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
name: Mapped[str] = mapped_column(String(60), unique=True, nullable=False)
|
||||
deck: Mapped[str] = mapped_column(String(60), nullable=False)
|
||||
description: Mapped[str] = mapped_column(String(60))
|
||||
index_file: Mapped[str] = mapped_column(String(40), nullable=False)
|
||||
export_file: Mapped[Optional[str]] = mapped_column(String(40), nullable=True)
|
||||
|
||||
passwords : Mapped[List[Password]] = relationship("Password", secondary=deck_password, back_populates='decks')
|
||||
tokens : Mapped[List[Token]] = relationship("Token", secondary=deck_token, back_populates='decks')
|
||||
passwords: Mapped[List[Password]] = relationship(
|
||||
"Password", secondary=deck_password, back_populates="decks"
|
||||
)
|
||||
tokens: Mapped[List[Token]] = relationship(
|
||||
"Token", secondary=deck_token, back_populates="decks"
|
||||
)
|
||||
course_id: Mapped[int] = mapped_column(ForeignKey("course.id"))
|
||||
course: Mapped['Course'] = relationship("Course", back_populates='decks')
|
||||
course: Mapped["Course"] = relationship("Course", back_populates="decks")
|
||||
|
||||
|
||||
def __init__(self, name: str, deck: str, course: Course, description: str = "", index_file: str = "index.html", export_file: str | None = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
deck: str,
|
||||
course: Course,
|
||||
description: str = "",
|
||||
index_file: str = "index.html",
|
||||
export_file: str | None = None,
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.deck = deck
|
||||
self.description = description
|
||||
self.course = course
|
||||
self.index_file = index_file
|
||||
if export_file:
|
||||
self.export_file = export_file
|
||||
self.export_file = export_file
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Deck(name={self.name}, deck={self.deck}, course={self.course}, index={self.index_file})>"
|
||||
|
||||
|
||||
class Token(db.Model):
|
||||
__tablename__ = "token"
|
||||
id : Mapped[int] = mapped_column(primary_key=True)
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
name: Mapped[str] = mapped_column(String(60), unique=True, nullable=False)
|
||||
active_course_id: Mapped[int] = mapped_column(ForeignKey("course.id"), nullable=True)
|
||||
active_course: Mapped[Course] = relationship("Course", foreign_keys=[active_course_id])
|
||||
decks : Mapped[List[Deck]]= relationship("Deck", secondary=deck_token, back_populates='tokens')
|
||||
created_at: Mapped[datetime] = mapped_column(UTCDateTime(), nullable=False)
|
||||
active_course_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("course.id"), nullable=True
|
||||
)
|
||||
active_course: Mapped[Course] = relationship(
|
||||
"Course", foreign_keys=[active_course_id]
|
||||
)
|
||||
decks: Mapped[List[Deck]] = relationship(
|
||||
"Deck", secondary=deck_token, back_populates="tokens"
|
||||
)
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
self.created_at = datetime.now(timezone.utc)
|
||||
self.active_course_id = current_app.config["DEFAULT_COURSE"]
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Token(id={self.id}, name={self.name}, active_course_id={self.active_course_id})>"
|
||||
|
||||
def is_unlocked(self, deck: str) -> bool :
|
||||
def is_unlocked(self, deck: str) -> bool:
|
||||
return deck in [deck.deck for deck in self.decks]
|
||||
|
||||
|
||||
class Course(db.Model):
|
||||
__tablename__ = "course"
|
||||
id : Mapped[int] = mapped_column(primary_key=True)
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
folder: Mapped[str] = mapped_column(String(20), unique=True, nullable=False)
|
||||
name: Mapped[str] = mapped_column(String(60), unique=True, nullable=False)
|
||||
decks: Mapped[List['Deck']] = relationship(back_populates='course')
|
||||
decks: Mapped[List["Deck"]] = relationship(back_populates="course")
|
||||
|
||||
def __init__(self, name: str, folder: str) -> None:
|
||||
self.name = name
|
||||
@@ -126,11 +161,27 @@ class Course(db.Model):
|
||||
|
||||
class InfoBanner(db.Model):
|
||||
__tablename__ = "info_banner"
|
||||
id : Mapped[int] = mapped_column(primary_key=True)
|
||||
content : Mapped[str] = mapped_column(String(280))
|
||||
is_active : Mapped[bool] = mapped_column(Boolean)
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
content: Mapped[str] = mapped_column(String(280))
|
||||
is_active: Mapped[bool] = mapped_column(Boolean)
|
||||
|
||||
def __init__(self, content : str) -> None:
|
||||
def __init__(self, content: str) -> None:
|
||||
self.content = content
|
||||
self.is_active = False
|
||||
|
||||
|
||||
class PageView(db.Model):
|
||||
__tablename__ = "page_view"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
deck_id: Mapped[int] = mapped_column(ForeignKey("deck.id"), nullable=False)
|
||||
token_id: Mapped[int] = mapped_column(ForeignKey("token.id"), nullable=False)
|
||||
viewed_at: Mapped[datetime] = mapped_column(UTCDateTime(), nullable=False)
|
||||
|
||||
deck: Mapped["Deck"] = relationship("Deck")
|
||||
token: Mapped["Token"] = relationship("Token")
|
||||
|
||||
def __init__(self, deck_id: int, token_id: int) -> None:
|
||||
self.deck_id = deck_id
|
||||
self.token_id = token_id
|
||||
self.viewed_at = datetime.now(timezone.utc)
|
||||
|
||||
@@ -15,6 +15,7 @@ from .models import Deck, InfoBanner, Token, db, Course
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
def init_password_generator():
|
||||
wordfile = xkcd_password.locate_wordfile()
|
||||
global words
|
||||
@@ -225,6 +226,7 @@ def get_cached_deck_info(token_id, course_folder, deck_slug):
|
||||
"auth": is_unlocked,
|
||||
"course_exists": True,
|
||||
"deck_exists": True,
|
||||
"deck_id": deck_obj.id,
|
||||
"deck_name": deck_obj.name,
|
||||
"index_file": deck_obj.index_file
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user