From 64f29c255018f1bd4e040009adca1403fcd79c20 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 10 Apr 2023 00:22:23 -0400 Subject: [PATCH 01/23] Use map for parse version --- jamstack/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jamstack/__init__.py b/jamstack/__init__.py index 9c78a78..17954f6 100644 --- a/jamstack/__init__.py +++ b/jamstack/__init__.py @@ -1,2 +1,2 @@ version_info = (1, 0, 0) -__version__ = ".".join([str(v) for v in version_info]) +__version__ = ".".join(map(str, version_info)) From 432bba2922d071a41057a6da91361f9ca17244a8 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 10 Apr 2023 00:23:55 -0400 Subject: [PATCH 02/23] Improve commands helps + Improve t function namespace, project variables assignation --- jamstack/__main__.py | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/jamstack/__main__.py b/jamstack/__main__.py index bab23cf..0a257a7 100644 --- a/jamstack/__main__.py +++ b/jamstack/__main__.py @@ -2,51 +2,39 @@ from pathlib import Path import click -from jamstack.api.file import trycopytree + +from jamstack.api.file import trycopy package_folder = Path(__file__).parent.absolute() sites_path = os.path.join(package_folder, 'sites') -@click.group(help="") +@click.group(help="Jamstack command line interface") def cli(): pass -@click.command(help="") +@click.command(help="Create an empty, plain repository") @click.argument('project_name') @click.option('--existing/--not-existing', default=False) def plain(project_name, existing): path = '.' - - dirs_exist_ok = False - if existing is True: - dirs_exist_ok = True - trycopytree( + trycopy( os.path.join(sites_path, 'plain'), - os.path.join(path, project_name), - dirs_exist_ok=dirs_exist_ok + os.path.join(path, project_name) ) -@click.command(help="") +@click.command(help="Create a repository from a template") @click.argument('template') @click.argument('project_name') @click.option('--existing/--not-existing', default=False) def t(template, project_name, existing): path = '.' - - namespace = template.split('/')[0] - project = template.split('/')[1] - - dirs_exist_ok = False - if existing is True: - dirs_exist_ok = True - - trycopytree( + namespace, project = template.split('/') + trycopy( os.path.join(sites_path, namespace, project), - os.path.join(path, project_name), - dirs_exist_ok=dirs_exist_ok + os.path.join(path, project_name) ) From 8921487649cbb75c5eaa8c67c4f6130341628b1e Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 10 Apr 2023 00:25:25 -0400 Subject: [PATCH 03/23] Improve trycopytree function + Add docstrings --- jamstack/api/file.py | 92 ++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 33 deletions(-) diff --git a/jamstack/api/file.py b/jamstack/api/file.py index 19aee07..e84b019 100644 --- a/jamstack/api/file.py +++ b/jamstack/api/file.py @@ -1,54 +1,34 @@ import os import shutil +import logging import uuid -# from werkzeug.utils import secure_filename - - -def trycopytree(source, dest, verbose=False, dirs_exist_ok=False): - """ - Recursive copy of folder - - Parameters - ---------- - source: str - source folder path - dest: str - destination folder path - - Returns - ------- - None - """ - try: - shutil.copytree(source, dest, dirs_exist_ok=dirs_exist_ok) - if verbose: - print("done copying {} to {}".format(source, dest)) - except Exception as e: - print(e) +logging.basicConfig(filename='example.log', level=logging.DEBUG) def trycopy(source, dest, verbose=False): """ - Non-ecursive copy of folder + Copy a file or directory from source to dest. Parameters ---------- source: str - source folder path + source file or directory path dest: str - destination folder path + destination file or directory path + verbose: bool, optional + If True, print debug information. Default is False. Returns ------- None """ try: - shutil.copy(source, dest) + shutil.copy2(source, dest) if verbose: print("done copying {} to {}".format(source, dest)) except Exception as e: - print(e) + logging.exception(e) def trymkdir(path, verbose=False): @@ -59,6 +39,8 @@ def trymkdir(path, verbose=False): ---------- path: str path with folder already in + verbose: bool, optional + If True, print debug information. Default is False. Returns ------- @@ -69,7 +51,7 @@ def trymkdir(path, verbose=False): if verbose: print("created dir at", path) except Exception as e: - print(e) + logging.exception(e) def trymkfile(path, content, verbose=False): @@ -82,20 +64,22 @@ def trymkfile(path, content, verbose=False): path to create file with filename included content: str file content + verbose: bool, optional + If True, print debug information. Default is False. Returns ------- None """ try: - with open(path, "w+") as f: + with open(path, "x") as f: f.write(content) if verbose: print("file created at {}".format(path)) print("with content:") print(content) except Exception as e: - print(e) + logging.exception(e) def absdiroffile(filepath): @@ -117,18 +101,60 @@ def absdiroffile(filepath): def get_folders(path): + """ + Get a list of directories in the given path. + + Parameters + ---------- + path: str + Path to search for directories. + + Returns + ------- + list + List of directories in the given path. + """ dirs = [d for d in os.listdir( path) if os.path.isdir(os.path.join(path, d))] return dirs def unique_filename(fname): + """ + Generate a unique filename by prepending a UUID to the given filename. + + Parameters + ---------- + fname: str + Original filename. + + Returns + ------- + str + Unique filename with prepended UUID. + """ prepended = str(uuid.uuid4()).replace("-", "_")[:10] return "{}_{}".format(prepended, fname) def delete_file(path): - os.remove(path) + """ + Delete the file at the given path. + + Parameters + ---------- + path: str + Path of the file to delete. + + Returns + ------- + None + """ + try: + os.remove(path) + logging.info("File deleted: {}".format(path)) + except Exception as e: + logging.exception(e) # def unique_sec_filename(filename): From a8771369389bbfe32c120fe41b7d733611e45646 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 10 Apr 2023 00:25:51 -0400 Subject: [PATCH 04/23] Add docstrings, clean code --- jamstack/api/template.py | 47 +++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/jamstack/api/template.py b/jamstack/api/template.py index 16e8fb7..1d796a9 100644 --- a/jamstack/api/template.py +++ b/jamstack/api/template.py @@ -8,43 +8,56 @@ def base_context(): + """ + Get a dictionary of built-in functions and variables. + + Returns + ------- + dict + Dictionary of built-in functions and variables. + """ defaults = [_ for _ in dir(builtins) if _[0] in string.ascii_lowercase and _ not in ['copyright', 'credits'] ] - attrs = [getattr(builtins, _) - for _ in defaults if _ not in ['copyright', 'credits']] + attrs = [getattr(builtins, _) for _ in defaults] builtins_dict = dict(zip(defaults, attrs)) return copy.deepcopy(builtins_dict) -def generate( - file_in_templates, - out_path, - template_dir='templates', - assets_path_append='', - **kwargs): +def generate(file_in_templates, out_path, template_dir='templates', assets_path_append='', **kwargs): """ - Generates necessary file(s) - :param file_in_templates: template to work with - :param out_path: output path to save the generated file to - :param template_dir: templates directory - :param assets_path_append: - :param kwargs: variables - :return: None + Generates necessary file(s) from a Jinja2 template. + + Parameters + ---------- + file_in_templates: str + Template file to work with. + out_path: str + Output path to save the generated file to. + template_dir: str, optional + Templates directory. Default is 'templates'. + assets_path_append: str, optional + Path to append to assets. Default is ''. + kwargs: dict + Variables to pass to the template. + + Returns + ------- + None """ - file_loader = FileSystemLoader(template_dir) env = Environment(loader=file_loader) template = env.get_template(file_in_templates) - build_id = str(uuid.uuid4()) # to be used + build_id = str(uuid.uuid4()) output = template.render( kwargs, year=datetime.datetime.now().year, build_id=build_id, assets_path_append=assets_path_append) + with open(out_path, 'w+', encoding="utf8") as f: f.write(output) From f8ab586c03739e07613d37442b380e28c5d0bd78 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 10 Apr 2023 00:26:19 -0400 Subject: [PATCH 05/23] Clean code, refactors --- jamstack/jamdo/jamdo.py | 32 ++++++---------------- jamstack/sites/html5up/phantom/settings.py | 4 +-- jamstack/sites/plain/settings.py | 2 +- setup.py | 12 ++++---- 4 files changed, 18 insertions(+), 32 deletions(-) diff --git a/jamstack/jamdo/jamdo.py b/jamstack/jamdo/jamdo.py index aedca51..66846a3 100644 --- a/jamstack/jamdo/jamdo.py +++ b/jamstack/jamdo/jamdo.py @@ -1,11 +1,11 @@ import json import os -import urllib.request +from urllib.request import urlretrieve import requests from tqdm import tqdm -templates_url = ("https://api.github.com/repos/jamstackpy/" +TEMPLATES_URL = ("https://api.github.com/repos/jamstackpy/" "jamstack-templates/git/trees/main?recursive=1") @@ -19,8 +19,7 @@ def get_raw_url(file_path, url): def get_download_links(template): - api = requests.get(templates_url).text - print(templates_url) + api = requests.get(TEMPLATES_URL).text files = json.loads(api) output = [] location = dict() @@ -32,10 +31,7 @@ def get_download_links(template): output.append(tmp) else: location[i['path']] = k - files = output - location = location - - return (files, location) + return output, location def mkdirs(path): @@ -44,29 +40,19 @@ def mkdirs(path): def download(template, target_folder='*', recursive=True): - data = get_download_links(template) - files = data[0] - location = data[1] - - # mkdirs(".") + files, location = get_download_links(template) if target_folder == '*': start = 0 else: - tmp_target = target_folder.replace('./', '') - tmp_target = tmp_target.replace('../', '') - - # Remove "/" - tmp_target = (tmp_target if tmp_target[-1] != '/' - else tmp_target[:-1]) + # tmp_target = target_folder.replace('./', '').replace('../', '') + # tmp_target = tmp_target.rstrip('/') start = location[target_folder] - # Start download with tqdm(total=len(files), desc="Downloading assets...") as pbar: for i in files[start:]: ndir = i[0].replace('templates/' + template, 'dist/assets/') - if recursive or ndir.split(target_folder)[1].count('/') \ - <= 1: + if recursive or ndir.split(target_folder)[1].count('/') <= 1: mkdirs('.' + '/' + os.path.dirname(ndir)) - urllib.request.urlretrieve(i[1], '.' + '/' + ndir) + urlretrieve(i[1], '.' + '/' + ndir) pbar.update(1) diff --git a/jamstack/sites/html5up/phantom/settings.py b/jamstack/sites/html5up/phantom/settings.py index 6271e89..59533be 100644 --- a/jamstack/sites/html5up/phantom/settings.py +++ b/jamstack/sites/html5up/phantom/settings.py @@ -4,8 +4,8 @@ info = None posts = None -with open('info.json') as f: +with open('info.json', 'r') as f: info = json.load(f) -with open('posts.json') as f: +with open('posts.json', 'r') as f: posts = json.load(f) diff --git a/jamstack/sites/plain/settings.py b/jamstack/sites/plain/settings.py index 8973fdd..d4abb28 100644 --- a/jamstack/sites/plain/settings.py +++ b/jamstack/sites/plain/settings.py @@ -3,5 +3,5 @@ OUTPUT_FOLDER = 'dist/' info = None -with open('info.json') as f: +with open('info.json', 'r') as f: info = json.load(f) diff --git a/setup.py b/setup.py index 95decc3..f050ae3 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,9 @@ import os import sys -from jamstack import __version__ from setuptools import setup +from jamstack import __version__ here = os.path.abspath(os.path.dirname(__file__)) @@ -14,6 +14,10 @@ with open(os.path.join(here, "README.md"), encoding="utf-8") as f: long_description = f.read() + +with open(os.path.join(here, "requirements.txt"), encoding="utf-8") as f: + install_requires = f.read().split("\n") + setup( name="jamstack", version=__version__, @@ -36,11 +40,7 @@ packages=["jamstack"], include_package_data=True, python_requires=">=3.6", - install_requires=open( - os.path.join(here, "requirements.txt"), encoding="utf-8" - ) - .read() - .split("\n"), + install_requires=install_requires, project_urls={ "Bug Reports": "https://github.com/jamstackpy/jamstack/issues", "Source": "https://github.com/jamstackpy/jamstack", From b916a0763dd8e8981a02e4cd3231aeea150cbc89 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 10 Apr 2023 19:02:43 -0400 Subject: [PATCH 06/23] Remove usage of dash-separated --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 224a779..08aedd7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,2 @@ [metadata] -description-file = README.md \ No newline at end of file +description_file = README.md From 5f5a7a7defef396924d46c1d868a1095d8663a13 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 10 Apr 2023 19:03:24 -0400 Subject: [PATCH 07/23] Back to shutil.copytree --- jamstack/api/file.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/jamstack/api/file.py b/jamstack/api/file.py index e84b019..cce8be1 100644 --- a/jamstack/api/file.py +++ b/jamstack/api/file.py @@ -3,10 +3,8 @@ import logging import uuid -logging.basicConfig(filename='example.log', level=logging.DEBUG) - -def trycopy(source, dest, verbose=False): +def trycopytree(source, dest, dirs_exist_ok): """ Copy a file or directory from source to dest. @@ -16,17 +14,17 @@ def trycopy(source, dest, verbose=False): source file or directory path dest: str destination file or directory path - verbose: bool, optional - If True, print debug information. Default is False. - + dirs_exist_ok: + especifies if the project folder already exist Returns ------- None """ try: - shutil.copy2(source, dest) - if verbose: - print("done copying {} to {}".format(source, dest)) + shutil.copytree(source, dest, dirs_exist_ok=dirs_exist_ok) + print('Project created successfully! :)') + except FileExistsError: + print('Project folder already exist! Use --existing if you want to override it.') except Exception as e: logging.exception(e) From 225690f7984677863f8f3ad600b5af375877ede2 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 10 Apr 2023 19:04:48 -0400 Subject: [PATCH 08/23] Back to trycopytree --- jamstack/__main__.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/jamstack/__main__.py b/jamstack/__main__.py index 0a257a7..1754516 100644 --- a/jamstack/__main__.py +++ b/jamstack/__main__.py @@ -3,7 +3,7 @@ import click -from jamstack.api.file import trycopy +from jamstack.api.file import trycopytree package_folder = Path(__file__).parent.absolute() sites_path = os.path.join(package_folder, 'sites') @@ -19,9 +19,11 @@ def cli(): @click.option('--existing/--not-existing', default=False) def plain(project_name, existing): path = '.' - trycopy( + + trycopytree( os.path.join(sites_path, 'plain'), - os.path.join(path, project_name) + os.path.join(path, project_name), + existing ) @@ -32,9 +34,11 @@ def plain(project_name, existing): def t(template, project_name, existing): path = '.' namespace, project = template.split('/') - trycopy( + + trycopytree( os.path.join(sites_path, namespace, project), - os.path.join(path, project_name) + os.path.join(path, project_name), + existing ) From 65c16f49aa61e193f5f94f094875be200b40bbb9 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 10 Apr 2023 19:05:17 -0400 Subject: [PATCH 09/23] Ignore .idea folder --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2757c2c..38a24a9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ __pycache__/ *.py[cod] *$py.class - +.idea/ # C extensions *.so From 885ac8789bafb5f31ce2721d95d00f337194e78d Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 17 Apr 2023 22:38:16 -0400 Subject: [PATCH 10/23] Downgrade `livereload` version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 61696fe..8c3bc3c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ click==7.1.2 Flask==1.1.2 itsdangerous==1.1.0 Jinja2==2.11.3 -livereload==2.6.3 +livereload==2.5.1 MarkupSafe==1.1.1 six==1.15.0 tornado==6.1 From 1d7e673ce6245919248ee2bcf6d4856c0c1fe163 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 17 Apr 2023 22:39:20 -0400 Subject: [PATCH 11/23] Some format tasks --- README.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2cd20d8..e442ea7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ ![](https://img.shields.io/pypi/v/jamstack) -Also known as Jamstackpy, is a tool that allows you to create static websites using the power of **Python** hand in hand with the [Flask](https://github.com/pallets/flask) library. Its operation is based on templates which are rendered with the powerful Jinja engine generating your website with all its dependencies. +Also known as Jamstackpy, is a tool that allows you to create static websites using the power of **Python** hand in hand +with the [Flask](https://github.com/pallets/flask) library. Its operation is based on templates which are rendered with +the powerful Jinja engine generating your website with all its dependencies. ## Installation @@ -15,16 +17,16 @@ python -m pip install jamstack ## Create basic project ```bash -jamstack plain # or jamstack plain . --existing +jamstack plain ``` ## Templates Jamstack has templates available courtesy of [html5up](https://html5up.net). -| Template | Command | Tutorial | -| ------------------------------------------ | ----------------- | ------------------------------------------------------------ | -| [Massively](https://html5up.net/massively) | html5up/massively | | +| Template | Command | Tutorial | +|--------------------------------------------|-------------------|--------------------------------------------------------------------------| +| [Massively](https://html5up.net/massively) | html5up/massively | | | [Phantom](https://html5up.net/phantom) | html5up/phantom | [**HERE**](https://github.com/jamstackpy/jamstack/wiki/Phantom-template) | The syntax is as follows: @@ -39,7 +41,9 @@ Use the `--existing` flag if you want the project to be created in an existing f jamstack t html5up/massively myproject --existing ``` -By default, projects are created without the assets (stylesheets, images, etc...) to download them, you must pass the `--jamdo` option to the `static.py` file of the respective project. +By default, projects based in templates are created without the assets (stylesheets, images, etc...) to download them, +you must pass +the `--jamdo` option to the `static.py` file of the respective project. ## Build @@ -51,10 +55,16 @@ python static.py Your site will be generated in the **dist/** folder. -Alternatively you can use the `--server` flag if you want to start livewatch. +## Other project command-line options + +| Argument | Description | +|-----------|---------------------------------------------------------------------------| +| `--serve` | Optional. Start project livewatch (autoreload when files change). | +| `--watch` | Optional. Specify files and folders to watch. Must be separated by comma. | +| `--port` | Optional. Specify server port. | ## Sites using jamstack - [DeliciousPy Telegram Channel](https://deliciouspy.github.io/) - [The Flask Community Work Group](https://flaskcwg.github.io/) -- [A Personal Site](https://compileralchemy.github.io/) +- [Abdur-Rahmaan Janhangeer](https://compileralchemy.github.io/) From aaaff50d592f19ea0e1861857bc85e98af725181 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 17 Apr 2023 22:39:45 -0400 Subject: [PATCH 12/23] Impove html structure --- jamstack/sites/plain/templates/sections/head.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/jamstack/sites/plain/templates/sections/head.html b/jamstack/sites/plain/templates/sections/head.html index b1b579c..060aca2 100644 --- a/jamstack/sites/plain/templates/sections/head.html +++ b/jamstack/sites/plain/templates/sections/head.html @@ -1,5 +1,6 @@ -web and software dev - +Jamstack Plain Site + + @@ -7,4 +8,4 @@ - \ No newline at end of file + From 003394538890556b07c2bf6f5974b56ecaef3efc Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 17 Apr 2023 22:40:29 -0400 Subject: [PATCH 13/23] Remove weird `zfill` usage --- jamstack/sites/plain/templates/sections/body.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jamstack/sites/plain/templates/sections/body.html b/jamstack/sites/plain/templates/sections/body.html index c231a1e..6131e88 100644 --- a/jamstack/sites/plain/templates/sections/body.html +++ b/jamstack/sites/plain/templates/sections/body.html @@ -1,2 +1,2 @@ -

Hello World {{ str(59).zfill(3) }}

-generated using jamstack \ No newline at end of file +

Hello World!

+Generated using Jamstack \ No newline at end of file From 25463891236b8e698ddd3e1494f9691dd1a25ec4 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 17 Apr 2023 22:40:45 -0400 Subject: [PATCH 14/23] Minimal format task --- jamstack/sites/plain/templates/index.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/jamstack/sites/plain/templates/index.html b/jamstack/sites/plain/templates/index.html index 5bfdda5..df883c3 100644 --- a/jamstack/sites/plain/templates/index.html +++ b/jamstack/sites/plain/templates/index.html @@ -1,9 +1,9 @@ - - {% include 'sections/head.html' %} - + + {% include 'sections/head.html' %} + - {% include 'sections/body.html' %} + {% include 'sections/body.html' %} - \ No newline at end of file + From 1b5ca02b89084e80023b39af3ca5bba9eab92fdd Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 17 Apr 2023 22:43:02 -0400 Subject: [PATCH 15/23] Minimal comment --- jamstack/api/file.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jamstack/api/file.py b/jamstack/api/file.py index cce8be1..86d4c8b 100644 --- a/jamstack/api/file.py +++ b/jamstack/api/file.py @@ -1,6 +1,6 @@ +import logging import os import shutil -import logging import uuid @@ -22,6 +22,7 @@ def trycopytree(source, dest, dirs_exist_ok): """ try: shutil.copytree(source, dest, dirs_exist_ok=dirs_exist_ok) + os.mkdir(os.path.join(dest, 'dist')) # Maybe place in static.py? print('Project created successfully! :)') except FileExistsError: print('Project folder already exist! Use --existing if you want to override it.') @@ -154,6 +155,5 @@ def delete_file(path): except Exception as e: logging.exception(e) - # def unique_sec_filename(filename): # return unique_filename(secure_filename(filename)) From 830bb313a8b6f456b76ca2ac9978e9ee6e7376fe Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 17 Apr 2023 22:45:47 -0400 Subject: [PATCH 16/23] =?UTF-8?q?So=20much=20improvements=20and=20refactor?= =?UTF-8?q?s=20=F0=9F=98=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Documentation - Remove unused code - Improve perfomance - Improve code readability --- jamstack/api/template.py | 51 +++++++----- jamstack/jamdo/jamdo.py | 50 ++++++----- jamstack/sites/html5up/massively/settings.py | 2 +- jamstack/sites/html5up/massively/static.py | 70 +++++++--------- jamstack/sites/html5up/phantom/info.json | 4 +- jamstack/sites/html5up/phantom/settings.py | 11 +-- jamstack/sites/html5up/phantom/static.py | 87 ++++++++------------ jamstack/sites/plain/info.json | 12 +-- jamstack/sites/plain/settings.py | 6 +- jamstack/sites/plain/static.py | 60 ++++++-------- 10 files changed, 155 insertions(+), 198 deletions(-) diff --git a/jamstack/api/template.py b/jamstack/api/template.py index 1d796a9..1f19945 100644 --- a/jamstack/api/template.py +++ b/jamstack/api/template.py @@ -1,13 +1,18 @@ import builtins import copy import datetime +import json import string -import uuid from jinja2 import Environment, FileSystemLoader -def base_context(): +def load_json(file): + with open(file, 'r') as f: + return json.load(f) + + +def get_builtins_context() -> dict: """ Get a dictionary of built-in functions and variables. @@ -16,48 +21,50 @@ def base_context(): dict Dictionary of built-in functions and variables. """ - defaults = [_ for _ in dir(builtins) - if _[0] in string.ascii_lowercase and - _ not in ['copyright', 'credits'] - ] - attrs = [getattr(builtins, _) for _ in defaults] - - builtins_dict = dict(zip(defaults, attrs)) + builtins_dict = {} + for name in dir(builtins): + if name[0] in string.ascii_lowercase and name not in ['copyright', 'credits']: + value = getattr(builtins, name) + builtins_dict[name] = value return copy.deepcopy(builtins_dict) -def generate(file_in_templates, out_path, template_dir='templates', assets_path_append='', **kwargs): +def generate(template_name: str, output_path: str, template_dir: str = 'templates', assets_path_append: str = '', + context: dict = None) -> None: """ Generates necessary file(s) from a Jinja2 template. Parameters ---------- - file_in_templates: str + template_name: str Template file to work with. - out_path: str + output_path: str Output path to save the generated file to. template_dir: str, optional Templates directory. Default is 'templates'. assets_path_append: str, optional Path to append to assets. Default is ''. - kwargs: dict - Variables to pass to the template. + context: dict, optional + Variables to pass to the template. Additional context variables can be provided using this parameter. Returns ------- None """ + if context is None: + context = {} + file_loader = FileSystemLoader(template_dir) env = Environment(loader=file_loader) - template = env.get_template(file_in_templates) + template = env.get_template(template_name) - build_id = str(uuid.uuid4()) + context.update({ + 'year': datetime.datetime.now().year, + 'assets_path_append': assets_path_append, + **get_builtins_context(), + }) - output = template.render( - kwargs, - year=datetime.datetime.now().year, - build_id=build_id, - assets_path_append=assets_path_append) + output = template.render(context) - with open(out_path, 'w+', encoding="utf8") as f: + with open(output_path, 'w+', encoding="utf8") as f: f.write(output) diff --git a/jamstack/jamdo/jamdo.py b/jamstack/jamdo/jamdo.py index 66846a3..4ca1d2b 100644 --- a/jamstack/jamdo/jamdo.py +++ b/jamstack/jamdo/jamdo.py @@ -1,52 +1,48 @@ -import json import os -from urllib.request import urlretrieve +import urllib.request import requests from tqdm import tqdm -TEMPLATES_URL = ("https://api.github.com/repos/jamstackpy/" - "jamstack-templates/git/trees/main?recursive=1") +TEMPLATES_URL = "https://api.github.com/repos/jamstackpy/jamstack-templates/git/trees/main?recursive=1" + + +def mkdirs(path): + if not os.path.isdir(path): + os.makedirs(path) def get_raw_url(file_path, url): - tmp_url = url.replace( + raw_url = url.replace( 'https://api.github.com/repos/', 'https://raw.githubusercontent.com/') - tmp_url = tmp_url.split('/git/blobs/')[0] - tmp_url = tmp_url + '/master/' + file_path - return tmp_url + raw_url = raw_url.split('/git/blobs/')[0] + raw_url = raw_url + '/master/' + file_path + return raw_url def get_download_links(template): - api = requests.get(TEMPLATES_URL).text - files = json.loads(api) + api = requests.get(TEMPLATES_URL).json() + files = api['tree'] output = [] - location = dict() - for (k, i) in enumerate(files['tree']): - if template in i['path']: - if i['type'] == 'blob': - tmp = [i['path']] - tmp += [get_raw_url(tmp[0], i['url'])] - output.append(tmp) + location = {} + for i, file in enumerate(files): + if template in file['path']: + if file['type'] == 'blob': + output.append([file['path'], get_raw_url(file['path'], file['url'])]) else: - location[i['path']] = k + location[file['path']] = i return output, location -def mkdirs(path): - if not os.path.isdir(path): - os.makedirs(path) - - def download(template, target_folder='*', recursive=True): - files, location = get_download_links(template) + data = get_download_links(template) + files = data[0] + location = data[1] if target_folder == '*': start = 0 else: - # tmp_target = target_folder.replace('./', '').replace('../', '') - # tmp_target = tmp_target.rstrip('/') start = location[target_folder] with tqdm(total=len(files), desc="Downloading assets...") as pbar: @@ -54,5 +50,5 @@ def download(template, target_folder='*', recursive=True): ndir = i[0].replace('templates/' + template, 'dist/assets/') if recursive or ndir.split(target_folder)[1].count('/') <= 1: mkdirs('.' + '/' + os.path.dirname(ndir)) - urlretrieve(i[1], '.' + '/' + ndir) + urllib.request.urlretrieve(i[1], '.' + '/' + ndir) pbar.update(1) diff --git a/jamstack/sites/html5up/massively/settings.py b/jamstack/sites/html5up/massively/settings.py index caf13a2..ff5e831 100644 --- a/jamstack/sites/html5up/massively/settings.py +++ b/jamstack/sites/html5up/massively/settings.py @@ -2,7 +2,7 @@ info = { "social": { "facebook": "", - "twitter": "https://twitter.com/osdotsystem", + "twitter": "", "instagram": "", "github": "" }, diff --git a/jamstack/sites/html5up/massively/static.py b/jamstack/sites/html5up/massively/static.py index a302d00..8680250 100644 --- a/jamstack/sites/html5up/massively/static.py +++ b/jamstack/sites/html5up/massively/static.py @@ -1,54 +1,42 @@ -# https://github.com/pymug/website-AV19-AV20 - -import sys +import argparse from os.path import join -import settings -from flask import Flask -from jamstack.api.template import base_context, generate -from jamstack.jamdo.jamdo import download as download_template from livereload import Server -context = base_context() -context.update({ - "info": settings.info -}) - - -def main(args): - def gen(): - generate('index.html', join( - settings.OUTPUT_FOLDER, 'index.html'), **context) - generate('index2.html', join( - settings.OUTPUT_FOLDER, 'index2.html'), **context) - generate('index3.html', join( - settings.OUTPUT_FOLDER, 'index3.html'), **context) +import settings +from jamstack.api.template import generate +from jamstack.jamdo.jamdo import download as download_template - if len(args) > 1 and args[1] == '--server': - app = Flask(__name__) - # remember to use DEBUG mode for templates auto reload - # https://github.com/lepture/python-livereload/issues/144 - app.debug = True - server = Server(app.wsgi_app) +def generate_site(): + extra_context = {"info": settings.info} - # run a shell command - # server.watch('.', 'make static') + generate('index.html', join(settings.OUTPUT_FOLDER, 'index.html'), context=extra_context) + generate('index2.html', join(settings.OUTPUT_FOLDER, 'index2.html'), context=extra_context) + generate('index3.html', join(settings.OUTPUT_FOLDER, 'index3.html'), context=extra_context) - # run a function - server.watch('.', gen, delay=5) - server.watch('*.py') +def serve_files(port, watch): + server = Server() + for x in watch.split('|'): + server.watch(x, func=generate_site) + try: + server.serve(root=settings.OUTPUT_FOLDER, port=port) + except KeyboardInterrupt: + print("Shutting down...") - # output stdout into a file - # server.watch('style.less', shell('lessc style.less', output='style.css')) - server.serve() - elif len(args) > 1 and args[1] == '--jamdo': +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Project manager.') + parser.add_argument('--serve', action='store_true', help='Serve files for livewatch') + parser.add_argument('--jamdo', action='store_true', help='Download template assets') + parser.add_argument('--watch', type=str, default='*.py|templates|templates/sections', help='Files/Folders to watch') + parser.add_argument('--port', type=int, default=8000, help='Port to serve') + args = parser.parse_args() + + if args.serve: + serve_files(args.port, args.watch) + elif args.jamdo: download_template('html5up/massively') else: - gen() - - -if __name__ == '__main__': - main(sys.argv) + generate_site() diff --git a/jamstack/sites/html5up/phantom/info.json b/jamstack/sites/html5up/phantom/info.json index 7dffa0e..7efdeaf 100644 --- a/jamstack/sites/html5up/phantom/info.json +++ b/jamstack/sites/html5up/phantom/info.json @@ -64,8 +64,8 @@ } }, "metatags": { - "description": "Webpage description", - "keywords": "jamstack, python, html5up", + "description": "A modern and responsive Jamstack website built with Python", + "keywords": "Python, Jamstack, website, responsive, modern", "author": "Jhon Doe", "copyright": "Jhon Doe" }, diff --git a/jamstack/sites/html5up/phantom/settings.py b/jamstack/sites/html5up/phantom/settings.py index 59533be..34d89e1 100644 --- a/jamstack/sites/html5up/phantom/settings.py +++ b/jamstack/sites/html5up/phantom/settings.py @@ -1,11 +1,6 @@ -import json +from jamstack.api.template import load_json OUTPUT_FOLDER = 'dist/' -info = None -posts = None -with open('info.json', 'r') as f: - info = json.load(f) - -with open('posts.json', 'r') as f: - posts = json.load(f) +info = load_json('info.json') +posts = load_json('posts.json') diff --git a/jamstack/sites/html5up/phantom/static.py b/jamstack/sites/html5up/phantom/static.py index a8b93ea..e38ec90 100644 --- a/jamstack/sites/html5up/phantom/static.py +++ b/jamstack/sites/html5up/phantom/static.py @@ -1,65 +1,50 @@ -# https://github.com/pymug/website-AV19-AV20 - -import sys +import argparse from os.path import join -import settings -from flask import Flask -from jamstack.api.template import base_context, generate -from jamstack.jamdo.jamdo import download as download_template from livereload import Server -context = base_context() -context.update({ - "info": settings.info, - "posts": settings.posts -}) - - -def main(args): - def gen(): - generate('index.html', join( - settings.OUTPUT_FOLDER, 'index.html'), **context) - - # Generate pages based in sidebar items - sidebar = settings.info['sidebar'] - for item in sidebar: - page = sidebar[item]['page'] - generate(page, join(settings.OUTPUT_FOLDER, page), **context) +import settings +from jamstack.api.template import generate +from jamstack.jamdo.jamdo import download as download_template - # Generate posts pages - posts = settings.posts['posts'] - for post in posts: - page = posts[post]['page'] - generate(page, join( - settings.OUTPUT_FOLDER, page), **context) - if len(args) > 1 and args[1] == '--server': - app = Flask(__name__) +def generate_site(): + extra_context = {"info": settings.info, + "posts": settings.posts} + generate('index.html', join(settings.OUTPUT_FOLDER, 'index.html'), context=extra_context) - # remember to use DEBUG mode for templates auto reload - # https://github.com/lepture/python-livereload/issues/144 - app.debug = True - server = Server(app.wsgi_app) + # Generate pages based on sidebar items + for item in settings.info['sidebar'].values(): + page = item['page'] + generate(page, join(settings.OUTPUT_FOLDER, page), context=extra_context) - # run a shell command - # server.watch('.', 'make static') + # Generate posts pages + for post in settings.posts['posts'].values(): + page = post['page'] + generate(page, join(settings.OUTPUT_FOLDER, page), context=extra_context) - # run a function - server.watch('.', gen, delay=5) - server.watch('*.py') +def serve_files(port, watch): + server = Server() + for x in watch.split('|'): + server.watch(x, func=generate_site) + try: + server.serve(root=settings.OUTPUT_FOLDER, port=port) + except KeyboardInterrupt: + print("Shutting down...") - # output stdout into a file - # server.watch('style.less', shell('lessc style.less',\ - # output='style.css')) - server.serve() - elif len(args) > 1 and args[1] == '--jamdo': +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Project manager.') + parser.add_argument('--serve', action='store_true', help='Serve files for livewatch') + parser.add_argument('--jamdo', action='store_true', help='Download template assets') + parser.add_argument('--watch', type=str, default='*.py|templates|templates/sections', help='Files/Folders to watch') + parser.add_argument('--port', type=int, default=8000, help='Port to serve') + args = parser.parse_args() + + if args.serve: + serve_files(args.port, args.watch) + elif args.jamdo: download_template('html5up/phantom') else: - gen() - - -if __name__ == '__main__': - main(sys.argv) + generate_site() diff --git a/jamstack/sites/plain/info.json b/jamstack/sites/plain/info.json index 5a28a13..89fc7cb 100644 --- a/jamstack/sites/plain/info.json +++ b/jamstack/sites/plain/info.json @@ -1,8 +1,8 @@ { - "head":{ - "description": "Basic jamstack site", - "keywords": "python, jamstack", - "author": "R Ja", - "theme-color": "#ffb74d" - } + "head": { + "description": "A modern and responsive Jamstack website built with Python", + "keywords": "Python, Jamstack, website, responsive, modern", + "author": "Jhon Doe", + "theme-color": "#4fc3f7" + } } \ No newline at end of file diff --git a/jamstack/sites/plain/settings.py b/jamstack/sites/plain/settings.py index d4abb28..16d2f94 100644 --- a/jamstack/sites/plain/settings.py +++ b/jamstack/sites/plain/settings.py @@ -1,7 +1,5 @@ -import json +from jamstack.api.template import load_json OUTPUT_FOLDER = 'dist/' -info = None -with open('info.json', 'r') as f: - info = json.load(f) +info = load_json('info.json') diff --git a/jamstack/sites/plain/static.py b/jamstack/sites/plain/static.py index fdf1966..32a053c 100644 --- a/jamstack/sites/plain/static.py +++ b/jamstack/sites/plain/static.py @@ -1,47 +1,35 @@ -# https://github.com/pymug/website-AV19-AV20 - -import sys +import argparse from os.path import join -import settings -from flask import Flask -from jamstack.api.template import base_context, generate from livereload import Server -context = base_context() -context.update({ - "info": settings.info -}) - - -def main(args): - def gen(): - generate('index.html', join( - settings.OUTPUT_FOLDER, 'index.html'), **context) - - if len(args) > 1 and args[1] == '--server': - app = Flask(__name__) - - # remember to use DEBUG mode for templates auto reload - # https://github.com/lepture/python-livereload/issues/144 - app.debug = True - server = Server(app.wsgi_app) +import settings +from jamstack.api.template import generate - # run a shell command - # server.watch('.', 'make static') - # run a function +def generate_site(): + extra_context = {"info": settings.info} + generate('index.html', join(settings.OUTPUT_FOLDER, 'index.html'), context=extra_context) - server.watch('.', gen, delay=5) - server.watch('*.py') - # output stdout into a file - # server.watch('style.less', shell('lessc style.less', output='style.css')) - - server.serve() - else: - gen() +def serve_files(port, watch): + server = Server() + for x in watch.split('|'): + server.watch(x, func=generate_site) + try: + server.serve(root=settings.OUTPUT_FOLDER, port=port) + except KeyboardInterrupt: + print("Shutting down...") if __name__ == '__main__': - main(sys.argv) + parser = argparse.ArgumentParser(description='Project manager.') + parser.add_argument('--serve', action='store_true', help='Serve files for livewatch') + parser.add_argument('--watch', type=str, default='*.py|templates|templates/sections', help='Files/Folders to watch') + parser.add_argument('--port', type=int, default=8000, help='Port to serve') + args = parser.parse_args() + + if args.serve: + serve_files(args.port, args.watch) + else: + generate_site() From f10a862f7d9c97ed333bb3f3164f2c94b7d4c01e Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 17 Apr 2023 23:06:04 -0400 Subject: [PATCH 17/23] Add TODO section --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index e442ea7..9c13f79 100644 --- a/README.md +++ b/README.md @@ -68,3 +68,8 @@ Your site will be generated in the **dist/** folder. - [DeliciousPy Telegram Channel](https://deliciouspy.github.io/) - [The Flask Community Work Group](https://flaskcwg.github.io/) - [Abdur-Rahmaan Janhangeer](https://compileralchemy.github.io/) + +# TODO + +- [ ] Replace `python static.py --serve/--jamdo` by something like `jamstack --serve/--jamdo` +- From bfdd15a3441729781c7e70def4880d7647684a41 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 17 Apr 2023 23:06:47 -0400 Subject: [PATCH 18/23] Set version after refactor --- jamstack/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jamstack/__init__.py b/jamstack/__init__.py index 17954f6..9260b35 100644 --- a/jamstack/__init__.py +++ b/jamstack/__init__.py @@ -1,2 +1,2 @@ -version_info = (1, 0, 0) +version_info = (1, 1, 0) __version__ = ".".join(map(str, version_info)) From dc398374a53727192409b1ff1c3f33359a25ea40 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Mon, 17 Apr 2023 23:11:45 -0400 Subject: [PATCH 19/23] Fix typo --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9c13f79..6a725c5 100644 --- a/README.md +++ b/README.md @@ -72,4 +72,3 @@ Your site will be generated in the **dist/** folder. # TODO - [ ] Replace `python static.py --serve/--jamdo` by something like `jamstack --serve/--jamdo` -- From 55a6f6cace49977b1649cdcce10e79ebdd8f5885 Mon Sep 17 00:00:00 2001 From: Jalkhov Date: Tue, 18 Apr 2023 07:38:58 -0400 Subject: [PATCH 20/23] Add TODO task Also improve example command --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a725c5..8d4ee1d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Jamstack has templates available courtesy of [html5up](https://html5up.net). The syntax is as follows: ```bash -jamstack t