feat: add common view module
This commit is contained in:
9
gallery/easel/route/view/common/__init__.py
Normal file
9
gallery/easel/route/view/common/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
|
||||
def mount(app: FastAPI):
|
||||
base_dir = Path(__file__).parent
|
||||
app.mount("/static/common", StaticFiles(directory=base_dir / "static"))
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
76
gallery/easel/route/view/common/static/style.css
Normal file
76
gallery/easel/route/view/common/static/style.css
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
base
|
||||
*/
|
||||
body {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
/*
|
||||
table
|
||||
*/
|
||||
table {
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table,
|
||||
th,
|
||||
td {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.1rem 0.4rem;
|
||||
}
|
||||
|
||||
/*
|
||||
a.button
|
||||
*/
|
||||
a.button {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.button.disabled {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
color: gray;
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
/*
|
||||
app
|
||||
*/
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
ul.app-list {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ul.app-list > li {
|
||||
border: 1px solid lightgrey;
|
||||
}
|
||||
|
||||
ul.app-list > li > a {
|
||||
display: block;
|
||||
padding: 0.5rem 2rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
ul.app-list > li:hover {
|
||||
border-color: blue;
|
||||
}
|
||||
|
||||
ul.app-list > li:hover > a {
|
||||
color: blue;
|
||||
}
|
||||
@@ -6,16 +6,18 @@ from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from gallery.easel.route.view.weather.util import TagType, TagUtil
|
||||
from gallery.sketch.schedule.api import ScheduleApi
|
||||
from gallery.version import __version__
|
||||
|
||||
from ..common.util import TagType, TagUtil
|
||||
from .filters import timedelta_format
|
||||
|
||||
|
||||
def mount(app: FastAPI):
|
||||
base_dir = Path(__file__).parent
|
||||
app.mount(
|
||||
"/schedule/static", StaticFiles(directory=base_dir / "static"), name="static"
|
||||
)
|
||||
app.mount("/static/schedule", StaticFiles(directory=base_dir / "static"))
|
||||
templates = Jinja2Templates(directory=base_dir / "templates")
|
||||
templates.env.filters["timedelta_format"] = timedelta_format
|
||||
|
||||
@app.get("/schedule", response_class=HTMLResponse)
|
||||
async def get_schedule_list(request: Request):
|
||||
@@ -25,6 +27,7 @@ def mount(app: FastAPI):
|
||||
request=request,
|
||||
name="index.html",
|
||||
context={
|
||||
"version": __version__,
|
||||
"channels": channels,
|
||||
},
|
||||
)
|
||||
@@ -45,6 +48,7 @@ def mount(app: FastAPI):
|
||||
request=request,
|
||||
name="schedule.html",
|
||||
context={
|
||||
"version": __version__,
|
||||
"tag_util": TagUtil,
|
||||
"datetime": datetime,
|
||||
"response": response,
|
||||
|
||||
5
gallery/easel/route/view/schedule/filters.py
Normal file
5
gallery/easel/route/view/schedule/filters.py
Normal file
@@ -0,0 +1,5 @@
|
||||
import datetime
|
||||
|
||||
|
||||
def timedelta_format(value: datetime.timedelta) -> str:
|
||||
return ":".join(str(value).split(":")[:2])
|
||||
@@ -1,21 +1,7 @@
|
||||
body {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
table,
|
||||
th,
|
||||
td {
|
||||
/* border: 1px solid rgba(0, 0, 0, 0.2); */
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.1rem 0.4rem;
|
||||
tr.live {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@@ -9,14 +9,16 @@
|
||||
content="ie=edge">
|
||||
<title>ТВ</title>
|
||||
<link rel="stylesheet"
|
||||
href="/schedule/static/style.css?v=1">
|
||||
href="/static/common/style.css?v={{version}}">
|
||||
<link rel="stylesheet"
|
||||
href="/static/schedule/style.css?v={{version}}">
|
||||
<link rel="icon"
|
||||
href="/schedule/static/favicon.ico"
|
||||
href="/static/common/favicon.ico"
|
||||
type="image/x-icon">
|
||||
</head>
|
||||
|
||||
<body class="app-container">
|
||||
<ul>
|
||||
<ul class="app-list">
|
||||
{% for channel in channels %}
|
||||
<li><a href="schedule/{{channel}}">{{channel}}</a></li>
|
||||
{% endfor %}
|
||||
|
||||
@@ -9,28 +9,38 @@
|
||||
content="ie=edge">
|
||||
<title>Программа | {{response.channel.name}} | {{response.date.strftime('%a, %d %B %Y')}}</title>
|
||||
<link rel="stylesheet"
|
||||
href="/schedule/static/style.css?v=1">
|
||||
href="/static/common/style.css?v={{version}}">
|
||||
<link rel="stylesheet"
|
||||
href="/static/schedule/style.css?v={{version}}">
|
||||
<link rel="icon"
|
||||
href="/schedule/static/favicon.ico"
|
||||
href="/static/common/favicon.ico"
|
||||
type="image/x-icon">
|
||||
</head>
|
||||
|
||||
<body class="app-container">
|
||||
<h3>
|
||||
{{response.channel.name}} | {{response.date.strftime('%a, %d %B %Y')}}
|
||||
<a class="button {{'disabled' if response.date == datetime.date.today() else ''}}"
|
||||
href="../tag/{{tag_util.create_tag('day', response.date, -1)}}">⬅️</a>
|
||||
<a class="button"
|
||||
href="../..">⬆️</a>
|
||||
<span>{{response.channel.name}} | {{response.date.strftime('%a, %d %B %Y')}}</span>
|
||||
<a class="button"
|
||||
href="../tag/{{tag_util.create_tag('day', response.date, 1)}}">➡️</a>
|
||||
</h3>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>TIME</td>
|
||||
<td>NAME</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for value in response.values %}
|
||||
<tr>
|
||||
<tr class="{{'live' if value.live else ''}}">
|
||||
<td>{{value.start.strftime('%H:%M')}}</td>
|
||||
<td>{{(value.end - value.start) | timedelta_format}}</td>
|
||||
<td>{{value.label}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -9,16 +9,15 @@ from fastapi.templating import Jinja2Templates
|
||||
from gallery.sketch.weather.api import WeatherApi
|
||||
from gallery.sketch.weather.mock import WEATHER_MOCK_DATA
|
||||
from gallery.sketch.weather.model import WeatherResponse
|
||||
from gallery.version import __version__
|
||||
|
||||
from ..common.util import TagType, TagUtil
|
||||
from .filters import cloudness_icon, wind_direction_icon
|
||||
from .util import TagType, TagUtil
|
||||
|
||||
|
||||
def mount(app: FastAPI):
|
||||
base_dir = Path(__file__).parent
|
||||
app.mount(
|
||||
"/weather/static", StaticFiles(directory=base_dir / "static"), name="static"
|
||||
)
|
||||
app.mount("/static/weather", StaticFiles(directory=base_dir / "static"))
|
||||
templates = Jinja2Templates(directory=base_dir / "templates")
|
||||
templates.env.filters["wind_direction_icon"] = wind_direction_icon
|
||||
templates.env.filters["cloudness_icon"] = cloudness_icon
|
||||
@@ -28,6 +27,7 @@ def mount(app: FastAPI):
|
||||
request=request,
|
||||
name="weather.html",
|
||||
context={
|
||||
"version": __version__,
|
||||
"tag_util": TagUtil,
|
||||
"datetime": datetime,
|
||||
"response": response,
|
||||
@@ -42,6 +42,7 @@ def mount(app: FastAPI):
|
||||
request=request,
|
||||
name="index.html",
|
||||
context={
|
||||
"version": __version__,
|
||||
"locations": locations,
|
||||
},
|
||||
)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,47 +1,3 @@
|
||||
body {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
a.button {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.button.disabled {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
color: gray;
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
table {
|
||||
/* width: 100%; */
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table,
|
||||
th,
|
||||
td {
|
||||
/* border: 1px solid rgba(0, 0, 0, 0.2); */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.1rem 0.4rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: 1rem;
|
||||
text-align: left;
|
||||
|
||||
@@ -9,14 +9,16 @@
|
||||
content="ie=edge">
|
||||
<title>Погода</title>
|
||||
<link rel="stylesheet"
|
||||
href="/weather/static/style.css?v=1">
|
||||
href="/static/common/style.css?v={{version}}">
|
||||
<link rel="stylesheet"
|
||||
href="/static/weather/style.css?v={{version}}">
|
||||
<link rel="icon"
|
||||
href="/weather/static/favicon.ico"
|
||||
href="/static/common/favicon.ico"
|
||||
type="image/x-icon">
|
||||
</head>
|
||||
|
||||
<body class="app-container">
|
||||
<ul>
|
||||
<ul class="app-list">
|
||||
{% for location in locations %}
|
||||
<li><a href="weather/{{location}}">{{location}}</a></li>
|
||||
{% endfor %}
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
content="ie=edge">
|
||||
<title>Погода | {{response.location}} | {{response.date.strftime('%a, %d %B %Y')}}</title>
|
||||
<link rel="stylesheet"
|
||||
href="/weather/static/style.css?v=1">
|
||||
href="/static/common/style.css?v={{version}}">
|
||||
<link rel="stylesheet"
|
||||
href="/static/weather/style.css?v={{version}}">
|
||||
<link rel="icon"
|
||||
href="/weather/static/favicon.ico"
|
||||
href="/static/common/favicon.ico"
|
||||
type="image/x-icon">
|
||||
</head>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user