示例#1
0
    def __init__(self, allowed_keys, **kwargs):
        allowed_keys_base = dict()
        allowed_keys_base.update(allowed_keys) 
        MASTObj.__init__(self, allowed_keys_base, **kwargs)

        work_dir = '/'.join(self.keywords['name'].split('/')[:-1])
        topmeta = Metadata(metafile='%s/metadata.txt' % work_dir)
        data = topmeta.read_data(self.keywords['name'].split('/')[-1])

        self.meta_dict = dict()
        if data:
            for datum in data.split(';'):
                self.meta_dict[datum.split(':')[0]] = datum.split(':')[1].strip()

        self.metafile = Metadata(metafile='%s/metadata.txt' % self.keywords['name'])

        self.program = self.keywords['program_keys']['mast_program'].lower()
        
        self.logger = loggerutils.get_mast_logger(self.keywords['name'])
        
        sdir=os.path.join(os.path.dirname(self.keywords['name']),"structure_index_files")
        if os.path.exists(sdir):
            self.atomindex = AtomIndex(structure_index_directory=sdir)
        else:
            self.atomindex = None

        if self.program == 'vasp':
            self.checker = VaspChecker(name=self.keywords['name'],
            program_keys = self.keywords['program_keys'],
            structure = self.keywords['structure'])
            self.errhandler = VaspError(name=self.keywords['name'],
            program_keys = self.keywords['program_keys'],
            structure = self.keywords['structure'])
        elif self.program == 'vasp_neb':
            self.checker = VaspNEBChecker(name=self.keywords['name'],
            program_keys = self.keywords['program_keys'],
            structure = self.keywords['structure'])
            self.errhandler = VaspNEBError(name=self.keywords['name'],
            program_keys = self.keywords['program_keys'],
            structure = self.keywords['structure'])
        elif self.program == 'phon':
            self.checker = PhonChecker(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
            self.errhandler = PhonError(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
        elif self.program == 'lammps':
            self.checker = LammpsChecker(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
            self.errhandler = GenericError(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
        elif self.program =='structopt':
            self.checker = StructoptChecker(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
            self.errhandler = GenericError(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
        else:
            allowed_keys={'name','program_keys','structure'}
            self.checker = GenericChecker(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
            self.errhandler = GenericError(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
示例#2
0
    def __init__(self, allowed_keys, **kwargs):
        allowed_keys_base = dict()
        allowed_keys_base.update(allowed_keys)
        MASTObj.__init__(self, allowed_keys_base, **kwargs)

        work_dir = '/'.join(self.keywords['name'].split('/')[:-1])
        topmeta = Metadata(metafile='%s/metadata.txt' % work_dir)
        data = topmeta.read_data(self.keywords['name'].split('/')[-1])

        self.meta_dict = dict()
        if data:
            for datum in data.split(';'):
                self.meta_dict[datum.split(':')[0]] = datum.split(
                    ':')[1].strip()

        self.metafile = Metadata(metafile='%s/metadata.txt' %
                                 self.keywords['name'])

        self.program = self.keywords['program_keys']['mast_program'].lower()

        self.logger = loggerutils.get_mast_logger(self.keywords['name'])

        sdir = os.path.join(os.path.dirname(self.keywords['name']),
                            "structure_index_files")
        if os.path.exists(sdir):
            self.atomindex = AtomIndex(structure_index_directory=sdir)
        else:
            self.atomindex = None

        if self.program == 'vasp':
            self.checker = VaspChecker(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
            self.errhandler = VaspError(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
        elif self.program == 'vasp_neb':
            self.checker = VaspNEBChecker(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
            self.errhandler = VaspNEBError(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
        elif self.program == 'phon':
            self.checker = PhonChecker(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
            self.errhandler = PhonError(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
        elif self.program == 'lammps':
            self.checker = LammpsChecker(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
            self.errhandler = GenericError(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
        elif self.program == 'structopt':
            self.checker = StructoptChecker(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
            self.errhandler = GenericError(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
        else:
            allowed_keys = {'name', 'program_keys', 'structure'}
            self.checker = GenericChecker(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
            self.errhandler = GenericError(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
示例#3
0
class BaseIngredient(MASTObj):
    """Base Ingredient class
        Attributes:
            self.meta_dict <dict>: Metadata dictionary
            self.metafile <Metadata>: Metadata file
            self.program <str>: program name, all lowercase,
                                from 'mast_program' in input
                                file
            self.checker <VaspChecker, PhonChecker, etc.>:
                    program-dependent checker object
            self.errhandler <VaspError, PhonError, etc.>:
                    program-dependent handler object
            self.atomindex <AtomIndex>: atom index object
    """
    def __init__(self, allowed_keys, **kwargs):
        allowed_keys_base = dict()
        allowed_keys_base.update(allowed_keys)
        MASTObj.__init__(self, allowed_keys_base, **kwargs)

        work_dir = '/'.join(self.keywords['name'].split('/')[:-1])
        topmeta = Metadata(metafile='%s/metadata.txt' % work_dir)
        data = topmeta.read_data(self.keywords['name'].split('/')[-1])

        self.meta_dict = dict()
        if data:
            for datum in data.split(';'):
                self.meta_dict[datum.split(':')[0]] = datum.split(
                    ':')[1].strip()

        self.metafile = Metadata(metafile='%s/metadata.txt' %
                                 self.keywords['name'])

        self.program = self.keywords['program_keys']['mast_program'].lower()

        self.logger = loggerutils.get_mast_logger(self.keywords['name'])

        sdir = os.path.join(os.path.dirname(self.keywords['name']),
                            "structure_index_files")
        if os.path.exists(sdir):
            self.atomindex = AtomIndex(structure_index_directory=sdir)
        else:
            self.atomindex = None

        if self.program == 'vasp':
            self.checker = VaspChecker(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
            self.errhandler = VaspError(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
        elif self.program == 'vasp_neb':
            self.checker = VaspNEBChecker(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
            self.errhandler = VaspNEBError(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
        elif self.program == 'phon':
            self.checker = PhonChecker(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
            self.errhandler = PhonError(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
        elif self.program == 'lammps':
            self.checker = LammpsChecker(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
            self.errhandler = GenericError(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
        elif self.program == 'structopt':
            self.checker = StructoptChecker(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
            self.errhandler = GenericError(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
        else:
            allowed_keys = {'name', 'program_keys', 'structure'}
            self.checker = GenericChecker(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])
            self.errhandler = GenericError(
                name=self.keywords['name'],
                program_keys=self.keywords['program_keys'],
                structure=self.keywords['structure'])

    def write_directory(self):
        try:
            os.makedirs(self.keywords['name'])
            self.metafile.write_data('directory created', time.asctime())
            self.metafile.write_data('name',
                                     self.keywords['name'].split('/')[-1])
            self.metafile.write_data('program', self.program)
            self.metafile.write_data('ingredient type',
                                     self.__class__.__name__)
            #if 'mast_charge' in self.keywords['program_keys']:
            #    self.metafile.write_data('charge', self.keywords['program_keys']['mast_charge'])
            for key, value in self.meta_dict.items():
                self.metafile.write_data(key, value)

        except OSError:
            self.logger.info("Directory for %s already exists." %
                             self.keywords['name'])
        return

    def close_logger(self):
        """Close logger handlers 
            (prevents IOError from too many handlers being open)
        """
        handlerlist = list(
            self.logger.handlers)  #TTM 428; also deleted return after OSError
        for myhandler in handlerlist:
            self.logger.removeHandler(myhandler)
            myhandler.flush()
            myhandler.close()
        return

    def is_complete(self):
        '''Function to check if Ingredient is ready'''
        if not self.checker.is_started():
            return False  #hasn't started running yet
        complete = self.checker.is_complete()
        frozen = self.checker.is_frozen()
        if complete or frozen:
            errct = self.errhandler.loop_through_errors()
            if errct > 0:
                if os.path.isfile(
                        os.path.join(self.keywords['name'], 'error.5.tar.gz')):
                    self.logger.error(
                        "Ingredient directory already has 5 error zip files. A manual look is required."
                    )
                    self.change_my_status("E")
                    return False
                if 'mast_auto_correct' in self.keywords['program_keys'].keys():
                    if str(self.keywords['program_keys']
                           ['mast_auto_correct']).strip()[0].lower() == 'f':
                        self.change_my_status("E")
                    else:
                        self.change_my_status("S")
                else:
                    self.change_my_status("S")
                self.errhandler.clean_up_directory()
                return False
            else:
                if complete:
                    self.metafile.write_data('completed on', time.asctime())
                    if 'get_energy_from_energy_file' in dirutil.list_methods(
                            self.checker, 0):
                        energy = self.checker.get_energy_from_energy_file()
                        self.metafile.write_data('energy', energy)
                    return True
                else:
                    return False
        else:
            return False

    def directory_is_locked(self):
        return dirutil.directory_is_locked(self.keywords['name'])

    def lock_directory(self):
        return dirutil.lock_directory(self.keywords['name'])

    def unlock_directory(self):
        return dirutil.unlock_directory(self.keywords['name'])

    def wait_to_write(self):
        return dirutil.wait_to_write(self.keywords['name'])

    def is_ready_to_run(self):
        if self.directory_is_locked():
            return False
        return self.checker.is_ready_to_run()

    def getpath(self):
        '''getpath returns the directory of the ingredient'''
        return self.keywords['name']

    def write_submit_script(self):
        from MAST.submit import script_commands
        script_commands.write_submit_script(self.keywords)
        return

    def run(self, mode='serial', curdir=os.getcwd()):
        if "dagman" in dirutil.get_mast_platform():
            return
        from MAST.submit import queue_commands
        if mode.lower() == 'noqsub':
            curdir = os.getcwd()
            os.chdir(self.keywords['name'])
            programpath = queue_commands.direct_shell_command()
            p = subprocess.Popen(programpath,
                                 shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
            p.wait()
            os.chdir(curdir)
            self.metafile.write_data('run', time.asctime())
        elif mode.lower() == 'serial':
            queuesub = queue_commands.write_to_submit_list(
                self.keywords['name'])
            #runme = subprocess.Popen(queuesub, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            #runme.wait()
            # for scheduling other jobs
            #runme.wait()
            self.metafile.write_data('queued', time.asctime())
        return

    def get_name(self):
        return self.keywords['name'].split('/')[-1]

    @property
    def name(self):
        return self.get_name()

    def get_keywords(self):
        return self.keywords.copy()

    def __repr__(self):
        return 'Ingredient %s of type %s' % (
            self.keywords['name'].split('/')[-1], self.__class__.__name__)

    def get_my_label(self, label):
        """Get the value of a label in the metadata file.
            Args:
                label <str>: Label to search for.
            Returns:
                <str>: Value of the label as a string, stripped.
        """
        myname = self.keywords['name']
        mymeta = Metadata(metafile=os.path.join(myname, "metadata.txt"))
        mylabel = mymeta.search_data(label)
        if mylabel == "":
            raise MASTError(self.__class__.__name__,
                            "No metadata for tag %s" % label)
        return mylabel[1]

    def change_my_status(self, newstatus):
        """Change an ingredient status by writing the new status to 
            change_status.txt in the ingredient folder, to get picked
            up by the recipe plan.
            Args:
                newstatus <str>: New status to which to change the ingredient.
        """
        ingdir = self.keywords['name']
        oneup = os.path.dirname(ingdir)
        tryrecipe = os.path.basename(oneup)
        statuspath = ""
        if dirutil.dir_is_in_scratch(tryrecipe):
            statuspath = "%s/change_status.txt" % ingdir
        else:
            twoup = os.path.dirname(oneup)
            tryrecipe = os.path.basename(twoup)
            if dirutil.dir_is_in_scratch(tryrecipe):
                statuspath = "%s/change_status.txt" % oneup
            else:
                raise MASTError(
                    self.__class__.__name__,
                    "Cannot change status of ingredient %s as recipe %s or %s is not found in $MAST_SCRATCH."
                    % (self.keywords['name'], oneup, twoup))
        if os.path.isfile(statuspath):
            statusfile = MASTFile(statuspath)
        else:
            statusfile = MASTFile()
        statusfile.data.append("%s:recommend:%s" % (newstatus, time.asctime()))
        statusfile.to_file(statuspath)
        self.logger.info("Recommending status change to %s" % newstatus)
示例#4
0
class BaseIngredient(MASTObj):
    """Base Ingredient class
        Attributes:
            self.meta_dict <dict>: Metadata dictionary
            self.metafile <Metadata>: Metadata file
            self.program <str>: program name, all lowercase,
                                from 'mast_program' in input
                                file
            self.checker <VaspChecker, PhonChecker, etc.>:
                    program-dependent checker object
            self.errhandler <VaspError, PhonError, etc.>:
                    program-dependent handler object
            self.atomindex <AtomIndex>: atom index object
    """
    def __init__(self, allowed_keys, **kwargs):
        allowed_keys_base = dict()
        allowed_keys_base.update(allowed_keys) 
        MASTObj.__init__(self, allowed_keys_base, **kwargs)

        work_dir = '/'.join(self.keywords['name'].split('/')[:-1])
        topmeta = Metadata(metafile='%s/metadata.txt' % work_dir)
        data = topmeta.read_data(self.keywords['name'].split('/')[-1])

        self.meta_dict = dict()
        if data:
            for datum in data.split(';'):
                self.meta_dict[datum.split(':')[0]] = datum.split(':')[1].strip()

        self.metafile = Metadata(metafile='%s/metadata.txt' % self.keywords['name'])

        self.program = self.keywords['program_keys']['mast_program'].lower()
        
        self.logger = loggerutils.get_mast_logger(self.keywords['name'])
        
        sdir=os.path.join(os.path.dirname(self.keywords['name']),"structure_index_files")
        if os.path.exists(sdir):
            self.atomindex = AtomIndex(structure_index_directory=sdir)
        else:
            self.atomindex = None

        if self.program == 'vasp':
            self.checker = VaspChecker(name=self.keywords['name'],
            program_keys = self.keywords['program_keys'],
            structure = self.keywords['structure'])
            self.errhandler = VaspError(name=self.keywords['name'],
            program_keys = self.keywords['program_keys'],
            structure = self.keywords['structure'])
        elif self.program == 'vasp_neb':
            self.checker = VaspNEBChecker(name=self.keywords['name'],
            program_keys = self.keywords['program_keys'],
            structure = self.keywords['structure'])
            self.errhandler = VaspNEBError(name=self.keywords['name'],
            program_keys = self.keywords['program_keys'],
            structure = self.keywords['structure'])
        elif self.program == 'phon':
            self.checker = PhonChecker(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
            self.errhandler = PhonError(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
        elif self.program == 'lammps':
            self.checker = LammpsChecker(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
            self.errhandler = GenericError(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
        elif self.program =='structopt':
            self.checker = StructoptChecker(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
            self.errhandler = GenericError(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
        else:
            allowed_keys={'name','program_keys','structure'}
            self.checker = GenericChecker(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])
            self.errhandler = GenericError(name=self.keywords['name'],program_keys=self.keywords['program_keys'],structure=self.keywords['structure'])

    def write_directory(self):
        try:
            os.makedirs(self.keywords['name'])
            self.metafile.write_data('directory created', time.asctime())
            self.metafile.write_data('name', self.keywords['name'].split('/')[-1])
            self.metafile.write_data('program', self.program)
            self.metafile.write_data('ingredient type', self.__class__.__name__)
            #if 'mast_charge' in self.keywords['program_keys']:
            #    self.metafile.write_data('charge', self.keywords['program_keys']['mast_charge'])
            for key, value in self.meta_dict.items():
                self.metafile.write_data(key, value)

        except OSError:
            self.logger.info("Directory for %s already exists." % self.keywords['name'])
        return
    
    def close_logger(self):
        """Close logger handlers 
            (prevents IOError from too many handlers being open)
        """
        handlerlist = list(self.logger.handlers) #TTM 428; also deleted return after OSError
        for myhandler in handlerlist:
            self.logger.removeHandler(myhandler)
            myhandler.flush()
            myhandler.close()
        return

    def is_complete(self):
        '''Function to check if Ingredient is ready'''
        if not self.checker.is_started():
            return False #hasn't started running yet
        complete = self.checker.is_complete()
        frozen = self.checker.is_frozen()
        if complete or frozen:
            errct = self.errhandler.loop_through_errors()
            if errct > 0:
                if os.path.isfile(os.path.join(self.keywords['name'],'error.5.tar.gz')):
                    self.logger.error("Ingredient directory already has 5 error zip files. A manual look is required.")
                    self.change_my_status("E")
                    return False
                if 'mast_auto_correct' in self.keywords['program_keys'].keys():
                    if str(self.keywords['program_keys']['mast_auto_correct']).strip()[0].lower() == 'f':
                        self.change_my_status("E")
                    else:
                        self.change_my_status("S")
                else:
                    self.change_my_status("S")
                self.errhandler.clean_up_directory()
                return False
            else:
                if complete:
                    self.metafile.write_data('completed on', time.asctime())
                    if 'get_energy_from_energy_file' in dirutil.list_methods(self.checker,0):
                        energy = self.checker.get_energy_from_energy_file()
                        self.metafile.write_data('energy', energy)
                    return True
                else:
                    return False
        else:
            return False

    def directory_is_locked(self):
        return dirutil.directory_is_locked(self.keywords['name'])

    def lock_directory(self):
        return dirutil.lock_directory(self.keywords['name'])

    def unlock_directory(self):
        return dirutil.unlock_directory(self.keywords['name'])

    def wait_to_write(self):
        return dirutil.wait_to_write(self.keywords['name'])
   
    def is_ready_to_run(self):
        if self.directory_is_locked():
            return False
        return self.checker.is_ready_to_run()
        
    def getpath(self):
        '''getpath returns the directory of the ingredient'''
        return self.keywords['name']
    
    def write_submit_script(self):
        from MAST.submit import script_commands
        script_commands.write_submit_script(self.keywords)
        return
    
    def run(self, mode='serial', curdir=os.getcwd()):
        if "dagman" in dirutil.get_mast_platform():
            return
        from MAST.submit import queue_commands 
        if mode.lower() == 'noqsub':
            curdir = os.getcwd()
            os.chdir(self.keywords['name'])
            programpath = queue_commands.direct_shell_command()
            p = subprocess.Popen(programpath, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            p.wait()
            os.chdir(curdir)
            self.metafile.write_data('run', time.asctime())
        elif mode.lower() == 'serial':
            queuesub = queue_commands.write_to_submit_list(self.keywords['name'])
            #runme = subprocess.Popen(queuesub, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            #runme.wait()
            # for scheduling other jobs
            #runme.wait()
            self.metafile.write_data('queued', time.asctime())
        return

    def get_name(self):
        return self.keywords['name'].split('/')[-1]

    @property
    def name(self):
        return self.get_name()

    def get_keywords(self):
        return self.keywords.copy()

    def __repr__(self):
        return 'Ingredient %s of type %s' % (self.keywords['name'].split('/')[-1], self.__class__.__name__)

    def get_my_label(self, label):
        """Get the value of a label in the metadata file.
            Args:
                label <str>: Label to search for.
            Returns:
                <str>: Value of the label as a string, stripped.
        """
        myname = self.keywords['name']
        mymeta = Metadata(metafile=os.path.join(myname, "metadata.txt"))
        mylabel = mymeta.search_data(label)
        if mylabel == "":
            raise MASTError(self.__class__.__name__, 
                "No metadata for tag %s" % label)
        return mylabel[1]

    def change_my_status(self, newstatus):
        """Change an ingredient status by writing the new status to 
            change_status.txt in the ingredient folder, to get picked
            up by the recipe plan.
            Args:
                newstatus <str>: New status to which to change the ingredient.
        """
        ingdir = self.keywords['name']
        oneup = os.path.dirname(ingdir)
        tryrecipe = os.path.basename(oneup)
        statuspath = ""
        if dirutil.dir_is_in_scratch(tryrecipe):
            statuspath = "%s/change_status.txt" % ingdir
        else:
            twoup = os.path.dirname(oneup)
            tryrecipe = os.path.basename(twoup)
            if dirutil.dir_is_in_scratch(tryrecipe):
                statuspath = "%s/change_status.txt" % oneup
            else:
                raise MASTError(self.__class__.__name__, "Cannot change status of ingredient %s as recipe %s or %s is not found in $MAST_SCRATCH." % (self.keywords['name'],oneup, twoup))
        if os.path.isfile(statuspath):
            statusfile = MASTFile(statuspath)
        else:
            statusfile=MASTFile()
        statusfile.data.append("%s:recommend:%s" % (newstatus, time.asctime()))
        statusfile.to_file(statuspath)
        self.logger.info("Recommending status change to %s" % newstatus)