boop.py 7.06 KB
Newer Older
1
2
3
#!/bin/env python3

import os
4
import sys
5
import distutils.dir_util
6
import locale
7
import datetime
8

9
import pytz
10

11
12
13
14
15
import boop.boopsy as boopsy
import boop.boopfeed as boopfeed
import boop.environment as environment
import boop.configuration as config
import boop.utils as utils
16
17


18
locale.setlocale(locale.LC_ALL, "")
19

20

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class ProgramError(Exception):
    """Exception raised during the program execution.

    Examples:

    >>> try:
    ...     raise ProgramError("Oops")
    ... except ProgramError as e:
    ...     print(e.args[0])
    Oops
    """

    pass


36
37
38
def main(environment):
    # Make sure ./site folder is empty
    output_path = os.path.join(os.curdir, "site")
39
    utils.init_output_folder(output_path)
40
41
42

    # Load the configuration (it can be overidden by a configuration.yml file)
    configuration_path = os.path.join(os.curdir, "configuration.yml")
43
    configuration = config.load(configuration_path, environment, output_path)
44

45
46
47
    # STEP 1: we load all the Articles and Pages
    pages = []
    articles = []
berumuron's avatar
berumuron committed
48
    articles_by_series = {}
49
50

    # Check that index file exists
51
52
53
    index_filepath = os.path.join(os.curdir, "index.html")
    if not os.path.exists(index_filepath):
        raise ProgramError("./index.html file does not exist")
54

55
56
57
58
    index_page = utils.build_page_from_filepath(
        index_filepath, configuration, basename=os.curdir
    )
    pages.append(index_page)
59
60

    # Check that pages directory exists and load Pages from it
berumuron's avatar
berumuron committed
61
62
    pages_path = os.path.join(os.curdir, "pages")
    if os.path.isdir(pages_path):
63
        for filename in utils.dir_tree(pages_path, only=["html"]):
berumuron's avatar
berumuron committed
64
            page_filepath = os.path.join(pages_path, filename)
65
66
67
68
            page = utils.build_page_from_filepath(
                page_filepath, configuration, basename=pages_path
            )
            pages.append(page)
69

70
71
72
    # Check that articles directory exists and load Articles from it
    articles_path = os.path.join(os.curdir, "articles")
    if os.path.isdir(articles_path):
73
74
75
        # We iterate on the directories of articles dir. If they contain
        # "serie.html" file, it means they contain a serie. Otherwise, we list
        # articles as usual.
76
        dirs_with_articles = list(utils.list_dirs(articles_path)) + ["."]
77
78
79
        for dirname in dirs_with_articles:
            dirpath = os.path.realpath(os.path.join(articles_path, dirname))

80
81
            serie_filepath = os.path.join(dirpath, "serie.html")
            serie = None
82
            if dirname != "." and os.path.exists(serie_filepath):
83
84
85
                # We have a serie Page! Each article of the directory must be
                # considered part of the serie.
                slug = f"serie/{dirname}"
86
87
88
89
90
                serie = utils.build_page_from_filepath(
                    serie_filepath, configuration, slug=slug
                )
                pages.append(serie)
                articles_by_series[serie] = []
berumuron's avatar
berumuron committed
91

92
            for filename in utils.list_files(dirpath, only=["md"]):
93
                article_filepath = os.path.join(dirpath, filename)
94
                article = utils.build_article_from_filepath(
95
96
97
98
99
                    article_filepath, configuration, serie
                )
                articles.append(article)
                if serie:
                    articles_by_series[serie].append(article)
100

101
    # STEP 2: we write all the pages and articles
102
103
104
105
    for page in pages:
        output_filepath = os.path.join(output_path, f"{page.slug()}.html")

        # Create dir structure for the file
106
        utils.mkdirs_for_file(output_filepath)
107
108
109
110

        # And write the file
        with open(output_filepath, "w") as output_file:
            output_file.write(page.render())
111
        print(f"Written page: {page.url()}")
112

113
114
    # Sort the articles by publication date
    articles.sort(key=lambda article: article.date(), reverse=True)
berumuron's avatar
berumuron committed
115
116
    for serie_articles in articles_by_series.values():
        serie_articles.sort(key=lambda article: article.date(), reverse=True)
117

118
119
    # And write them in the ./site folder
    for article in articles:
120
        output_filepath = os.path.join(output_path, f"{article.slug()}.html")
121
        with open(output_filepath, "w") as output_file:
122
            output_file.write(article.render())
123
        print(f"Written article: {article.url()}")
124

125
126
127
128
129
130
131
132
133
134
    # Write the page with the list of articles if blog template exists
    blog_template_filepath = os.path.join("templates", "blog.html")
    if os.path.exists(blog_template_filepath):
        blog_template = boopsy.Template(blog_template_filepath)
        context = {}
        context.update(configuration)
        context["ARTICLES"] = articles
        blog_output_filepath = os.path.join(output_path, f"blog.html")
        with open(blog_output_filepath, "w") as output_file:
            output_file.write(blog_template.render(context))
135
        print(f"Written page: {configuration['SITE_URL']}blog.html")
136

berumuron's avatar
berumuron committed
137
    # STEP 3: we write also the Atom feeds (the main one + the series').
138
139
140
141
142
143
144
145
    # Just create some variables so it's easier to manipulate after
    site_url = configuration["SITE_URL"]
    site_uuid = configuration["SITE_UUID"]
    site_title = configuration["SITE_TITLE"]
    site_timezone = configuration["SITE_TIMEZONE"]
    timezone = pytz.timezone(site_timezone)
    current_datetime = datetime.datetime.now(tz=timezone)

berumuron's avatar
berumuron committed
146
147
148
149
    feed_dirpath = os.path.join(output_path, "feeds")
    os.mkdir(feed_dirpath)

    # Build the main Atom feed
150
151
152
153
154
155
156
157
158
    feed = boopfeed.Atom()
    feed.add_header(
        {
            "title": site_title,
            "self_link": f"{site_url}feeds/all.atom.xml",
            "site_link": site_url,
            "id": f"urn:uuid:{site_uuid}",
            "updated": current_datetime,
        }
159
    )
160
    utils.add_articles_to_feed(feed, articles)
161
162
163

    # And write it on the filesystem
    feed_filepath = os.path.join(feed_dirpath, "all.atom.xml")
164
    feed.write(feed_filepath)
165
166
    print(f"Written feed: {site_url}feeds/all.atom.xml")

berumuron's avatar
berumuron committed
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
    # Now, build the feeds for series (same process)
    for serie, serie_articles in articles_by_series.items():
        feed_filename = f"{os.path.basename(serie.slug())}.atom.xml"
        feed_filepath = os.path.join(feed_dirpath, feed_filename)

        feed = boopfeed.Atom()
        feed.add_header(
            {
                "title": f"{serie.title()} - {site_title}",
                "self_link": f"{site_url}feeds/{feed_filename}",
                "site_link": serie.url(),
                "id": f"urn:uuid:{serie.uuid()}",
                "updated": current_datetime,
            }
        )
182
        utils.add_articles_to_feed(feed, serie_articles)
berumuron's avatar
berumuron committed
183
184
185
186
187

        # And write it on the filesystem
        feed.write(feed_filepath)
        print(f"Written feed: {site_url}feeds/{feed_filename}")

188
189
190
191
192
    # STEP 4: and we finish by copying the static folder.
    static_dirpath = os.path.join(os.curdir, "static")
    if os.path.isdir(static_dirpath):
        distutils.dir_util.copy_tree(static_dirpath, output_path)
        print(f"Static files copied")
193

194
195

if __name__ == "__main__":
196
    environment = environment.load_from_argv(sys.argv)
197
    try:
198
        main(environment)
199
200
201
        print("Boop!")
    except ProgramError as e:
        print(e.args[0], file=sys.stderr)