Start tackling mypy errors
This commit is contained in:
parent
fcb12ec630
commit
3a7b37e4c7
7 changed files with 821 additions and 523 deletions
1141
poetry.lock
generated
1141
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -16,6 +16,7 @@ tqdm = "^4.64.0"
|
||||||
psycopg = {extras = ["c", "pool"], version = "^3.0"}
|
psycopg = {extras = ["c", "pool"], version = "^3.0"}
|
||||||
tornado-openapi3 = "^1.1.0"
|
tornado-openapi3 = "^1.1.0"
|
||||||
PyYAML = "^6.0"
|
PyYAML = "^6.0"
|
||||||
|
returns = "^0.19.0"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pytest = "*"
|
pytest = "*"
|
||||||
|
@ -25,6 +26,24 @@ mypy = "*"
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
tutor = 'tutor.cli:main'
|
tutor = 'tutor.cli:main'
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
types-pyyaml = "^6.0.12.6"
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
plugins = "returns.contrib.mypy.returns_plugin"
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = "humanize"
|
||||||
|
ignore_missing_imports = true
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = "parsy"
|
||||||
|
ignore_missing_imports = true
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = "tqdm"
|
||||||
|
ignore_missing_imports = true
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry>=0.12"]
|
requires = ["poetry>=0.12"]
|
||||||
build-backend = "poetry.masonry.api"
|
build-backend = "poetry.masonry.api"
|
||||||
|
|
|
@ -16,14 +16,13 @@ class ImportMode(enum.Enum):
|
||||||
|
|
||||||
async def load(
|
async def load(
|
||||||
settings: dict, stream: typing.IO, mode: ImportMode = ImportMode.add
|
settings: dict, stream: typing.IO, mode: ImportMode = ImportMode.add
|
||||||
) -> typing.Iterable[typing.Tuple[tutor.models.Card, int]]:
|
) -> typing.AsyncIterable[typing.Tuple[tutor.models.CardCopy, int]]:
|
||||||
"""Load cards from a CSV file.
|
"""Load cards from a CSV file.
|
||||||
|
|
||||||
Currently supports the following formats:
|
Currently supports the following formats:
|
||||||
- Deckbox (set name match can fail)
|
- Deckbox (set name match can fail)
|
||||||
- MTGStand (uses Scryfall ID)
|
- MTGStand (uses Scryfall ID)
|
||||||
"""
|
"""
|
||||||
cards = []
|
|
||||||
async with await psycopg.AsyncConnection.connect(
|
async with await psycopg.AsyncConnection.connect(
|
||||||
settings["database"], autocommit=False
|
settings["database"], autocommit=False
|
||||||
) as conn:
|
) as conn:
|
||||||
|
|
|
@ -24,7 +24,7 @@ def convert_price(price: typing.Optional[str]) -> typing.Optional[decimal.Decima
|
||||||
|
|
||||||
|
|
||||||
async def search(
|
async def search(
|
||||||
db: psycopg.Cursor,
|
db: psycopg.AsyncCursor,
|
||||||
name: typing.Optional[str] = None,
|
name: typing.Optional[str] = None,
|
||||||
collector_number: typing.Optional[str] = None,
|
collector_number: typing.Optional[str] = None,
|
||||||
set_code: typing.Optional[str] = None,
|
set_code: typing.Optional[str] = None,
|
||||||
|
@ -39,7 +39,7 @@ async def search(
|
||||||
db.row_factory = psycopg.rows.dict_row
|
db.row_factory = psycopg.rows.dict_row
|
||||||
joins = []
|
joins = []
|
||||||
constraints = []
|
constraints = []
|
||||||
params = {}
|
params: typing.Dict[str, typing.Any] = {}
|
||||||
if name is not None:
|
if name is not None:
|
||||||
constraints.append("cards.name LIKE %(name)s")
|
constraints.append("cards.name LIKE %(name)s")
|
||||||
params["name"] = name
|
params["name"] = name
|
||||||
|
@ -102,7 +102,7 @@ async def search(
|
||||||
|
|
||||||
|
|
||||||
async def advanced_search(
|
async def advanced_search(
|
||||||
db: psycopg.Cursor,
|
db: psycopg.AsyncCursor,
|
||||||
search: tutor.search.Search,
|
search: tutor.search.Search,
|
||||||
limit: int = 10,
|
limit: int = 10,
|
||||||
offset: int = 0,
|
offset: int = 0,
|
||||||
|
@ -112,8 +112,7 @@ async def advanced_search(
|
||||||
db.row_factory = psycopg.rows.dict_row
|
db.row_factory = psycopg.rows.dict_row
|
||||||
joins = []
|
joins = []
|
||||||
constraints = []
|
constraints = []
|
||||||
params = {}
|
params: typing.Dict[str, typing.Any] = {}
|
||||||
sets = []
|
|
||||||
|
|
||||||
logger.debug("Performing search for: %s", search)
|
logger.debug("Performing search for: %s", search)
|
||||||
for i, criterion in enumerate(search.criteria):
|
for i, criterion in enumerate(search.criteria):
|
||||||
|
@ -244,7 +243,7 @@ async def advanced_search(
|
||||||
|
|
||||||
|
|
||||||
async def oracle_id_by_name(
|
async def oracle_id_by_name(
|
||||||
db: psycopg.Cursor, name: str
|
db: psycopg.AsyncCursor, name: str
|
||||||
) -> typing.Optional[uuid.UUID]:
|
) -> typing.Optional[uuid.UUID]:
|
||||||
db.row_factory = psycopg.rows.dict_row
|
db.row_factory = psycopg.rows.dict_row
|
||||||
await db.execute(
|
await db.execute(
|
||||||
|
@ -253,9 +252,11 @@ async def oracle_id_by_name(
|
||||||
row = await db.fetchone()
|
row = await db.fetchone()
|
||||||
if row:
|
if row:
|
||||||
return row["oracle_id"]
|
return row["oracle_id"]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def store_card(db: psycopg.Cursor, card: tutor.models.Card) -> None:
|
async def store_card(db: psycopg.AsyncCursor, card: tutor.models.Card) -> None:
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO tmp_cards
|
INSERT INTO tmp_cards
|
||||||
|
@ -286,7 +287,7 @@ async def store_card(db: psycopg.Cursor, card: tutor.models.Card) -> None:
|
||||||
|
|
||||||
|
|
||||||
async def store_price(
|
async def store_price(
|
||||||
db: psycopg.Cursor, date: datetime.date, card: tutor.models.Card
|
db: psycopg.AsyncCursor, date: datetime.date, card: tutor.models.Card
|
||||||
) -> None:
|
) -> None:
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
|
@ -306,7 +307,7 @@ async def store_price(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def store_set(db: psycopg.Cursor, set_code: str, name: str) -> None:
|
async def store_set(db: psycopg.AsyncCursor, set_code: str, name: str) -> None:
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO "sets" ("set_code", "name")
|
INSERT INTO "sets" ("set_code", "name")
|
||||||
|
@ -317,7 +318,7 @@ async def store_set(db: psycopg.Cursor, set_code: str, name: str) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def store_copy(db: psycopg.Cursor, copy: tutor.models.CardCopy) -> None:
|
async def store_copy(db: psycopg.AsyncCursor, copy: tutor.models.CardCopy) -> None:
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO copies ("scryfall_id", "isFoil", "collection", "condition")
|
INSERT INTO copies ("scryfall_id", "isFoil", "collection", "condition")
|
||||||
|
@ -332,24 +333,29 @@ async def store_copy(db: psycopg.Cursor, copy: tutor.models.CardCopy) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def clear_copies(db: psycopg.Cursor, collection: typing.Optional[str] = None):
|
async def clear_copies(
|
||||||
|
db: psycopg.AsyncCursor, collection: typing.Optional[str] = None
|
||||||
|
):
|
||||||
if collection:
|
if collection:
|
||||||
await db.execute("DELETE FROM copies WHERE collection = %s", collection)
|
await db.execute("DELETE FROM copies WHERE collection = %s", collection)
|
||||||
else:
|
else:
|
||||||
await db.execute("DELETE FROM copies")
|
await db.execute("DELETE FROM copies")
|
||||||
|
|
||||||
|
|
||||||
async def store_deck(db: psycopg.Cursor, name: str) -> None:
|
async def store_deck(db: psycopg.AsyncCursor, name: str) -> typing.Optional[int]:
|
||||||
await db.execute(
|
await db.execute(
|
||||||
'INSERT INTO "decks" ("name") VALUES (%(name)s) RETURNING "deck_id"',
|
'INSERT INTO "decks" ("name") VALUES (%(name)s) RETURNING "deck_id"',
|
||||||
{"name": name},
|
{"name": name},
|
||||||
)
|
)
|
||||||
result = await db.fetchone()
|
result = await db.fetchone()
|
||||||
return result[0]
|
if result:
|
||||||
|
return result[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def store_deck_card(
|
async def store_deck_card(
|
||||||
db: psycopg.Cursor, deck_id: int, oracle_id: uuid.UUID, quantity: int = 1
|
db: psycopg.AsyncCursor, deck_id: int, oracle_id: uuid.UUID, quantity: int = 1
|
||||||
) -> None:
|
) -> None:
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
|
@ -361,7 +367,7 @@ async def store_deck_card(
|
||||||
|
|
||||||
|
|
||||||
async def get_decks(
|
async def get_decks(
|
||||||
db: psycopg.Cursor, limit: int = 10, offset: int = 0
|
db: psycopg.AsyncCursor, limit: int = 10, offset: int = 0
|
||||||
) -> typing.List[tutor.models.Deck]:
|
) -> typing.List[tutor.models.Deck]:
|
||||||
db.row_factory = psycopg.rows.dict_row
|
db.row_factory = psycopg.rows.dict_row
|
||||||
await db.execute(
|
await db.execute(
|
||||||
|
@ -443,7 +449,7 @@ async def get_decks(
|
||||||
|
|
||||||
|
|
||||||
async def get_deck(
|
async def get_deck(
|
||||||
db: psycopg.Cursor, deck_id: int
|
db: psycopg.AsyncCursor, deck_id: int
|
||||||
) -> typing.Optional[tutor.models.Deck]:
|
) -> typing.Optional[tutor.models.Deck]:
|
||||||
db.row_factory = psycopg.rows.dict_row
|
db.row_factory = psycopg.rows.dict_row
|
||||||
await db.execute(
|
await db.execute(
|
||||||
|
@ -519,9 +525,11 @@ async def get_deck(
|
||||||
if card and card.get("oracle_id")
|
if card and card.get("oracle_id")
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def store_var(db: psycopg.Cursor, key: str, value: str) -> None:
|
async def store_var(db: psycopg.AsyncCursor, key: str, value: str) -> None:
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO "vars" ("key", "value")
|
INSERT INTO "vars" ("key", "value")
|
||||||
|
@ -533,7 +541,7 @@ async def store_var(db: psycopg.Cursor, key: str, value: str) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def collection_stats(db: psycopg.Cursor) -> dict:
|
async def collection_stats(db: psycopg.AsyncCursor) -> typing.Optional[dict]:
|
||||||
db.row_factory = psycopg.rows.dict_row
|
db.row_factory = psycopg.rows.dict_row
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -15,10 +15,11 @@ class Color(enum.IntEnum):
|
||||||
Red = 5
|
Red = 5
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return dict(zip(Color, "CWUBGR")).get(self.value).replace("C", "")
|
colors: typing.Dict[int, str] = dict(zip(Color, "CWUBGR"))
|
||||||
|
return colors.get(self.value, "").replace("C", "")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_string(colors: typing.List["Color"]) -> str:
|
def to_string(colors: typing.Iterable["Color"]) -> str:
|
||||||
return "".join(map(str, sorted(colors)))
|
return "".join(map(str, sorted(colors)))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -84,7 +85,7 @@ class Card:
|
||||||
set_code: str
|
set_code: str
|
||||||
set_name: str
|
set_name: str
|
||||||
collector_number: str
|
collector_number: str
|
||||||
rarity: str
|
rarity: Rarity
|
||||||
color_identity: typing.List[Color]
|
color_identity: typing.List[Color]
|
||||||
cmc: decimal.Decimal
|
cmc: decimal.Decimal
|
||||||
type_line: str
|
type_line: str
|
||||||
|
|
|
@ -1,46 +1,57 @@
|
||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
|
import uuid
|
||||||
|
from returns.maybe import Maybe
|
||||||
|
|
||||||
import tutor.models
|
import tutor.models
|
||||||
|
|
||||||
|
|
||||||
def to_card(data: dict) -> tutor.models.Card:
|
def to_card(data: dict) -> Maybe[tutor.models.Card]:
|
||||||
prices = {
|
prices = {
|
||||||
k: decimal.Decimal(v) if v else None for k, v in data.get("prices", {}).items()
|
k: decimal.Decimal(v) if v else None for k, v in data.get("prices", {}).items()
|
||||||
}
|
}
|
||||||
return tutor.models.Card(
|
return Maybe.do(
|
||||||
scryfall_id=data["id"],
|
tutor.models.Card(
|
||||||
oracle_id=data.get("oracle_id"),
|
scryfall_id=data["id"],
|
||||||
name=data["name"],
|
oracle_id=oracle_id,
|
||||||
set_code=data["set"].upper() if "set" in data else None,
|
name=data["name"],
|
||||||
set_name=data.get("set_name"),
|
set_code=set_code,
|
||||||
collector_number=data.get("collector_number"),
|
set_name=set_name,
|
||||||
rarity=tutor.models.Rarity.from_string(data.get("rarity", "n/a")),
|
collector_number=collector_number,
|
||||||
color_identity=tutor.models.Color.from_string(
|
rarity=tutor.models.Rarity.from_string(data.get("rarity", "n/a")),
|
||||||
"".join(data.get("color_identity", []))
|
color_identity=tutor.models.Color.from_string(
|
||||||
),
|
"".join(data.get("color_identity", []))
|
||||||
cmc=decimal.Decimal(data.get("cmc", "0")),
|
),
|
||||||
mana_cost=data.get("mana_cost"),
|
cmc=decimal.Decimal(data.get("cmc", "0")),
|
||||||
type_line=data.get("type_line", ""),
|
mana_cost=data.get("mana_cost"),
|
||||||
release_date=datetime.date.fromisoformat(data["released_at"])
|
type_line=data.get("type_line", ""),
|
||||||
if "released_at" in data
|
release_date=release_date,
|
||||||
else None,
|
games={
|
||||||
games={
|
game
|
||||||
game for game in tutor.models.Game if game.value in data.get("games", [])
|
for game in tutor.models.Game
|
||||||
},
|
if game.value in data.get("games", [])
|
||||||
legalities={
|
},
|
||||||
game_format: {l.value: l for l in tutor.models.Legality}[legality]
|
legalities={
|
||||||
for game_format, legality in data.get("legalities", {}).items()
|
game_format: {l.value: l for l in tutor.models.Legality}[legality]
|
||||||
},
|
for game_format, legality in data.get("legalities", {}).items()
|
||||||
edhrec_rank=(
|
},
|
||||||
int(data.get("edhrec_rank"))
|
edhrec_rank=Maybe.from_optional(data.get("edhrec_rank"))
|
||||||
if data.get("edhrec_rank") is not None
|
.map(int)
|
||||||
else None
|
.value_or(None),
|
||||||
),
|
oracle_text=data.get("oracle_text"),
|
||||||
oracle_text=data.get("oracle_text"),
|
price_usd=prices.get("usd"),
|
||||||
price_usd=prices.get("usd"),
|
price_usd_foil=prices.get("usd_foil"),
|
||||||
price_usd_foil=prices.get("usd_foil"),
|
price_eur=prices.get("eur"),
|
||||||
price_eur=prices.get("eur"),
|
price_eur_foil=prices.get("eur_foil"),
|
||||||
price_eur_foil=prices.get("eur_foil"),
|
price_tix=prices.get("tix"),
|
||||||
price_tix=prices.get("tix"),
|
)
|
||||||
|
for oracle_id in Maybe.from_optional(data.get("oracle_id")).map(uuid.UUID)
|
||||||
|
for set_code in Maybe.from_optional(data.get("set")).map(str.upper)
|
||||||
|
for set_name in Maybe.from_optional(data.get("set_name")).map(str)
|
||||||
|
for collector_number in Maybe.from_optional(data.get("collector_number")).map(
|
||||||
|
str
|
||||||
|
)
|
||||||
|
for release_date in Maybe.from_optional(data.get("released_at")).map(
|
||||||
|
datetime.date.fromisoformat
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -92,7 +92,7 @@ class JSONEncoder(json.JSONEncoder):
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def _card_copy(self, copy: tutor.models.Card) -> dict:
|
def _card_copy(self, copy: tutor.models.CardCopy) -> dict:
|
||||||
return {
|
return {
|
||||||
"card": self._card(copy.card),
|
"card": self._card(copy.card),
|
||||||
"foil": copy.foil,
|
"foil": copy.foil,
|
||||||
|
@ -133,16 +133,6 @@ class OpenAPIRequestHandler(tornado_openapi3.handler.OpenAPIRequestHandler):
|
||||||
|
|
||||||
|
|
||||||
class RequestHandler(tornado.web.RequestHandler):
|
class RequestHandler(tornado.web.RequestHandler):
|
||||||
def set_links(self, **links) -> None:
|
|
||||||
self.set_header(
|
|
||||||
"Link",
|
|
||||||
", ".join(
|
|
||||||
[f'<{self.url(url)}>; rel="{rel}"' for rel, url in links.items()]
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SearchHandler(RequestHandler):
|
|
||||||
def url(self, url: str) -> str:
|
def url(self, url: str) -> str:
|
||||||
scheme_override = self.application.settings["scheme"]
|
scheme_override = self.application.settings["scheme"]
|
||||||
if not scheme_override:
|
if not scheme_override:
|
||||||
|
@ -158,13 +148,30 @@ class SearchHandler(RequestHandler):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def set_links(self, **links) -> None:
|
||||||
|
self.set_header(
|
||||||
|
"Link",
|
||||||
|
", ".join(
|
||||||
|
[f'<{self.url(url)}>; rel="{rel}"' for rel, url in links.items()]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pool(self) -> psycopg_pool.AsyncConnectionPool:
|
||||||
|
pool = getattr(self.application, "pool", None)
|
||||||
|
if not pool:
|
||||||
|
raise RuntimeError("Database pool not initialized")
|
||||||
|
return pool
|
||||||
|
|
||||||
|
|
||||||
|
class SearchHandler(RequestHandler):
|
||||||
async def get(self) -> None:
|
async def get(self) -> None:
|
||||||
async with self.application.pool.connection() as conn:
|
async with self.pool.connection() as conn:
|
||||||
async with conn.cursor() as cursor:
|
async with conn.cursor() as cursor:
|
||||||
query = self.get_argument("q", "")
|
query = self.get_argument("q", "")
|
||||||
in_collection = self.get_argument("in_collection", None)
|
in_collection = self.get_argument("in_collection", None)
|
||||||
page = max(1, int(self.get_argument("page", 1)))
|
page = max(1, int(self.get_argument("page", "1")))
|
||||||
limit = int(self.get_argument("limit", 10))
|
limit = int(self.get_argument("limit", "10"))
|
||||||
sort_by = self.get_argument("sort_by", "rarity")
|
sort_by = self.get_argument("sort_by", "rarity")
|
||||||
search = tutor.search.search.parse(query)
|
search = tutor.search.search.parse(query)
|
||||||
copies = await tutor.database.advanced_search(
|
copies = await tutor.database.advanced_search(
|
||||||
|
@ -192,7 +199,7 @@ class SearchHandler(RequestHandler):
|
||||||
|
|
||||||
class CollectionHandler(RequestHandler):
|
class CollectionHandler(RequestHandler):
|
||||||
async def get(self) -> None:
|
async def get(self) -> None:
|
||||||
async with self.application.pool.connection() as conn:
|
async with self.pool.connection() as conn:
|
||||||
async with conn.cursor() as cursor:
|
async with conn.cursor() as cursor:
|
||||||
self.write(
|
self.write(
|
||||||
json.dumps(
|
json.dumps(
|
||||||
|
@ -203,9 +210,9 @@ class CollectionHandler(RequestHandler):
|
||||||
|
|
||||||
class DecksHandler(RequestHandler):
|
class DecksHandler(RequestHandler):
|
||||||
async def get(self) -> None:
|
async def get(self) -> None:
|
||||||
page = max(1, int(self.get_argument("page", 1)))
|
page = max(1, int(self.get_argument("page", "1")))
|
||||||
limit = int(self.get_argument("limit", 10))
|
limit = int(self.get_argument("limit", "10"))
|
||||||
async with self.application.pool.connection() as conn:
|
async with self.pool.connection() as conn:
|
||||||
async with conn.cursor() as cursor:
|
async with conn.cursor() as cursor:
|
||||||
decks = await tutor.database.get_decks(
|
decks = await tutor.database.get_decks(
|
||||||
cursor, limit=limit + 1, offset=limit * (page - 1)
|
cursor, limit=limit + 1, offset=limit * (page - 1)
|
||||||
|
@ -229,7 +236,7 @@ class DeckHandler(RequestHandler):
|
||||||
async def get(self, deck_id) -> None:
|
async def get(self, deck_id) -> None:
|
||||||
self.set_header("Content-Type", "application/json")
|
self.set_header("Content-Type", "application/json")
|
||||||
self.set_header("Access-Control-Allow-Origin", "*")
|
self.set_header("Access-Control-Allow-Origin", "*")
|
||||||
async with self.application.pool.connection() as conn:
|
async with self.pool.connection() as conn:
|
||||||
async with conn.cursor() as cursor:
|
async with conn.cursor() as cursor:
|
||||||
deck = await tutor.database.get_deck(cursor, deck_id)
|
deck = await tutor.database.get_deck(cursor, deck_id)
|
||||||
if not deck:
|
if not deck:
|
||||||
|
@ -253,7 +260,7 @@ class TemplateHandler(RequestHandler):
|
||||||
|
|
||||||
async def get(self) -> None:
|
async def get(self) -> None:
|
||||||
self.set_header("Content-Type", self.content_type)
|
self.set_header("Content-Type", self.content_type)
|
||||||
return self.render(self.path, **self.vars)
|
self.render(self.path, **self.vars)
|
||||||
|
|
||||||
|
|
||||||
class StaticFileHandler(tornado.web.StaticFileHandler):
|
class StaticFileHandler(tornado.web.StaticFileHandler):
|
||||||
|
|
Loading…
Reference in a new issue