Ejemplo n.º 1
0
def get_project_status(project):
    # get method logger
    log = get_wrapped_logger(__name__ + '.' + inspect.stack()[0][3])

    try:
        # create an instance of Envoy client
        client = get_envoy_client()

        log.info("Getting project status for %s..." % project.name)
        resp = client.get_project_status(project.name)

        # convert to string
        project.status = json.dumps(resp)

        log.info("Project status response: %s" % project.status)

    except InvalidInputError as e:
        log.warning("Invalid Input Error: " + e.user_message)
    except AuthenticationError as e:
        log.error("Authentication Error: " + e.user_message)
    except InsufficientCreditsError as e:
        log.error("Insufficient Credits Error: " + e.user_message)
    except InvalidRequestError as e:
        log.error("Invalid Request Error: " + e.user_message)
    except APIError as e:
        log.error("API Error: " + str(e.user_message))
Ejemplo n.º 2
0
def delete_temp_files_for_project(project_name):
    """ Attempts to delete the temporary project files for associated with a given project name

    :param project_name: The name of the project
    :type project_name: str
    :rtype: void
    """

    # get method logger
    log = get_wrapped_logger(__name__ + '.' + inspect.stack()[0][3])
    log.info("Starting delete_temp_files_for_project operation")

    # get the temporary directory manager
    temporary_directory_manager = TempDirectoryManager.get_temp_directory_manager(
    )

    # find the association for this project
    association = temporary_directory_manager.get_association_with_project_name(
        project_name)

    # if the association exists
    if association:
        log.info("Found association")

        # delete the temporary files for the project
        association.delete_temporary_directory()
    else:
        log.info("Project association does not exist")
Ejemplo n.º 3
0
def _add_project_to_list(project_name, props):

    log = get_wrapped_logger(__name__ + '.' + inspect.stack()[0][3])
    log.info("Starting add_project_to_list operation...")

    # if props already contains a project with the given name
    existing_projects_with_name = list(
        filter(lambda x: x.name == project_name, props.projects))

    if existing_projects_with_name:
        # don't add it to the list and return the existing project
        log.info(
            "A project with the name %s already exists, returning existing project..."
            % project_name)
        return existing_projects_with_name[0]

    # add a project to the list
    log.info("Adding project to list...")

    project = props.projects.add()

    project.id = utils.get_unique_id(props.projects)
    project.name = project_name

    # select the new project
    log.info("Selecting new project...")
    props.selected_project = len(props.projects) - 1

    # force region to redraw otherwise the list wont update until next event (mouse over, etc)
    force_redraw_addon()

    return project
Ejemplo n.º 4
0
def get_envoy_client():
    """ Gets the existing EnvoyClient instance unless the authentication credentials have changed

    :return: The existing envoy client or a new one if the credentials have changed
    :rtype: gridmarkets.envoy_client.EnvoyClient

    :raises: InvalidInputError, AuthenticationError
    """
    global _envoy_client

    log = get_wrapped_logger(__name__ + '.' + inspect.stack()[0][3])
    log.info("Getting Envoy client....")

    if _envoy_client:

        log.info("Found existing Envoy client, comparing credentials...")

        # get the add-on preferences
        addon_prefs = bpy.context.preferences.addons[
            constants.ADDON_PACKAGE_NAME].preferences

        # check neither the email or access key have changed
        if addon_prefs.auth_email == _envoy_client.email and addon_prefs.auth_accessKey == _envoy_client.access_key:
            log.info("Returning existing Envoy client...")
            return _envoy_client

    # update the client
    _envoy_client = get_new_envoy_client()

    return _envoy_client
Ejemplo n.º 5
0
def validate_credentials(email=None, access_key=None):
    """Blender specific authentication validator, uses the gridmarkets validate_auth() function internally but adds
        additional checks and blender specific error messages.

    :param email: The user's GridMarkets account email, if None or not given then it will be read from the preferences
    :type email: str
    :param access_key: The user's GridMarkets access key, if None or not given then it will be read from the preferences
    :type access_key: str
    :return: true if credentials are valid
    :rtype: bool

    :raises: InvalidInputError, AuthenticationError
    """

    log = get_wrapped_logger(__name__ + '.' + inspect.stack()[0][3])
    log.info("Validating credentials...")

    # only read from preferences if a parameter is None
    if email is None or access_key is None:

        # get the add-on preferences
        addon_prefs = bpy.context.preferences.addons[
            constants.ADDON_PACKAGE_NAME].preferences

        if email is None:
            email = addon_prefs.auth_email

        if access_key is None:
            access_key = addon_prefs.auth_accessKey

    # check an email address has been entered
    has_email = isinstance(email, str) and len(email) > 0

    # check a auth key has been entered
    has_key = isinstance(access_key, str) and len(access_key) > 0

    # validate
    if not has_email and not has_key:
        message = "No " + constants.COMPANY_NAME + " email address or access key provided." + \
                  constants.AUTHENTICATION_HELP_MESSAGE
        raise InvalidInputError(message=message)
    elif not has_email:
        message = "No " + constants.COMPANY_NAME + " email address provided." + constants.AUTHENTICATION_HELP_MESSAGE
        raise InvalidInputError(message=message)
    elif not has_key:
        message = "No " + constants.COMPANY_NAME + " access key provided." + constants.AUTHENTICATION_HELP_MESSAGE
        raise InvalidInputError(message=message)

    # if initial checks pass try the client api validate_auth function
    # create an instance of Envoy client
    client = EnvoyClient(email=email, access_key=access_key)
    client.validate_auth()

    return True
Ejemplo n.º 6
0
def pack_blend_file(blend_file_path, target_dir_path, progress_cb=None):
    """ Packs a Blender .blend file to a target folder using blender_asset_tracer

    :param blend_file_path: The .blend file to pack
    :type blend_file_path: str
    :param target_dir_path: The path to the directory which will contain the packed files
    :type target_dir_path: str
    :param progress_cb: The progress reporting callback instance
    :type progress_cb: blender_asset_tracer.pack.progress.Callback
    """

    log = get_wrapped_logger(__name__ + '.' + inspect.stack()[0][3])
    log.info("Starting pack operation....")

    blend_file_path = pathlib.Path(blend_file_path)
    project_root_path = blend_file_path.parent
    target_dir_path = pathlib.Path(target_dir_path)

    log.info("blend_file_path: %s" % str(blend_file_path))
    log.info("project_root_path: %s" % str(project_root_path))
    log.info("target_dir_path: %s" % str(target_dir_path))

    # create packer
    with pack.Packer(
            blend_file_path,
            project_root_path,
            target_dir_path,
            noop=False,  # no-op mode, only shows what will be done
            compress=False,
            relative_only=False) as packer:

        log.info("Created packer")

        if progress_cb:
            log.info("Setting packer progress callback...")
            packer._progress_cb = progress_cb

        # plan the packing operation (must be called before execute)
        log.info("Plan packing operation...")
        packer.strategise()

        # attempt to pack the project
        try:
            log.info("Plan packing operation...")
            packer.execute()
        except pack.transfer.FileTransferError as ex:
            log.warning(
                str(len(ex.files_remaining)) +
                " files couldn't be copied, starting with " +
                str(ex.files_remaining[0]))
            raise SystemExit(1)
        finally:
            log.info("Exiting packing operation...")
    def delete_temporary_directory(self):
        # then log the problem and fail
        log = get_wrapped_logger()
        log.info("Starting delete_temporary_directory operation")

        if self._temp_directory:
            try:
                log.info("Attempting to delete temporary directory")
                self._temp_directory.cleanup()
                self._temp_directory = None

            # If the file cannot be cleaned up because it is being used by another process (it shouldn't be!)
            except OSError as e:
                log.exception("OSError when attempting to clean up temporary files.")
                raise e
Ejemplo n.º 8
0
def get_job(context, render_file, enable_logging=True):
    scene = context.scene
    props = scene.props

    # get method logger
    log = get_wrapped_logger(__name__ + '.' + inspect.stack()[0][3])

    if enable_logging:
        log.info("Getting job settings...")

    # if the job options are set to 'use blender render settings' then set the job options accordingly
    if props.job_options == constants.JOB_OPTIONS_BLENDERS_SETTINGS_VALUE:
        if enable_logging:
            log.info("Using default blender render settings")
        job = get_default_blender_job(context, render_file)

    # otherwise set the job options based on a user defined job
    elif props.job_options.isnumeric():
        if enable_logging:
            log.info("Using custom job")

        selected_job_option = int(props.job_options)
        selected_job = props.jobs[selected_job_option -
                                  constants.JOB_OPTIONS_STATIC_COUNT]

        job = Job(
            selected_job.name,
            constants.JOB_PRODUCT_TYPE,
            constants.JOB_PRODUCT_VERSION,
            constants.JOB_OPERATION,
            render_file,
            frames=get_job_frame_ranges(context, job=selected_job),
            output_prefix=get_job_output_prefix(context, job=selected_job),
            output_format=get_job_output_format(context, job=selected_job),
            engine=get_job_render_engine(context, job=selected_job),
            gpu=selected_job.render_device == constants.RENDER_DEVICE_GPU)
    else:
        raise InvalidInputError(message="Invalid job option")

    return job
Ejemplo n.º 9
0
def get_new_envoy_client():
    """ Gets a new EnvoyClient instance using the authentication preferences for credentials

    :return: The envoy client
    :rtype: gridmarkets.envoy_client.EnvoyClient

    :raises: InvalidInputError, AuthenticationError
    """

    log = get_wrapped_logger(__name__ + '.' + inspect.stack()[0][3])
    log.info("Getting new Envoy client....")

    # get the add-on preferences
    addon_prefs = bpy.context.preferences.addons[
        constants.ADDON_PACKAGE_NAME].preferences

    # validate the authentication credentials
    validate_credentials(email=addon_prefs.auth_email,
                         access_key=addon_prefs.auth_accessKey)

    # return the new client
    return EnvoyClient(email=addon_prefs.auth_email,
                       access_key=addon_prefs.auth_accessKey)
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####

import bpy
from gridmarkets_blender_addon import constants
from gridmarkets_blender_addon.temp_directory_manager import TempDirectoryManager

from gridmarkets_blender_addon.blender_logging_wrapper import get_wrapped_logger
log = get_wrapped_logger(__name__)


class GRIDMARKETS_OT_copy_temporary_file_location(bpy.types.Operator):
    bl_idname = constants.OPERATOR_COPY_TEMPORARY_FILE_LOCATION_ID_NAME
    bl_label = constants.OPERATOR_COPY_TEMPORARY_FILE_LOCATION_LABEL
    bl_options = {'INTERNAL'}

    def execute(self, context):
        props = context.scene.props
        project_count = len(props.projects)
        selected_project_index = props.selected_project

        # get the selected project
        if project_count > 0 and selected_project_index >= 0 and selected_project_index < project_count:
            project = props.projects[selected_project_index]
Ejemplo n.º 11
0
def submit_job(context,
               temp_dir_manager,
               new_project_name=None,
               operator=None,
               min_progress=0,
               max_progress=100,
               progress_attribute_name="submitting_project",
               progress_percent_attribute_name="submitting_project_progress",
               progress_status_attribute_name="submitting_project_status"):
    """Submits a job to a existing or new project.

    :param context: Depends on the area of Blender which is currently being accessed
    :type context: bpy.context
    :param temp_dir_manager: The temporary directory manager used to store packed projects
    :type temp_dir_manager: TempDirectoryManager
    :param new_project_name: The name of the project to use if uploading a new project
    :type new_project_name: str
    :param operator: An optional instance of an operator so that invalid input can be reported correctly
    :type operator: bpy.types.Operator
    :param min_progress: The min value to set the progress too
    :type min_progress: float
    :param max_progress: The max value to set the progress too
    :type max_progress: float
    :param progress_attribute_name: The name of the property flag which indicated that the operation is running
    :type progress_attribute_name: str
    :param progress_percent_attribute_name: The name of the property which represents the operations progress
    :type progress_percent_attribute_name: str
    :param progress_status_attribute_name: The name of the property which represents the operations status
    :type progress_status_attribute_name: str
    :rtype: void
    :raises: InvalidInputError, AuthenticationError, InsufficientCreditsError, InvalidRequestError, APIError
    """

    scene = context.scene
    props = scene.props

    # get method logger
    log = get_wrapped_logger(__name__ + '.' + inspect.stack()[0][3])

    # define a helper function for setting the progress
    def _set_progress(progress=None, status=None):
        if progress is not None:
            # re-scale progress so that it is between the min and max values
            scaled_progress = min_progress + (
                (max_progress - min_progress) / 100) * progress
            setattr(context.scene.props, progress_percent_attribute_name,
                    scaled_progress)

        if status is not None:
            setattr(context.scene.props, progress_status_attribute_name,
                    status)

    # set the submitting flag to true
    setattr(context.scene.props, progress_attribute_name, True)

    # set the progress to 0 and set a status message
    _set_progress(progress=0, status="Starting job submission")

    # log the start of the operation
    log.info("Starting submit operation...")

    try:
        # if the user has selected to upload the current scene as a new project then upload current scene as new project
        if props.project_options == constants.PROJECT_OPTIONS_NEW_PROJECT_VALUE:
            log.info("Uploading project for job submit...")
            _set_progress(progress=10, status="Uploading project")

            if new_project_name is None:
                raise InvalidInputError("Project name can not be None type")

            project_name = new_project_name

            # upload the project
            upload_project(
                context,
                project_name,
                temp_dir_manager,
                operator=operator,
                min_progress=10,
                max_progress=40,
                progress_attribute_name=progress_attribute_name,
                progress_percent_attribute_name=progress_percent_attribute_name,
                progress_status_attribute_name=progress_status_attribute_name,
                # skip upload here otherwise when we submit this job the project may not have finished
                # uploading. Instead use upload project to pack the files and upload when the job is
                # submitted
                skip_upload=True)

            skip_upload = False

        elif props.project_options.isnumeric():
            log.info("Project already uploaded, submitting job...")
            # get the name of the selected project
            selected_project_option = int(props.project_options)
            selected_project = props.projects[
                selected_project_option -
                constants.PROJECT_OPTIONS_STATIC_COUNT]
            project_name = selected_project.name
            skip_upload = True
        else:
            raise InvalidInputError(message="Invalid project option")

        association = temp_dir_manager.get_association_with_project_name(
            project_name)
        project = association.get_project()
        render_file = association.get_relative_render_file()

        job = get_job(context, render_file)

        # create an instance of Envoy client
        client = get_envoy_client()

        # remove all previously run jobs
        project.jobs = list()

        # add job to project
        project.add_jobs(job)

        # results regex pattern to download
        # `project.remote_output_folder` provides the remote root folder under which results are available
        # below is the regex to look for all folders and files under `project.remote_output_folder`
        output_pattern = '{0}/.+'.format(project.remote_output_folder)

        download_path = get_job_output_path_abs(context)

        # create a watch file
        watch_file = WatchFile(output_pattern, download_path)

        # set the output directory
        project.add_watch_files(watch_file)

        log.info("Submitted Job Settings: \n" +
                 "\tproject_name: %s\n" % project_name +
                 "\tjob.app: %s\n" % job.app + "\tjob.name: %s\n" % job.name +
                 "\tjob.app_version: %s\n" % job.app_version +
                 "\tjob.operation: %s\n" % job.operation +
                 "\tjob.path: %s\n" % job.path +
                 "\tjob.frames: %s\n" % job.params['frames'] +
                 "\tjob.output_prefix: %s\n" % job.params['output_prefix'] +
                 "\tjob.output_format: %s\n" % job.params['output_format'] +
                 "\tjob.engine: %s\n" % job.params['engine'] +
                 "\tdownload_path: %s\n" % download_path)

        # submit project
        log.info("Submitting job (skip_upload=" + str(skip_upload) + ")...")
        _set_progress(progress=50, status="Submitting project")
        client.submit_project(project, skip_upload=skip_upload)

        # skip upload is only false if its a new project, in which case it needs adding to the list
        if not skip_upload:
            project_item = _add_project_to_list(project_name, props)

            # callback for keeping track of the upload progress
            def progress_callback(percent, status):
                scaled_percent = 50 + percent / 2
                _set_progress(progress=scaled_percent, status=status)

            clean_up_temporary_files(project_item, progress_callback)

        log.info("Job submitted")

    except InvalidInputError as e:
        log.warning("Invalid Input Error: " + e.user_message)
        if operator:
            operator.report({'ERROR_INVALID_INPUT'}, e.user_message)
    except AuthenticationError as e:
        log.error("Authentication Error: " + e.user_message)
    except InsufficientCreditsError as e:
        log.error("Insufficient Credits Error: " + e.user_message)
    except InvalidRequestError as e:
        log.error("Invalid Request Error: " + e.user_message)
    except APIError as e:
        log.error("API Error: " + str(e.user_message))
    finally:
        log.info("Finished submit operation")
        setattr(context.scene.props, progress_attribute_name, False)
Ejemplo n.º 12
0
def upload_project(
        context,
        project_name,
        temp_dir_manager,
        operator=None,
        skip_upload=False,
        min_progress=0,
        max_progress=100,
        progress_attribute_name="uploading_project",
        progress_percent_attribute_name="uploading_project_progress",
        progress_status_attribute_name="uploading_project_status"):
    """Uploads the current scene with the provided project name to Envoy.

    :param context: The operator's context
    :type context: bpy.types.Context
    :param project_name: The name of the project as it will appear in Envoy
    :type project_name: str
    :param temp_dir_manager: The temporary directory manager used to store packed projects
    :type temp_dir_manager: TempDirectoryManager
    :param operator: The operator that called the function
    :type operator: bpy.types.Operator
    :param skip_upload: Pack files but don't upload project files
    :param operator: An optional instance of an operator so that invalid input can be reported correctly
    :type operator: bpy.types.Operator
    :param min_progress: The min value to set the progress too
    :type min_progress: float
    :param max_progress: The max value to set the progress too
    :type max_progress: float
    :param progress_attribute_name: The name of the property flag which indicated that the operation is running
    :type progress_attribute_name: str
    :param progress_percent_attribute_name: The name of the property which represents the operations progress
    :type progress_percent_attribute_name: str
    :param progress_status_attribute_name: The name of the property which represents the operations status
    :type progress_status_attribute_name: str
    :rtype: void
    :raises: InvalidInputError, AuthenticationError, InsufficientCreditsError, InvalidRequestError, APIError
    """

    # helper function for setting the progress of the operation
    def _set_progress(progress=None, status=None):
        if progress is not None:
            # re-scale progress so that it is between the min and max values
            scaled_progress = _scale_percentage(min_progress, max_progress,
                                                progress)
            setattr(context.scene.props, progress_percent_attribute_name,
                    scaled_progress)

        if status is not None:
            setattr(context.scene.props, progress_status_attribute_name,
                    status)

    # get method logger
    log = get_wrapped_logger(__name__ + '.' + inspect.stack()[0][3])

    setattr(context.scene.props, progress_attribute_name, True)
    _set_progress(progress=0, status="Starting project upload")

    # if the file has been saved
    if bpy.context.blend_data.is_saved:
        # use the name of the saved .blend file
        blend_file_name = bpy.path.basename(bpy.context.blend_data.filepath)
        log.info(".blend file is saved, using '%s' as packed file name." %
                 blend_file_name)
    else:
        # otherwise create a name for the packed .blend file
        blend_file_name = 'main_GM_blend_file_packed.blend'
        log.info(".blend file is not saved, using '%s' as packed file name." %
                 blend_file_name)

    # create a new temp directory
    temp_dir_path = temp_dir_manager.get_temp_directory()

    blend_file_path = temp_dir_path / blend_file_name

    # save a copy of the current scene to the temp directory. This is so that if the file has not been saved
    # or if it has been modified since it's last save then the submitted .blend file will represent the
    # current state of the scene
    _set_progress(progress=20, status="Saving scene data")
    bpy.ops.wm.save_as_mainfile(copy=True,
                                filepath=str(blend_file_path),
                                relative_remap=True,
                                compress=True)
    log.info("Saved copy of scene to '%s'" % str(blend_file_path))

    upload_file_as_project(context, str(blend_file_path), project_name,
                           temp_dir_manager, operator, skip_upload,
                           _scale_percentage(min_progress, max_progress, 20),
                           max_progress, progress_attribute_name,
                           progress_percent_attribute_name,
                           progress_status_attribute_name)
Ejemplo n.º 13
0
def upload_file_as_project(
        context,
        file_path,
        project_name,
        temp_dir_manager,
        operator=None,
        skip_upload=False,
        min_progress=0,
        max_progress=100,
        progress_attribute_name="uploading_project",
        progress_percent_attribute_name="uploading_project_progress",
        progress_status_attribute_name="uploading_project_status"):
    """Uploads the provided .blend file to Envoy as a new project, using the provided project name.

    :param context: The operator's context
    :type context: bpy.types.Context
    :param file_path: The path to the blend file that needs uploading
    :type file_path: str
    :param project_name: The name of the project as it will appear in Envoy
    :type project_name: str
    :param temp_dir_manager: The temporary directory manager used to store packed projects
    :type temp_dir_manager: TempDirectoryManager
    :param operator: The operator that called the function
    :type operator: bpy.types.Operator
    :param skip_upload: Pack files but don't upload project files
    :param operator: An optional instance of an operator so that invalid input can be reported correctly
    :type operator: bpy.types.Operator
    :param min_progress: The min value to set the progress too
    :type min_progress: float
    :param max_progress: The max value to set the progress too
    :type max_progress: float
    :param progress_attribute_name: The name of the property flag which indicated that the operation is running
    :type progress_attribute_name: str
    :param progress_percent_attribute_name: The name of the property which represents the operations progress
    :type progress_percent_attribute_name: str
    :param progress_status_attribute_name: The name of the property which represents the operations status
    :type progress_status_attribute_name: str
    :rtype: void
    :raises: InvalidInputError, AuthenticationError, InsufficientCreditsError, InvalidRequestError, APIError
    """

    # helper function for setting the progress of the operation
    def _set_progress(progress=None, status=None):
        if progress is not None:
            # re-scale progress so that it is between the min and max values
            scaled_progress = _scale_percentage(min_progress, max_progress,
                                                progress)
            setattr(context.scene.props, progress_percent_attribute_name,
                    scaled_progress)

        if status is not None:
            setattr(context.scene.props, progress_status_attribute_name,
                    status)

    # get method logger
    log = get_wrapped_logger(__name__ + '.' + inspect.stack()[0][3])

    setattr(context.scene.props, progress_attribute_name, True)
    _set_progress(progress=0, status="Starting project upload")

    try:
        # create an instance of Envoy client
        client = get_envoy_client()

        # create a new temp directory
        temp_dir_path = temp_dir_manager.get_temp_directory()

        # create directory to contain packed project
        packed_dir = temp_dir_path / project_name

        log.info("Creating packed directory '%s'..." % str(packed_dir))
        _set_progress(progress=40, status="Creating packed directory")
        os.mkdir(str(packed_dir))

        # create the progress callback
        progress_cb = BatProgressCallback(log)

        # pack the .blend file to the pack directory
        _set_progress(progress=60, status="Packing scene data")
        pack_blend_file(file_path, str(packed_dir), progress_cb=progress_cb)

        # delete pack-info.txt if it exists
        pack_info_file = pathlib.Path(packed_dir / 'pack-info.txt')
        if pack_info_file.is_file():
            log.info("Removing '%s'..." % str(pack_info_file))
            pack_info_file.unlink()

        # create the project
        project = Project(str(packed_dir), project_name)

        # associate this project with the temp directory so the temp directory can be removed once the project is
        # complete
        temp_dir_manager.associate_with_temp_dir(str(temp_dir_path), project,
                                                 pathlib.Path(file_path).name)
        # add files to project
        # only files and folders within the project path can be added, use relative or full path
        # any other paths passed will be ignored
        log.info("Adding '" + str(packed_dir) + "' to " +
                 constants.COMPANY_NAME + " project...")
        project.add_folders(str(packed_dir))

        if skip_upload:
            log.info("Skipping project upload...")
        else:
            client.submit_project(project)  # returns project name

            # add the new project to the projects list
            project_item = _add_project_to_list(project_name,
                                                context.scene.props)

            # callback for keeping track of the upload progress
            def progress_callback(percent, status):
                scaled_percent = 80 + percent / (100 / (100 - 80))
                _set_progress(progress=scaled_percent, status=status)

            clean_up_temporary_files(project_item, progress_callback)

    except InvalidInputError as e:
        log.warning("Invalid Input Error: " + e.user_message)
        if operator:
            operator.report({'ERROR_INVALID_INPUT'}, e.user_message)
        raise e
    except AuthenticationError as e:
        log.error("Authentication Error: " + e.user_message)
        raise e
    except InsufficientCreditsError as e:
        log.error("Insufficient Credits Error: " + e.user_message)
        raise e
    except InvalidRequestError as e:
        log.error("Invalid Request Error: " + e.user_message)
        raise e
    except APIError as e:
        log.error("API Error: " + str(e.user_message))
        raise e
    finally:
        # hide the progress meter if unless skipping upload
        if not skip_upload:
            setattr(context.scene.props, progress_attribute_name, False)
Ejemplo n.º 14
0
def get_wrapped_logger(name):
    from gridmarkets_blender_addon.blender_logging_wrapper import get_wrapped_logger
    return get_wrapped_logger(name)
Ejemplo n.º 15
0
def clean_up_temporary_files(project_item, progress_callback):
    """ Checks the status of the project until it has finished uploading and then deletes the temporary files """

    # get method logger
    log = get_wrapped_logger(__name__ + '.' + inspect.stack()[0][3])

    if not constants.PROJECT_STATUS_POLLING_ENABLED:
        log.info("Uploading Project...")
        return

    # number of times to retry if receiving malformed responses
    bad_response_retires = 10

    log.info("Uploading Project...")
    progress_callback(0, "Uploading Project")

    while bad_response_retires > 0:
        import time

        # sleep for 10 seconds to give time for the project to upload
        log.info("Checking project upload status in " + str(10) + " seconds")
        time.sleep(10)

        try:
            # create an instance of Envoy client
            client = get_envoy_client()

            log.info("Checking project status for %s..." % project_item.name)
            resp = client.get_project_status(project_item.name)

            # convert to string
            project_item.status = json.dumps(resp)
            log.info("Project status response: %s" % project_item.status)

            # redraw the add-on to show the latest update
            force_redraw_addon()
        except AuthenticationError as e:
            log.error("Authentication Error: " + e.user_message)
            bad_response_retires -= 1
            continue
        except InsufficientCreditsError as e:
            log.error("Insufficient Credits Error: " + e.user_message)
            bad_response_retires -= 1
            continue
        except InvalidRequestError as e:
            log.error("Invalid Request Error: " + e.user_message)
            bad_response_retires -= 1
            continue
        except APIError as e:
            log.error("API Error: " + str(e.user_message))
            bad_response_retires -= 1
            raise e
            continue

        # parse the json status response
        project_status = json.loads(project_item.status)
        uploading_status = project_status['State']

        if uploading_status == 'Completed' or uploading_status == 'Submitted':
            progress_callback(100, "Uploaded Project")
            log.info("Uploaded Project")
            break
        elif uploading_status == 'Uploading':
            if "BytesDone" in project_status and "BytesTotal" in project_status:
                bytes_done = project_status["BytesDone"]
                bytes_total = project_status["BytesTotal"]

                try:
                    # try converting to floats
                    bytes_done_f = float(bytes_done)
                    bytes_total_f = float(bytes_total)

                    # avoid dividing by zero errors
                    if bytes_total_f > 0:
                        percent = (bytes_done_f / bytes_total_f) * 100
                        progress_callback(percent, "Uploading Project...")

                except ValueError:
                    log.warning("bytes_done or bytes_total was not a float")
            continue
        elif uploading_status == 'Error':

            if 'Message' not in project_status:
                log.warning("Upload Error: No Error message")
                bad_response_retires -= 1
                continue

            error_message = project_status['Message']

            # the api returns an error status even if the project has uploaded correctly but it was uploaded without a
            # job, we need to check to make sure that this 'Error' isn't just a uploaded project without a job
            if error_message == "Job validation failed.":

                # check that all the details have finished uploading
                if 'Details' not in project_status:
                    log.warning(
                        "Project status does not contain 'Details' attribute")
                else:
                    details = project_status['Details']

                    details_uploaded = True

                    for key, value in details.items():

                        # the Details attribute can contain it's own details attribute which we should ignore
                        if key == 'details':
                            continue

                        if 'BytesDone' not in value:
                            log.warning(
                                "project status detail does not contain 'BytesDone' attribute"
                            )
                            details_uploaded = False
                            break

                        if 'BytesTotal' not in value:
                            log.warning(
                                "project status detail does not contain 'Bytestotal' attribute"
                            )
                            details_uploaded = False
                            break

                        bytes_done = value['BytesDone']
                        bytes_total = value['BytesTotal']

                        # if the detail is not finished uploading then break
                        if bytes_done != bytes_total:
                            log.warning(
                                "project status detail %s does has not finished uploading"
                                % key)
                            details_uploaded = False
                            break

                    if details_uploaded:
                        progress_callback(100, "Uploaded Project")
                        log.info("Uploaded Project")
                        break
                    else:
                        log.warning("Not all details have been uploaded")

            else:
                log.warning("Upload Error: %s" % error_message)
                bad_response_retires -= 1
                continue

        else:
            log.warning("Unrecognised status: %s" % uploading_status)
            bad_response_retires -= 1
            continue

    delete_temp_files_for_project(project_item.name)