Source code for duck.cli.commands.collectstatic
"""
Module containing collectstatic command class.
"""
import os
import shutil
import pathlib
from typing import List, Generator, Tuple
from duck.routes import Blueprint
from duck.utils.path import joinpaths
from duck.logging import console
[docs]
class CollectStaticCommand:
# collectstatic command
[docs]
@classmethod
def setup(cls):
pass
[docs]
@classmethod
def main(cls, skip_confirmation: bool = False):
cls.setup()
cls.collectstatic(skip_confirmation)
[docs]
@classmethod
def collectstatic(cls, skip_confirmation: bool = False) -> None:
# Execute command after setup.
from duck.settings import SETTINGS
static_root = str(SETTINGS["STATIC_ROOT"])
blueprint_static_dirs: List[str, Blueprint] = list(cls.find_blueprint_static_dirs())
blueprint_staticfiles: List[str] = list(cls.get_blueprint_staticfiles(blueprint_static_dirs))
global_static_dirs = SETTINGS["GLOBAL_STATIC_DIRS"] or []
global_staticfiles = []
for static_dir in global_static_dirs:
if os.path.isdir(static_dir):
staticfiles = list(cls.recursive_getfiles(static_dir))
global_staticfiles.extend(staticfiles)
staticfiles_len = len(blueprint_staticfiles) + len(global_staticfiles)
if staticfiles_len == 0:
console.log_raw("\nNo staticfiles found!", level=console.WARNING)
return
if not skip_confirmation:
# Show confirmation prompt
console.log_raw(
f"\nWould you like to copy {staticfiles_len} staticfile(s) to {static_root} (y/N): ",
end="",
level=console.DEBUG,
)
# Obtain confirmation from console.
choice = input("")
if not choice.lower().startswith("y"):
console.log_raw("\nCancelled, bye!", level=console.WARNING)
return
for static_dir in global_static_dirs:
destination = static_root
if os.path.isdir(static_dir):
shutil.copytree(static_dir, destination, dirs_exist_ok=True)
for static_dir, blueprint in blueprint_static_dirs:
# Convert staticdir to relative path
abs_static_dir = static_dir
# Build destination path
destination = joinpaths(
static_root,
blueprint.name,
)
# Copy static files
shutil.copytree(abs_static_dir, destination, dirs_exist_ok=True)
console.log_raw(f"\nSuccessfully copied {staticfiles_len} staticfile(s) to {static_root}", level=console.SUCCESS)
[docs]
@classmethod
def recursive_getfiles(cls, directory: str) -> Generator:
"""
Returns a generator for all files and subfiles within the directory.
"""
directory = pathlib.Path(directory)
if directory.is_dir():
for dir_entry in os.scandir(directory):
if dir_entry.is_file():
yield dir_entry.path
else:
for i in cls.recursive_getfiles(dir_entry.path):
yield i
[docs]
@classmethod
def find_blueprint_static_dirs(cls) -> Generator[Tuple[str, Blueprint], None, None]:
"""
Finds and returns static directories from all blueprint base directories.
Returns:
Generator: The generator of static directory and blueprint pair.
"""
from duck.settings.loaded import SettingsLoaded
blueprints = SettingsLoaded.BLUEPRINTS
for blueprint in blueprints:
if blueprint.enable_static_dir:
# Access to blueprint static files is allowed.
static_dir = joinpaths(
blueprint.root_directory,
blueprint.static_dir,
)
if pathlib.Path(static_dir).is_dir():
yield (static_dir, blueprint)
[docs]
@classmethod
def get_blueprint_staticfiles(cls, blueprint_static_dirs: Tuple[str, Blueprint]):
"""
Returns the generator to the found staticfiles.
Args:
blueprint_static_dirs (Tuple[str, Blueprint]): The collection of the static directory and the respective blueprint.
"""
for static_dir, blueprint in blueprint_static_dirs:
for static_file in cls.recursive_getfiles(static_dir):
yield static_file