diff --git a/hshassets/app_settings/__init__.py b/hshassets/app_settings/__init__.py index fad7a30e25812ba608faebf5da9490a4ca66ca24..843d131dfc6e311f9e909449a7bd814624fc2c1b 100644 --- a/hshassets/app_settings/__init__.py +++ b/hshassets/app_settings/__init__.py @@ -1,7 +1,7 @@ from .defaults import * +from .logging import * from django import conf - # merge defaults with customized user settings for setting_name in [k for k in globals().keys() if k.isupper()]: diff --git a/hshassets/app_settings/logging.py b/hshassets/app_settings/logging.py new file mode 100644 index 0000000000000000000000000000000000000000..067fab6660737f7cb2b2bc89146711cbdb260392 --- /dev/null +++ b/hshassets/app_settings/logging.py @@ -0,0 +1,84 @@ +import logging.config + + +LOGGING_CONFIG = None + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse', + }, + 'require_debug_true': { + '()': 'django.utils.log.RequireDebugTrue', + }, + }, + 'formatters': { + 'with_timestamp': { + 'format': '[%(asctime)s %(levelname)s %(thread)d] %(message)s', + 'datefmt': '%Y-%m-%d %H:%M:%S', + }, + 'without_timestamp': { + 'format': '%(name)s %(levelname)s: %(message)s', + } + }, + 'handlers': { + 'console': { + 'level': 'INFO', + 'class': 'logging.StreamHandler', + 'formatter': 'with_timestamp' + }, + 'null': { + 'class': 'logging.NullHandler', + }, + 'console_without_timestamp': { + 'level': 'INFO', + 'class': 'logging.StreamHandler', + 'formatter': 'without_timestamp' + } + }, + 'loggers': { + '': { + 'handlers': ['console'], + 'level': 'INFO' + }, + 'django': { + 'level': 'WARNING', + 'handlers': ['console'], + 'propagate': False, + }, + 'django.db': { + 'level': 'INFO', + 'handlers': ['console'], + 'propagate': False, + }, + 'django.request': { + 'handlers': ['console'], + 'level': 'WARNING', + 'propagate': False, + }, + 'django.server': { + 'handlers': ['console'], + 'level': 'WARNING', + 'propagate': False, + }, + 'django.security': { + 'handlers': ['console'], + 'level': 'WARNING', + 'propagate': False, + }, + 'django.template': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'hshassets': { + 'handlers': ['console_without_timestamp', ], + 'level': 'INFO', + 'propagate': False, + }, + } +} + +logging.config.dictConfig(LOGGING) diff --git a/hshassets/assets/sass/default.scss b/hshassets/assets/sass/default.scss index 9ac7c5daca9b32ab461b3fb3e5e8877954c8e6f2..41edb92d5d443d299e4bd286a51b948c900d4661 100644 --- a/hshassets/assets/sass/default.scss +++ b/hshassets/assets/sass/default.scss @@ -38,13 +38,13 @@ $custom-colors: ($hsh-colors); // COLOR SCHEME FOR CORPORATE DESIGN // by default service will be used, if you need another one comment and uncomment approprate -//@import "cd/f1.scss"; -//@import "cd/f2.scss"; -//@import "cd/f3.scss"; -//@import "cd/f4.scss"; -//@import "cd/f5.scss"; -@import "cd/service.scss"; // default color scheme for hshassets -//@import "cd/zsw.scss"; +//@import "cd/f1"; +//@import "cd/f2"; +//@import "cd/f3"; +//@import "cd/f4"; +//@import "cd/f5"; +@import "cd/service"; // default color scheme for hshassets +//@import "cd/zsw"; @import "lib/bulma-0.7.2/sass/utilities/derived-variables"; @@ -151,4 +151,4 @@ $panel-block-hover-background-color: $cd-link-hover; // import customized hshstyle @import "corporate_design"; -@import "hshstyles.scss"; \ No newline at end of file +@import "hshstyles"; \ No newline at end of file diff --git a/hshassets/management/commands/buildassets.py b/hshassets/management/commands/buildassets.py index c8e468054e43b7fd615b9d7aac333283d47817ba..b8e8f80490c35e6be628d345ff417fd8d47effc5 100644 --- a/hshassets/management/commands/buildassets.py +++ b/hshassets/management/commands/buildassets.py @@ -1,22 +1,11 @@ from django.core.management.base import BaseCommand -from django.conf import settings from hshassets import utils -from importlib import import_module - -import os -import sass -import jsmin - class Command(BaseCommand): help = 'Builds the projects assets' def handle(self, *args, **options): - verbose = bool(options.get('verbosity', 0)) - - print('\nStart generating styles ...\n') - utils.do_everything(verbose=verbose) - print('Everything finished! \033[92m(ಠ‿↼)\033[0m\n') + utils.build_assets() diff --git a/hshassets/management/commands/initassets.py b/hshassets/management/commands/initassets.py new file mode 100644 index 0000000000000000000000000000000000000000..0ea41f85d4d20164592e95c14d66c6a3e73e09f2 --- /dev/null +++ b/hshassets/management/commands/initassets.py @@ -0,0 +1,39 @@ +from django.core.management.base import BaseCommand +from django.conf import settings +from hshassets import utils +from hshassets import logger + + +class Command(BaseCommand): + + help = 'Initializes the projects assets by creating an assets directory and copying scss file' + + def add_arguments(self, parser): + parser.add_argument('app_name', nargs='?', type=str) + + parser.add_argument( + '-e', + '--empty', + action="store_true", + dest='empty', + help='Create an empty _init.scss file and directories', + ) + + def handle(self, *args, **options): + + app_name = options.get('app_name') + + if options['empty']: + empty=True + else: + empty=False + + if app_name is None or app_name in settings.INSTALLED_APPS: + logger.debug('\nStart initalise assets.\n') + utils.init_assets(app_name, empty) + logger.debug('Initialisation finished!\n') + else: + logger.info('The name is not found in installed apps of your project, no initialisation possible.') + + if options['empty']: + pass \ No newline at end of file diff --git a/hshassets/utils.py b/hshassets/utils.py index c4e27247dee967f5a1e99d4a1a732dae173b73a7..5d114bb367c204b53d7a8b0a48dabd7945c87cf8 100644 --- a/hshassets/utils.py +++ b/hshassets/utils.py @@ -1,313 +1,314 @@ from django.conf import settings -from hshassets import logger -from hshassets.app_settings import SCSS_INCLUDE_PATHS + +from . import logger from importlib import import_module import os -import time import sass import jsmin import shutil import re -def get_asset_directories(): +def get_installed_apps_with_asset_directory(include_project=False): """ - Get all apps and app path of installed apps that contains an asset directory on its root. - :return: dictionary of apps with path + Returns a list of all installed apps that have an asset directory, except 'hshassets' and + according to parameter without_project with or without the project itself. + :return: list of appnames """ - asset_apps = {} - + apps = [] for app_name in settings.INSTALLED_APPS: - asset_apps.update(get_directories(app_name)) + if os.path.isdir(get_assets_path(app_name)): + if app_name is not 'hshassets': + apps.append(app_name) + if not include_project and get_projectname() in apps: + apps.remove(get_projectname()) - return asset_apps + return apps -def get_directories(app_name): +def get_app_path(app_name): """ - Create a dictionary for each app that includes asset directory. - The dictionary includes information about app path, static directory path, - asset directory path and variable to save color scheme of that app. - :param app_name: name of the app as string - :return: dictionary including information of app with its associated paths for - app, static and assets and color scheme + Returns the path of the app. + :param app_name: string + :return: path of app as string """ app_module = import_module(app_name) - app_path = app_module.__path__[0] - app_dirs = {} + return app_path + + +def get_static_path(app_name): + """ + Returns path of static directory with app directory included regardless it exists. + :param app_name: name of the app as string, app_path as string + :return: static app path as string + """ + return os.path.join(get_app_path(app_name), 'static', app_name) - if os.path.isdir(os.path.join(app_path, 'assets')): # directory assets exist in app - app_dirs = {app_name: {'app_path': app_path, - 'static_path': os.path.join(app_path, 'static', app_name), - 'asset_path': os.path.join(app_path, 'assets'), - 'color_scheme': None, - }} - return app_dirs +def get_assets_path(app_name): + """ + Returns path of assets directory regardless it exists. + :param app_name as string + :return: assets app path as string + """ + return os.path.join(get_app_path(app_name), 'assets') -def get_color_scheme(app_name, asset_path): + +def get_sass_path(app_name): + """ + Returns path of sass directory regardless it exists. + :param app_name: path of app as string + :return: path of sass directory as string + """ + return os.path.join(get_assets_path(app_name), 'sass') + + +def get_init_scss_path(app_name): + """ + Returns the path of the '_init.scss'-file if exists otherwise None + :param app_name: name as string + :return: path as string or None + """ + return os.path.join(get_sass_path(app_name), '_init.scss') + + +def get_color_scheme(source): """ Search for given color scheme in _init.scss file by searching for command to import corporate colors, for example @import 'cd/service.scss'. - :param asset_path: path where sass/_init.scss can be found (asset path does not contain sass/_init.scss) :return: color scheme as string or None if no color scheme can be found """ - color = None - print('Searching for color scheme of {} ...'.format(app_name)) + logger.info('Searching for color scheme ...') - if app_name == 'hshassets': - scss_file = os.path.join(asset_path, 'sass', 'default.scss') - else: - scss_file = os.path.join(asset_path, 'sass', '_init.scss') - - if os.path.isfile(scss_file): + if os.path.isfile(source): - with open(scss_file, 'r') as scss: + with open(source, 'r') as scss: content = scss.read() + '\n' # Search whether or rather which color is imported into init.scss - color = re.search(r"^@import [\'\"]cd/(?P<cd_color>[a-z0-9]*)[.sc]*[\'\"];", content, re.MULTILINE) + color = re.search(r"^@import [\'\"]cd/(?P<cd_color>[a-z0-9]*)[\'\"];", content, re.MULTILINE) if color: color_scheme = (color.groupdict().get('cd_color')) - print('Found color scheme', color_scheme, '\n') + logger.info('Found color scheme {} \n'.format(color_scheme)) else: color_scheme = None - print('No color scheme found.\n') + logger.info('No color scheme found.\n') return color_scheme -def separate_asset_directories(asset_directories): - # Separating the app directories from the project directories for further processing - - # get name of the django project - project_name = os.path.basename(settings.BASE_DIR) - project_directories = {} - - if project_name in asset_directories.keys(): # project has asset directory - project_directories.update({project_name: asset_directories.pop(project_name)}) - else: - # the project has no asset directory - app_path = os.path.join(os.path.dirname(settings.BASE_DIR), 'swamp') - project_directories = ({project_name: {'app_path': app_path, - 'static_path': app_path + '/static/' + project_name, - 'asset_path': app_path + '/assets', - 'color_scheme': None}}) - - return asset_directories, project_directories - - -def get_hshassets_directories(): +def copy_favicon(app_name, color_scheme): """ - Get the information for hshassets - :return: dictionary with app_path, static_path, asset_path and color_scheme + Copies favicon.ico from 'hshassets' depending on chosen color scheme to static 'img' directory of an app. + If no 'img' directory exists it will be created. + :param app_name: + :param color_scheme: + :return: no return """ - return (get_directories('hshassets')).get('hshassets') - - -def get_scss_include_paths(asset_directories): - """ Get additional include_paths for the scss parser - to be able to load e.g. bulma without getting insane """ - include_paths = [] - for app_name, rel_paths in SCSS_INCLUDE_PATHS: - if app_name in asset_directories.keys(): - app_meta = asset_directories[app_name] - for rel_path in rel_paths: - lib_path = os.path.join(app_meta['asset_path'], rel_path) - if os.path.exists(lib_path): - include_paths.append(lib_path) - else: - logger.warning('You specified an additional scss path which can not be found: {}'.format(lib_path)) - else: - logger.warning( - 'You specified the app {} in "SCSS_INCLUDE_PATHS" that can not be found. Is it in INSTALLED_APPS?' - .format(app_name) - ) - return include_paths + img_path = os.path.join(get_static_path(app_name), 'img') + # no color, no favicon to copy + if color_scheme: + # get favicon source in hshassets + favicon_source = os.path.join(get_assets_path('hshassets'), 'img', 'hsh_brand', 'favicons', color_scheme, + 'favicon.ico') -def build_static(app_name, app_directories): + # create img directory in static to save favicon inside + if not os.path.isdir(img_path): + os.makedirs(img_path) + logger.info('Create "img" directory to save favicon.') - print('Building \033[34mstatic\033[0m for app \033[31m{}\033[0m ...\n'.format(app_name)) - create_static_directory(app_directories['static_path']) - copy_directories(app_name, app_directories) + shutil.copyfile(favicon_source, os.path.join(img_path, 'favicon.ico')) # favicon for every app + logger.info('Copy favicon for color scheme {} to static'.format(color_scheme)) + else: + if os.path.isfile(os.path.join(img_path, 'favicon.ico')): + os.remove(os.path.join(img_path, 'favicon.ico')) -def create_static_directory(static_path): - # delete old static folders of app - if os.path.isdir(static_path): - shutil.rmtree(static_path) - print('Remove directory: {}'.format(static_path)) - # logger.info('Remove directory: {}'.format(static_path)) - # create new static folder of app - os.makedirs(static_path) - print('Create directory: {}\n'.format(static_path)) - # logger.info('Create directory: {}'.format(static_path)) +def get_filepaths(path, file_ending): + """ + Returns a list of paths to files. The type of files is determined by parameter file_ending. They are + searched in sub directory of path that is named equal to file_ending. For example: JavaScript files + with ending '.js' are searched in subdirectory 'js' or css-files in sub directory 'css'. + :param path: path as string + :param file_ending: type of files as string + :return: list of filepaths + """ + files_list = [] + for dirpath, dirs, files in os.walk(os.path.join(path, file_ending)): + for file in files: + if file.endswith(file_ending): + files_list.append(os.path.join(dirpath, file)) + return files_list -def copy_css_directories(app_name, app_drectories): - css_files = [] - for root, dirs, files in os.walk(os.path.join(app_directories['asset_path'], 'css')): - for f in files: - if f.endswith('.css'): - css_files.append(os.path.join(root, f)) +def remove_empty_directory(path): + """ + Deletes subdirectory of path as long as they are empty. + Checks if directory is empty and deletes it if + :param path: string + :return: no return + """ + try: + count_path_entries = len(os.listdir(path)) + except FileNotFoundError: + logger.debug('This path does not belong to a directory.') + else: + if count_path_entries == 0: + os.rmdir(path) + logger.info('Remove empty directory {}'.format(os.path.basename(path))) + path = os.path.dirname(path) + remove_empty_directory(path) - for css_file in css_files: - expand, mini = build_scss_output(css_file) +def remove_static_minimized_file(app_name, source_path): + """ + Removes the js and js min file if found in static directory of app_name. + :param app_name: name as string + :param source_path: path as string + :return: no return + """ - with open(css_file, 'r') as infile: - content = infile.read() + subdirectory = str(get_file_extension(source_path)).strip('.') - dest_js_path = os.path.join(app_directories['static_path'], 'js') - dest_file_name = os.path.basename(js_file) - dest_file_name_stripped, dest_file_extenstion = os.path.splitext(dest_file_name) + static_path = os.path.join(get_static_path(app_name), subdirectory) - if not os.path.isdir(dest_js_path): - os.makedirs(dest_js_path) + file_name = os.path.basename(source_path) + min_file_name = get_min_name(file_name) - with open(os.path.join(dest_js_path, dest_file_name), 'w') as outfile: - outfile.write(content + '\n') + if os.path.isfile(os.path.join(static_path, file_name)): + os.remove(os.path.join(static_path, file_name)) + logger.info('Removed {} from static of {}.'.format(file_name, app_name)) + if os.path.isfile(os.path.join(static_path, min_file_name)): + os.remove(os.path.join(static_path, min_file_name)) + logger.info('Removed {} from static of {}.'.format(min_file_name, app_name)) - with open(os.path.join(dest_js_path, dest_file_name_stripped + '.min' + dest_file_extenstion), 'w') as outfile: - outfile.write(jsmin.jsmin(content)) + # if directory is empty now remove directory + remove_empty_directory(static_path) -def copy_directories(app_name, app_directories): +def minimize_js(app_name, js_file_path): + """ + Minimize js file which means remove whitspaces and calls function write_files to save minimized js in static + directory of the app + :param app_name: name as string + :param js_file_path: path as string + :return: no return + """ + static_js_path = os.path.join(get_static_path(app_name), 'js') - if not os.path.isdir(app_directories['asset_path']): - print('No asset directory found.') - return + with open(js_file_path, 'r') as infile: + content = infile.read() - # copy all directories from assets to static except sass and js - for entry in os.scandir(app_directories['asset_path']): - if entry.is_dir and not entry.name in ['sass', 'js']: + min_content = (jsmin.jsmin(content)) - print('Found {} in assets directory of {}'.format(entry.name, app_name)) + dest_file_name = os.path.basename(js_file_path) - print('Start copying {} to static ...'.format(entry.name)) + if not os.path.isdir(static_js_path): + os.makedirs(static_js_path) - if os.path.isdir(os.path.join(app_directories['static_path'], entry.name)): - shutil.rmtree(os.path.join(app_directories['static_path'], entry.name)) + write_files(static_js_path, dest_file_name, content, min_content) - shutil.copytree( - (os.path.join(app_directories['asset_path'], entry.name)), - (os.path.join(app_directories['static_path'], entry.name)) - ) - print('Done!\n') +def copy_and_compile_assets_js_to_static(app_name): + """ + Copies js directory in assets directory to static. If js exists in static it will be removed with content files. + Catch all js files found in assets/js and calls function minimize_js to minimize content of js files. + :param app_name: name as string + :return: no return + """ + logger.info('Building \033[33mjavascript\033[0m for app "{}" ...'.format(app_name)) + assets_path = get_assets_path(app_name) + static_js_path = os.path.join(get_static_path(app_name), 'js') -def copy_favicon(static_path, color_scheme): - # no color, no favicon to copy - if not color_scheme: - return + # remove existing js directory in static + if os.path.exists(static_js_path): + shutil.rmtree(static_js_path) + logger.debug('Removed found directory {} in static directory'.format(os.path.basename(static_js_path))) - # get directories to copy the corresponding favicon - hshassets_directories = get_hshassets_directories() + js_files = get_filepaths(assets_path, 'js') - # create img directory in static to save favicon inside - if not os.path.isdir(os.path.join(static_path, 'img')): - os.makedirs(os.path.join(static_path, 'img')) - logger.info('Create "img" directory to save favicon.') + for js_file in js_files: + minimize_js(app_name, js_file) - # copy favicon from hshassets to static directory - shutil.copy2( - os.path.join(hshassets_directories['asset_path'], 'img', 'hsh_brand', 'favicons', color_scheme, 'favicon.ico'), - os.path.join(static_path, 'img', 'favicon.ico') # favicon for every app - ) - logger.info('Copy favicon for color scheme {} to static'.format(color_scheme)) +def build_css_from_scss(app_name): + """ + Write style css files for an app based on scss files. If no scss file exists as sass source + the default scss in hshassets will be taken. This is only useful for project if the style + is based on hshassets without any changes. + :param app_name: name as string + :return: no return + """ + sass_source = get_init_scss_path(app_name) + # if no path to a sass file exists in projekt take the default sass file of 'hshassets' + if not os.path.isfile(sass_source) and app_name is get_projectname(): + sass_source = get_default_scss_path() -def build_javascript(app_name, app_directories, verbose=True): - if verbose: - print('Building \033[33mjavascript\033[0m for app "{}" ...'.format(app_name)) + if os.path.isfile(sass_source): + destination_path = get_static_path(app_name) - start = time.time() + # list of path to find sass source files to @import + include_paths = [get_sass_path('hshassets'), get_sass_path(app_name)] - js_files = [] - for root, dirs, files in os.walk(os.path.join(app_directories['asset_path'], 'js')): - for f in files: - if f.endswith('.js'): - js_files.append(os.path.join(root, f)) + expanded_content, compressed_content = get_compiled_css(sass_source, include_paths) + write_files(destination_path, 'styles.css', expanded_content, compressed_content) - for js_file in js_files: - with open(js_file, 'r') as infile: - content = infile.read() - dest_js_path = os.path.join(app_directories['static_path'], 'js') - dest_file_name = os.path.basename(js_file) - dest_file_name_stripped, dest_file_extenstion = os.path.splitext(dest_file_name) +def get_min_name(file_name): + """ + Returns the name of file with additional '.min' in front of file extension. + :param file_name: file name as string + :return: name as string or None, example: 'name.min.css' + """ + min_name = None - if not os.path.isdir(dest_js_path): - os.makedirs(dest_js_path) + name, extension = os.path.splitext(file_name) - with open(os.path.join(dest_js_path, dest_file_name), 'w') as outfile: - outfile.write(content + '\n') + if extension: + min_name = '{}.min{}'.format(name, extension) - with open(os.path.join(dest_js_path, dest_file_name_stripped + '.min' + dest_file_extenstion), 'w') as outfile: - outfile.write(jsmin.jsmin(content)) + return min_name -def build_css_for_app(app_name, app_directories): +def write_files(path, file_name, expanded_output, compressed_output): """ - Function gets the source file for further handling regarding to the appname which means it differs - between hshassets app and other apps to get the scss source. Calls function build_scss_output with source file - and calls function write_scss_files to write style.css and style.min.css. - :param app_name: - :param app_directories: - :return: - + Writes at the destination determined by parameter path an expanded file with name file_name + and a minimized file with 'min' included in name. + :param path: destination for files as string + :param file_name: name file as string + :param expanded_output: string content + :param compressed_output: minimized string content + :return: no return """ - hshassets_directories = get_hshassets_directories() - include_paths = [os.path.join(hshassets_directories['asset_path'], 'sass')] + min_file_name = get_min_name(file_name) - sass_dir = os.path.join(app_directories['asset_path'], 'sass') - if os.path.isdir(sass_dir): - if app_name == 'hshassets': - source_file = os.path.join(sass_dir, 'default.scss') - else: - source_file = os.path.join(app_directories['asset_path'], 'sass', '_init.scss') - include_paths.append(sass_dir) - if os.path.isfile(source_file): - expanded_output, compressed_output = build_scss_output(source_file, include_paths) + if not os.path.exists(path): + os.makedirs(path) - write_css_files(app_directories, expanded_output, compressed_output) - - -def build_css_for_project(app_name, app_directories): - """ - Function gets the source file for further handling. If the project does not contain a scss source to compile it - takes the hshsassets scss source to compile into css. Calls function build_scss_output with source file - and calls function write_scss_files to write style.css and style.min.css. - :param app_name: - :param app_directories: - :return: - - """ - hshassets_directories = get_hshassets_directories() - source_file = os.path.join(hshassets_directories['asset_path'], 'sass', 'default.scss') - include_paths = [os.path.join(hshassets_directories['asset_path'], 'sass')] + with open(os.path.join(path, file_name), 'w') as outfile: + outfile.write(expanded_output) - # if no asset directory exist, css will be build of hshasset scss files - if os.path.isdir(os.path.join(app_directories['asset_path'], 'sass')): - if os.path.isfile(os.path.join(app_directories['asset_path'], 'sass', '_init.scss')): - source_file = os.path.join(app_directories['asset_path'], 'sass', '_init.scss') - include_paths.append(os.path.join(app_directories['asset_path'], 'sass')) + logger.info('Write {}'.format(file_name)) - expanded_output, compressed_output = build_scss_output(source_file, include_paths) + with open(os.path.join(path, min_file_name), 'w') as outfile: + outfile.write(compressed_output) - write_css_files(app_directories, expanded_output, compressed_output) + logger.info('Write {}'.format(min_file_name)) -def build_scss_output(source_file, include_paths=None): +def get_compiled_css(source_file, include_paths=None): """ This function compiles sass input into css string. :param source_file: filename of sass/scss source code to compile @@ -320,328 +321,194 @@ def build_scss_output(source_file, include_paths=None): return expanded_output, compressed_output -def write_css_files(app_directories, expanded_output, compressed_output): - - with open(os.path.join(app_directories['static_path'], 'styles.css'), 'w') as outfile: - outfile.write(expanded_output) +def copy_directory_tree(source, destination): + """ + Copy directory tree located in source path to destination path. + If directory tree in destination path already exists it will be removed before copying. + :param source: path as string + :param destination: path as string + :return: no return + """ + # remove subdirectories + if os.path.isdir(destination): + shutil.rmtree(destination) + + # copy directory tree + shutil.copytree( + (os.path.join(source)), + (os.path.join(destination)) + ) - logger.info('Write style.css') - with open(os.path.join(app_directories['static_path'], 'styles.min.css'), 'w') as outfile: - outfile.write(compressed_output) +def copy_and_compile_assets_css_to_static(app_name): + """ + Copies each css file found in assets subdirectory css, compiles css files to minimized css files + and calls write_file functions to write css files to static app directory. + :param app_name: + :return: no return + """ + asset_css_path = get_assets_path(app_name) + static_css_path = os.path.join(get_static_path(app_name), 'css') - logger.info('Write style.min.css') + if os.path.exists(static_css_path): + shutil.rmtree(static_css_path) + # no directory 'css', no need to copy... + if os.path.isdir(os.path.join(asset_css_path, 'css')): + css_files = get_filepaths(asset_css_path, 'css') + for css_file in css_files: + file_name = os.path.basename(css_file) + expanded_content, compressed_content = get_compiled_css(css_file) + write_files(static_css_path, file_name, expanded_content, compressed_content) -def build_scss(app_directories, content, include_paths): - expanded_output = sass.compile(string=content, include_paths=include_paths, output_style='expanded') - compressed_output = sass.compile(string=content, include_paths=include_paths, output_style='compressed') +def copy_assets_dirs_to_static(app_name): + """ + Copies recursive all sub directories found in assets directory except sub directories 'scss', 'css' and 'js' + because they have to be treated differently. + :param app_name: as string + :return: no return + """ + asset_path = get_assets_path(app_name) + # copy all directories from assets to static except sass, css and js + if os.path.isdir(asset_path): + static_path = get_static_path(app_name) + if not os.path.isdir(static_path): + os.makedirs(static_path) + logger.info('Create static directory for {}'.format(app_name)) + else: + logger.info('Remove {} from "{}"'.format(static_path, app_name)) + shutil.rmtree(static_path) + for assets_entry in os.scandir(asset_path): + if assets_entry.is_dir and assets_entry.name not in ['sass', 'js', 'css']: - with open(os.path.join(app_directories['static_path'], 'styles.css'), 'w') as outfile: - outfile.write(expanded_output) - logger.info('Write style.scss') + logger.info('Found {} in assets directory of {}'.format(assets_entry.name, app_name)) - with open(os.path.join(app_directories['static_path'], 'styles.min.css'), 'w') as outfile: - outfile.write(compressed_output) + destination_path = os.path.join(get_static_path(app_name), assets_entry.name) + source_path = os.path.join(get_assets_path(app_name), assets_entry.name) + copy_directory_tree(source_path, destination_path) - logger.info('Write style.min.scss') + logger.info('Copied {} to static'.format(assets_entry.name)) + else: + logger.info('No assets directory found in {}'.format(app_name)) -def do_everything(verbose=False): +def get_projectname(): """ - Function initiates building css files for project and apps on base of hshassets respectively bulma. - The way of handling is different for apps and project. Even if the project does not include an assetfolder - a style.css or style.min.css file will be generate by on base of hshassets. - A style.css for an app will be only generated if an app includes an asset folder with _init.scss. - :param verbose: - :return: - + Returns name of the project + :return: name as string """ - asset_directories = get_asset_directories() # directories for apps and project app + project_name = os.path.basename(settings.BASE_DIR) - # get color schemes for all apps - for app_name, app_directories in asset_directories.items(): - app_directories['color_scheme'] = get_color_scheme(app_name, app_directories['asset_path']) + return project_name - # separate apps and prdject directories for different treatment - app_asset_directories, project_asset_directories = separate_asset_directories(asset_directories) - # include_paths = (os.path.join(get_hshassets_directories()['asset_path'], 'sass')) +def get_default_scss_path(): + """ + Returns path to default.scss file in hshassets. + :return: path as string + """ + return os.path.join(get_sass_path('hshassets'), 'default.scss') - # hshassets_directories = get_hshassets_directories() - # build_static('hshassets', hshassets_directories) - # if hshassets_directories['color_scheme']: - # copy_favicon(hshassets_directories) +def build_app_css_from_scss(app_name): - for project_name, project_directories in project_asset_directories.items(): - build_static(project_name, project_directories) - # get favicon according to the given information in _intitial.scss - if project_directories['color_scheme']: - copy_favicon(project_directories['static_path'], project_directories['color_scheme']) - else: - hshassets_directories = get_hshassets_directories() - # copy favicon according to default color scheme of hshassets - copy_favicon(project_directories['static_path'], hshassets_directories['color_scheme']) - build_javascript(project_name, project_directories, verbose=verbose) - build_css_for_project(project_name, project_directories) + init_path = get_init_scss_path(app_name) + build_css_from_scss(app_name) + if os.path.isfile(init_path): + color_scheme = get_color_scheme(init_path) + else: + color_scheme = None - for app_name, app_directories in app_asset_directories.items(): - build_static(app_name, app_directories) - if app_directories['color_scheme']: - copy_favicon(app_directories['static_path'], app_directories['color_scheme']) - build_javascript(app_name, app_directories, verbose=verbose) - build_css_for_app(app_name, app_directories) + if not color_scheme and app_name == get_projectname(): + # get default color scheme from HSHASSETS + color_scheme = get_color_scheme(get_default_scss_path()) or 'service' + copy_favicon(app_name, color_scheme) -def build_specific(app_name, app_directories, file_path, scss_include_paths): - if file_path.endswith(('.sass', '.scss', '.css')): - build_scss(app_name, app_directories, scss_include_paths) - elif file_path.endswith('.js'): - build_javascript(app_name, app_directories) +def build_assets(): + """ + This function calls all functions needed for execution. + """ + # PROJECT + project_name = get_projectname() + apps = get_installed_apps_with_asset_directory(include_project=True) + if project_name not in apps: + apps.append(project_name) -def create_asset_directory(): + for app_name in apps: + copy_assets_dirs_to_static(app_name) + copy_and_compile_assets_css_to_static(app_name) + copy_and_compile_assets_js_to_static(app_name) + build_app_css_from_scss(app_name) - project_name = os.path.basename(settings.BASE_DIR) - project_path = get_app_path(project_name) - if not os.path.isdir(os.path.join(project_path, 'assets')): - os.mkdir(os.path.join(project_path, 'assets')) - logger.info('Create asset directory.') +def init_assets(name=None, empty_scss=False): + """ + Creates directories for assets and sass if not existing and copy hshassets default.scss file + to _init.scss if empty_scss is False. If empty_scss is True it creates an empty _init.scss file instead. + If name is None the projectname will be used, apps can be initialised if parameter name is app name. + Also an app can be initialised by using its name. + :param name: app name as sting + :param empty_scss: boolean + :return: no return + """ + if name is None: + name = get_projectname() + # create directories on path to sass directory if not existing + sass_path = get_sass_path(name) + if not os.path.isdir(sass_path): + os.makedirs(sass_path) + + if empty_scss: + # create empty scss file + init_file = open(os.path.join(sass_path, '_init.scss'), 'w') + init_file.close() else: - logger.info("Asset directory already exists.") + # copy default scss file from hshassets + shutil.copyfile(get_default_scss_path(), os.path.join(get_sass_path(name), '_init.scss')) -def create_init_scss(app_name): - pass +def build_specific(app_name, file_path): + if file_path.endswith('.scss'): + build_css_from_scss(app_name) + elif file_path.endswith('.css'): + copy_and_compile_assets_css_to_static(app_name) + elif file_path.endswith('.js'): + copy_and_compile_assets_js_to_static(app_name) -def get_app_path(app_name): +def get_appname_from_assets_path(path): + """ + Returns the app name extract from asset path. The last-but-one element + of the path is equal to the app name. + :param path: path to asset directory as string + :return: name of app as string + """ + app_path = path.rsplit('/assets', maxsplit=2)[0] + app_name = app_path.rsplit('/', maxsplit=1)[-1] + if app_name in get_installed_apps_with_asset_directory(include_project=True): + return app_name + else: + return None - app_module = import_module(app_name) - app_path = app_module.__path__[0] - return app_path +def get_assets_subdirectory(path): + """ + :param path: path as string + :return: subdirectory as string + """ + return (path.rsplit('/assets/', maxsplit=1)[-1]).split('/', maxsplit=1)[0] -def get_projectname(): - # get name of the django project - name = os.path.basename(settings.BASE_DIR) - - return name - - -# -# def get_asset_directories(): -# ''' get every app path that contains an "asset" folder on its root ''' -# -# asset_apps = {} -# -# for app_name in settings.INSTALLED_APPS: -# app_module = import_module(app_name) -# app_path = app_module.__path__[0] -# -# if os.path.isdir(app_path + '/assets'): -# asset_apps.update({ -# app_name: { -# 'app_path': app_path, -# 'static_path': app_path + '/static/' + app_name, -# 'asset_path': app_path + '/assets' -# } -# }) -# -# return asset_apps -# -# -# def get_scss_include_paths(asset_directories): -# """ Get additional include_paths for the scss parser - to be able to load e.g. bulma without getting insane """ -# include_paths = [] -# for app_name, rel_paths in SCSS_INCLUDE_PATHS: -# if app_name in asset_directories.keys(): -# app_meta = asset_directories[app_name] -# for rel_path in rel_paths: -# lib_path = os.path.join(app_meta['asset_path'], rel_path) -# if os.path.exists(lib_path): -# include_paths.append(lib_path) -# else: -# logger.warning('You specified an additional scss path which can not be found: {}'.format(lib_path)) -# else: -# logger.warning( -# 'You specified the app {} in "SCSS_INCLUDE_PATHS" that can not be found. Is it in INSTALLED_APPS?' -# .format(app_name) -# ) -# return include_paths -# -# -# def insert_corporate_design_import(filepath): -# content = '' -# tmp_filename = '/sass/_cd_init.tmp.scss' -# -# with open(filepath + '/sass/_init.scss', 'r') as scss_file: -# content = scss_file.read() -# -# content = '@import "cd/{}.scss";\n\n'.format(COLOR_SCHEME) + content -# -# with open(filepath + tmp_filename, 'w') as tmp_scss_file: -# tmp_scss_file.write(content) -# -# return filepath + tmp_filename -# -# -# def remove_corporate_design_import(filepath): -# os.remove(filepath) -# -# -# def build_scss(app_name, app_directories, include_paths, verbose=True): -# if verbose: -# print('Building \033[36msass\033[0m for app "{}" ...'.format(app_name)) -# -# start = time.time() -# -# init_file_path = app_directories['asset_path'] + '/sass/_init.scss' -# -# if not os.path.isdir(os.path.dirname(init_file_path)): -# return -# elif not os.path.isfile(init_file_path): -# print('Init scss file not found! Searched for "{}"'.format(init_file_path)) -# return -# -# with open(init_file_path, 'r') as init_scss_file: -# content = init_scss_file.read() -# -# if app_name == 'hshassets': -# style_file = app_directories['asset_path'] + '/sass/cd/{}.scss'.format(COLOR_SCHEME) -# -# if os.path.isfile(style_file): -# with open(style_file, 'r') as cdfile: -# content = cdfile.read() + '\n\n' + content -# -# # add this apps sass folder to the include paths -# include_paths = [os.path.join(app_directories['asset_path'], 'sass')] + include_paths -# -# expanded_output = sass.compile(string=content, include_paths=include_paths, output_style='expanded') -# compressed_output = sass.compile(string=content, include_paths=include_paths, output_style='compressed') -# -# with open(app_directories['static_path'] + '/styles.css', 'w') as outfile: -# outfile.write(expanded_output) -# with open(app_directories['static_path'] + '/styles.min.css', 'w') as outfile: -# outfile.write(compressed_output) -# -# if verbose: -# print('Finished after {:.3f} seconds\n'.format(time.time() - start)) -# -# -# def build_javascript(app_name, app_directories, verbose=True): -# if verbose: -# print('Building \033[33mjavascript\033[0m for app "{}" ...'.format(app_name)) -# -# start = time.time() -# -# js_files = [] -# for root, dirs, files in os.walk(app_directories['asset_path'] + '/js'): -# for f in files: -# if f.endswith('.js'): -# js_files.append(root + '/' + f) -# -# for js_file in js_files: -# with open(js_file, 'r') as infile: -# content = infile.read() -# -# dest_js_path = app_directories['static_path'] + '/js/' -# dest_file_name = os.path.basename(js_file) -# dest_file_name_stripped, dest_file_extenstion = os.path.splitext(dest_file_name) -# -# if not os.path.isdir(dest_js_path): -# os.makedirs(dest_js_path) -# -# with open(dest_js_path + dest_file_name, 'w') as outfile: -# outfile.write(content + '\n') -# -# with open(dest_js_path + dest_file_name_stripped + '.min' + dest_file_extenstion, 'w') as outfile: -# outfile.write(jsmin.jsmin(content)) -# -# if verbose: -# print('Finished after {:.3f} seconds\n'.format(time.time() - start)) -# -# -# def copy_images(app_name, app_directories, verbose=True): -# if not os.path.isdir(app_directories['asset_path'] + '/img'): -# return -# -# if app_name == 'hshassets': -# shutil.copy2( -# app_directories['asset_path'] + '/img/hsh_brand/favicons/{}/favicon.ico'.format(COLOR_SCHEME), -# app_directories['static_path'] + '/../favicon.ico' # favicon for every app -# ) -# -# start = time.time() -# if verbose: -# print('Copying images ...') -# -# if os.path.isdir(app_directories['static_path'] + '/img'): -# shutil.rmtree(app_directories['static_path'] + '/img') -# -# shutil.copytree( -# app_directories['asset_path'] + '/img', -# app_directories['static_path'] + '/img' -# ) -# -# if verbose: -# print('Finished after {:.3f} seconds\n'.format(time.time() - start)) -# -# -# def copy_fonts(app_name, app_directories, verbose=True): -# if not os.path.isdir(app_directories['asset_path'] + '/fonts'): -# return -# -# start = time.time() -# if verbose: -# print('Copying fonts ...') -# -# if os.path.isdir(app_directories['static_path'] + '/fonts'): -# shutil.rmtree(app_directories['static_path'] + '/fonts') -# -# shutil.copytree( -# app_directories['asset_path'] + '/fonts', -# app_directories['static_path'] + '/fonts' -# ) -# -# if verbose: -# print('Finished after {:.3f} seconds\n'.format(time.time() - start)) -# -# -# def discover_app(file_path): -# app_directories = {} -# -# for app_name, current_app_directories in get_asset_directories().items(): -# if file_path.startswith(current_app_directories['app_path']): -# app_directories = current_app_directories -# break -# -# return app_name, app_directories -# -# -# def do_everything(verbose=False): -# asset_directories = get_asset_directories() -# include_paths = get_scss_include_paths(asset_directories) -# for app_name, app_directories in asset_directories.items(): -# -# if os.path.isdir(app_directories['static_path']): -# shutil.rmtree(app_directories['static_path']) -# -# os.makedirs(app_directories['static_path']) -# -# build_scss(app_name, app_directories, include_paths, verbose=verbose) -# build_javascript(app_name, app_directories, verbose=verbose) -# copy_images(app_name, app_directories, verbose=verbose) -# copy_fonts(app_name, app_directories, verbose=verbose) -# -# -# def build_specific(app_name, app_directories, file_path, scss_include_paths): -# if file_path.endswith(('.sass', '.scss', '.css')): -# build_scss(app_name, app_directories, scss_include_paths) -# elif file_path.endswith(('.js')): -# build_javascript(app_name, app_directories) +def get_file_extension(path): + """ + Returns the extension of path, for example "/home/..../base.js" returns ".js". If no extension found + empty string will be returned + :param path: path as string + :return: extension as string or empty string + """ + return os.path.splitext(path)[-1]