1
0

2 Revīzijas c149f704c2 ... d15f932d20

Autors SHA1 Ziņojums Datums
  Danilo Gómez Gómez d15f932d20 Mayor updates 5 gadi atpakaļ
  Danilo Gómez Gómez 4829b45573 Add plugin to load images 5 gadi atpakaļ

+ 2 - 1
.gitignore

@@ -1,2 +1,3 @@
 build/
-.vscode/
+.vscode/
+__pycache__/

+ 2 - 11
layout/_base.html

@@ -33,7 +33,7 @@
 				</div>
 				<object id="logo" type="image/svg+xml" data="{{ 'svg/alucho-logo.svg' | static }}"></object>
 				<div class="vbox">
-					<div id="alucho-title" class="hbox"><h1 class="header-title"><a>Alucho Rodríguez</a></h1></div>
+					<div id="alucho-title" class="hbox"><h1 class="header-title"><a>Alejandro Rodríguez</a></h1></div>
 					<div id="designer-subtitle" class="hbox"><a>{{ 'graphic designer' | lang }}</a></div>
 				</div>
 			</div>
@@ -43,8 +43,7 @@
 							<h1 class="header-title">
 								<div><a href="{{ '/work' | cur_lang }}" {% if work_selected %}class="selected"{% endif %}>{{ 'work' | lang }}</a></div> /
 								<div><a href="{{ '/news' | cur_lang }}" {% if news_selected %}class="selected"{% endif %}>{{ 'news' | lang }}</a></div> /
-								<div><a href="{{ '/about' | cur_lang }}" {% if about_selected %}class="selected"{% endif %}>{{ 'about' | lang }}</a></div> /
-								<div><a href="{{ '/contact' | cur_lang }}" class="last{% if contact_selected %} selected{% endif %}">{{ 'contact' | lang }}</a></div>
+								<div><a href="{{ '/about-and-contact' | cur_lang }}" class="last{% if contact_selected %} selected{% endif %}">{{ 'about & contact' | lang }}</a></div>
 							</h1>
 						</div>
 						<div class="hbox">
@@ -74,14 +73,6 @@
 			Tw
 			In
 		</div>
-		<div class="hbox">
-			<h2 class="header-title">
-				<div><a href="{{ '/work' | cur_lang }}">{{ 'work' | lang }}</a></div> /
-				<div><a href="{{ '/news' | cur_lang }}">{{ 'news' | lang }}</a></div> /
-				<div><a href="{{ '/about' | cur_lang }}">{{ 'about' | lang }}</a></div> /
-				<div><a href="{{ '/contact' | cur_lang }}" class="last">{{ 'contact' | lang }}</a></div>
-			</h2>
-		</div>
 	</footer>
 
 	<form name="setLangEnglish" action="/i18n/setlang/" method="POST">

+ 10 - 7
layout/work.html

@@ -1,15 +1,18 @@
-{% extends "_base.html"%}
+{% extends "_base.html" %}
 
 {% set work_selected = True %}
 {% set all_selected = False if no_all_selected else True %}
 
 {% block main %}
-{% for _ in range(15) %}
-	<figure class="v">
-		<img src="{{ 'image/tile.jpg' | static }}" alt="Ejemplo">
-		<figcaption>
-			<h2>Club De Jazz - 2019</h2>
-			poster
+{% for image in images('work/all') %}
+	<figure>
+		<img src="{{ image.filenames[0] | static }}" alt="Ejemplo">
+		<!-- <svg width="1px" height="1px" style="background-color: gray"></svg> -->
+		<figcaption class="hbox">
+			<div class="caption">
+				<h1>{{ image.name }} / {{ image.year }}</h1>
+				{{ image.category }}
+			</div>
 		</figcaption>
 	</figure>
 {% endfor %}

+ 20 - 1
layout/work/branding.html

@@ -1,3 +1,22 @@
 {% extends "_work_no_all.html"%}
 
-{% set branding_selected = True %}
+{% set branding_selected = True %}
+
+{% block main %}
+<style>
+	:root {
+		--columns: 4;
+	}
+</style>
+{% for _ in range(12) %}
+	<figure>
+		<svg width="1px" height="1px" style="background-color: gray"></svg>
+		<figcaption class="hbox">
+			<div class="caption">
+				<h1>Work Name Here / Year</h1>
+				branding
+			</div>
+		</figcaption>
+	</figure>
+{% endfor %}
+{% endblock %}

+ 14 - 0
layout/work/editorial.html

@@ -1,3 +1,17 @@
 {% extends "_work_no_all.html"%}
 
 {% set editorial_selected = True %}
+
+{% block main %}
+{% for _ in range(15) %}
+	<figure>
+		<svg width="1px" height="1.4px" style="background-color: gray"></svg>
+		<figcaption class="hbox">
+			<div class="caption">
+				<h1>Work Name Here / Year</h1>
+				editorial
+			</div>
+		</figcaption>
+	</figure>
+{% endfor %}
+{% endblock %}

+ 18 - 0
layout/work/illustration.html

@@ -1,3 +1,21 @@
 {% extends "_work_no_all.html"%}
 
 {% set illustration_selected = True %}
+{% block main %}
+<style>
+	:root {
+		--columns: 3;
+	}
+</style>
+{% for _ in range(15) %}
+	<figure>
+		<svg width="1.75px" height="1px" style="background-color: gray"></svg>
+		<figcaption class="hbox">
+			<div class="caption">
+				<h1>Work Name Here / Year</h1>
+				illustration
+			</div>
+		</figcaption>
+	</figure>
+{% endfor %}
+{% endblock %}

+ 14 - 0
layout/work/other.html

@@ -1,3 +1,17 @@
 {% extends "_work_no_all.html"%}
 
 {% set other_selected = True %}
+
+{% block main %}
+{% for _ in range(15) %}
+	<figure>
+		<svg width="1px" height="1px" style="background-color: gray"></svg>
+		<figcaption class="hbox">
+			<div class="caption">
+				<h1>Work Name Here / Year</h1>
+				other
+			</div>
+		</figcaption>
+	</figure>
+{% endfor %}
+{% endblock %}

+ 14 - 0
layout/work/poster.html

@@ -1,3 +1,17 @@
 {% extends "_work_no_all.html"%}
 
 {% set poster_selected = True %}
+
+{% block main %}
+{% for _ in range(15) %}
+	<figure>
+		<svg width="1px" height="1.4px" style="background-color: gray"></svg>
+		<figcaption class="hbox">
+			<div class="caption">
+				<h1>Work Name Here / Year</h1>
+				poster
+			</div>
+		</figcaption>
+	</figure>
+{% endfor %}
+{% endblock %}

+ 61 - 0
plugins/alucho_images.py

@@ -0,0 +1,61 @@
+from collections import namedtuple
+from json import loads as from_json
+from pathlib import Path
+
+'''
+This plugin helps loading images for each section.
+Images should be located at
+    <static-folder>/<image-path>/<section-name>/<image-folder>
+
+eg.
+    static/image/section1/folder-for-my-image/
+    static/image/section2/subsection/folder-for-another-image/
+    ...
+
+Each image folder should contain a file named `_metadata.json`
+with the following information
+    {
+        "name": "<image-name>",
+        "year": "<year>",
+        "category": "<category-name>",
+        "images": ["<main-image-name>", "<image-variant-1>", "<image-variant-2>", ...]
+    }
+
+Along with the image files which should be called
+    <main-image-name>
+    <image-variant-1>
+    <image-variant-2>
+    ...
+
+'''
+
+IMAGE_PATH = 'image' # relative to the static path
+STATIC = ''
+
+Image = namedtuple('Image', ['name', 'year', 'category', 'description', 'filenames'])
+
+# Functions
+
+def images(section):
+    sec_path = Path(STATIC, IMAGE_PATH, section)
+    for path in sec_path.iterdir():
+        if path.name.startswith('_'):
+            continue
+        metafile = path.joinpath('_metadata.json')
+        if metafile.exists():
+            img_base = path.relative_to(STATIC)
+            metadata = from_json(metafile.read_text())
+            img = Image(
+                metadata.get('name'),
+                metadata.get('year'),
+                metadata.get('category'),
+                metadata.get('description'),
+                [img_base.joinpath(img_name) for img_name in metadata.get('images', [])])
+            print(img)
+            yield img
+
+
+def init_plugin(env, config):
+    global STATIC
+    STATIC = config['STATIC']
+    env.globals['images'] = images

+ 100 - 20
sitegen.py

@@ -1,54 +1,127 @@
 import argparse
 from functools import lru_cache
+from importlib import import_module
 from jinja2 import Environment, FileSystemLoader, contextfilter
 from pathlib import Path
 from os import walk, makedirs, listdir, symlink, readlink
 from os.path import join, exists, splitext, split, islink, isdir
-from shutil import rmtree, copy2, copystat, Error
+from shutil import rmtree, copy2, copystat, ignore_patterns, Error
 from time import sleep
 from traceback import print_exc
+import os
 
 #TODO: load from config file (and watch it too)
 LANGUAGES = 'languages'
+PLUGINS = 'plugins'
 ROOT = 'root'
 STATIC = 'static'
 DEFAULT_LANG = 'en'
 OTHER_LANGS = set(['es'])
 WATCH_INTERVAL = 1 # in secs
 
+config = {
+    'LANGUAGES': LANGUAGES,
+    'PLUGINS': PLUGINS,
+    'ROOT': ROOT,
+    'STATIC': STATIC,
+    'DEFAULT_LANG': DEFAULT_LANG,
+    'OTHER_LANGS': OTHER_LANGS,
+    'WATCH_INTERVAL': WATCH_INTERVAL
+}
+
 # Utils
 
-def copytree(src, dst, symlinks=False):
-    names = listdir(src)
-    makedirs(dst, exist_ok=True)
+ignore_underscores = ignore_patterns('_*')
+
+def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,
+             ignore_dangling_symlinks=False):
+    """Recursively copy a directory tree.
+
+    The destination directory must not already exist.
+    If exception(s) occur, an Error is raised with a list of reasons.
+
+    If the optional symlinks flag is true, symbolic links in the
+    source tree result in symbolic links in the destination tree; if
+    it is false, the contents of the files pointed to by symbolic
+    links are copied. If the file pointed by the symlink doesn't
+    exist, an exception will be added in the list of errors raised in
+    an Error exception at the end of the copy process.
+
+    You can set the optional ignore_dangling_symlinks flag to true if you
+    want to silence this exception. Notice that this has no effect on
+    platforms that don't support os.symlink.
+
+    The optional ignore argument is a callable. If given, it
+    is called with the `src` parameter, which is the directory
+    being visited by copytree(), and `names` which is the list of
+    `src` contents, as returned by os.listdir():
+
+        callable(src, names) -> ignored_names
+
+    Since copytree() is called recursively, the callable will be
+    called once for each directory that is copied. It returns a
+    list of names relative to the `src` directory that should
+    not be copied.
+
+    The optional copy_function argument is a callable that will be used
+    to copy each file. It will be called with the source path and the
+    destination path as arguments. By default, copy2() is used, but any
+    function that supports the same signature (like copy()) can be used.
+
+    """
+    names = os.listdir(src)
+    if ignore is not None:
+        ignored_names = ignore(src, names)
+    else:
+        ignored_names = set()
+
+    os.makedirs(dst, exist_ok=True)
     # `exist_ok=True`, very important for hot reloading
     errors = []
     for name in names:
-        srcname = join(src, name)
-        dstname = join(dst, name)
+        if name in ignored_names:
+            continue
+        srcname = os.path.join(src, name)
+        dstname = os.path.join(dst, name)
         try:
-            if symlinks and islink(srcname):
-                linkto = readlink(srcname)
-                symlink(linkto, dstname)
-            elif isdir(srcname):
-                copytree(srcname, dstname, symlinks)
+            if os.path.islink(srcname):
+                linkto = os.readlink(srcname)
+                if symlinks:
+                    # We can't just leave it to `copy_function` because legacy
+                    # code with a custom `copy_function` may rely on copytree
+                    # doing the right thing.
+                    os.symlink(linkto, dstname)
+                    copystat(srcname, dstname, follow_symlinks=not symlinks)
+                else:
+                    # ignore dangling symlink if the flag is on
+                    if not os.path.exists(linkto) and ignore_dangling_symlinks:
+                        continue
+                    # otherwise let the copy occurs. copy2 will raise an error
+                    if os.path.isdir(srcname):
+                        copytree(srcname, dstname, symlinks, ignore,
+                                 copy_function)
+                    else:
+                        copy_function(srcname, dstname)
+            elif os.path.isdir(srcname):
+                copytree(srcname, dstname, symlinks, ignore, copy_function)
             else:
-                copy2(srcname, dstname)
-            # XXX What about devices, sockets etc.?
-        except OSError as why:
-            errors.append((srcname, dstname, str(why)))
+                # Will raise a SpecialFileError for unsupported file types
+                copy_function(srcname, dstname)
         # catch the Error from the recursive copytree so that we can
         # continue with other files
         except Error as err:
             errors.extend(err.args[0])
+        except OSError as why:
+            errors.append((srcname, dstname, str(why)))
     try:
         copystat(src, dst)
     except OSError as why:
-        # can't copy file access times on Windows
-        if why.winerror is None:
-            errors.extend((src, dst, str(why)))
+        # Copying file access times may fail on Windows
+        if getattr(why, 'winerror', None) is None:
+            errors.append((src, dst, str(why)))
     if errors:
         raise Error(errors)
+    return dst
 
 def mtimes(target_dir):
     ''''get modification time of files in `target_dir`'''
@@ -172,6 +245,13 @@ def init_gen(args):
     env.filters['cur_lang'] = cur_lang
     env.filters['static'] = static
 
+    # Load plugins
+    for mod_path in Path(PLUGINS).glob('*.py'):
+        mod_name = '.'.join(mod_path.with_suffix('').parts)
+        print(f'* loading {mod_name}')
+        import_module(mod_name).init_plugin(env, config)
+        print('  done!')
+
     # Clean target
     if exists(args.target):
         rmtree(args.target)
@@ -191,10 +271,10 @@ def gen_layout(args):
         compile(env, path, args.target)
 
 def gen_root(args):
-    copytree(ROOT, args.target)
+    copytree(ROOT, args.target, ignore=ignore_underscores)
 
 def gen_static(args):
-    copytree(STATIC, join(args.target, 'static'))
+    copytree(STATIC, join(args.target, 'static'), ignore=ignore_underscores)
 
 def save_generate(generator, args, msg):
     print(f'* {msg}')

+ 36 - 33
static/css/main.css

@@ -1,10 +1,10 @@
 /* Variables */
 :root {
-	--preferred-columns: 5;
-	--columns: var(--preferred-columns);
 	--tile-margin: .9vw;
+	--caption-hmargin: calc(2 * var(--tile-margin));
+	--caption-vmargin: calc(1 * var(--tile-margin));
 	--body-margin: 3.6vw;
-	--theme-color: #ff2d00;
+	--theme-color: #46c8a5;
 	/* --tile-margin: 12.75px; */
 	/* --body-margin: 66px; */
 	--header-inner-space: calc(8 * var(--tile-margin));
@@ -12,25 +12,22 @@
 }
 /* Columns */
 @media (min-width: 1501px) {
-	:root { --columns: min(var(--preferred-columns), 6); }
+	:root { --columns: 5; }
 }
 @media (max-width: 1500px) {
-	:root { --columns: min(var(--preferred-columns), 5); }
-}
-@media (max-width: 1500px) {
-	:root { --columns: min(var(--preferred-columns), 5); }
+	:root { --columns: 5; }
 }
 @media (max-width: 1000px) {
-	:root { --columns: min(var(--preferred-columns), 4); }
+	:root { --columns: 4; }
 }
 @media (max-width: 700px) {
-	:root { --columns: min(var(--preferred-columns), 3); }
+	:root { --columns: 3; }
 }
 @media (max-width: 500px) {
-	:root { --columns: min(var(--preferred-columns), 2); }
+	:root { --columns: 2; }
 }
 @media (max-width: 400px) {
-	:root { --columns: min(var(--preferred-columns), 1); }
+	:root { --columns: 2; }
 }
 /* Font Size */
 :root {
@@ -65,28 +62,28 @@ h1 {
 }
 #logo {
 	width: 10vw;
-	margin: 0 .7vw -.5vw .5vw;
+	margin: 0 .7vw -.9vw .5vw;
 }
 /* @media (max-width: 460px) {
 	#logo { width: 130px; }
 } */
 /* Typographies */
 @font-face {
-	font-family: 'nexa-light';
-	src: url('/static/font/nexa-light-regular.otf') format('opentype');
+	font-family: 'akrobat-regular';
+	src: url('/static/font/Akrobat-Regular.otf') format('opentype');
 }
 @font-face {
-	font-family: 'geoslab';
-	src: url('/static/font/geoslab703-md-bt-bold.ttf') format('truetype');
+	font-family: 'akrobat-bold';
+	src: url('/static/font/Akrobat-Bold.otf') format('opentype');
 }
 /* Rules */
 :root {
-	font-family: 'nexa-light';
+	font-family: 'akrobat-regular';
 	letter-spacing: 1.5px;
 }
 .sec-div {
 	font-weight: bold;
-	font-family: 'nexa-light';
+	font-family: 'akrobat-regular';
 }
 a {
 	color: black;
@@ -97,17 +94,18 @@ a.en {
 	margin-bottom: .5em;
 }
 h1 {
-	margin: .5em 0;
+	font-size: 1.1em;
 	text-align: center;
 	letter-spacing: 1px;
 	font-weight: 100;
-	font-family: 'geoslab';
+	font-family: 'akrobat-bold';
 	text-decoration: none;
 	text-transform: uppercase;
+	margin: .2em 0;
 }
 h2 {
 	font-size: .9em;
-	font-family: 'geoslab';
+	font-family: 'akrobat-bold';
 	text-decoration: none;
 	text-transform: uppercase;
 }
@@ -249,31 +247,36 @@ figure {
 	margin: var(--tile-margin);
 	overflow: hidden;
 }
-figure img {
+figure img, svg {
 	display: flex;
 	width: 100%;
+	height: 100%;
 }
 figure figcaption {
+	font-size: 0;
+	padding: 0 var(--caption-hmargin);
 	display: flex;
 	flex-direction: column;
 	position: absolute;
-	padding: calc(2 * var(--tile-margin));
-	background-color: var(--theme-color);
+	background-color: white;
 	bottom: 0;
-	width: calc(100% - var(--tile-margin) * 4);
-	opacity: 0;
+	width: calc(100% - 2 * var(--caption-hmargin));
+	opacity: 1;
 	height: 0;
 }
 figure:hover figcaption {
-	opacity: 1;
+	font-size: .9em;
+	padding: var(--caption-vmargin) var(--caption-hmargin);
 	transition: .3s ease;
+	height: auto;
 }
-figure.v:hover figcaption {
-	height: 21%;
+figcaption h1 {
+	margin: 0;
 }
-figure.h:hover figcaption,
-figure.s:hover figcaption {
-	height: 30%;
+.caption {
+	display: flex;
+	flex-direction: column;
+	align-items: flex-start;
 }
 footer {
 	display: flex;

BIN
static/font/Akrobat-Black.otf


BIN
static/font/Akrobat-Bold.otf


BIN
static/font/Akrobat-ExtraBold.otf


BIN
static/font/Akrobat-ExtraLight.otf


BIN
static/font/Akrobat-Light.otf


BIN
static/font/Akrobat-Regular.otf


BIN
static/font/Akrobat-SemiBold.otf


BIN
static/font/Akrobat-Thin.otf


BIN
static/font/geoslab703-md-bt-bold.ttf


BIN
static/font/nexa-light-regular.otf


BIN
static/image/tile.jpg


BIN
static/image/white.jpg