from fabric.api import env, local, task from fabric.context_managers import lcd import fabric.operations as fabric_ops from fabric.contrib.files import exists import os import sys import pathlib ERRCODE_DJANGO_COVERAGE_NO_APPLICATION_PARAMETER_SET = -3 dir_parent = pathlib.Path(os.path.abspath(__file__)).parents[2] sys.path.append(str(dir_parent)) try: import customfabric.modules.pip as pip import customfabric.modules.utils as modules_utils from customfabric.modules.utils import virtualenv from customfabric.modules.utils import loggify, print_run, booleanize from customfabric.modules.utils import generate_template_build_path from customfabric.modules.utils import generate_template_files_path from customfabric.modules.utils import handle_flag_message from customfabric.modules.utils import prompt_continue except ImportError: raise # from modules.utils import virtualenv # from .utils import loggify, print_run, booleanize # from .utils import generate_template_build_path # from .utils import generate_template_files_path # from .utils import handle_flag_message # from .utils import prompt_continue # # import os def generate_secret_key(): """ helper to generate django secret key """ import random import string SECRET_KEY = ''.join([random.SystemRandom().choice( "{}{}{}".format( string.ascii_letters, string.digits, '!#$%()*+,-./:;<=>?@[\\]_{}~')) for i in range(50)]) return SECRET_KEY @task def admin(args="help"): """ Executes a Django admin command within a specified virtual environment and Django project directory. This method uses Fabric's context managers to change the current directory to the Django project root and then executes the 'django-admin' command with the given arguments. The Django project settings and python path are set according to the configuration defined in 'env.config'. It's important to note that this method is designed to be run within a Fabric task context. :param args: The arguments to pass to the 'django-admin' command. Defaults to "help", which displays the help message for 'django-admin'. :type args: str :raises ImportError: If required packages like 'fabric' or 'django' are not installed. :raises OSError: If there is an issue with changing directories or executing the command. Note: The method assumes that 'env.config' is properly set with necessary paths and settings. It's crucial to ensure that 'shell' is set to '/bin/bash' for the command execution environment. """ configuration = env.config from fabric.context_managers import cd with virtualenv(): with cd(configuration.paths.django.root): fabric_ops.run( "django-admin {args} --pythonpath='{djangoroot}' " "--settings={djangosettings}".format( djangoroot=configuration.paths.django.root, args=args, djangosettings=configuration.imports.settings, ), # MAKE SURE THIS IS ALWAYS HERE! shell='/bin/bash' ) @task def collectstatic(): """ Collects static media files for a Django project. This method ensures that the static and dynamic media directories, as defined in `configuration.paths.server.media`, exist before executing the `collectstatic` command via Django's manage.py. It's a crucial step in deploying or running a Django project, ensuring that all static assets are gathered in the locations specified in the Django settings. Note: - This method assumes the existence of `env.config` with the correct configuration paths. - The `--noinput` flag is used with `manage("collectstatic --noinput")` to run the command without interactive prompts, suitable for automated deployment scripts. """ configuration = env.config exists(configuration.paths.server.media.static) exists(configuration.paths.server.media.dynamic) manage("collectstatic --noinput") @task def commandline(args="", workingdir=None): """ prints out what you need to run on the command line to invoke manage.py :parameter args: the arguments you would normally say the django.manage. defaults to empty string :type args: str :parameter workingdir: the working directory of the manage.py script. defaults to None, which means it uses the working directory that are given in the project settings :type workingdir: str """ configuration = env.config if workingdir: commandline = "{djangoroot}/manage.py {args} " \ "--pythonpath='{djangoroot}' " \ "--settings={djangosettings}".format( djangoroot=configuration.paths.django.root, args=args, djangosettings=configuration.imports.settings,) else: commandline = "{djangoroot}/manage.py {args} --pythonpath='"\ "{djangoroot}' --settings={djangosettings}".format( djangoroot=configuration.paths.django.root, args=args, djangosettings=configuration.imports.settings,) print(commandline) @task def manage(args="help", workingdir=None, prefix="", suffix=""): """ wrapper method for the django manage.py command, takes values given and runs it using the project settings. It automatically applies the correct virtualenvwrapper for this project, sets the correct python path, and locates the correct settings file :parameter args: usually the argument that manage.py will take. defaults to 'help' :type args: str :parameter workingdir: the working directory of the manage.py script. defaults to None, which means it uses the working directory that are given in the project settings :type workingdir: str :param prefix: any bash commands we'd want to place before manage.py :type prefix: str :param suffix: any bash commands we'd want to place after manage.py :type suffix: str """ configuration = env.config # changes the working directory to the djangoroot from fabric.context_managers import cd with virtualenv(): if workingdir: with cd(workingdir): fabcommand = "{prefix} {djangoroot}/manage.py {args} " \ "--pythonpath='{djangoroot}' " \ "--settings={djangosettings} {suffix}".format( prefix=prefix, djangoroot=configuration.paths.django.root, args=args, djangosettings=configuration.imports.settings, suffix=suffix ) if env.debug: print("fabcommand: %s" % fabcommand) print("workingdir: %s" % workingdir) else: output = fabric_ops.run( fabcommand, # MAKE SURE THIS IS ALWAYS HERE! shell='/bin/bash') else: with cd(configuration.paths.django.root): cmd = "{prefix} {djangoroot}/manage.py {args} " \ " --pythonpath='{djangoroot}' " \ "--settings={djangosettings} {suffix}".format( prefix=prefix, djangoroot=configuration.paths.django.root, args=args, djangosettings=configuration.imports.settings, suffix=suffix) if env.debug: print("command: with cd(%s)" % configuration.paths.django.root) print("command: fabric_ops.run(%s)" % cmd) else: output = fabric_ops.run( cmd, # MAKE SURE THIS IS ALWAYS HERE! shell='/bin/bash' ) # fabric.run has the ability to give me back the output return output # NOTE: # there was a major problem using fabric commands of "local" or "prefix" # to work on remote machines, the problem was that for whatever cracked # up reason, fabric would assume I'm using a /bin/sh shell, and /bin/sh # CANNOT run all the commends that /bin/bash can. SO YOU MUST SPECIFY # shell='/bin/bash' in all uses of local! def _coverage_test(): """ helper method to determine if coverage config exists and if not print out an error message """ configuration = env.config if configuration.coverage is None: print("\n\nWARNING! There is no coverage file specified " "in the virtualenv settings for this branch!") print("\ncoverage file settings should resemble the following:\n") print("coverage:") print(" config: setup.cfg") print(" paths:") print(" root: share/coverage") print(" html: htmlcov") print("\nExiting.") import sys sys.exit() @task def coverage(application=None, args="test", workingdir=None, outputdir=None, coveragerc=True, report=True, html=True): """ Runs Django manage.py tests with coverage analysis on the specified application. :param application: Name of the installed application to test. If not provided, a message is displayed, and the program exits, defaults to None. :type application: str, optional :param args: Manage.py command for coverage to run, defaults to "test". :type args: str, optional :param workingdir: Working directory for manage.py command, defaults to None. :type workingdir: str, optional :param outputdir: Directory for outputting file results, defaults to None. :type outputdir: str, optional :parameter coveragerc: Flags whether or not there is a coverage settings file which determines what coverage will or wont do. coveragerc file is located in share/coverage/setup.cfg you can modify this in the virtualenv settings for this branch. defaults to True :type coveragerc: bool, optional :param report: Flags whether to generate a coverage report, defaults to True. :type report: bool, optional :param html: Flags whether to generate an HTML version of the coverage report, defaults to True. :type html: bool, optional :raises ImportError: If the 'coverage' package is not installed. :raises TypeError: If the types of the parameters provided do not match the expected types. :raises FileNotFoundError: If 'workingdir' or 'outputdir' are provided but do not refer to existing directories. :return: This function does not return a value but may exit the script if required parameters are missing. :rtype: None NOTE: the application parameter is required because currently I do not know how to run manage.py test on all installed applications. TODO: currently, django.manage:test is not working properly because it does not find the tests on all installed applications. Instead, each application must be tested seperately. For this reason, I require this method to specifically ask for the applications being used """ if not pip.check_package_installed('coverage'): raise ImportError( "The 'coverage' package is required but not installed. Install it" " via pip with 'pip install coverage'.") # Check if 'workingdir' is provided and exists if workingdir and not os.path.isdir(workingdir): raise FileNotFoundError( f"The specified working directory '{workingdir}' does not exist.") # Check if 'outputdir' is provided and exists if outputdir and not os.path.isdir(outputdir): raise FileNotFoundError( f"The specified output directory '{outputdir}' does not exist.") # # make sure that the application parameter was set if application is None: modules_utils.printerr( message="\nThe application parameter for django.coverage" " was not set! Please make sure you have set application" " to the application you wish to have coverage applied to!" "\nExiting.", errcode=ERRCODE_DJANGO_COVERAGE_NO_APPLICATION_PARAMETER_SET) configuration = env.config _coverage_test() prefix = "coverage run" suffix = "" if coveragerc: prefix += " --rcfile=%s" % configuration.coverage.config if report: suffix += " && coverage report" if html: suffix += " && coverage html" suffix += " -d %s" % configuration.coverage.paths.html if env.debug: print("args: %s" % args) print("workigindir: %s" % args) print("\nis report: %s" % report) print("is html: %s" % html) print("\nprefix: %s" % prefix) print("suffix: %s" % suffix) args = args + " %s" % application manage(args=args, workingdir=workingdir, prefix=prefix, suffix=suffix) @task def run(args=None): """ Starts the Django development server with the specified arguments. This method constructs and executes a `runserver` command using Django's manage.py. The host and port for the server are taken from `configuration.server.django`. This method is primarily used for local development purposes. :param args: Additional arguments to pass to the `runserver` command. Defaults to None. :type args: str, optional :return: The output from the `manage` command execution. :rtype: str Note: - The `configuration` object must be properly set up in `env.config` with the server host and port. """ configuration = env.config command = "runserver {host}:{port}".format( host=configuration.server.django.host, port=configuration.server.django.port) output = manage(command) return output @task def startapp(appname='help'): """ Creates a new Django app with the specified name within the project. This method serves as a wrapper for Django's `startapp` command. It takes the name of the app and creates it in the `code/apps` directory of the Django project. If 'help' or no app name is provided, it displays a help message and exits. :param appname: The name of the new Django app to create. Defaults to 'help' which triggers a help message. :type appname: str Note: - The `configuration.paths.django.apps` path in `env.config` is used as the working directory for app creation. - Exiting with `sys.exit()` on receiving 'help' as input is a part of its functionality. """ configuration = env.config msg_help = """ django.startapp takes one of two values: \thelp - this help message \tappname - the name of the app you want to start """ import sys if handle_flag_message(appname, msg_help, 'help'): sys.exit() command = "startapp {appname}".format( appname=appname) manage(command, workingdir=configuration.paths.django.apps) # with lcd(configuration.paths.django.apps): # manage(command) @task def installed_apps(): """ List the currently installed apps in the settings.py file for this project """ configuration = env.config printecho = "print('\\n')" printcommand = "print('\\n').join([ item for item" \ " in {settings}.INSTALLED_APPS])".format( settings=configuration.imports.settings) command = "python -c \"import {settings}; {printecho};" \ " {printcommand}; {printecho}\"".format( settings=configuration.imports.settings, printecho=printecho, printcommand=printcommand) with lcd(configuration.paths.django.root): local(command) @task def src(): """ locate the django source files in the site-packages directory """ command = """ python -c " import sys sys.path = sys.path[1:] import django print(django.__path__)" """ command = """ python -c "import sys; sys.path=sys.path[1:];""" \ """ import django; print(django.__path__)[0]" """ with virtualenv(): local(command) @task def create_project(): configuration = env.config logger = loggify("django", "create_project") project_path = configuration.paths.django.root project_base = configuration.paths.django.settings.base project_name = configuration.project.name import os full_project_path = os.path.join(project_path, project_base) django_cmd = \ "django-admin startproject {project_base} {project_path}".format( project_base=project_base, project_path=project_path) manage_path = "%s/manage.py" % project_path logger.debug("django_root : %s" % configuration.paths.django.root) logger.debug("project_path : %s" % project_path) logger.debug("project_base : %s" % project_base) logger.debug("project_name : %s" % project_name) # I accidentally deleted the code directory, this checks to see if the # project path exists, if not, create it. if not exists(project_path): fabric_ops.run("mkdir -p %s" % project_path) if exists(manage_path): fabric_ops.run("rm %s" % manage_path) if exists(full_project_path): # we need to make sure that # a. we back up the current settings for the project # b. that we don't override the OLD project backup settings # c. and that we give ourselves the option to NOT backup # # so if the system sees that we already have a backup of the main # folder, ie. projectname.old, then it will give us the option to # either overwrite the old backup (Y), or to stop the proecess and do # whatever needs to be done to clear things up project_path_old = "{project_path}/{project_base}.old".format( project_path=project_path, project_base=project_base) if exists(project_path_old): prompt_continue( message="found an already existing old " "version of {project_path}, do you want to ignore" " backing up the version or exit? Y/n ( Y to " "continue without backing up. N to exit )", default="n") fabric_ops.run("rm -Rf {project_path}/{project_base}".format( project_base=project_base, project_path=project_path)) else: # backup whatever is there fabric_ops.run("mv {project_path}/{project_base}" " {project_path}/{project_base}.old".format( project_base=project_base, project_path=project_path)) with virtualenv(): fabric_ops.run(django_cmd) django_path = "{project_path}/{project_base}".format( project_base=project_base, project_path=project_path) fabric_ops.run("mkdir %s/_settings" % django_path) fabric_ops.run("touch %s/_settings/__init__.py" % django_path) generate('settings', True) generate('local', True) generate('wsgi', True) def generate_scripts(template_name, make_copy=False): """ Generates Django settings files based on a specified template. This function is designed to generate different types of Django settings files (like local or main settings) based on the provided template name. It's meant to be used within another function that specifies the type of script required. :param template_name: The name of the template to use for generating the script. :type template_name: str :param make_copy: Flag to determine whether to make a copy of the generated script, defaults to False. :type make_copy: bool Note: - The function relies on `env.config` for various paths and settings. - `make_copy` parameter should be booleanized before use. - the function is meant to be called privately TODO: privatize the function """ configuration = env.config # make sure to booleanize ALL boolean values! make_copy = booleanize(make_copy) if env.debug: logger = loggify("django", "generate_scripts") project_base = configuration.paths.django.settings.base project_branch = configuration.project.branch project_path = configuration.paths.django.root project_name = configuration.project.name secret_key = generate_secret_key() files_name = getattr(configuration.templates.django, template_name).src build_name = getattr(configuration.templates.django, template_name).dst build_path = generate_template_build_path('django', template_name) files_path = generate_template_files_path('django') context = dict() context['project_name'] = project_name context['project_base'] = project_base context['project_branch'] = project_branch context['secret_key'] = secret_key context['paths_tools_fabric'] = configuration.paths.tools.fabric copy_path = "{project_path}/{project_base}".format( project_path=project_path, project_base=project_base) if template_name == 'local': copy_path = "{project_path}/{project_base}/_settings".format( project_path=project_path, project_base=project_base) build_name = "%s.py" % project_branch # generate the local generate scripts generate('local.generated', make_copy) elif template_name == 'local.generated': copy_path = "{project_path}/{project_base}/_settings".format( project_path=project_path, project_base=project_base) build_name = "%s_generated.py" % project_branch # add extra local specfic context variables # NOTE: # if you change project settings, you MUST run generate context['paths_django_templates'] = \ configuration.paths.django.templates context['server_database_backend'] = \ configuration.server.database.backend context['server_database_name'] = configuration.server.database.name context['server_database_user'] = configuration.server.database.user context['server_database_password'] = \ configuration.server.database.password context['server_database_host'] = configuration.server.database.host context['server_database_port'] = configuration.server.database.port context['paths_server_media_static'] = \ configuration.paths.server.media.static context['paths_server_media_dynamic'] = \ configuration.paths.server.media.dynamic context['paths_project_root'] = configuration.paths.project.root context['project_django_allowedhosts'] = \ configuration.project.django.allowedhosts # convenience variable naming, otherwise it's too long to deal with file_log_handler = configuration.logging.django.handlers.file_log file_debug_handler = configuration.logging.django.handlers.file_debug context['file_log_handler__name_project'] = \ file_log_handler.name.project context['file_log_handler__path_project'] = \ file_log_handler.path.project context['file_debug_handler__name_project'] = \ file_debug_handler.name.project context['file_debug_handler__path_project'] = \ file_debug_handler.path.project context['file_debug_handler__name_server'] = \ file_debug_handler.name.server context['file_debug_handler__path_server'] = \ file_debug_handler.path.server copy_full_path = "{copy_path}/{build_name}".format( copy_path=copy_path, build_name=build_name) copy_cmd = "cp {build_path} {copy_full_path}".format( build_path=build_path, copy_full_path=copy_full_path) backup_cmd = "cp {copy_full_path} " \ "{copy_full_path}.bak".format(copy_full_path=copy_full_path) from .utils import upload_template as utils_upload_template if env.debug: logger.debug("template_name : %s" % template_name) logger.debug("project_branch : %s" % project_branch) logger.debug("project_base : %s" % project_base) logger.debug("build_path : %s" % build_path) logger.debug("files_path : %s" % files_path) logger.debug("files_name : %s" % files_name) logger.debug("copy_path : %s" % copy_path) logger.debug("copy_full_path : %s" % copy_full_path) logger.debug("build_name : %s" % build_name) upload_msg = utils_upload_template( filename=files_name, destination=build_path, context=context, use_jinja=True, use_sudo=False, backup=True, template_dir=files_path, debug=True) logger.debug("upload_msg : %s" % upload_msg) logger.debug("make_copy : %s" % make_copy) logger.debug("copy_cmd : %s" % copy_cmd) logger.debug("backup_cmd : %s" % backup_cmd) else: utils_upload_template( filename=files_name, destination=build_path, context=context, use_jinja=True, use_sudo=False, backup=True, template_dir=files_path, debug=False) if make_copy: if exists(copy_full_path): fabric_ops.run(backup_cmd) fabric_ops.run(copy_cmd) print("\n\n------------------------------") print("project_base : %s" % project_base) print("project_branch : %s" % project_branch) print("project_path : %s" % project_path) print("template_name : %s" % template_name) print("build_path : %s" % build_path) print("files_path : %s" % files_path) print("files_name : %s" % files_name) print("copy_path : %s" % copy_path) print("copy_full_path : %s" % copy_full_path) print("build_name : %s" % build_name) upload_msg = utils_upload_template( filename=files_name, destination=build_path, context=context, use_jinja=True, use_sudo=False, backup=True, template_dir=files_path, debug=True) print("upload_msg : %s" % upload_msg) print("make_copy : %s" % make_copy) print("copy_cmd : %s" % copy_cmd) print("backup_cmd : %s" % backup_cmd) print("------------------------------\n\n") @task def generate(param="help", make_copy=False): """ Generates scripts for Django settings, local settings, or WSGI based on the specified parameter. This method acts as a wrapper for generating various Django-related scripts. It supports generating settings, local settings, local.generated, and WSGI scripts. A specific script type is selected through the 'param' argument. :param param: The type of script to generate. Can be 'settings', 'local', 'local.generated', 'wsgi', or 'gunicorn', defaults to "help". :type param: str :param make_copy: Determines whether to copy the generated file to its practical location, defaults to False. :type make_copy: bool Note: - 'make_copy' must be set to True for the function to perform any copying. - If 'param' is not specified, the function will attempt to generate all scripts. """ SCRIPT_LIST = ['settings', 'local', 'local.generated', 'wsgi'] PARAM_LIST = list(SCRIPT_LIST) PARAM_LIST.append('gunicorn') make_copy = booleanize(make_copy) if param: err_msg = None if param == "help": err_msg = """ fab django.generate:param="help",make_copy=False param - file to be generated can be of type: %s make_copy - determines whether or generate copies the generated file into it's practical location (otherwise doesn't do anything) """ % PARAM_LIST elif param not in PARAM_LIST: err_msg = "You asked to generate %s, that value isn't available" \ " possible script values available: %s" % (param, PARAM_LIST) if err_msg: import sys sys.exit(err_msg) print("django:generate make_copy : %s\n" % make_copy) print("django:generate script : %s" % param) if env.debug: print("django:generate script : %s" % param) print("django:generate make_copy : %s\n" % make_copy) else: pass # env.debug does not block the rest of the commands because this # function acts primarily as a wrapper for the following cmomands, in # those fucntion env.debug will be used to decide if anything should # happen or not if not param: # this is where script=None, generate all scripts for scriptkey in SCRIPT_LIST: generate_scripts(scriptkey, make_copy) generate_gunicorn(make_link=make_copy) elif param == 'gunicorn': generate_gunicorn(make_link=make_copy) else: generate_scripts(param, make_copy) def generate_gunicorn(make_link=True): """ Creates and links the Gunicorn configuration script. This function generates the Gunicorn configuration script and places it in the build folder. It optionally creates a symbolic link to the script in the scripts directory. :param make_link: Flag to determine whether to create a symbolic link to the generated script, defaults to True. :type make_link: bool Note: - The function relies on `env.config` for configuration paths and settings. - `make_link` parameter should be booleanized before use. """ configuration = env.config make_link = booleanize(make_link) if env.debug: logger = loggify("django", "generate_gunicorn") files_path = os.path.join( configuration.templates.gunicorn.path.local, 'files') build_path = os.path.join( configuration.templates.gunicorn.path.remote, 'build', configuration.templates.gunicorn.conf.dst) link_path = os.path.join( configuration.paths.server.scripts, configuration.templates.gunicorn.conf.dst ) context = dict() context['host'] = configuration.server.django.host context['port'] = configuration.server.django.port context['user'] = configuration.project.user context['group'] = configuration.project.group context['settings_module'] = configuration.imports.settings context['extended_name'] = configuration.project.extendedname context['logging_access'] = configuration.logging.gunicorn.access context['logging_error'] = configuration.logging.gunicorn.error msg_link_gunicorn = "ln -sf {gunicorn_root} {link_gunicorn}".format( gunicorn_root=build_path, link_gunicorn=link_path) print_run(msg_link_gunicorn) if env.debug: logger.debug("\n") logger.debug("--- in gunicorn ---\n") for key in context.keys(): logger.debug("%s\t: %s" % (key, context[key])) logger.debug('build_path\t: %s' % build_path) logger.debug('files_path\t: %s' % files_path) logger.debug('files config src: %s' % configuration.templates.gunicorn.conf.src) logger.debug('\n%s' % print_run(msg_link_gunicorn)) from .utils import upload_template as utils_upload_template upload_msg = utils_upload_template( filename=configuration.templates.gunicorn.conf.src, destination=build_path, context=context, use_jinja=True, use_sudo=False, backup=True, template_dir=files_path, debug=True) logger.debug("upload_msg : %s" % upload_msg) else: from fabric.contrib.files import upload_template upload_template( filename=configuration.templates.gunicorn.conf.src, destination=build_path, context=context, use_jinja=True, backup=True, template_dir=files_path) if make_link: print("\nlinking the generating gunicorn file in conf to " "the server diretory\n") fabric_ops.run(msg_link_gunicorn) else: print("\nNOTE: not linking the generated gunicorn file" "to the server directory\n") @task def edit(param='help'): """ Facilitates editing of Django and Gunicorn configuration files. This method provides a convenient way to open various configuration files related to Django and Gunicorn for editing, using the 'mvim' editor. The specific file to be edited is determined by the 'param' argument. :param param: Specifies the configuration file to edit. Options include paths to gunicorn.conf, Django settings files, log files, etc., defaults to 'help'. :type param: str Note: - The configuration paths are taken from `env.config`. - If 'param' is 'help', the function displays a list of editable locations and their descriptions. """ from .maintenance import edit as maintenance_edit configuration = env.config link_path = os.path.join( configuration.paths.server.scripts, configuration.templates.gunicorn.conf.dst ) build_path = os.path.join( configuration.templates.gunicorn.path.remote, 'build', configuration.templates.gunicorn.conf.dst) project_branch = configuration.project.branch project_path = configuration.paths.django.root project_settings_dir = configuration.paths.django.settings.base django_path = "{project_path}/{project_settings_dir}".format( project_path=project_path, project_settings_dir=project_settings_dir ) settings_path = "{django_path}/settings.py".format( django_path=django_path) settings_local_path = "{django_path}/_settings/{project_branch}.py".format( django_path=django_path, project_branch=project_branch) settings_generated_path = "{django_path}/_settings/" \ "{project_branch}_generated.py".format( django_path=django_path, project_branch=project_branch) file_debug_handler = configuration.logging.django.handlers.file_debug # locations = ['coverage', 'gunicorn', 'gunicorn_link', 'gunicorn_build', # 'settings', 'local'] locations = { 'gunicorn': { 'path': link_path, 'desc': 'gunicorn.conf file', }, 'gunicorn_build': { 'path': build_path, 'desc': "gunicorn.conf file in scripts/conf/gunicorn/build" }, 'gunicorn.log.error': { 'path': configuration.logging.gunicorn.error, 'desc': "gunicorn error log file", }, 'gunicorn.log.access': { 'path': configuration.logging.gunicorn.access, 'desc': "gunicorn error log file", }, 'settings': { 'path': settings_path, 'desc': 'main settings file for django project', }, 'local': { 'path': settings_local_path, 'desc': 'local settings file for django project', }, 'generated': { 'path': settings_generated_path, 'desc': 'generated settings file for django project', }, 'server_log': { 'path': file_debug_handler.path.server, 'desc': 'django server log - handler name: %s' % file_debug_handler.name.server, }, 'project_log': { 'path': file_debug_handler.path.project, 'desc': 'django project log - handler name: %s' % file_debug_handler.name.project, }, } # # set .coveragerc path and filename # and add a coverage entry to locations if param == 'coverage': _coverage_test() locations['coverage'] = { 'path': configuration.coverage.config, 'desc': 'coveragerc config file', } if param in locations.keys(): remote_path = locations[param]['path'] maintenance_edit(remote_path=remote_path) else: # if param == 'help': print(""" "fab django.edit" automates editing files important to django whether locally or remotely to use this you must pass one of the editable locations in as a parameter currently editable locations are: """) for k_loc in locations.keys(): print("\t{0: <20} - {1}".format(k_loc, locations[k_loc]['desc'])) return @task def clearmigrations(appname="help"): if appname == "help": print(""" "fab django.clearmigration:{appname}" clears out all migrations for the specified appname if no appname is given, or if you pass the "help" appnameter in place of an appname then this help message will appear. Note: if your appname is actually "help" you might want to go into this function and change it up a bit! """) return configuration = env.config import os app_path = os.path.join( configuration.paths.django.apps, appname) path_migrations = os.path.join( app_path, 'migrations') path_migrations_old = os.path.join( app_path, 'migrations.old') import fabric if fabric.contrib.files.exists(path_migrations): # get rid of any old migration backups if fabric.contrib.files.exists(path_migrations_old): cmd_rm_migration_old = "rm -Rf %s" % path_migrations_old fabric.operations.run(cmd_rm_migration_old) # move the original migrations folder to migrations.old cmd_migration = "mv %s %s" % ( path_migrations, path_migrations_old) fabric.operations.run(cmd_migration) manage("makemigrations --empty %s" % appname) manage("makemigrations") manage("migrate --fake %s 0002" % appname) @task def makemigrations_empty(param="help"): if param == "help": print("print this help message") return manage("makemigrations --empty %s" % param) @task def fixtures(appname=None, backup=False): """ Generates a JSON fixture for the specified Django app and optionally backs it up. This method dumps the data of a specified Django app into a JSON fixture. If the `backup` parameter is set to True, the fixture is stored in the server's backup directory. If no app name is specified, it generates a fixture for all apps. :param appname: Name of the Django app for which to generate a fixture. If None, fixtures for all apps are generated, defaults to None. :type appname: str, optional :param backup: Flag to determine whether to store the fixture in the server's backup directory, defaults to False. :type backup: bool Note: - The fixture file is named as '.json' or 'all.json' if no appname is specified. - The paths for fixtures and backups are retrieved from `configuration.paths`. """ configuration = env.config # booleanize the backup parameter backup = booleanize(backup) print("debug - appname: %s" % appname) # from fabric.api import * path_data = configuration.paths.django.fixtures path_backup = configuration.paths.server.backups.fixtures print("debug - path_backup: %s" % path_backup) if appname is not None: path_data = os.path.join(path_data, appname) path_backup = os.path.join(path_backup, appname) fixture_name = "%s.json" % appname else: fixture_name = "all.json" if backup: fix_split = os.path.splitext(fixture_name) fixture_name = fix_split[0] + "_backup" + fix_split[1] path_fixture = os.path.join(path_backup, fixture_name) else: path_fixture = os.path.join(path_data, fixture_name) print("debug - path_fixture: %s" % path_fixture) from .utils import ensure_dir ensure_dir(path_data) ensure_dir(path_backup) from fabric.context_managers import hide with hide('running', 'stdout', 'stderr'): # get rid of things like "localhost: out" output = manage('dumpdata %s --indent 2' % appname) # Now go through the output and ignore all the extra # information there. import re # first get rid of everything until the last time # "[localhost] out:" is printed if configuration.project.host == 'localhost': p = re.compile(r"\[localhost\] out:") for match in p.finditer(output): pass try: pos = match.end() output = output[pos:] except Exception: pass # now find the first occurence of [ on a newline, e.g. # [ # { # "model.auth": 40, # etc. # p = re.compile(r"\n\[") # m = p.search(output) # pos = m.end() - 1 # output = output[pos:] from io import StringIO fixture_iostring = StringIO(output) fabric_ops.put(fixture_iostring, path_fixture) @task def loaddata(param=None): """ Loads fixture data into the specified Django app. This method imports data from a JSON fixture file into a specified Django app. The fixture file should be located in the 'extras/data' directory. If no app name is provided, the method exits without performing any action. :param param: Name of the Django app to which the fixture data will be loaded, defaults to None. :type param: str, optional Note: - The fixture file is expected to be named as '.json'. - The path for fixtures is retrieved from `configuration.paths.django.fixtures`. """ configuration = env.config if param is None: print("you must specify an appname to load the fixture to") return else: appname = param path_data = configuration.paths.django.fixtures if appname is not None: path_data = os.path.join(path_data, appname) path_fixture = os.path.join(path_data, "%s.json" % appname) else: path_fixture = os.path.join(path_data, "all.json") manage("loaddata %s" % path_fixture) @task def check_app(appname): """ check if is installed in settings @appname: the name of the app being checked for returns True/False """ from fabric.context_managers import hide with hide('running', 'stderr'): output = manage('listapps') import re p = re.compile(appname) match = p.search(output) if match: return True else: return False @task def test(args=None): """ the purpose of this method is just to use as a placeholder for any kind of testing of methods """ print("debug - testing checkapp(sorl.thumbnail): %s" % check_app("sorl.thumbnail"))