A => .hgignore +1 -0
@@ 0,0 1,1 @@
+__pycache__
A => cogito2/__init__.py +0 -0
A => cogito2/app.py +28 -0
@@ 0,0 1,28 @@
+from .database.filerepository import get_file, get_page_files, get_file_count
+from .database.article_factory import build_from_posix_file, build_from_posix_files
+from .database.markdown import get_html
+from math import ceil
+
+from flask import Flask, render_template, abort, request
+app = Flask(__name__)
+
+@app.route("/")
+@app.route("/archive")
+def article_archive():
+ page = request.args.get('page', 0, type=int)
+ item_per_page=2
+ page_count=ceil(get_file_count() / item_per_page)
+
+ files = get_page_files(page=page, items_per_page=item_per_page)
+ articles = build_from_posix_files(files)
+ return render_template('archive.html', articles=articles, page=page, page_count=page_count)
+
+@app.route("/archive/<path:id>")
+def article_show(id):
+ file = get_file(id)
+ try:
+ article = build_from_posix_file(file)
+ except FileNotFoundError:
+ abort(404)
+
+ return render_template('show.html', article=article)
A => cogito2/database/article.py +11 -0
@@ 0,0 1,11 @@
+class Article:
+ def __init__(self, id, content, created_at):
+ self.id = id
+ self.content = content
+ self.created_at = created_at
+ def get_title(self):
+ return "title"
+ def get_teasing(self):
+ return "teasing"
+ def get_body(self):
+ return self.content
A => cogito2/database/article_factory.py +17 -0
@@ 0,0 1,17 @@
+from .article import Article
+from pathlib import PosixPath
+from datetime import datetime
+
+def build_from_posix_file(posix_file: PosixPath):
+ return Article(
+ id=str(posix_file),
+ content=posix_file.read_text(),
+ created_at=datetime.fromtimestamp(posix_file.stat().st_ctime)
+ )
+
+def build_from_posix_files(posix_files: list):
+ articles = []
+ for posix_file in posix_files:
+ articles.append(build_from_posix_file(posix_file))
+
+ return articles
A => cogito2/database/filerepository.py +22 -0
@@ 0,0 1,22 @@
+from pathlib import Path, PosixPath
+import os
+
+file_root_path=os.getenv('DATA_ROOT_DIR', 'data')
+
+def get_file_count():
+ p = Path(file_root_path)
+ return len(list(p.glob('**/*.md')))
+
+def get_file(file_path):
+ return PosixPath(file_path)
+
+def get_page_files(page=0, items_per_page=10):
+ p = Path(file_root_path)
+ items = list(p.glob('**/*.md'))
+ sorted_items = sorted(items, key=lambda item: item.stat().st_mtime, reverse=True)
+
+ print(page);
+ print(items_per_page);
+
+ return sorted_items[page * items_per_page:page * items_per_page + items_per_page]
+
A => cogito2/database/markdown.py +4 -0
@@ 0,0 1,4 @@
+from markdown import markdown
+
+def get_html(content):
+ return markdown(content)
A => cogito2/static/css/blog.css +55 -0
@@ 0,0 1,55 @@
+body {
+ font-family: sans-serif;
+}
+
+.icon {
+ display: block;
+ width: 5rem;
+ height: 5rem;
+ object-fit: cover;
+}
+
+.side-bar {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.date {
+ color: #999;
+}
+
+h1 {
+ font-size: 16pt;
+}
+
+h2 {
+ font-size: 12pt;
+}
+
+p {
+ font-size: 12pt;
+}
+
+h2 > span {
+ margin-right: 1rem;
+}
+
+.pagination {
+ margin-top: 1rem;
+ display: flex;
+ justify-content: space-between;
+}
+
+.container {
+ max-width: 960px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+@media only screen and (min-width: 960px) {
+ .content {
+ display: grid;
+ grid-template-columns: 3fr 1fr;
+ }
+}
A => cogito2/static/img/icon.jpg +0 -0
A => cogito2/templates/archive.html +36 -0
@@ 0,0 1,36 @@
+{% extends "base.html" %}
+
+{% block body %}
+ <div class="posts">
+ {% for article in articles %}
+ <div class="post">
+ <h2>
+ <span class="date">{{ article.created_at.strftime('%Y-%m-%d') }}</span>
+ <span>
+ <a href="{{ url_for('article_show', id=article.id) }}">{{ article.get_title() }}</a>
+ </span>
+ </h2>
+ {{ article.get_teasing()|safe }}
+ </div>
+ {% endfor %}
+
+ <div class="pagination">
+ {% if 0 < page %}
+ <a href="{{ url_for('article_archive', page=page-1) }}">Page {{ page-1 }}</a>
+ {% else %}
+ <span></span>
+ {% endif %}
+ {% if page + 1 < page_count %}
+ <a href="{{ url_for('article_archive', page=page+1) }}">Page {{ page+1 }}</a>
+ {% else %}
+ <span></span>
+ {% endif %}
+ </div>
+ </div>
+{% endblock %}
+
+{% block sidebar %}
+ <img src="{{ url_for('static', filename='img/icon.jpg') }}" class="icon"/>
+{% endblock %}
+
+
A => cogito2/templates/base.html +25 -0
@@ 0,0 1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>{% block title %}Welcome!{% endblock %}</title>
+ <link href="{{ url_for('static', filename='css/blog.css') }}" rel="stylesheet"/>
+ {% block stylesheets %}{% endblock %}
+ </head>
+ <body>
+ <div class="container">
+ <div class="page-title">
+ <h1>The Eddie Barraco's blog</h1>
+ </div>
+ <div class="content">
+ <article>
+ {% block body %}{% endblock %}
+ </article>
+ <div class="side-bar">
+ {% block sidebar %}{% endblock %}
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
+
A => cogito2/templates/show.html +17 -0
@@ 0,0 1,17 @@
+{% extends "base.html" %}
+
+{% block body %}
+ <h2><span>{{ article.get_title() }}</span></h2>
+ <p class="date">
+ Published {{ article.created_at.strftime('%Y-%m-%d') }} on <a href="{{ url_for('article_archive') }}">Eddie Barraco's blog</a>
+ </p>
+ <div>
+ {{ article.get_teasing()|safe }}
+ {{ article.get_body()|safe }}
+ </div>
+{% endblock %}
+
+{% block sidebar %}
+ <img src="{{ url_for('static', filename='img/icon.jpg') }}" class="icon"/>
+{% endblock %}
+