2 Commits c149f704c2 ... d15f932d20

Author SHA1 Message Date
  Danilo Gómez Gómez d15f932d20 Mayor updates 5 years ago
  Danilo Gómez Gómez 4829b45573 Add plugin to load images 5 years ago

+ 2 - 1
.gitignore

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

+ 2 - 11
layout/_base.html

@@ -33,7 +33,7 @@
 				</div>
 				</div>
 				<object id="logo" type="image/svg+xml" data="{{ 'svg/alucho-logo.svg' | static }}"></object>
 				<object id="logo" type="image/svg+xml" data="{{ 'svg/alucho-logo.svg' | static }}"></object>
 				<div class="vbox">
 				<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 id="designer-subtitle" class="hbox"><a>{{ 'graphic designer' | lang }}</a></div>
 				</div>
 				</div>
 			</div>
 			</div>
@@ -43,8 +43,7 @@
 							<h1 class="header-title">
 							<h1 class="header-title">
 								<div><a href="{{ '/work' | cur_lang }}" {% if work_selected %}class="selected"{% endif %}>{{ 'work' | lang }}</a></div> /
 								<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="{{ '/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>
 							</h1>
 						</div>
 						</div>
 						<div class="hbox">
 						<div class="hbox">
@@ -74,14 +73,6 @@
 			Tw
 			Tw
 			In
 			In
 		</div>
 		</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>
 	</footer>
 
 
 	<form name="setLangEnglish" action="/i18n/setlang/" method="POST">
 	<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 work_selected = True %}
 {% set all_selected = False if no_all_selected else True %}
 {% set all_selected = False if no_all_selected else True %}
 
 
 {% block main %}
 {% 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>
 		</figcaption>
 	</figure>
 	</figure>
 {% endfor %}
 {% endfor %}

+ 20 - 1
layout/work/branding.html

@@ -1,3 +1,22 @@
 {% extends "_work_no_all.html"%}
 {% 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"%}
 {% extends "_work_no_all.html"%}
 
 
 {% set editorial_selected = True %}
 {% 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"%}
 {% extends "_work_no_all.html"%}
 
 
 {% set illustration_selected = True %}
 {% 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"%}
 {% extends "_work_no_all.html"%}
 
 
 {% set other_selected = True %}
 {% 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"%}
 {% extends "_work_no_all.html"%}
 
 
 {% set poster_selected = True %}
 {% 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
 import argparse
 from functools import lru_cache
 from functools import lru_cache
+from importlib import import_module
 from jinja2 import Environment, FileSystemLoader, contextfilter
 from jinja2 import Environment, FileSystemLoader, contextfilter
 from pathlib import Path
 from pathlib import Path
 from os import walk, makedirs, listdir, symlink, readlink
 from os import walk, makedirs, listdir, symlink, readlink
 from os.path import join, exists, splitext, split, islink, isdir
 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 time import sleep
 from traceback import print_exc
 from traceback import print_exc
+import os
 
 
 #TODO: load from config file (and watch it too)
 #TODO: load from config file (and watch it too)
 LANGUAGES = 'languages'
 LANGUAGES = 'languages'
+PLUGINS = 'plugins'
 ROOT = 'root'
 ROOT = 'root'
 STATIC = 'static'
 STATIC = 'static'
 DEFAULT_LANG = 'en'
 DEFAULT_LANG = 'en'
 OTHER_LANGS = set(['es'])
 OTHER_LANGS = set(['es'])
 WATCH_INTERVAL = 1 # in secs
 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
 # 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
     # `exist_ok=True`, very important for hot reloading
     errors = []
     errors = []
     for name in names:
     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:
         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:
             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
         # catch the Error from the recursive copytree so that we can
         # continue with other files
         # continue with other files
         except Error as err:
         except Error as err:
             errors.extend(err.args[0])
             errors.extend(err.args[0])
+        except OSError as why:
+            errors.append((srcname, dstname, str(why)))
     try:
     try:
         copystat(src, dst)
         copystat(src, dst)
     except OSError as why:
     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:
     if errors:
         raise Error(errors)
         raise Error(errors)
+    return dst
 
 
 def mtimes(target_dir):
 def mtimes(target_dir):
     ''''get modification time of files in `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['cur_lang'] = cur_lang
     env.filters['static'] = static
     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
     # Clean target
     if exists(args.target):
     if exists(args.target):
         rmtree(args.target)
         rmtree(args.target)
@@ -191,10 +271,10 @@ def gen_layout(args):
         compile(env, path, args.target)
         compile(env, path, args.target)
 
 
 def gen_root(args):
 def gen_root(args):
-    copytree(ROOT, args.target)
+    copytree(ROOT, args.target, ignore=ignore_underscores)
 
 
 def gen_static(args):
 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):
 def save_generate(generator, args, msg):
     print(f'* {msg}')
     print(f'* {msg}')

+ 36 - 33
static/css/main.css

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