Skip to content

Commit ea24cd0

Browse files
committed
feat: PageIndex uses get_ftoc for cached ftocs to reduce load time
1 parent 0d49d1b commit ea24cd0

File tree

4 files changed

+91
-6
lines changed

4 files changed

+91
-6
lines changed

otterwiki/helper.py

+56-1
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,20 @@
1111

1212
import os
1313
import re
14+
import os
15+
from hashlib import sha256
16+
import json
17+
from datetime import datetime
1418
from collections import namedtuple
15-
from otterwiki.server import app, mail, storage, Preferences
19+
from otterwiki.server import app, mail, storage, Preferences, db, app_renderer
1620
from otterwiki.gitstorage import StorageError
1721
from flask import flash, url_for, session
1822
from threading import Thread
1923
from flask_mail import Message
2024
from itsdangerous import URLSafeTimedSerializer, BadSignature, SignatureExpired
2125
from otterwiki.util import split_path, join_path, clean_slashes, titleSs
2226
from otterwiki.renderer import OtterwikiRenderer
27+
from otterwiki.models import Cache
2328

2429

2530
class SerializeError(ValueError):
@@ -283,3 +288,53 @@ def patchset2urlmap(patchset, rev_b, rev_a=None):
283288
}
284289
url_map[file.path] = namedtuple('UrlData', d.keys())(*d.values())
285290
return url_map
291+
292+
293+
def sha256sum(s: str) -> str:
294+
hash = sha256()
295+
hash.update(s.encode())
296+
return hash.hexdigest()
297+
298+
299+
def update_ftoc_cache(filename, ftoc, mtime=None):
300+
if mtime is None:
301+
mtime = storage.mtime(filename)
302+
hash = sha256sum(f"ftoc://{filename}")
303+
value = json.dumps({"filename": filename, "ftoc": ftoc})
304+
# check if key exists in Cache
305+
c = Cache.query.filter(Cache.key == hash).first()
306+
if c is None:
307+
c = Cache()
308+
c.key = hash
309+
c.value = value
310+
c.datetime = mtime
311+
# and update in the database
312+
db.session.add(c)
313+
db.session.commit()
314+
315+
316+
def get_ftoc(filename, mtime=None):
317+
if mtime is None:
318+
mtime = storage.mtime(filename)
319+
hash = sha256sum(f"ftoc://{filename}")
320+
# check if hash is in the Cache
321+
result = Cache.query.filter(
322+
db.and_(Cache.key == hash, Cache.datetime >= mtime)
323+
).first()
324+
if result is not None:
325+
try:
326+
value = json.loads(result.value)
327+
try:
328+
# check
329+
if filename == value["filename"]:
330+
return value["ftoc"]
331+
except KeyError:
332+
pass
333+
except:
334+
pass
335+
content = storage.load(filename)
336+
# parse file contents
337+
_, ftoc = app_renderer.markdown(content)
338+
update_ftoc_cache(filename, ftoc, mtime)
339+
340+
return ftoc

otterwiki/models.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,6 @@ def __repr__(self):
7575

7676
class Cache(db.Model):
7777
__tablename__ = "cache"
78-
key = db.Column(db.String(64), primary_key=True)
78+
key = db.Column(db.String(64), index=True, primary_key=True)
7979
value = db.Column(db.Text)
8080
datetime = db.Column(TimeStamp())

otterwiki/wiki.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
patchset2urlmap,
4444
get_breadcrumbs,
4545
upsert_pagecrumbs,
46+
get_ftoc,
47+
update_ftoc_cache,
4648
)
4749
from otterwiki.auth import has_permission, current_user
4850
from otterwiki.plugins import chain_hooks
@@ -144,10 +146,8 @@ def __init__(self, path=None):
144146
f,
145147
full=False,
146148
)
147-
# read file
148-
content = storage.load(f)
149-
# parse file contents
150-
_, ftoc = app_renderer.markdown(content)
149+
ftoc = get_ftoc(f)
150+
151151
# add headers to page toc
152152
# (4, '2 L <strong>bold</strong>', 1, '2 L bold', '2-l-bold')
153153
for i, header in enumerate(ftoc):
@@ -549,6 +549,7 @@ def view(self):
549549
htmlcontent, toc = app_renderer.markdown(
550550
self.content, page_url=self.page_url
551551
)
552+
update_ftoc_cache(self.filename, ftoc=toc)
552553

553554
if len(toc) > 0:
554555
# use first headline to overwrite pagename

tests/test_helper.py

+29
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import pytest
55
import flask
6+
from datetime import datetime
67
import otterwiki
78
import otterwiki.gitstorage
89

@@ -252,3 +253,31 @@ def test_get_pagename_prefixes(test_client):
252253
'Example',
253254
'Random page',
254255
]
256+
257+
258+
def test_ftoc_cache(create_app, req_ctx):
259+
assert create_app
260+
assert req_ctx
261+
from otterwiki.helper import get_ftoc, update_ftoc_cache
262+
263+
mtime = datetime.fromtimestamp(0)
264+
filename = "ftoc.md"
265+
ftoc = [
266+
[0, "Ftoc", 1, "Ftoc", "ftoc"],
267+
[1, "Header 2", 2, "Header 2", "header-2"],
268+
[2, "Header 3", 3, "Header 3", "header-3"],
269+
]
270+
update_ftoc_cache(filename=filename, ftoc=ftoc, mtime=mtime)
271+
assert get_ftoc(filename=filename, mtime=mtime) == ftoc
272+
# store the file
273+
assert True == create_app.storage.store(
274+
filename,
275+
content="# Header 1\n\n## Header 2",
276+
author=("John", "[email protected]"),
277+
message=f"added {filename}",
278+
)
279+
280+
assert [
281+
(0, 'Header 1', 1, 'Header 1', 'header-1'),
282+
(1, 'Header 2', 2, 'Header 2', 'header-2'),
283+
] == get_ftoc(filename)

0 commit comments

Comments
 (0)