Skip to content

Commit a2272de

Browse files
authored
Merge pull request #73 from filips123/performance-improvements
2 parents 12ae8a1 + fd6877e commit a2272de

18 files changed

+1824
-1798
lines changed

.github/workflows/api.yaml

+9-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ on:
55
paths:
66
- .github/workflows/api.yaml
77
- API/**
8+
branches:
9+
- main
810
pull_request:
911
paths:
1012
- .github/workflows/api.yaml
@@ -28,12 +30,12 @@ jobs:
2830
uses: actions/cache@v3
2931
with:
3032
path: |
33+
~/.cache/black
3134
~/.cache/pip
3235
~/.cache/pypoetry
3336
~/.local/share/pypoetry
3437
~/.local/bin/poetry
35-
./.flakeheaven_cache
36-
./.mypy_cache
38+
./.ruff_cache
3739
key: ${{ runner.os }}-poetry-lint-${{ hashFiles('**/poetry.lock') }}
3840
restore-keys: ${{ runner.os }}-poetry-lint-
3941

@@ -51,8 +53,11 @@ jobs:
5153
- name: Install dependencies
5254
run: poetry install --extras sentry
5355

54-
- name: Lint the project
55-
run: flakeheaven lint
56+
- name: Lint the project with ruff
57+
run: ruff gimvicurnik
58+
59+
- name: Lint the project with black
60+
run: black gimvicurnik --check
5661

5762
typecheck:
5863
name: Typechecking
@@ -70,7 +75,6 @@ jobs:
7075
~/.cache/pypoetry
7176
~/.local/share/pypoetry
7277
~/.local/bin/poetry
73-
./.flakeheaven_cache
7478
./.mypy_cache
7579
key: ${{ runner.os }}-poetry-typecheck-${{ hashFiles('**/poetry.lock') }}
7680
restore-keys: ${{ runner.os }}-poetry-typecheck-

.github/workflows/deploy.yaml

-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ jobs:
2828
~/.cache/pypoetry
2929
~/.local/share/pypoetry
3030
~/.local/bin/poetry
31-
./.flakeheaven_cache
32-
./.mypy_cache
3331
key: ${{ runner.os }}-poetry-deploy-${{ hashFiles('**/poetry.lock') }}
3432
restore-keys: ${{ runner.os }}-poetry-
3533

.github/workflows/website.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ on:
55
paths:
66
- .github/workflows/website.yaml
77
- website/**
8+
branches:
9+
- main
810
pull_request:
911
paths:
1012
- .github/workflows/website.yaml

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ tmp/
8383
.node_repl_history
8484
.npm
8585
.nyc_output
86+
.ruff_cache
8687
.yarn-integrity
8788
*.tmp
8889

API/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ You can retrieve all API routes using the `gimvicurnik routes` commands. The off
5757

5858
## Contributing
5959

60-
The API uses FlakeHeaven, Black and mypy for linting the code. They are included in project's development dependencies.
60+
The API uses ruff, black and mypy for linting the code. They are included in project's development dependencies.
6161

6262
Please make sure that your changes are formatted correctly according to the code style:
6363

64-
* Linting: `flakeheaven lint`
64+
* Linting: `ruff gimvicurnik`
6565
* Typechecking: `mypy gimvicurnik`
6666
* Formatting: `black gimvicurnik`

API/gimvicurnik/blueprints/calendar.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
from ..utils.sentry import start_span, with_span
1414

1515
if typing.TYPE_CHECKING:
16-
from typing import Any, Iterator
16+
from typing import Any
17+
from collections.abc import Iterator
1718
from flask import Blueprint, Response
1819
from sqlalchemy.orm.query import RowReturningQuery
1920
from ..config import Config, ConfigLessonTime

API/gimvicurnik/database/__init__.py

+7-13
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from __future__ import annotations
22

33
import enum
4+
from collections.abc import Iterator
45
from datetime import date as date_, datetime, time as time_
5-
from typing import Annotated, Any, Iterator
6+
from typing import Annotated, Any
67

78
from sqlalchemy import (
89
Enum,
@@ -187,27 +188,20 @@ def get_empty(cls) -> Iterator[dict[str, Any]]:
187188
if times[0] is None or times[1] is None:
188189
return []
189190

190-
classrooms = list(Session.query(Classroom).order_by(Classroom.name))
191-
lessons = list(Session.query(Lesson).join(Classroom))
191+
classrooms = Session.query(Classroom.name).order_by(Classroom.name).distinct().all()
192+
occupied = set(Session.query(Lesson.day, Lesson.time, Classroom.name).join(Classroom).distinct())
192193

193194
for day in range(days[0], days[1] + 1):
194195
for time in range(times[0], times[1] + 1):
195-
for classroom in classrooms:
196-
is_classroom_empty = True
197-
198-
for lesson in lessons:
199-
if lesson.day == day and lesson.time == time and lesson.classroom == classroom:
200-
is_classroom_empty = False
201-
break
202-
203-
if is_classroom_empty:
196+
for (classroom,) in classrooms:
197+
if (day, time, classroom) not in occupied:
204198
yield {
205199
"day": day,
206200
"time": time,
207201
"subject": None,
208202
"class": None,
209203
"teacher": None,
210-
"classroom": classroom.name,
204+
"classroom": classroom,
211205
}
212206

213207

API/gimvicurnik/updaters/base.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
from ..utils.sentry import sentry_available, with_span
1313

1414
if typing.TYPE_CHECKING:
15-
from typing import ClassVar, Iterator
15+
from typing import ClassVar
16+
from collections.abc import Iterator
1617
from logging import Logger
1718
from sqlalchemy.orm import Session
1819
from sentry_sdk.tracing import Span
@@ -79,6 +80,15 @@ class BaseMultiUpdater(ABC):
7980
Must be set by subclasses before running `update`.
8081
"""
8182

83+
requests: requests.Session
84+
"""
85+
A requests session that the updater should use.
86+
Will be set automatically by the base updater.
87+
"""
88+
89+
def __init__(self) -> None:
90+
self.requests = requests.Session()
91+
8292
def update(self) -> None:
8393
"""Get all available documents and update them."""
8494

@@ -347,14 +357,14 @@ def download_document(self, document: DocumentInfo) -> tuple[bytes, str]:
347357
"""Download a document and return its content and hash"""
348358

349359
try:
350-
response = requests.get(self.tokenize_url(document.url))
360+
response = self.requests.get(self.tokenize_url(document.url))
351361
response.raise_for_status()
352362

353363
content = response.content
354364
sha = sha256(content).hexdigest()
355365
return content, sha
356366

357-
except IOError as error:
367+
except OSError as error:
358368
raise self.error(f"Error while downloading a {document.type.value} document") from error
359369

360370
# noinspection PyMethodMayBeStatic

API/gimvicurnik/updaters/eclassroom.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from datetime import date, datetime, timezone
1111
from itertools import product
1212

13-
import requests
1413
from mammoth import convert_to_html # type: ignore
1514
from sqlalchemy import insert
1615

@@ -22,7 +21,8 @@
2221
from ..utils.sentry import with_span
2322

2423
if typing.TYPE_CHECKING:
25-
from typing import Any, Iterator
24+
from typing import Any
25+
from collections.abc import Iterator
2626
from mammoth.documents import Image # type: ignore
2727
from sqlalchemy.orm import Session
2828
from sentry_sdk.tracing import Span
@@ -49,6 +49,8 @@ def __init__(self, config: ConfigSourcesEClassroom, session: Session) -> None:
4949
self.config = config
5050
self.session = session
5151

52+
super().__init__()
53+
5254
def get_documents(self) -> Iterator[DocumentInfo]:
5355
"""Get all documents from the e-classroom."""
5456

@@ -70,9 +72,9 @@ def _mark_course_viewed(self) -> None:
7072
}
7173

7274
try:
73-
response = requests.post(self.config.webserviceUrl, params=params, data=data)
75+
response = self.requests.post(self.config.webserviceUrl, params=params, data=data)
7476
response.raise_for_status()
75-
except (IOError, ValueError) as error:
77+
except (OSError, ValueError) as error:
7678
raise ClassroomApiError("Error while accessing e-classroom API") from error
7779

7880
def _get_internal_urls(self) -> Iterator[DocumentInfo]:
@@ -86,11 +88,11 @@ def _get_internal_urls(self) -> Iterator[DocumentInfo]:
8688
}
8789

8890
try:
89-
response = requests.post(self.config.webserviceUrl, params=params, data=data)
91+
response = self.requests.post(self.config.webserviceUrl, params=params, data=data)
9092
contents = response.json()
9193

9294
response.raise_for_status()
93-
except (IOError, ValueError) as error:
95+
except (OSError, ValueError) as error:
9496
raise ClassroomApiError("Error while accessing e-classroom API") from error
9597

9698
# Handle API errors
@@ -129,11 +131,11 @@ def _get_external_urls(self) -> Iterator[DocumentInfo]:
129131
}
130132

131133
try:
132-
response = requests.post(self.config.webserviceUrl, params=params, data=data)
134+
response = self.requests.post(self.config.webserviceUrl, params=params, data=data)
133135
contents = response.json()
134136

135137
response.raise_for_status()
136-
except (IOError, ValueError) as error:
138+
except (OSError, ValueError) as error:
137139
raise ClassroomApiError("Error while accessing e-classroom API") from error
138140

139141
# Handle API errors

API/gimvicurnik/updaters/menu.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@
77
import tempfile
88
import typing
99

10-
import requests
1110
from bs4 import BeautifulSoup, ParserRejectedMarkup
1211
from openpyxl import load_workbook
1312

1413
from .base import BaseMultiUpdater, DocumentInfo
1514
from ..database import DocumentType, LunchMenu, SnackMenu
1615
from ..errors import MenuApiError, MenuDateError, MenuFormatError
17-
from ..utils.sentry import with_span
1816
from ..utils.pdf import extract_tables
17+
from ..utils.sentry import with_span
1918

2019
if typing.TYPE_CHECKING:
21-
from typing import Iterator, Any
20+
from typing import Any
21+
from collections.abc import Iterator
2222
from sqlalchemy.orm import Session
2323
from sentry_sdk.tracing import Span
2424
from ..config import ConfigSourcesMenu
@@ -33,15 +33,17 @@ def __init__(self, config: ConfigSourcesMenu, session: Session):
3333
self.config = config
3434
self.session = session
3535

36+
super().__init__()
37+
3638
def get_documents(self) -> Iterator[DocumentInfo]:
3739
"""Download and parse the website to retrieve all menu URLs."""
3840

3941
try:
40-
response = requests.get(self.config.url)
42+
response = self.requests.get(self.config.url)
4143
response.raise_for_status()
4244

4345
soup = with_span(op="soup")(BeautifulSoup)(response.text, features="lxml")
44-
except (IOError, ParserRejectedMarkup) as error:
46+
except (OSError, ParserRejectedMarkup) as error:
4547
raise MenuApiError("Error while downloading or parsing menu index") from error
4648

4749
menus = soup.find_all("li", {"class": "jedilnik"})

API/gimvicurnik/updaters/timetable.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def _download(self) -> tuple[str, str]:
5656
response = requests.get(self.config.url)
5757
content = response.content
5858
response.raise_for_status()
59-
except IOError as error:
59+
except OSError as error:
6060
raise TimetableApiError("Error while downloading the timetable") from error
6161

6262
return content.decode("utf8"), sha256(content).hexdigest()

API/gimvicurnik/utils/errors.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from traceback import TracebackException as _TracebackException
1515

1616
if typing.TYPE_CHECKING:
17-
from typing import Iterable, Iterator
17+
from collections.abc import Iterable, Iterator
1818

1919

2020
class TracebackException(_TracebackException):

API/gimvicurnik/utils/sentry.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
if typing.TYPE_CHECKING:
77
from types import TracebackType
8-
from typing import Any, Callable, TypeVar, ParamSpec
8+
from typing import Any, TypeVar, ParamSpec
9+
from collections.abc import Callable
910

1011
TP = ParamSpec("TP")
1112
TR = TypeVar("TR")

0 commit comments

Comments
 (0)