示例#1
0
    def setProperty(self, code, value):
        '''
        Set a property for the component.

        Args:
            code (str): the code for the property to be set.
            value (int|float|str|bool): the value for the property.
        '''

        if not self.expert:

            # If we haven't retrieved the json for the component type, do so now
            if self.componentType_json == None:
                self.__getComponentType()

            # Check if the property code is in the component type's associated properties
            properties = [
                property['code']
                for property in self.componentType_json['properties']
            ]
            if code not in properties:
                WARNING(
                    'Property \'{0}\' is not associated with component type \'{1}\'.'
                    .format(property, self.type['code']))
                INFO('Property not set.')
                return False

            i = properties.index(code)

            # Check if the value is set to None and if the property is required (i.e., cannot be set to None)
            if value == None and properties[i]['required']:
                WARNING(
                    'Property \'{0}\' is required and cannot be set to None.'.
                    format(property))
                INFO('Property not set.')
                return False

            # Check that the value of the property has the right type (if not None)
            elif not self.__isType(value, properties[i]['dataType']):
                WARNING(
                    'Property \'{0}\' is not associated with component type \'{1}\'.'
                    .format(property, self.type['code']))
                INFO('Property not set.')
                return False

        # Update the stage and fetch the updated json
        dbCommands['setComponentProperty'].run(component=self.component,
                                               code=code,
                                               value=value)
        if not self.expert:
            self.get()
            INFO('Component code {0} updated to stage \'{1}\'.'.format(
                self.component, stage))
        return True
示例#2
0
    def deleteComment(self, code):
        '''
        Delete a comment associated with the component.

        Args:
            code (str): the code for the comment to be deleted.
        '''

        # Check that the comment code is associated with a comment for the component
        if not self.expert and code not in [
                comment['code'] for comment in self.json['comments']
        ]:
            WARNING(
                'Code {0} is not associated with any comments of component code {1}.'
                .format(code, self.component))
            INFO('No comments deleted.')
            return False

        # Delete the comment and fetch the updated json
        dbCommands['deleteComponentComment'].run(component=self.component,
                                                 code=code)
        if not self.expert:
            self.get()
            INFO('Comment code {0} deleted from component code {1}.'.format(
                code, self.component))
        return True
示例#3
0
    def createComment(self, comments):
        '''
        Create a list of comments associated with the component.

        Args:
            comments (list[str]): a list of comments to be created.
        '''

        if not self.expert:

            if not isinstance(comments, list) and [
                    comment
                    for comment in comments if not isinstance(comment, str)
            ] != []:
                WARNING('Comment(s) must be given as a list of strings.')
                INFO('No comment(s) created.')
                return False

        # Add the comments and fetch the updated json
        dbCommands['createComponentComment'].run(component=self.component,
                                                 comments=comments)
        if not self.expert:
            self.get()

            # Report the codes for the new comments (at the end of the comments list from the json)
            comments_new = self.json['comments'][(len(self.json['comments']) -
                                                  len(comments)):]
            INFO('Comment code(s) {0} created for component code {1}.'.format(
                ', '.join([comment['code'] for comment in comments_new]),
                self.component))
        return True
示例#4
0
    def updateComment(self, code, comment):
        '''
        Update a comment associated with the shipment.

        Args:
            code (str): the code for the comment to be updated.
            comment (str): the content the comment will be updated with.
        '''

        # Check the provided code to see if it's associated with a comment of the shipment
        if not self.expert and code not in [
                comment['code'] for comment in self.json['comments']
        ]:
            WARNING(
                'Code {0} is not associated with any comments of shipment id {1}.'
                .format(code, self.shipment))
            INFO('No comments deleted.')
            return False

        # Update the comment and fetch the updated json
        dbCommands['updateShipmentComment'].run(shipment=self.shipment,
                                                code=code,
                                                comment=comment)
        if not self.expert:
            self.get()
            INFO('Comment code {0} updated for shipment id {1}.'.format(
                code, self.shipment))
        return True
示例#5
0
    def deleteAttachment(self, code):
        '''
        Delete an attachment associated with the shipment.

        Args:
            code (str): the code for attachment to be deleted.
        '''

        # Check the provided code to see if it's associated with an attachment of the shipment
        if not self.expert and code not in [
                attachment['code'] for attachment in self.json['attachments']
        ]:
            WARNING(
                'Code {0} is not associated with any attachments of shipment id {1}.'
                .format(code, self.shipment))
            INFO('No attachments deleted.')
            return False

        # Delete the attachment and fetch the updated json
        dbCommands['deleteShipmentAttachment'].run(shipment=self.shipment,
                                                   code=code)
        if not self.expert:
            self.get()
            INFO('Attachment code {0} deleted from shipment id {1}.'.format(
                code, self.shipment))
        return True
示例#6
0
    def __getDate(self, prompt):

        PROMPT(prompt)
        while True:

            # Get our input
            response = input().strip()

            # Skip empty inputs:
            if response == '':
                continue

            # Include the &CANCEL escape code
            if response == '&CANCEL':
                WARNING('Session cancelled.')
                raise Cancel

            # Validate the format of the inputted date
            # See: https://stackoverflow.com/questions/16870663/how-do-i-validate-a-date-string-format-in-python
            try:
                datetime.datetime.strptime(response, '%Y-%m-%d')
                if len(response.split('-')[1]) != 2 or len(
                        response.split('-')[2]) != 2:
                    raise ValueError
            except ValueError:
                PROMPT(
                    'Invalid date format, please use YYYY-MM-DD and try again:'
                )
                continue

            # Return our date
            return response
示例#7
0
    def setStage(self, stage):
        '''
        Set the stage for the component.

        Args:
            stage (str): the code for the stage of the component.
        '''

        if not self.expert:

            # If we haven't retrieved the json for the component type, do so now
            if self.componentType_json == None:
                self.__getComponentType()

            # Check if stage is in the component type's associated stages
            if stage not in [
                    stage['code']
                    for stage in self.componentType_json['stages']
            ]:
                WARNING(
                    'Stage \'{0}\' is not associated with component type \'{1}\'.'
                    .format(stage, self.type['code']))
                INFO('Stage not set.')
                return False

        # Update the stage and fetch the updated json
        dbCommands['setComponentStage'].run(component=self.component,
                                            stage=self.stage)
        if not self.expert:
            self.get()
            INFO('Component code {0} updated to stage \'{1}\'.'.format(
                self.component, stage))
        return True
示例#8
0
    def printFetchedList(self):
        '''
        Print a pretty summary of the shipments currently stored in self.json.
        '''

        if self.json == None:
            if self.verbose:
                WARNING(
                    'No shipment list currently fetched from the ITkPD -- nothing printed.'
                )
        else:
            if self.verbose:
                INFO('Printing fetched list of shipments:\n')
            header = [
                'Index', 'ID', 'Shipment Name', 'Sender', 'Recipient',
                'Shipping Service', 'Tracking Number', 'Type', 'Status'
            ]
            format_new = '    {:<10}{:<30}{:<20}{:<15}{:<15}{:<25}{:<20}{:<20}{:<20}'
            print(Colours.BOLD + Colours.WHITE + format_new.format(*header) +
                  Colours.ENDC)
            for i, shipment in enumerate(self.json):
                row = [
                    str(i), shipment['id'], shipment['name'],
                    shipment['sender']['code'], shipment['recipient']['code'],
                    shipment['shippingService'], shipment['trackingNumber'],
                    shipment['type'], shipment['status']
                ]
                print(format_new.format(*row))
            if self.verbose:
                print('')
        return True
示例#9
0
    def __cutOnValue(self, component, allowedValues, keywords):

        try:

            # Get value at the level of the first keyword
            value = component[keywords[0]]

            # If there are multiple keywords, continue to update value (note, the order of the keywords should match the order in the uuComponent object)
            for keyword in keywords[1:]:
                value = value[keyword]

            # If the value passes the cut, return True
            if value in allowedValues:
                return True

            # If the value fails the cut, return False
            else:
                if self.verbose:
                    INFO('Component \'%s\' failed at \'%s\' cut: \'%s\' = %s' % (component['code'], keywords[0], keywords[0], value))
                return False

        # If the keywords do not actually exist for the object, return False too
        except KeyError as error:
            if self.verbose:
                WARNING('Component \'%s\' failed due to KeyError: %s' % (component['code'], error))
            return False
示例#10
0
    def createComment(self, comments):
        '''
        Create a list of comments associated with the shipment.

        Args:
            comments (list[str]): a list of comments to be created.
        '''

        # Perform type checking on the provided args
        if not self.expert and not isinstance(comments, list) and [
                comment
                for comment in comments if not isinstance(comment, str)
        ] != []:
            WARNING('Comment(s) must be given as a list of strings.')
            INFO('No comment(s) created.')
            return False

        # Create the comments and fetch the updated json
        dbCommands['createShipmentComment'].run(shipment=self.shipment,
                                                comments=comments)
        if not self.expert:
            self.get()
            comments_new = self.json['comments'][(len(self.json['comments']) -
                                                  len(comments)):]
            INFO('Comment code(s) {0} created for shipment id {1}.'.format(
                ', '.join([comment['code'] for comment in comments_new]),
                self.shipment))
        return True
示例#11
0
    def updateAttachment(self, code, title=None, description=None):
        '''
        Update an attachment associated with the shipment.

        Args:
            title (str): the updated title for the attachment (default: None).
            description (str): the updated description for the attachment (default: None).
        '''

        # Check the provided code to see if it's associated with an attachment of the shipment
        if not self.expert and code not in [
                attachment['code'] for attachment in self.json['attachments']
        ]:
            WARNING(
                'Code {0} is not associated with any attachments of shipment id {1}.'
                .format(code, self.shipment))
            INFO('No attachments updated.')
            return False

        # Fetch our args and remove any keys with values set to None
        kwargs = {
            'shipment': self.shipment,
            'code': code,
            'title': title,
            'description': description
        }
        dtoIn = {k: v for k, v in kwargs.items() if v is not None}

        # Update the attachment and fetch the updated json
        dbCommands['updateShipmentAttachment'].run(**dtoIn)
        if not self.expert:
            self.get()
            INFO('Attachment code {0} updated for shipment id {1}.'.format(
                code, self.shipment))
        return True
示例#12
0
    def __askForMultipleThings(self, prompt, options):

        # Generate our list of codes
        codes = [item['code'] for item in options]
        PROMPT(prompt)

        # If _always_print, print the available options
        if self._always_print:
            INFO('Printing options:\n')
            self.__printNamesAndCodes(options)
            print('')
            PROMPT('Please enter a space separated list of codes from above:')

        while True:

            # Get our user input
            response = input().strip().upper().split()

            # If nothing, do nothing
            if response == []:
                continue

            # Escape code &PRINT -- print the available options
            elif response == ['&PRINT']:
                INFO('Printing options:\n')
                self.__printNamesAndCodes(options)
                print('')
                PROMPT(
                    'Please enter a list of space separated codes from above:')
                continue

            # Escape code &ALL -- select all available options
            elif response == ['&ALL']:
                INFO('Using all options.')
                return codes

            # Escape code &CANCEL -- raise Cancel exception
            elif response == ['&CANCEL']:
                WARNING('Session cancelled.')
                raise Cancel

            # If the user enters a valid list of codes, return that code and its index
            not_allowed = [code for code in response if code not in codes]
            if not_allowed == []:
                return_list = []
                INFO('Using code(s):\n')
                for code in response:
                    i = codes.index(code)
                    return_list.append(code)
                    print('    {0} ({1})'.format(code, options[i]['name']))
                print('')
                return return_list

            # Else the input is invalid
            else:
                PROMPT('Invalid input, please try again:')
                continue
示例#13
0
 def __addToCounter(self, component, counter, split):
     date = component['cts'][0:10]
     counter['total'].append(date)
     if split:
         try:
             splitType = component[split]['code']
         except TypeError as error:
             if self.verbose:
                 WARNING('Component \'%s\' failed to be added to counter due to TypeError: %s' % (component['code'], error))
             return counter
         except KeyError as error:
             if self.verbose:
                 WARNING('Component \'%s\' failed to be added to counter due to KeyError: %s' % (component['code'], error))
             return counter
         if splitType in counter.keys():
             counter[splitType].append(date)
         else:
             counter[splitType] = [date]
     if self.verbose:
         INFO('Component \'%s\' successfully added to counter.' % component['code'])
     return counter
示例#14
0
    def __askForSomething(self, prompt, options):

        # Generate our list of codes
        codes = [item['code'] for item in options]
        PROMPT(prompt)

        # If always_print, print the available options for the code
        if self.always_print:
            INFO('Printing options:\n')
            self.__printNamesAndCodes(options)
            print('')
            PROMPT('Please enter a code from above:')

        while True:

            # Get our user input
            response = input().upper().strip()

            # If nothing, do nothing
            if response == '':
                continue

            # Escape code &PRINT -- print the available options
            elif response == '&PRINT':
                INFO('Printing options:\n')
                self.__printNamesAndCodes(options)
                print('')
                PROMPT('Please enter a code from above:')

            # Escape code &JSON -- print JSON to show what has already been selected
            elif response == '&JSON':
                INFO('Printing JSON:\n')
                pp.pprint(self.json)
                print('')
                PROMPT('Please enter a code:')

            # Escape code &CANCEL -- raise our Cancel exception
            elif response == '&CANCEL':
                WARNING('Registration cancelled.')
                raise Cancel

            # If the user enters a valid code, return that code and its index
            elif response in codes:
                i = codes.index(response)
                INFO('Using code: {0} ({1})'.format(response,
                                                    options[i]['name']))
                return i, response

            # Else the input is invalid
            else:
                PROMPT('Invalid input, please try again:')
                continue
示例#15
0
    def setGrade(self, grade, comment=None):
        '''
        Set the grade for the component.

        Args:
            grade (int): the grade for the component.
            comment (str): a comment to be included alongside the grade (default: None).
        '''

        # Check that grade is an integer and comment is a string
        if not self.expert:
            if not isinstance(grade, int):
                WARNING('Grade must be an integer.')
                INFO('Grade not set.')
                return False
            elif comment != None and not isinstance(comment, str):
                WARNING('Comment must be a string.')
                INFO('Grade not set.')
                return False

        # If no comment is provided, don't include it in the uuCMD call
        if comment == None:
            dbCommands['setComponentGrade'].run(component=self.component,
                                                grade=grade)

        # If a comment is provided, include it in the uuCMD call
        else:
            dbCommands['setComponentGrade'].run(component=self.component,
                                                grade=grade,
                                                comment=comment)

        # Fetch the updated json
        if not self.expert:
            self.get()
            INFO('Grade set to {0} for component code {1}.'.format(
                str(grade), self.component))
        return True
示例#16
0
 def __addToCounterWithStages(self, component, counter, **kwargs):
     try:
         for i, stage in enumerate(component['stages']):
             date = stage['dateTime'][0:10]
             if stage in counter.keys():
                 if i == 0:
                     counter[stage].append(date)
                 else:
                     counter[stage].append(date)
                     counter[stages[i-1]].append('-' + date)
             else:
                 if i == 0:
                     counter[stage].append(date)
                 else:
                     counter[stage].append(date)
                     counter[stages[i-1]].append('-' + date)
     except TypeError as error:
         if self.verbose:
             WARNING('Component \'%s\' failed due to TypeError: %s' % (component['code'], error))
         return counter
     except KeyError as error:
         if self.verbose:
             WARNING('Component \'%s\' failed due to KeyError: %s' % (component['code'], error))
         return counter
示例#17
0
    def getListByInstitution(self, code, status=None):
        '''
        Loads a list of shipments associated with an institution into self.json.

        Args:
            code (list[str]): a list of institution codes to filter shipments by.
            status (list[str]): a list of status codes to filter shipments by. Choose status codes from ['prepared'|'inTransit'|'delivered'|'deliveredWithDamage'|'undelivered'] (default None).

        Notes:
            If code and/or status are given as strings instead of list of strings, those strings are implicitly converted to lists of strings.
        '''

        if not isinstance(code, list):
            code = [code]
        dtoIn = {'code': code, 'status': status}
        if status == None:
            del dtoIn['status']
        else:
            if not isinstance(status):
                status = [status]
            allowed_statuses = [
                'prepared', 'inTransit', 'delivered', 'deliveredWithDamage',
                'undelivered'
            ]
            unknown_statuses = [
                status_item for status_item in status
                if status_item not in allowed_statuses
            ]
            if unknown_statuses != []:
                if self.verbose:
                    WARNING(
                        'Shipping status(es) \'{0}\' is(are) not recognized.'.
                        format('\', \''.join(unknown_statuses)))
                    INFO('No list generated.')
                return False
        self.json = dbCommands['listShipmentsByInstitution'].run(**dtoIn)
        if self.verbose:
            if status == None:
                INFO(
                    'Retrieved list of shipments by institution using code(s) \'{0}\'.'
                    .format('\', \''.join(code)))
            else:
                INFO(
                    'Retrieved list of shipments by component using code(s) \'{0}\' and filtering by status(es) \'{1}\'.'
                    .format('\', \''.join(code), '\', \''.join(status)))
        return True
示例#18
0
    def storeShipments(self, *args):
        '''
        Store shipments in self.shipments.

        Args:
            args (int): the index(indices) of the shipment(s) in self.shipments to be stored.

        Notes:
            If args == (), then all of the shipments in self.json are stored.
            Args that are not integers are ignored
        '''

        if self.shipments == []:
            ids_stored = []
        else:
            ids_stored = [shipment.json['id'] for shipment in self.shipments]
        if args == ():
            args = range(len(self.json))
        for i in args:
            try:
                id_current = self.json[i]['id']
                if id_current in ids_stored:
                    if self.verbose:
                        WARNING(
                            'Shipment id {0} ({1} --> {2}) is already in stored list of shipments -- skipping.'
                            .format(id_current, self.json[i]['sender']['code'],
                                    self.json[i]['recipient']['code']))
                else:
                    self.shipments.append(
                        Shipment(shipment=id_current, get_immediately=True))
                    if self.verbose:
                        INFO(
                            'Added shipment id {0} ({1} --> {2}) to stored list of shipments.'
                            .format(id_current, self.json[i]['sender']['code'],
                                    self.json[i]['recipient']['code']))
            except (IndexError, TypeError):
                pass
        self.shipments = list(
            reversed(
                sorted(self.shipments,
                       key=lambda shipment: shipment.json['history'][-1][
                           'dateTime'])))
        return True
示例#19
0
    def setCompleted(self, completed):
        '''
        Set the Completed status for the component.

        Args:
            completed (bool): set the component as Completed if True.
        '''

        # Check that completed is a boolean
        if not self.expert and not isinstance(completed, bool):
            WARNING('Completed must be a boolean.')
            INFO('Completed not set.')
            return

        # Set completed and fetch the updated json
        dbCommands['setComponentCompleted'].run(component=self.component,
                                                completed=completed)
        if not self.expert:
            self.get()
            INFO('Completed set to {0} for component code {1}.'.format(
                completed, self.component))
示例#20
0
 def __uploadFile(self, ITkPDSession, test_number, path_type):
     try:
         fields = {
             'data':
             (os.path.basename(
                 self.tests[test_number]['files_to_upload'][path_type]),
              open(self.tests[test_number]['files_to_upload'][path_type],
                   'rb')),
             'type':
             'file',
             'component':
             self.tests[test_number]['JSON']['component'],
             'title':
             os.path.basename(
                 self.tests[test_number]['files_to_upload'][path_type])
         }
         if path_type == 'strobe_delay_path':
             fields['description'] = ' ITSDAQ strobe delay scan PDF file.'
         elif path_type == 'det_path':
             fields['description'] = 'ITSDAQ hybrid config file.'
         elif path_type == 'three_point_gain_path':
             fields[
                 'description'] = 'ITSDAQ three point gain scan PDF file.'
         elif path_type == 'response_curve_path':
             fields['description'] = 'ITSDAQ response curve PDF file.'
         elif path_type == 'trim_path':
             fields['description'] = 'ITSDAQ trim range scan trim file.'
         elif path_type == 'mask_path':
             fields['description'] = 'ITSDAQ trim range scan mask file.'
         elif path_type == 'NO_path':
             fields['description'] = 'ITSDAQ noise occupancy scan PDF file.'
         data = MultipartEncoder(fields=fields)
         ITkPDSession.doSomething(action='createComponentAttachment',
                                  method='POST',
                                  data=data)
     except KeyError:
         WARNING(
             'File specified by path \'%s\' could not be found for run number %s (%s) -- skipping.'
             % (path_type, self.tests[test_number]['JSON']['runNumber'],
                self.tests[test_number]['JSON']['testType']))
示例#21
0
    def getListByComponent(self, component, status=None):
        '''
        Load a list of shipments associated with a component code into self.json.

        Args:
            code (list[str]): the code for the component to filter shipments by.
            status (list[str]): a status codes to filter shipments by. Choose status codes from ['prepared'|'inTransit'|'delivered'|'deliveredWithDamage'|'undelivered'] (default None).
        '''

        dtoIn = {'component': component, 'status': status}
        if status == None:
            del dtoIn['status']
        else:
            allowed_statuses = [
                'prepared', 'inTransit', 'delivered', 'deliveredWithDamage',
                'undelivered'
            ]
            unknown_statuses = [
                status_item for status_item in status
                if status_item not in allowed_statuses
            ]
            if unknown_statuses != []:
                if self.verbose:
                    WARNING(
                        'Shipping status(es) \'{0}\' not recognized.'.format(
                            '\', \''.join(unknown_statuses)))
                    INFO('No list generated.')
                return False
        self.json = dbCommands['listShipmentsByComponent'].run(**dtoIn)
        if self.verbose:
            if status == None:
                INFO(
                    'Retrieved list of shipments by component using code \'{0}\'.'
                    .format(code))
            else:
                INFO(
                    'Retrieved list of shipments by component using code \'{0}\' and filtering by status(es) \'{1}\'.'
                    .format(code, '\', \''.join(status)))
        return True
示例#22
0
    def setTrashed(self, trashed):
        '''
        Set the Trashed status for the component.

        Args:
            trashed (bool): set the component as Trashed if True.
        '''

        # Check that trashed is a boolean
        if not self.expert and not isinstance(trashed, bool):
            WARNING('Trashed must be a boolean.')
            INFO('Trashed not set.')
            return False

        # Set trashed and fetch the updated json
        dbCommands['setComponentTrashed'].run(component=self.component,
                                              trashed=trashed)
        if not self.expert:
            self.get()
            INFO('Trashed set to {0} for component code {1}.'.format(
                trashed, self.component))
        return True
示例#23
0
    def __askForSerialNumber(self, first_part_of_sn):

        INFO(
            'Serial number is required to be manually entered the component type.'
        )
        PROMPT('Enter the last 7 digits of the component\'s serial number:')

        while True:

            # Get our user input
            response = input().strip()

            # If nothing, do nothing
            if response == '':
                continue

            # Escape code &JSON -- print JSON to show what has already been selected
            elif response == '&JSON':
                INFO('Printing JSON:\n')
                pp.pprint(self.json)
                print('')
                PROMPT('Please enter a serial number:')

            # Escape code &CANCEL -- raise our Cancel exception
            elif response == '&CANCEL':
                WARNING('Registration cancelled.')
                raise Cancel

            # If the user enters a valid serial number, return that number (string)
            elif len(response) == 7 and response.isdigit():
                serial_number = first_part_of_sn + response
                INFO('Using serial number: ' + serial_number)
                return serial_number

            # Else the input is invalid
            else:
                PROMPT('Invalid input, please enter a 7 digit serial number:')
                continue
示例#24
0
    def updateAttachment(self, code, title=None, description=None):
        '''
        Update an attachment associated with the component.

        Args:
            title (str): the updated title for the attachment (default: None).
            description (str): the updated description for the attachment (default: None).
        '''

        # Check that the attachment code is associated with a attachment for the component
        if not self.expert and code not in [
                attachment['code'] for attachment in self.json['attachments']
        ]:
            WARNING(
                'Code {0} is not associated with any attachments of component code {1}.'
                .format(code, self.component))
            INFO('No attachments updated.')
            return False

        # Get our function call kwargs
        kwargs = {
            'component': self.component,
            'code': code,
            'title': title,
            'description': description
        }

        # Generate our dtoIn by removing the items in kwargs which are None
        dtoIn = {k: v for k, v in kwargs.items() if v is not None}

        # Update our attachment and fetch the updated json
        dbCommands['updateComponentAttachment'].run(**dtoIn)
        if not self.expert:
            self.get()
            INFO('Attachment code {0} updated for component code {1}.'.format(
                code, self.component))
        return True
示例#25
0
    def makePlots(self, **kwargs):

        # Get our keys for kwargs
        keys = kwargs.keys()

        # Define a general error class
        class Error(Exception):
            def __init__(self, message):
                self.message = message

        # Perform some argument checking
        # Note, this does not check every required argument for makePlot() is present in kwargs
        ########################################################################################################
        # This is bad of me, it should be fixed in the future if this class is to be used outside of this file #
        ########################################################################################################
        INFO('Peforming argument checking...')
        try:
            if 'project' not in keys:
                raise Error('Keyword argument \'project\' is required by PlotMaker.makePlots().')
            if 'componentType' not in keys:
                raise Error('Keyword argument \'componentType\' is required by PlotMaker.makePlots().')
            if 'savePath' not in keys:
                raise Error('Keyword argument \'savePath\' is required by PlotMaker.makePlots().')
            if kwargs['savePath'][-4:] != '.pdf' and kwargs['savePath'][-4:] != '.png':
                raise Error('--savePath must have suffix \'.pdf\' or \'.png\'": --savePath = %s' % kwargs['savePath'])
            if not os.path.exists(os.path.dirname(kwargs['savePath'])):
                raise Error('Parent directory does not exist: %s' % os.path.dirname(kwargs['savePath']))
            if kwargs['type'] != None or kwargs['currentStage'] != None or kwargs['split'] in ['type', 'currentStage']:
                componentTypeJson = self.doSomething(action = 'getComponentTypeByCode', method = 'GET', data = {'project': kwargs['project'], 'code': kwargs['componentType']})
                if componentTypeJson['types'] == [] and (kwargs['split'] == 'type' or kwargs['type'] != []):
                    raise Error('Component type \'%s\' does not have any types and so it does not make sense to split/filter by type.' % kwargs['componentType'])
                if kwargs['type'] != None:
                    unknownTypes = [type for type in kwargs['type'] if type not in [type2['code'] for type2 in componentTypeJson['types']]]
                    if unknownTypes != []:
                        raise Error('Unknown type code(s) for component type \'%s\': [\'%s\']' % (kwargs['componentType'], '\', \''.join(unknownTypes)))
                if componentTypeJson['stages'] == [] and (kwargs['split'] == 'currentStage' or kwargs['currentStage'] != []):
                    raise Error('Component type \'%s\' does not have any types and so it does not make sense to split/filter by the current stage.' % kwargs['componentType'])
                if kwargs['currentStage'] != None:
                    unknownStages = [stage for stage in kwargs['currentStage'] if stage not in [stage2['code'] for stage2 in componentTypeJson['stages']]]
                    if unknownStages != []:
                        raise Error('Unknown stage code(s) for component type \'%s\': [\'%s\']' % (kwargs['componentType'], '\', \''.join(unknownStages)))
            if kwargs['currentLocation'] != None or kwargs['institution'] != None:
                institutionsJson = self.doSomething('listInstitutions', 'GET', data = {})
                if kwargs['currentLocation'] != None:
                    unknownCurrentLocations = [currentLocation for currentLocation in kwargs['currentLocation'] if currentLocation not in [currentLocation2['code'] for currentLocation2 in institutionsJson]]
                    if unknownCurrentLocations != []:
                        raise Error('Unknown current location code(s): [\'%s\']' % '\', \''.join(unknownCurrentLocations))
                if kwargs['institution'] != None:
                    unknownInstitutions = [institution for institution in kwargs['institution'] if institution not in [institution2['code'] for institution2 in institutionsJson]]
                    if unknownInstitutions != []:
                        raise Error('Unknown institution code(s): [\'%s\']' % '\', \''.join(unknownInstitutions))
        except Error as e:
            ERROR(e.message)
            STATUS('Finished with error.', False)
            sys.exit(1)
        INFO('Argument checking passed.')

        # Get our initial list of components from the DB
        data = {'project': kwargs['project'], 'componentType': kwargs['componentType']}
        if kwargs['type'] != None:
            data['type'] = kwargs['type']
        if kwargs['currentStage'] != None:
            data['currentStage'] = kwargs['currentStage']
        INFO('Fetching a list of components filtered by project/componentType/type/currentStage from the ITkPD...')
        components = self.doSomething(action = 'listComponents', method = 'GET', data = data)
        INFO('List of components successfully fetched (%s entries).' % len(components))

        # Get our cuts
        cuts = self.__getCuts(**kwargs)

        # Apply our cuts to each component and keep only the components which pass all cuts
        INFO('Applying cuts to the list of components and generating counter...')
        if kwargs['currentStage'] != None or kwargs['split'] == 'currentStage':
            addToCounter = self.__addToCounterWithStages
            finalizeCounter = lambda counter: self.__finalizeCounterWithStages(counter, **kwargs)
        else:
            addToCounter = self.__addToCounter
            finalizeCounter = lambda counter: self.__finalizeCounter(counter, **kwargs)
        split = kwargs['split']
        passedComponents = 0
        counter = {'total': []}
        for component in components:
            if self.__applyCuts(component, cuts):
                passedComponents += 1
                addToCounter(component, counter, split)

        if [0 for value in counter.values() if value == []] != []:
            INFO('Cuts successfully applied to list of components (0 entries).')
            WARNING('No components passed the cuts -- no plot to be generated.')

        else:

            # Finalize the counter and delete our old reference to components
            finalizeCounter(counter)
            del components
            INFO('Cuts successfully applied to list of components (%s entries).' % passedComponents)

            self.__drawPlots(counter, **kwargs)
def main(args):

    try:

        # Use the global definition of input
        global input

        print('')
        print(
            '******************************************************************************'
        )
        print(
            '*                            {0}{1}uploadITSDAQTests.py{2}                            *'
            .format(Colours.WHITE, Colours.BOLD, Colours.ENDC))
        print(
            '******************************************************************************'
        )
        print('')

        # Rename our argument variables
        results_path, all_files, all_tests, get_confirm, ps_path, cfg_path, up_files = args.results_path, args.all_files, args.all_tests, args.get_confirm, args.ps_path, args.cfg_path, args.up_files
        if ps_path != None:
            ps_path = os.path.abspath(ps_path) + '/'
        elif up_files:
            ERROR('--psPath must not be None if uploading files.')
            STATUS('Finished with error.', False)
            sys.exit(1)
        if cfg_path != None:
            cfg_path = os.path.abspath(cfg_path) + '/'
        elif up_files:
            ERROR('--cfgPath must not be None if uploading files.')
            STATUS('Finished with error.', False)
            sys.exit(1)
        if results_path != None:
            results_path = os.path.abspath(
                results_path) + '/' if os.path.isdir(
                    results_path) else os.path.abspath(results_path)

        # Check if results_path/ps_path/cfg_path exists (and that ps_path/cfg_path are directories)
        if not os.path.exists(results_path):
            raise FileNotFoundError(results_path)
        if up_files:
            if not os.path.exists(ps_path):
                raise FileNotFoundError(ps_path)
            if not os.path.isdir(ps_path):
                ERROR('--psPath is not a directory: ' + ps_path)
                STATUS('Finished with error.', False)
                sys.exit(1)
            if not os.path.exists(cfg_path):
                raise FileNotFoundError(cfg_path)
            if not os.path.isdir(cfg_path):
                ERROR('--cfgPath is not a directory: ' + cfg_path)
                STATUS('Finished with error.', False)
                sys.exit(1)

        INFO('Using:')
        print('    results_path = %s' % results_path)
        print('    all_files    = %s' % all_files)
        print('    all_tests    = %s' % all_tests)
        print('    get_confirm  = %s' % get_confirm)
        print('    ps_path      = %s' % ps_path)
        print('    cfg_path     = %s' % cfg_path)
        print('    up_files     = %s' % up_files)
        INFO('Launching ITkPDSession.')

        session = ITkPDSession()
        session.authenticate()

        fullTest = False

        # Check if results_path points to a directory
        if os.path.isdir(results_path):

            # Open our results directory
            results_directory = ResultsDirectory(results_path, ps_path,
                                                 cfg_path)
            results_directory.getResultsSummaryFilepaths()

            # Select everything if all_files
            if all_files:
                results_directory.openResultsSummaryFiles()
                summary_files = results_directory.returnResultsSummaryFiles()

            # State there is nothing if there is nothing
            elif results_directory.returnLengthFilepaths == 0:

                STATUS('Finished successfully.', True)
                sys.exit(0)

            # Else ask the user what they want to upload
            else:

                INFO('Looking in: ' + results_path)
                INFO('The following files were found:\n')
                results_directory.printSummary()
                print('')
                indices = getIndices(results_directory.returnLengthFilepaths())
                results_directory.openResultsSummaryFiles(*indices)
                summary_files = results_directory.returnResultsSummaryFiles()

        else:

            # Else if results_path points to a file, summary_files will contain only that file

            summary_files = [
                ResultsSummaryFile(results_path, ps_path, cfg_path)
            ]

        # Iterate over all of our summary files
        for summary_file in summary_files:

            INFO('Looking in: ' + summary_file.returnFilepath())

            # Get our tests
            try:
                summary_file.getTests()
            except FileNotFoundError as path:
                WARNING(
                    'Filepath does not exist: {0} -- skipping'.format(path))
                continue

            # If all_tests, automatically select all tests
            if all_tests:
                indices = range(summary_file.returnLengthTests())

            # Else, print a summary table and ask the user to specify which tests
            else:

                tests = summary_file.returnTests()

                INFO('The following tests were found:\n')
                summary_file.printSummary()
                print('')

                if len(tests) >= 7:
                    for i, test in enumerate(tests):
                        if ((test['JSON']['runNumber'].split('-')[0]
                             == tests[i +
                                      6]['JSON']['runNumber'].split('-')[0])
                                and
                            ((int(tests[i +
                                        6]['JSON']['runNumber'].split('-')[1])
                              - int(test['JSON']['runNumber'].split('-')[1]))
                             == 41)):
                            INFO(
                                'Run numbers {0} through {1} appear to be part of a FullTest, would you like to only upload those results?'
                                .format(test['JSON']['runNumber'],
                                        tests[i + 6]['JSON']['runNumber']))
                            INFO(
                                'Only the following files will be uploaded: <config>.det, <trim file>.trim, <mask file>.mask, and <post-trim response curve>.pdf.'
                            )
                            if getYesOrNo(
                                    'Please enter \'y\' to confirm the FullTest or \'n\' to only upload specific results:'
                            ):
                                INFO(
                                    'Only uploading results from the FullTest.'
                                )
                                indices = list(range(i, i + 7))
                                fullTest = True
                                break
                            else:
                                indices = getIndices(
                                    summary_file.returnLengthTests())
                                break

                else:
                    indices = getIndices(summary_file.returnLengthTests())

            # For each selected test, finalize the JSON
            for length_index, i in enumerate(indices):

                # If finalizeJSON() raises a skip, the component cannot be identified and so cannot be uploaded
                # i.e., we want to just continue over that component (this would likely happen to every other component in the ResultsSummaryFile)
                try:
                    summary_file.finalizeJSON(session, i)
                except Skip:
                    continue

                # If get_confirm, print the JSON and ask the user 'y' or 'n'
                if get_confirm:
                    INFO('Printing JSON for run number: ' +
                         summary_file.returnTests(i)['JSON']['runNumber'])
                    INFO('JSON:')
                    summary_file.printJSON(i)
                    if up_files:
                        if fullTest:
                            if length_index == 0:
                                del summary_file.tests[i]['files_to_upload'][
                                    'strobe_delay_path']
                                INFO(
                                    'The following file(s) will also be uploaded:'
                                )
                                try:
                                    print('    ' + summary_file.returnTests(i)
                                          ['files_to_upload']['det_path'])
                                except KeyError:
                                    print('    <None found>')
                            elif length_index == 2:
                                INFO(
                                    'The following file(s) will also be uploaded:'
                                )
                                try:
                                    print('    ' + summary_file.returnTests(i)
                                          ['files_to_upload']['trim_path'])
                                    print('    ' + summary_file.returnTests(i)
                                          ['files_to_upload']['mask_path'])
                                except KeyError:
                                    print('    <None found>')
                            elif length_index == 4:
                                INFO(
                                    'The following file(s) will also be uploaded:'
                                )
                                try:
                                    print('    ' + summary_file.returnTests(
                                        i)['files_to_upload']
                                          ['response_curve_path'])
                                except KeyError:
                                    print('    <None found>')
                        else:
                            INFO(
                                'The following file(s) will also be uploaded:')
                            if summary_file.returnTests(
                                    i)['files_to_upload'].keys() == []:
                                print('    <None found>')
                            else:
                                for file_key in summary_file.returnTests(
                                        i)['files_to_upload'].keys():
                                    print('    ' + summary_file.returnTests(i)
                                          ['files_to_upload'][file_key])
                    if not getYesOrNo(
                            'Please enter \'y\' to confirm the upload or \'n\' to cancel:'
                    ):
                        INFO('Cancelled upload of run number: ' +
                             summary_file.returnTests(i)['JSON']['runNumber'])
                        continue

                summary_file.uploadTests(session, i)
                if up_files:
                    summary_file.uploadFiles(session, i)
                INFO('Uploaded run number: ' +
                     summary_file.returnTests(i)['JSON']['runNumber'])

        STATUS('Finished successfully.', True)
        sys.exit(0)

    except KeyboardInterrupt:
        print('')
        ERROR('Exectution terminated.')
        STATUS('Finished with error.', False)
        sys.exit(1)

    except FileNotFoundError as e:
        ERROR('Path does not exist: {0}'.format(e))
        STATUS('Finished with error.', False)
        sys.exit(1)
示例#27
0
    def __cutOnValueWithOperator(self, component, operator, cutValue, keywords):

        try:

            # Get value at the level of the first keyword
            value = component[keywords[0]]

            # If there are multiple keywords, continue to update value (note, the order of the keywords should match the order in the uuComponent object)
            for keyword in keywords[1:]:
                if ':' in keyword:
                    index1, index2 = [int(item) for item in keyword.split(':')]
                    value = value[index1:index2]
                else:
                    value = value[keyword]

            # If the value passes the cut, return True, else return False (note: we have to check each case for the operator to be used)
            if operator == '<':
                if value < cutValue:
                    return True
                else:
                    if self.verbose:
                        INFO('Component \'%s\' failed at \'%s\' cut: \'%s\' = %s' % (component['code'], keywords[0], keywords[0], value))
                    return False
            elif operator == '>':
                if value > cutValue:
                    return True
                else:
                    if self.verbose:
                        INFO('Component \'%s\' failed at \'%s\' cut: \'%s\' = %s' % (component['code'], keywords[0], keywords[0], value))
                    return False
            elif operator == '<=':
                if value <= cutValue:
                    return True
                else:
                    if self.verbose:
                        INFO('Component \'%s\' failed at \'%s\' cut: \'%s\' = %s' % (component['code'], keywords[0], keywords[0], value))
                    return False
            elif operator == '>=':
                if value >= cutValue:
                    return True
                else:
                    if self.verbose:
                        INFO('Component \'%s\' failed at \'%s\' cut: \'%s\' = %s' % (component['code'], keywords[0], keywords[0], value))
                    return False
            elif operator == '==':
                if value == cutValue:
                    return True
                else:
                    if self.verbose:
                        INFO('Component \'%s\' failed at \'%s\' cut: \'%s\' = %s' % (component['code'], keywords[0], keywords[0], value))
                    return False
            elif operator == '!=':
                if value != cutValue:
                    return True
                else:
                    if self.verbose:
                        INFO('Component \'%s\' failed at \'%s\' cut: \'%s\' = %s' % (component['code'], keywords[0], keywords[0], value))
                    return False

        # If the keywords do not actually exist for the object, return False too
        except KeyError as error:
            if self.verbose:
                WARNING('Component \'%s\' failed due to KeyError: %s' % (component['code'], error))
            return False
示例#28
0
    def finalizeJSON(self, ITkPDSession, *args):

        if args == ():
            args = range(len(self.tests))

        for test_number in args:
            try:

                # Get the serial number
                serial_number = self.tests[test_number]['JSON']['properties'][
                    'SERIAL_NUMBER']

                # If it looks like a real serial number, we can probe the database directly
                if self.__checkSerialNumber(serial_number):

                    # Fetch the component and update the component code and institution
                    component = ITkPDSession.doSomething(
                        action='getComponent',
                        method='GET',
                        data={'component': serial_number})
                    self.tests[test_number]['JSON'].update({
                        'component':
                        component['code'],
                        'institution':
                        component['institution']['code']
                    })

                    # If it has a local object name, include that in properties
                    for property in component['properties']:
                        if (property['code']
                                == 'LOCALNAME') or (property['code']
                                                    == 'LOCAL_NAME'):
                            self.tests[test_number]['JSON']['properties'][
                                'LOCAL_OBJECT_NAME'] = property['value']
                    if 'LOCAL_OBJECT_NAME' not in self.tests[test_number][
                            'JSON']['properties'].keys():
                        self.tests[test_number]['JSON']['properties'][
                            'LOCAL_OBJECT_NAME'] = None

                # Else -- if it is not a valid serial number
                else:

                    # We'll filter the database contents by the local name and RFID, hoping that the serial number actually points to one of those
                    property_filter = [{
                        'code': 'LOCALNAME',
                        'operator': '=',
                        'value': serial_number
                    }, {
                        'code': 'LOCAL_NAME',
                        'operator': '=',
                        'value': serial_number
                    }, {
                        'code': 'RFID',
                        'operator': '=',
                        'value': serial_number
                    }]

                    # Get a list of components with our filter
                    component_list = ITkPDSession.doSomething(
                        action='listComponentsByProperty',
                        method='POST',
                        data={
                            'project': 'S',
                            'componentType': 'HYBRID',
                            'propertyFilter': property_filter
                        })

                    # Assert that the list be of length 1, or else we did not find the proper component (e.g., maybe local name is not unique?)
                    if len(component_list) == 1:

                        component = component_list[0]

                        # Update JSON with the component code, real serial number, institution, and local name
                        self.tests[test_number]['JSON'].update({
                            'component':
                            component['code'],
                            'institution':
                            component['institution']['code']
                        })
                        if component['serialNumber'] == None:
                            self.tests[test_number]['JSON']['properties'][
                                'SERIAL_NUMBER'] = None
                        else:
                            self.tests[test_number]['JSON']['properties'][
                                'SERIAL_NUMBER'] = component['serialNumber']
                        for property in component['properties']:
                            if (property['code']
                                    == 'LOCALNAME') or (property['code']
                                                        == 'LOCAL_NAME'):
                                self.tests[test_number]['JSON']['properties'][
                                    'LOCAL_OBJECT_NAME'] = property['value']
                        if 'LOCAL_OBJECT_NAME' not in self.tests[test_number][
                                'JSON']['properties'].keys():
                            self.tests[test_number]['JSON']['properties'][
                                'LOCAL_OBJECT_NAME'] = None

                    else:
                        WARNING(
                            'Component could not be identified using the serial number in the results file -- skipping.'
                        )
                        raise Skip

            except (IndexError, TypeError):
                pass
示例#29
0
    def listInventory(self):

        # Get our list of components filtered by project and component type
        if self.componentType is None or self.institution is None:
            ERROR("listInventory needs componentType and institution")
            return

        INFO('Fetching an inventory list associated with project code \'{0}\', component type(s) [\'{1}\'], and institution(s) [\'{2}\'] from the ITkPD...'.format(
                self.project, '\', \''.join(self.componentType), '\', \''.join(self.institution)))
        timestamp = time.strftime('%Y/%m/%d-%H:%M:%S')
        components = dbCommands['listComponents'].run(project = 'S', componentType = self.componentType)

        # Filter the components by institution (or by current location)
        if self.useCurrentLocation:
            locationKey = 'currentLocation'
        else:
            locationKey = 'institution'
        if self.includeTrashed:
            filterFunction = lambda component: component[locationKey]['code'] in self.institution
        else:
            filterFunction = lambda component: component[locationKey]['code'] in self.institution and not component['trashed']

        # Preallocate our inventory dictionary
        inventory = {}
        for institution in self.institution:
            inventory[institution] = {}
            for componentType in self.componentType:
                inventory[institution][componentType] = []

        # Sort through our list of components and filter our the desired keys
        keys = ['dummy', 'currentGrade', 'reworked', 'trashed', 'assembled', 'qaPassed', 'qaState']
        KeyErrorCounter = 0
        TypeErrorCounter = 0
        for component in components:
            try:
                if filterFunction(component):
                    data = dict((key, component[key]) for key in keys)
                    data['type'] = component['type']['code']
                    data['currentStage'] = component['currentStage']['code']
                    data['code'] = component['serialNumber'] if component['serialNumber'] != None else component['code']
                    inventory[component[locationKey]['code']][component['componentType']['code']].append(data)
                else:
                    pass
            except TypeError as error:
                print('Encountered TypeError %s for component \'%s\' -- skipping.' % (error, component['code']))
                TypeErrorCounter += 1                

        if KeyErrorCounter > 1:
            WARNING('%s components skipped due to key errors.' % KeyErrorCounter)
        if TypeErrorCounter > 1:
            WARNING('%s components skipped due to type errors.' % TypeErrorCounter)

        # Print our inventory and save the associated json
        INFO('Printing inventory list:\n')
        header = {'code': 'Code/Serial Number', 'type': 'Type', 'currentStage': 'Current Stage', 'dummy': 'Dummy', 'currentGrade': 'Current Grade', 'reworked': 'Reworked',
                    'trashed': 'Trashed', 'assembled': 'Assembled', 'qaPassed': 'QA Passed', 'qaState': 'QA State'}
        form = '        {code:<40}{type:<15}{currentStage:<20}{dummy:<15}{currentGrade:<20}{reworked:<15}{trashed:<15}{assembled:<15}{qaPassed:<15}{qaState:<15}'
        for institution, componentTypes in inventory.items():
            for componentType, components in componentTypes.items():
                print('    {0}{1}{2} / {3} :{4}\n'.format(Colours.BOLD, Colours.WHITE, institution, componentType, Colours.ENDC))
                print(Colours.BOLD + Colours.WHITE + form.format(**header) + Colours.ENDC)
                for component in components:
                    print(form.format(**component))
                print('')
        if self.savePath != None:
            self.__save({'timestamp': timestamp, 'function': 'listInventory', 'args': {'project': self.project, 'componentType': self.componentType,
                            'institution': self.institution, 'useCurrentLocation': self.useCurrentLocation, 'includeTrashed': self.includeTrashed},
                            'content': inventory})
示例#30
0
    def update(self,
               sender=None,
               recipient=None,
               type=None,
               status=None,
               name=None,
               trackingNumber=None,
               shippingService=None,
               shipmentItems=None):
        '''
        Update the shipment in the ITkPD.

        Args:
            sender (str): the (updated) code for the institution sending the shipment (default: None).
            recipient (str): the (updated) code for the institution receiving the shipment (default: None).
            type (str): the (updated) code for the type of shipment. Choose from ['domestic'|'intraContinental'|'continental'].(default: None).
            status (str): the (updated) code for the status of the shipment. Choose from ['prepared'|'inTransit'|'delivered'|'deliveredWithDamage'|'undelivered'] (default: None).
            name (str): the (updated) name of the shipment (default: None).
            trackingNumber (str): the (updated) tracking number associated with the shipment (default: None).
            shippingService (str): the (updated) shipping service delivering the shipment (default: None).
            shipmentItems (list[str]): the (updated) list of component codes for the components contained in the shipment (default: None).

        Notes:
            The args sender, recipient, type, and status are required, but if they are not changed from their default values (None), they will assume
            the values currently stored in self.json (all of the other args, if not specified, will not not affect the shipment).
        '''

        # If self.json is not None, set a required arg for the uuCMD equal to its present value in self.json if it's equal to None in the function call
        if self.json != None:
            if sender == None:
                sender = self.json['sender']['code']
            if recipient == None:
                recipient = self.json['recipient']['code']
            if type == None:
                type = self.json['type']
            if status == None:
                status = self.json['status']

        # Perform checking on the provided args
        if not self.expert:
            if self.json == None:
                WARNING(
                    'Class\'s json is not populated - updating a shipment is disallowed.'
                )
                INFO('No shipment updated.')
                return False
            if type not in ['domestic', 'intraContinental', 'continental']:
                WARNING(
                    'Shipping type \'{0}\' is not recognized.'.format(status))
                INFO('Shipment id {0} not updated.'.format(self.shipment))
                return False
            elif status not in [
                    'prepared', 'inTransit', 'delivered',
                    'deliveredWithDamage', 'undelivered'
            ]:
                WARNING('Shipping status \'{0}\' is not recognized.'.format(
                    status))
                INFO('Shipment id {0} not updated.'.format(self.shipment))
                return False
            elif shipmentItems != None and not isinstance(
                    shipmentItems, list) and [
                        item
                        for item in shipmentItems if not isinstance(item, str)
                    ] != []:
                WARNING(
                    'Shipment item(s) must be given as a list of component codes (strings).'
                )
                INFO('Shipment id {0} not updated.'.format(self.shipment))
                return False

        # Fetch our args and remove any keys with values set to None
        kwargs = {
            'name': name,
            'shipment': self.shipment,
            'sender': sender,
            'recipient': recipient,
            'trackingNumber': trackingNumber,
            'shippingService': shippingService,
            'type': type,
            'status': status,
            'shipmentItems': shipmentItems
        }
        dtoIn = {k: v for k, v in kwargs.items() if v is not None}

        # Update the shipment and fetch the updated json
        dbCommands['updateShipment'].run(**dtoIn)
        if not self.expert:
            self.get()
            INFO('Shipment id {0} updated.'.format(self.shipment))
        return True