Skip to content

Commit

Permalink
Merge pull request #19 from davidomarf/cli
Browse files Browse the repository at this point in the history
Fix the CLI documentation and help messages.

Provide the CLI with better help messages, and document the related modules unsing numpydoc style.
  • Loading branch information
davidomarf committed Oct 23, 2019
2 parents 231f72f + b4b308e commit 31b5a40
Show file tree
Hide file tree
Showing 11 changed files with 344 additions and 67 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"python.pythonPath": "/home/d/.local/share/virtualenvs/ginpar-rqgfLzln/bin/python"
"python.pythonPath": "/home/d/dev/ginpar/venv/bin/python3.7",
"python.formatting.provider": "black"
}
201 changes: 161 additions & 40 deletions ginpar/build.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
"""
ginpar.build
~~~~~~~~~~~~
"""Build command for Ginpar projects.
This module implements the building command for the ginpar static content
generator.
`build` will read the configuration file in search for `build_path` and
`source_path`. If not defined, `build` will use `"public"` and
`"sketches`, respectively.
Examples
--------
To build your project according to your specifications in `config.yaml`::
ginpar build
Implements the generation of the static site.
To build targeting a custom path `_site/`::
ginpar build --path="_site"
Notes
-----
You cannot specify the content path. It is either ``config.content_path`` or
``"sketches"``.
"""

import os
import shutil

import yaml
import click

from jinja2 import Environment, FileSystemLoader

from ginpar.settings import read_config
import ginpar.generators as gg

from ginpar.utils.echo import echo, success
from ginpar.utils.strings import unkebab

Expand All @@ -28,22 +48,30 @@
_jinja_env.filters["unkebab"] = unkebab


def build_link(sketch):
title = sketch.split("/")[-1].split(".")[0]
return f'<a href="./{title}"">{title}</a><br/>\n'
def get_sketches(content_path):
"""Obtain the list of **valid** sketches inside `path`.
Valid sketches are directories containing at least two files:
`sketch.js` and `data.yaml`.
This function will create a list of sketch objects containing
`name`, `script`, and `data`.
def build_index(sketches):
content = ""
for s in sketches:
content += build_link(s)
return content
Parameters
----------
content_path : str
The path containing the sketches to fetch.
Returns
-------
list
Individual elements contain `{"name", "script", "data"}`.
"""

def get_sketches(path):
sketches = []

# Create a list with all the directories inside path
for r, d, _ in os.walk(path):
for r, d, _ in os.walk(content_path):
for sketch in d:
sketches.append(
{
Expand All @@ -65,6 +93,21 @@ def get_sketches(path):


def convert_information(sketch):
"""Convert the `["data"]` field of a sketch into a Python dictionary.
Parameters
----------
sketch : dict
It contains the sketch information. Must contain `["data"]`, and
`["data"]` must be a YAML-valid string.
Returns
-------
dictionary
`sketch` but with the updated `["data"]` field.
"""

path = sketch["data"]
with open(path, "r") as stream:
try:
Expand All @@ -75,81 +118,159 @@ def convert_information(sketch):
return sketch


def create_publishing_directory(path):
if os.path.exists(path):
shutil.rmtree(path)
os.mkdir(path)
def create_publishing_directory(build_path):
"""Remove existing directories with the same name, and create again.
Parameters
----------
build_path : str
Path of the build.
"""

if os.path.exists(build_path):
shutil.rmtree(build_path)
os.mkdir(build_path)


def copy_theme(path, theme_path):
def copy_theme(build_path, theme):
"""Copy the theme static content into the build static directory.
Parameters
----------
build_path : str
Path of the build.
theme : str
Name of the theme to install.
"""

## Copy the static/ folder of the theme
shutil.copytree(
os.path.join("themes", theme_path, "static"), os.path.join(path, "static")
os.path.join("themes", theme, "static"), os.path.join(build_path, "static")
)


def render_index(path, sketches, site):
## Create an index to contain all the sketches
def render_index(build_path, sketches, site):
"""Render the index using the list of sketches and site configuration
The index is rendered using a Jinja2 template inside the theme `templates/`
directory.
The index template must receive `sketches`, containing a list of the sketches
names; and `site`, containing the site configuration from ``config.yaml``.
Parameters
----------
build_path : str
Path of the build.
sketches : list
Contains all the sketches in the project. Must contain at leas `["name"]`.
site : dict
Contains the site information, such as `sitename` and `author`.
"""

# Obtain the template
_index_template = _jinja_env.get_template("index.html")
index = open(os.path.join(path, "index.html"), "w")

# Open the index in the build path for writing
index = open(os.path.join(build_path, "index.html"), "w")

# Write the contents of the rendered template into the index file
index.write(
_index_template.render(sketches=map(lambda a: a["name"], sketches), site=site)
)
index.close()


def render_sketch_page(path, s, site):
def render_sketch_page(build_path, sketch, site):
"""Render a sketch page
This generates the page for a single sketch. This will convert the
`sketch["data"]` into a form that will control the variables of the
script.
When `sketch["data"]` doesn't define fields that may be used at the moment
of the form generation, Ginpar will instead look up for those fields in
`site["sketch_defaults"]`.
When both `sketch["data"]` and `site["sketch_defaults"]` don't define those
fields, Ginpar will use the default values.
`Ginpar default values for sketch data <https://ginpar.readthedocs.io/en/latest/data.html>`_
Parameters
----------
build_path : str
Path of the build.
sketch : dict
Sketch information. Must contain `["data"]` and `["name"]`
site : dict
Site configuration.
"""
## Create a directory with the sketch title
os.mkdir(f"public/{s['name']}")
os.mkdir(os.path.join(build_path, sketch['name']))

## Convert the form JSON into a dict
form_dict = s["data"]
form_dict = sketch["data"]

## Add name key to the dict elements
form_dict = gg.add_name(form_dict)

## Create index.html
_sketch_template = _jinja_env.get_template("sketch.html")
sketch_index = open(f"public/{s['name']}/index.html", "w+")
sketch_index = open(f"public/{sketch['name']}/index.html", "w+")
sketch_index.write(
_sketch_template.render(
sketch=unkebab(s["name"]), form=gg.sketch_index(form_dict), site=_SITE
sketch=unkebab(sketch["name"]), form=gg.sketch_index(form_dict), site=_SITE
)
)
sketch_index.close()

## Create sketch.js
sketch_path = f"public/{s['name']}/sketch.js"
sketch = open(sketch_path, "w+")
sketch_path = f"public/{sketch['name']}/sketch.js"
sketch_script = open(sketch_path, "w+")

## Copy all the content from original sketches/{title}.js to sketch.js
sf = open(s["script"], "r")
sf = open(sketch["script"], "r")

sketch.write(gg.makeValueGetter(form_dict))
sketch_script.write(gg.makeValueGetter(form_dict))

for x in sf.readlines():
sketch.write(x)
sketch_script.write(x)
sf.close()
sketch.close()
sketch_script.close()


def build(path):
"""Main function of the module. This is what `ginpar build` calls.
Parameters
----------
build_path : str
Path of the build.
"""

create_publishing_directory(path)
echo(f"Building in `{os.path.abspath(path)}`")

copy_theme(path, _THEME)
echo(f"Building using theme `{_THEME}`")

## Create a sketches array
## Create the sketches list
sketches = list(get_sketches(_SKETCHES_PATH))
echo(f"Found {len(sketches)} sketch(es)")

render_index(path, sketches, _SITE)
echo("Building main page")

echo("Building sketches:")
for s in sketches:
echo(f" Building {s['name']}")
render_sketch_page(path, s, _SITE)
for sketch in sketches:
echo(f" Building {sketch['name']}")
render_sketch_page(path, sketch, _SITE)

success("Success.")
56 changes: 46 additions & 10 deletions ginpar/init.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,60 @@
"""
ginpar.init
~~~~~~~~~~~
"""Init command for Ginpar projects.
This module implements the initialization command for the ginpar static content
generator.
`init` will prompt for a series of values to write the site configuration file.
Examples
--------
To initialize a project in a standard way to specify the configuration values::
ginpar init
To skip the prompts and initialize the project with the default values:
ginpar init --quick
ginpar init --q
To force the initialization in case there is a directory with the same name
of the project to initialize:
Implements the initialization of a new project.
ginpar force --force
ginpar force -f
"""

import os

import click
from jinja2 import Environment, FileSystemLoader

from ginpar.utils.echo import info, echo, success, error, alert
from ginpar.utils.files import create_file, create_folder, try_remove
from ginpar.utils.strings import space_to_kebab

from jinja2 import Environment, FileSystemLoader

_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates")

_jinja_env = Environment(loader=FileSystemLoader(_TEMPLATES_DIR), trim_blocks=True)


def prompt_site_config():
"""Echo the prompts and create the configuration dict.
Echo the instructions and configuration fields, store each input,
and create a dictionary containing those values.
Returns
-------
dict
Used to generate the site configuration file.
"""
info("Welcome to ginpar! We'll ask for some values to initialize your project.")
click.pause()
echo("")
sitename = click.prompt("Site name", default="My Site")
description = click.prompt("Description", default="Cool site")
author = click.prompt("Author", default="John Doe")
url = click.prompt("url", default="johndoe.com")
info("\nIf you're unsure about the next prompts, accept the defaults")
click.pause()
echo("")
theme = click.prompt("Theme", default="gart")
content_path = click.prompt("Sketches path", default="sketches")
Expand All @@ -44,7 +71,16 @@ def prompt_site_config():


def init(force, quick):
""""""
"""Main function of the module. This is what `ginpar init` calls.
Parameters
----------
force : bool
Remove conflicting files when true.
quick : bool
Skip prompts when true.
"""
_config_template = _jinja_env.get_template("config.json.jinja2")

if force:
Expand Down

0 comments on commit 31b5a40

Please sign in to comment.