Home Download Direct linkSettings
Aa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
Copy
from dataclasses import dataclass
import json
from pathlib import Path
from mimetypes import guess_type

import flask
from markupsafe import Markup

import src.constants as const

PROJECT_PATH = Path(__file__).parent.parent


def test_all_suffixes(path: Path, suffixes: list[str]) -> Path | None:
    """
    Tests different options of file suffix and check its validity.
    """
    if path.suffix:  # if suffix is present
        if path.suffix in suffixes and path.exists():  # check its validity
            return path
        return None

    if path.exists():  # if suffix not present and file exists
        return path

    for suffix in suffixes:  # trying to find a suffix by brute force
        if (spath := path.with_suffix(suffix)).exists():
            return spath

    return None


def is_hidden(file: Path) -> bool:
    return file.name.startswith("__") or file.name.startswith(".")


def walk_subpath(path: Path, subpath: str, suffixes: list[str], hidden_func=is_hidden) -> Path:
    """
    Iterates over directory names in path to find file.
    """
    subpath_names = filter(bool, subpath.split("/"))

    for name in subpath_names:
        flask.g.rpath.add(name)
        file = path / name

        if hidden_func(file):
            flask.abort(404)

        file = test_all_suffixes(file, suffixes)
        if not file:
            flask.abort(404)

        path = file

    return path


def guess_type_and_load_media(path: Path, file_link: str) -> str:
    """
    Loads media template for any media file.
    """
    mime_type, _ = guess_type(path)
    if not mime_type:
        mime_type = "invalid/invalid"

    return flask.render_template(
        "source/media.html",
        tag=mime_type.split("/")[0],
        file_link=file_link,
        mime_type=mime_type,
    )


def load_content_file_template(path: Path) -> str:
    with open(path) as file:
        source = file.read()

    # Place for the code for processing content files other than HTML
    return flask.render_template_string(source)


def load_source_file_template(path: Path) -> str:
    """
    Loads source (text or media) file template.
    """
    if path.suffix in const.TEXT_SOURCE_SUFFIXES:
        with open(path) as file:
            source = file.read()

        return flask.render_template("source/file.html", source=source, filename=path.name)

    file_link = "/raw/" + str(path.relative_to(PROJECT_PATH))
    return guess_type_and_load_media(path, file_link)


def is_hidden_source(file: Path) -> bool:
    return file.name.startswith("__") or (file.name.startswith(".") and file.is_dir())


def content_filter(file: Path) -> bool:
    return not is_hidden(file) and file.suffix in const.CONTENT_SUFFIXES


def source_filter(file: Path) -> bool:
    return not is_hidden_source(file) and file.suffix in ("", *const.SOURCE_SUFFIXES)


def content_file_finder(subpath: str = "") -> str:
    """
    Finds and loads content file template.
    """
    path = walk_subpath(PROJECT_PATH / "content", subpath, const.CONTENT_SUFFIXES)

    if path.is_dir():
        flask.g.rpath.scan_dir(path, content_filter)
        path /= const.DIR_DESCR_FILENAME
        if not path.exists():
            flask.abort(404)

    return load_content_file_template(path)


def source_file_finder(subpath: str = "") -> str:
    """
    Finds and loads source file template.
    """
    flask.g.rpath.add("source")
    path = walk_subpath(PROJECT_PATH, subpath, const.SOURCE_SUFFIXES, is_hidden_source)

    if path.is_dir():
        flask.g.rpath.scan_dir(path, source_filter)
        return flask.render_template("source/folder.html")
    else:
        return load_source_file_template(path)


def raw_file_finder(subpath: str):
    """
    Find and loads raw files directly from directories.
    """
    flask.g.rpath.add("raw")
    path = walk_subpath(PROJECT_PATH, subpath, const.SOURCE_SUFFIXES, is_hidden_source)

    if not path.is_file():
        flask.abort(404)

    mimetype = None
    if path.suffix in const.TEXT_SOURCE_SUFFIXES:
        mimetype = "text/plain"

    return flask.send_from_directory(
        PROJECT_PATH,
        path.relative_to(PROJECT_PATH),
        mimetype=mimetype,
    )


@dataclass
class Article:
    """
    Dataclass representing meta information about the article.
    """
    path: Path
    link: str
    file_content: str
    date: str = "YYYY.MM.DD"
    number: int = 0

    def __lt__(self, other: "Article") -> bool:
        return (self.date, self.number) < (other.date, other.number)

    @staticmethod
    def _load_meta_block(article: str):
        return json.loads(flask.render_template_string(article, article_mode="meta"))

    @classmethod
    def parse(cls, article_path: Path, link: Path | str) -> "Article":
        with article_path.open() as file:
            file_content = file.read()

        meta_info = cls._load_meta_block(file_content)

        return cls(article_path, "/" + str(link), file_content, **meta_info)

    def include(self, mode="preview") -> Markup:
        template = flask.render_template_string(
            self.file_content,
            link=self.link,
            article_mode=mode,
        )
        return Markup(template)


def get_articles(subpath: str = "/articles") -> list[Article]:
    """
    Finds all articles in directory and loads information about it.
    """
    suffixes = const.CONTENT_SUFFIXES
    root = PROJECT_PATH / "content"
    dir_path = walk_subpath(root, subpath, suffixes)
    article_list: list[Article] = []

    for file in dir_path.iterdir():
        if is_hidden(file):
            continue

        if file.is_file() and file.suffix in suffixes:
            article = Article.parse(file, file.relative_to(root))
        elif (about := file / const.DIR_DESCR_FILENAME).exists():
            article = Article.parse(about, file.relative_to(root))
        else:
            continue

        article_list.append(article)

    return sorted(article_list, reverse=True)