import templates from bottle and django. Use bootstrap for HTML content
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from src.apis.version1 import comic, media, tysc
|
||||
|
||||
api_router = APIRouter(prefix="/api")
|
||||
api_router.include_router(comic.router, prefix="/comics", tags=["comics"])
|
||||
api_router.include_router(media.router, prefix="/media", tags=["media"])
|
||||
api_router.include_router(tysc.router, prefix="/tysc", tags=["tysc"])
|
||||
@@ -0,0 +1,8 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.db.session import get_db
|
||||
|
||||
SessionDep = Annotated[Session, Depends(get_db)]
|
||||
@@ -3,10 +3,10 @@ from typing import List
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
from sqlalchemy import select
|
||||
|
||||
from src.models.comics.comic import ComicResponse, ComicDetailsResponse, get_comic_details, get_short_info
|
||||
from src.models.comics.artist import ArtistCreation, ArtistDetailResponse, ArtistResponse, get_artist_details
|
||||
from src.routers import SessionDep
|
||||
from src.schema.comic import Comic, Artist
|
||||
from src.apis.utils import SessionDep
|
||||
from src.schema.comics.comic import ComicResponse, ComicDetailsResponse, get_comic_details, get_short_info
|
||||
from src.schema.comics.artist import ArtistCreation, ArtistDetailResponse, ArtistResponse, get_artist_details
|
||||
from src.db.models.comic import Comic, Artist
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/comic",
|
||||
@@ -4,9 +4,9 @@ from uuid import UUID
|
||||
from fastapi import APIRouter, status, HTTPException
|
||||
from sqlalchemy import select, Sequence
|
||||
|
||||
from src.models.media.file import MediaFileResponse, Link, get_file_details, set_file
|
||||
from src.routers import SessionDep
|
||||
from src.schema.media import MediaFile
|
||||
from src.apis.utils import SessionDep
|
||||
from src.schema.media.file import MediaFileResponse, Link, get_file_details, set_file
|
||||
from src.db.models.media import MediaFile
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/media",
|
||||
@@ -1,9 +1,9 @@
|
||||
from typing import List
|
||||
from fastapi import APIRouter
|
||||
|
||||
from src.models.tysc.sport import SportResponse
|
||||
from src.routers import SessionDep
|
||||
from src.schema.tysc import Sport
|
||||
from src.apis.utils import SessionDep
|
||||
from src.schema.tysc.sport import SportResponse
|
||||
from src.db.models.tysc import Sport
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/tysc",
|
||||
@@ -0,0 +1,23 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
env_path = Path(".") / ".env"
|
||||
load_dotenv(dotenv_path=env_path)
|
||||
|
||||
|
||||
class Settings:
|
||||
PROJECT_NAME: str = "Kontor"
|
||||
PROJECT_VERSION: str = "0.1.0"
|
||||
|
||||
MARIADB_USER: str = os.getenv("MARIADB_USER", "kontor")
|
||||
MARIADB_PASSWORD: str = os.getenv("MARIADB_PASSWORD", "kontor")
|
||||
MARIADB_SERVER: str = os.getenv("MARIADB_SERVER", "mariadb")
|
||||
MARIADB_PORT: str = os.getenv("MARIADB_PORT", 3306)
|
||||
MARIADB_DB: str = os.getenv("MARIADB_DB", "kontor")
|
||||
DATABASE_URL: str = f"mariadb+mariadbconnector://{MARIADB_USER}:{MARIADB_PASSWORD}@{MARIADB_SERVER}:{MARIADB_PORT}/{MARIADB_DB}"
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
@@ -4,7 +4,7 @@ from sqlalchemy import Column, ForeignKey, Integer, String
|
||||
from sqlalchemy.dialects.mysql import BIT
|
||||
from sqlalchemy.orm import relationship, mapped_column, Mapped
|
||||
|
||||
from .base import Base, BaseMixin
|
||||
from src.db.models.base import Base, BaseMixin
|
||||
|
||||
|
||||
class User(Base, BaseMixin):
|
||||
@@ -1,7 +1,7 @@
|
||||
from sqlalchemy import Column, ForeignKey, Integer, String
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from .base import Base, BaseMixin
|
||||
from src.db.models.base import Base, BaseMixin
|
||||
|
||||
|
||||
class Article(Base, BaseMixin):
|
||||
@@ -2,7 +2,7 @@ from sqlalchemy import Column, ForeignKey, Integer, String
|
||||
from sqlalchemy.dialects.mysql import BIT
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from .base import Base, BaseMixin
|
||||
from src.db.models.base import Base, BaseMixin
|
||||
|
||||
|
||||
class Publisher(Base, BaseMixin):
|
||||
@@ -3,7 +3,6 @@ import logging
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from enum import Enum, auto
|
||||
from logging import Logger
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -11,12 +10,12 @@ from sqlalchemy import select
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from .tysc import Card, CardSet, Rooster, Team, FieldPosition, Player, Vendor, Sport
|
||||
from .comic import Issue, TradePaperback, StoryArc, Volume, ComicWork, Artist, Comic, Publisher, WorkType
|
||||
from .bookshelf import ArticleAuthor, BookAuthor, BookshelfPublisher, Article, Book, Author
|
||||
from .admin import Mail, MailAccount, ModuleData, Role, User, Token, AuthorizationMatrix
|
||||
from .metadata import MetaDataTable, MetaDataColumn
|
||||
from .media import MediaVideo, MediaArticle, MediaFile, MediaActor, MediaActorFile
|
||||
from src.db.models.tysc import Card, CardSet, Rooster, Team, FieldPosition, Player, Vendor, Sport
|
||||
from src.db.models.comic import Issue, TradePaperback, StoryArc, Volume, ComicWork, Artist, Comic, Publisher, WorkType
|
||||
from src.db.models.bookshelf import ArticleAuthor, BookAuthor, BookshelfPublisher, Article, Book, Author
|
||||
from src.db.models.admin import Mail, MailAccount, ModuleData, Role, User, Token, AuthorizationMatrix
|
||||
from src.db.models.metadata import MetaDataTable, MetaDataColumn
|
||||
from src.db.models.media import MediaVideo, MediaArticle, MediaFile, MediaActor, MediaActorFile
|
||||
|
||||
|
||||
class ColumnEntry(Enum):
|
||||
@@ -10,7 +10,7 @@ from sqlalchemy import Column, String, ForeignKey
|
||||
from sqlalchemy.dialects.mysql import BIT
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from .base import Base, BaseMixin, BaseVideoMixin
|
||||
from src.db.models.base import Base, BaseMixin, BaseVideoMixin
|
||||
|
||||
|
||||
class MediaFile(Base, BaseMixin, BaseVideoMixin):
|
||||
@@ -2,7 +2,7 @@ from sqlalchemy import Column, String, ForeignKey, Integer
|
||||
from sqlalchemy.dialects.mysql import BIT
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from .base import Base, BaseMixin
|
||||
from src.db.models.base import Base, BaseMixin
|
||||
|
||||
|
||||
class MetaDataTable(Base, BaseMixin):
|
||||
@@ -2,7 +2,7 @@ from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint
|
||||
from sqlalchemy.dialects.mysql import BIT
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from .base import Base, BaseMixin
|
||||
from src.db.models.base import Base, BaseMixin
|
||||
|
||||
|
||||
class Sport(Base, BaseMixin):
|
||||
@@ -0,0 +1,15 @@
|
||||
from typing import Generator, Annotated
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker, Session
|
||||
|
||||
from src.core.config import settings
|
||||
|
||||
SQLALCHEMY_DATABASE_URL = settings.DATABASE_URL
|
||||
engine = create_engine(SQLALCHEMY_DATABASE_URL)
|
||||
|
||||
SessionLocal = sessionmaker(bind=engine)
|
||||
|
||||
def get_db() -> Generator:
|
||||
with SessionLocal() as db:
|
||||
yield db
|
||||
+24
-8
@@ -1,13 +1,29 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from src.routers import comic, media, tysc
|
||||
from src.apis.base import api_router
|
||||
from src.db.session import engine
|
||||
from src.webapps.base import api_router as web_app_router
|
||||
from src.core.config import settings
|
||||
from src.db.models.base import Base
|
||||
|
||||
app = FastAPI()
|
||||
def include_router(app: FastAPI):
|
||||
app.include_router(api_router)
|
||||
app.include_router(web_app_router)
|
||||
|
||||
app.include_router(comic.router)
|
||||
app.include_router(media.router)
|
||||
app.include_router(tysc.router)
|
||||
def configure_static(app: FastAPI):
|
||||
app.mount("/static", StaticFiles(directory="src/static"), name="static")
|
||||
|
||||
def create_tables():
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
def start_application():
|
||||
app = FastAPI(title=settings.PROJECT_NAME, version=settings.PROJECT_VERSION)
|
||||
include_router(app)
|
||||
configure_static(app)
|
||||
create_tables()
|
||||
return app
|
||||
|
||||
|
||||
kontor = start_application()
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import logging
|
||||
import os
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from src.schema.base import Base
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker, Session
|
||||
|
||||
connect_string = ('mariadb+mariadbconnector://{}:{}@{}:{}/{}'.format(
|
||||
os.environ.get('DB_USER', 'kontor'),
|
||||
os.environ.get('DB_PASSWORD', 'kontor'),
|
||||
os.environ.get('DB_HOST', 'mariadb'),
|
||||
os.environ.get('DB_PORT', 3306),
|
||||
os.environ.get('DB_NAME', 'kontor')
|
||||
))
|
||||
engine = create_engine(connect_string)
|
||||
SessionLocal = sessionmaker(bind=engine)
|
||||
Base.metadata.create_all(bind=engine, checkfirst=True)
|
||||
|
||||
def get_db():
|
||||
logging.info("get_db")
|
||||
with SessionLocal() as db:
|
||||
yield db
|
||||
|
||||
SessionDep = Annotated[Session, Depends(get_db)]
|
||||
@@ -1,9 +0,0 @@
|
||||
from .admin import User, Token, Role, AuthorizationMatrix, ModuleData, MailAccount, Mail
|
||||
from .bookshelf import Article, Book, Author, BookshelfPublisher, ArticleAuthor, BookAuthor
|
||||
from .comic import Comic, Artist, Publisher, Issue, StoryArc, TradePaperback, Volume, ComicWork, WorkType
|
||||
from .metadata import MetaDataTable, MetaDataColumn
|
||||
from .tysc import Card, CardSet, Sport, Team, FieldPosition, Rooster, Player, Vendor
|
||||
from .media import MediaFile, MediaArticle, MediaVideo
|
||||
from .base import Base
|
||||
from .database import KontorDB, ColumnEntry
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
from typing import List, Dict
|
||||
from uuid import UUID
|
||||
|
||||
from src.schema.comic import Artist
|
||||
from pydantic import BaseModel
|
||||
|
||||
from src.schema import Artist
|
||||
from src.db.models.comic import Artist
|
||||
|
||||
|
||||
class ArtistCreation(BaseModel):
|
||||
@@ -1,10 +1,9 @@
|
||||
from typing import List, Dict
|
||||
from uuid import UUID
|
||||
|
||||
from src.schema.comic import Comic
|
||||
from pydantic import BaseModel
|
||||
|
||||
from src.schema import Comic
|
||||
from src.db.models.comic import Comic
|
||||
|
||||
|
||||
class ComicResponse(BaseModel):
|
||||
@@ -14,6 +13,7 @@ class ComicResponse(BaseModel):
|
||||
|
||||
class ComicDetailsResponse(BaseModel):
|
||||
id: UUID
|
||||
created: str
|
||||
title: str
|
||||
completed : bool
|
||||
current_order : bool
|
||||
@@ -22,12 +22,12 @@ class ComicDetailsResponse(BaseModel):
|
||||
works: Dict[str, List[str]]
|
||||
|
||||
def get_short_info(comic: Comic) -> ComicResponse:
|
||||
reponse = ComicResponse(
|
||||
response = ComicResponse(
|
||||
id=comic.id,
|
||||
title=comic.title,
|
||||
completed=(comic.completed == 1)
|
||||
)
|
||||
return reponse
|
||||
return response
|
||||
|
||||
|
||||
def get_comic_details(comic: Comic) -> ComicDetailsResponse | None:
|
||||
@@ -44,6 +44,7 @@ def get_comic_details(comic: Comic) -> ComicDetailsResponse | None:
|
||||
works[work_type] = [artist_name]
|
||||
response = ComicDetailsResponse(
|
||||
id=comic.id,
|
||||
created=str(comic.created_date),
|
||||
title=comic.title,
|
||||
completed=(comic.completed == 1),
|
||||
current_order=(comic.current_order == 1),
|
||||
@@ -1,7 +1,7 @@
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from src.schema.media import MediaFile
|
||||
from src.db.models.media import MediaFile
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,5 @@
|
||||
$( function() {
|
||||
$( "#autocomplete" ).autocomplete({
|
||||
source: "/jobs/autocomplete"
|
||||
});
|
||||
} );
|
||||
@@ -0,0 +1,32 @@
|
||||
{% extends "shared/base.html" %}
|
||||
|
||||
{% block title %}
|
||||
<title>Comic Artists</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% with msg=msg %}
|
||||
{% include "components/alerts.html" %}
|
||||
{% endwith %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h1 class="display-5">Find Artists..</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
{% for artist in artists %}
|
||||
<div class="col-lg-4 col-md-3 col-sm-10 mr-auto">
|
||||
{% with obj=artist %}
|
||||
{% include "components/artist_cards.html" %}
|
||||
{% endwith %}
|
||||
|
||||
{% if loop.index %3 %}
|
||||
</div>
|
||||
{% else %}
|
||||
</div></div><br><div class="row">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,32 @@
|
||||
{% extends "shared/base.html" %}
|
||||
|
||||
{% block title %}
|
||||
<title>Comic List</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% with msg=msg %}
|
||||
{% include "components/alerts.html" %}
|
||||
{% endwith %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h1 class="display-5">Find Jobs..</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
{% for comic in comics %}
|
||||
<div class="col-lg-4 col-md-3 col-sm-10 mr-auto">
|
||||
{% with obj=comic %}
|
||||
{% include "components/comic_cards.html" %}
|
||||
{% endwith %}
|
||||
|
||||
{% if loop.index %3 %}
|
||||
</div>
|
||||
{% else %}
|
||||
</div></div><br><div class="row">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,53 @@
|
||||
{% extends "shared/base.html" %}
|
||||
|
||||
|
||||
{% block title %}
|
||||
<title>Comic Detail</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h1 class="display-5">Comic Detail</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<table class="table table-striped table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">Comic Title</th>
|
||||
<td colspan="2">{{comic.title}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Publisher</th>
|
||||
<td colspan="2"><a href="/comics/publishers/{{comic.publisher.id}}">{{comic.publisher.name}}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Completed</th>
|
||||
<td colspan="2">{{comic.completed}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Works</th>
|
||||
<td colspan="2">
|
||||
{% for work in comic.comic_works %}
|
||||
<p>
|
||||
{{work.work_type.name}}: <a href="/comics/artists/{{work.artist.id}}">{{work.artist.name}}</a>
|
||||
</p>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Data Created</th>
|
||||
<td colspan="2">{{comic.created_date}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Data Modified</th>
|
||||
<td colspan="2">{{comic.last_modified_date}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,5 @@
|
||||
{% if msg %}
|
||||
<div class="alert alert-info" role="alert">
|
||||
{{msg}}
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,6 @@
|
||||
<div class="card shadow p-3 mb-2 bg-body rounded" style="width: 18rem;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{obj.name}}</h5>
|
||||
<a href="/comic/artists/{{obj.id}}" class="btn btn-primary">Read more</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="card shadow p-3 mb-2 bg-body rounded" style="width: 18rem;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{obj.title}}</h5>
|
||||
<p class="card-text">Publisher : {{obj.publisher.name}}</p>
|
||||
<p class="card-text">Completed : {{obj.completed}}</p>
|
||||
<a href="/comic/comics/{{obj.id}}" class="btn btn-primary">Read more</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,60 @@
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light px-5">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">
|
||||
<img src="{{ url_for('static', path='images/logo.png') }}" alt="" width="30" height="24">
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="/">Home</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Comics
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<li><a class="dropdown-item" href="/comic/comics/">Comics</a></li>
|
||||
<li><a class="dropdown-item" href="/comic/artists/">Artists</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="#">Something else here</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/docs/">Docs</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/redoc/">Redoc</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Account
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<li><a class="dropdown-item" href="/register/">Signup</a></li>
|
||||
<li><a class="dropdown-item" href="/login/">Login</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="#">Something else here</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ml-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Jobs
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<li><a class="dropdown-item" href="/comics/">Comics</a></li>
|
||||
<li><a class="dropdown-item" href="/comics/">Media</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex" action="/search/">
|
||||
<input class="form-control me-2" name="query" id="autocomplete" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -0,0 +1,14 @@
|
||||
{% extends "shared/base.html" %}
|
||||
|
||||
|
||||
{% block title %}
|
||||
<title>Kontor</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% with msg=msg %}
|
||||
{% include "components/alerts.html" %}
|
||||
{% endwith %}
|
||||
<div class="container">
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
||||
{% block title %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% include "components/navbar.html" %}
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/js/bootstrap.bundle.min.js" integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0" crossorigin="anonymous"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
||||
<script src="{{ url_for('static', path='js/autocomplete.js') }}"></script>
|
||||
{% block scripts %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,13 @@
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from src.webapps.comic import route_comics
|
||||
|
||||
templates = Jinja2Templates(directory="src/templates")
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(route_comics.router)
|
||||
|
||||
@api_router.get("/")
|
||||
def home(request: Request, msg: str = None):
|
||||
return templates.TemplateResponse("index.html", {"request": request, "msg": msg})
|
||||
@@ -0,0 +1,26 @@
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from src.apis.utils import SessionDep
|
||||
from src.db.models.comic import Comic, Artist
|
||||
from src.schema.comics.comic import get_comic_details
|
||||
|
||||
templates = Jinja2Templates(directory="src/templates")
|
||||
router = APIRouter(include_in_schema=False, prefix="/comic")
|
||||
|
||||
@router.get("/comics")
|
||||
def get_comics(db: SessionDep, request: Request, msg: str = None):
|
||||
comics = db.query(Comic).all()
|
||||
return templates.TemplateResponse("comic/comics.html", {"request": request, "msg": msg, "comics": comics})
|
||||
|
||||
@router.get("/comics/{comic_id}")
|
||||
def comic_details(comic_id: UUID, request: Request, db: SessionDep):
|
||||
comic = db.get(Comic, comic_id)
|
||||
return templates.TemplateResponse("comic/detail.html", {"request": request, "comic":comic})
|
||||
|
||||
@router.get("/artists")
|
||||
def get_artists(db: SessionDep, request: Request, msg: str = None):
|
||||
artists = db.query(Artist).all()
|
||||
return templates.TemplateResponse("comic/artists.html", {"request": request, "msg": msg, "artists": artists})
|
||||
Reference in New Issue
Block a user