Skip to content

Commit 4fe169a

Browse files
committed
Fixes for yam & flake8
2 parents 585afeb + e2c5b06 commit 4fe169a

File tree

11 files changed

+395
-2
lines changed

11 files changed

+395
-2
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
dev.db
22
test.db
33
config.py
4+
.vscode/settings.json
45

56
# Byte-compiled / optimized / DLL files
67
__pycache__/
@@ -116,6 +117,8 @@ venv/
116117
ENV/
117118
env.bak/
118119
venv.bak/
120+
Scripts/*
121+
pyvenv.cfg
119122

120123
# Spyder project settings
121124
.spyderproject
@@ -138,4 +141,5 @@ dmypy.json
138141

139142
# settings
140143
.vscode/
141-
app/.vscode/
144+
app/.vscode/
145+
app/routers/stam

app/database/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class Event(Base):
5252

5353
owner = relationship("User")
5454
owner_id = Column(Integer, ForeignKey("users.id"))
55+
color = Column(String, nullable=True)
56+
5557
participants = relationship("UserEvent", back_populates="events")
5658

5759
# PostgreSQL

app/main.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
from app.config import PSQL_ENVIRONMENT
55
from app.database import models
66
from app.database.database import engine
7-
from app.dependencies import MEDIA_PATH, STATIC_PATH, templates
7+
from app.dependencies import (
8+
MEDIA_PATH, STATIC_PATH, templates)
89
from app.routers import (agenda, dayview, email, event, invitation, profile,
910
search)
1011

@@ -28,6 +29,7 @@ def create_tables(engine, psql_environment):
2829
app.include_router(profile.router)
2930
app.include_router(event.router)
3031
app.include_router(agenda.router)
32+
app.include_router(dayview.router)
3133
app.include_router(email.router)
3234
app.include_router(invitation.router)
3335
app.include_router(search.router)

app/routers/dayview.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
from datetime import datetime, timedelta
2+
from typing import Tuple, Union
3+
4+
from fastapi import APIRouter, Depends, Request
5+
from fastapi.templating import Jinja2Templates
6+
from sqlalchemy import and_, or_
7+
8+
from app.database.database import get_db
9+
from app.database.models import Event, User
10+
from app.dependencies import TEMPLATES_PATH
11+
12+
13+
templates = Jinja2Templates(directory=TEMPLATES_PATH)
14+
15+
16+
router = APIRouter()
17+
18+
19+
class DivAttributes:
20+
GRID_BAR_QUARTER = 1
21+
FULL_GRID_BAR = 4
22+
MIN_MINUTES = 0
23+
MAX_MINUTES = 15
24+
BASE_GRID_BAR = 5
25+
FIRST_GRID_BAR = 1
26+
LAST_GRID_BAR = 101
27+
DEFAULT_COLOR = 'grey'
28+
DEFAULT_FORMAT = "%H:%M"
29+
MULTIDAY_FORMAT = "%d/%m %H:%M"
30+
31+
def __init__(self, event: Event,
32+
day: Union[bool, datetime] = False) -> None:
33+
self.start_time = event.start
34+
self.end_time = event.end
35+
self.day = day
36+
self.start_multiday, self.end_multiday = self._check_multiday_event()
37+
self.color = self._check_color(event.color)
38+
self.total_time = self._set_total_time()
39+
self.grid_position = self._set_grid_position()
40+
41+
def _check_color(self, color: str) -> str:
42+
if color is None:
43+
return self.DEFAULT_COLOR
44+
return color
45+
46+
def _minutes_position(self, minutes: int) -> Union[int, None]:
47+
min_minutes = self.MIN_MINUTES
48+
max_minutes = self.MAX_MINUTES
49+
for i in range(self.GRID_BAR_QUARTER, self.FULL_GRID_BAR + 1):
50+
if min_minutes < minutes <= max_minutes:
51+
return i
52+
min_minutes = max_minutes
53+
max_minutes += 15
54+
55+
def _get_position(self, time: datetime) -> int:
56+
grid_hour_position = time.hour * self.FULL_GRID_BAR
57+
grid_minutes_modifier = self._minutes_position(time.minute)
58+
if grid_minutes_modifier is None:
59+
grid_minutes_modifier = 0
60+
return grid_hour_position + grid_minutes_modifier + self.BASE_GRID_BAR
61+
62+
def _set_grid_position(self) -> str:
63+
if self.start_multiday:
64+
start = self.FIRST_GRID_BAR
65+
else:
66+
start = self._get_position(self.start_time)
67+
if self.end_multiday:
68+
end = self.LAST_GRID_BAR
69+
else:
70+
end = self._get_position(self.end_time)
71+
return f'{start} / {end}'
72+
73+
def _get_time_format(self) -> str:
74+
for multiday in [self.start_multiday, self.end_multiday]:
75+
yield self.MULTIDAY_FORMAT if multiday else self.DEFAULT_FORMAT
76+
77+
def _set_total_time(self) -> None:
78+
length = self.end_time - self.start_time
79+
self.length = length.seconds / 60
80+
format_gen = self._get_time_format()
81+
start_time_str = self.start_time.strftime(next(format_gen))
82+
end_time_str = self.end_time.strftime(next(format_gen))
83+
return ' '.join([start_time_str, '-', end_time_str])
84+
85+
def _check_multiday_event(self) -> Tuple[bool]:
86+
start_multiday, end_multiday = False, False
87+
if self.day:
88+
if self.start_time < self.day:
89+
start_multiday = True
90+
self.day += timedelta(hours=24)
91+
if self.day <= self.end_time:
92+
end_multiday = True
93+
return (start_multiday, end_multiday)
94+
95+
96+
@router.get('/day/{date}')
97+
async def dayview(request: Request, date: str, db_session=Depends(get_db)):
98+
# TODO: add a login session
99+
user = db_session.query(User).filter_by(username='test1').first()
100+
day = datetime.strptime(date, '%Y-%m-%d')
101+
day_end = day + timedelta(hours=24)
102+
events = db_session.query(Event).filter(
103+
Event.owner_id == user.id).filter(
104+
or_(and_(Event.start >= day, Event.start < day_end),
105+
and_(Event.end >= day, Event.end < day_end),
106+
and_(Event.start < day_end, day_end < Event.end)))
107+
events_n_attrs = [(event, DivAttributes(event, day)) for event in events]
108+
return templates.TemplateResponse("dayview.html", {
109+
"request": request,
110+
"events": events_n_attrs,
111+
"month": day.strftime("%B").upper(),
112+
"day": day.day
113+
})

app/static/dayview.css

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
2+
:root {
3+
--primary:#30465D;
4+
--primary-variant:#FFDE4D;
5+
--secondary:#EF5454;
6+
--borders:#E7E7E7;
7+
--borders-variant:#F7F7F7;
8+
}
9+
10+
html {
11+
font-family: 'Assistant', sans-serif;
12+
text-align: center;
13+
}
14+
15+
#toptab {
16+
background-color: var(--primary);
17+
}
18+
19+
.schedule {
20+
display: grid;
21+
grid-template-rows: 1;
22+
}
23+
24+
.times {
25+
margin-top: 0.65em;
26+
grid-row: 1 / -1;
27+
grid-column: 1 / -1;
28+
z-index: 40;
29+
}
30+
31+
.baselines {
32+
grid-row: 1 / -1;
33+
grid-column: 1 / -1;
34+
z-index: 38;
35+
}
36+
37+
.eventgrid {
38+
grid-row: 1 / -1;
39+
grid-column: 1 / -1;
40+
display: grid;
41+
grid-template-rows: repeat(100, 0.375rem);
42+
z-index: 39;
43+
}
44+
45+
.hourbar {
46+
margin-top: -1px;
47+
}
48+
49+
.event {
50+
font-size: 1rem;
51+
}
52+
53+
.total-time {
54+
font-size: 0.4rem;
55+
line-height: 1rem;
56+
}
57+
58+
.title_size_small {
59+
font-size: 0.6em;
60+
}
61+
62+
.title_size_Xsmall {
63+
font-size: 0.4em;
64+
}
65+
66+
.title_size_tiny {
67+
font-size: 0.1em;
68+
line-height: 4em;
69+
}
70+
71+
.actiongrid {
72+
grid-row: 1 / -1;
73+
grid-column: 1 / -1;
74+
display: grid;
75+
grid-template-rows: repeat(100, 0.375rem);
76+
z-index: 42;
77+
}
78+
79+
.action-icon {
80+
visibility: hidden;
81+
}
82+
83+
.action-continer:hover {
84+
border-top: 1px dashed var(--borders);
85+
border-bottom: 1px dashed var(--borders);
86+
transition: 0.3;
87+
}
88+
89+
.action-continer:hover .action-icon {
90+
visibility: visible;
91+
}
Lines changed: 1 addition & 0 deletions
Loading

app/static/images/icons/pencil.svg

Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading

app/templates/dayview.html

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
7+
<link rel="stylesheet" href="{{ url_for('static', path='/dayview.css') }}">
8+
<link href="https://fonts.googleapis.com/css2?family=Assistant&family=Open+Sans&family=Roboto&family=Roboto+Condensed&display=swap" rel="stylesheet">
9+
<title>dayview</title>
10+
</head>
11+
<body>
12+
<div id="toptab" class="d-flex justify-content-around sticky-top">
13+
<a href="/closethedayview" title="close day view"><img class="pb-1" src="{{ url_for('static', path='/images/icons/close_sidebar.svg')}}" width="15em" height="15em"></a>
14+
<span class="month fw-bold text-white">{{month}}</span>
15+
<span class="day fw-bold text-white">{{day}}</span>
16+
</div>
17+
<div class="schedule">
18+
<div class="container times bg-primeary position">
19+
{% for i in range(24)%}
20+
<div class="row bg-transparent hourmark">
21+
{% set i = i|string() %}
22+
{{i.zfill(2)}}:00
23+
</div>
24+
{% endfor %}
25+
</div>
26+
<div class="eventgrid">
27+
{% for event, attr in events %}
28+
{% set totaltime = 'visible'%}
29+
{% if attr.length < 60 %}
30+
{% set size = 'title_size_small' %}
31+
{% set totaltime = 'invisible'%}
32+
{% if attr.length < 45 %}
33+
{% set size = 'title_size_Xsmall' %}
34+
{% if attr.length < 30 %}
35+
{% set size = 'title_size_tiny' %}
36+
{% endif %}
37+
{% endif %}
38+
{% endif %}
39+
<div id="event{{event.id}}" class="text-truncate px-5 d-flex flex-column justify-content-evenly event" style="background-color: {{attr.color}}; grid-row: {{attr.grid_position}}; max-hight:1.5rem;">
40+
<p class="text-truncate my-0 {{size}}">{{ event.title }}</p>
41+
{% if totaltime == 'visible' %}
42+
<p class="total-time text-truncate fw-light my-0">{{attr.total_time}}</p>
43+
{% endif %}
44+
</div>
45+
{% endfor %}
46+
</div>
47+
<div class="container baselines bg-transparent">
48+
{% for i in range(25)%}
49+
<div class="hourbar {{i}} row text-white border-bottom">---</div>
50+
{% endfor %}
51+
</div>
52+
<div class="actiongrid">
53+
{% for event, attr in events %}
54+
<div class="d-flex flex-row justify-content-around align-items-end action-continer" style="grid-row: {{attr.grid_position}};">
55+
<a href="/edit/{{event.id}}" title="Edit event" class="action-icon"><img class="pb-1" src="{{ url_for('static', path='/images/icons/pencil.svg')}}" width="15em" height="15em"></a>
56+
<a href="/delete/{{event.id}}" title="Delete event" class="action-icon"><img class="pb-1" src="{{ url_for('static', path='/images/icons/trash-can.svg')}}" width="15em" height="15em"></a>
57+
</div>
58+
{% endfor %}
59+
</div>
60+
</div>
61+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
62+
</body>
63+
</html>

requirements.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
aiofiles==0.6.0
2+
apipkg==1.5
23
arrow==0.17.0
34
atomicwrites==1.4.0
45
attrs==20.3.0
6+
beautifulsoup4==4.9.3
57
certifi==2020.12.5
68
chardet==4.0.0
79
click==7.1.2
810
colorama==0.4.4
911
coverage==5.3.1
12+
execnet==1.7.1
1013
fastapi==0.63.0
1114
fastapi_mail==0.3.3.1
1215
faker==5.6.2
@@ -31,7 +34,10 @@ py==1.10.0
3134
pydantic==1.7.3
3235
pyparsing==2.4.7
3336
pytest==6.2.1
37+
pytest-asyncio==0.14.0
3438
pytest-cov==2.10.1
39+
pytest-forked==1.3.0
40+
pytest-xdist==2.2.0
3541
python-dateutil==2.8.1
3642
python-dotenv==0.15.0
3743
python-multipart==0.0.5
@@ -41,6 +47,7 @@ requests==2.25.1
4147
requests-mock==1.8.0
4248
responses==0.12.1
4349
six==1.15.0
50+
soupsieve==2.1
4451
SQLAlchemy==1.3.22
4552
starlette==0.13.6
4653
toml==0.10.2

0 commit comments

Comments
 (0)