Ejemplo n.º 1
0
    def parse_idf(self, raw_idf, file_path=None):
        """Parse the provided idf file and populate an IDFFile object with objects.

        :param raw_idf: File-like object containing idf to parse
        :param str file_path: option file path to use to fetch an idf to parse
        :returns: Yields a progress counter between 0 and 100
        :rtype: generator
        """

        self.idf.file_path = file_path
        if file_path:
            total_size = os.path.getsize(file_path)
        else:
            total_size = len(raw_idf.getvalue())
        total_read = 0.0
        log.info('Parsing IDF: {} ({} bytes)'.format(
            file_path or 'pasted text', total_size))

        # Prepare some variables to store the results
        fields = StringIO()
        field_objects = list()
        comment_list = list()
        comment_list_special = list()

        # Cycle through each line in the file (yes, use while!)
        while True:

            # Parse this line using readline (so last one is a blank)
            line = raw_idf.readline()
            total_read += len(line)

            # Split out any comments and save them
            part, sep, comment = line.partition(COMMENT_DELIMITER_GENERAL)
            comment_cleaned = comment.strip()
            if comment_cleaned.startswith('-'):
                if comment_cleaned.lower().startswith('-option'):
                    options = [x for x in OPTIONS_LIST if x in comment_cleaned]
                    self.idf.options.extend(options)
                else:
                    comment_list_special.append(comment_cleaned)
            elif sep:
                comment_list.append(comment)

            # Write new fields to string containing previous fields
            fields.write(part)

            # Detect end of file and break. Do it this way to be sure
            # the last line can be identified as last AND still be processed!
            if not line:
                break

            # Check for end of an object and skip rest of loop unless we find one
            if part.find(OBJECT_END_DELIMITER) == -1:
                continue

            # ---- If we've gotten this far, we're at the end of an object ------

            # Clean up the fields and strip spaces, end of line chars
            fields = [
                str.strip(my_str) for my_str in fields.getvalue().split(',')
            ]
            fields[-1] = fields[-1].replace(OBJECT_END_DELIMITER, '')

            # The first field is the object class name
            obj_class = fields.pop(0).lower()

            # Detect idf file version and use it to select idd file
            if obj_class == 'version':
                self.assign_idd(fields[0])

            # Create a new IDF Object to contain the fields
            idf_object = idfmodel.IDFObject(self.idf, obj_class)

            # Save the comment variables to the idf_object
            idf_object.comments_special = comment_list_special
            idf_object.comments = comment_list

            # Strip white spaces, end of line chars from last comment
            if idf_object.comments:
                last_comment = idf_object.comments[-1]
                idf_object.comments[-1] = last_comment.rstrip()

            # Create local copies of some methods to speed lookups
            append_idf_object = idf_object.append
            create_field = idfmodel.IDFField
            append_new_field = field_objects.append

            try:
                # Create IDFField objects for all fields and add them to the IDFObject
                for index, value in enumerate(fields):
                    new_field = create_field(idf_object, value, index=index)
                    append_idf_object(new_field)

                    # Store the field in a list to be passed to SQL later
                    if file_path is not None:
                        append_new_field((new_field.uuid, obj_class,
                                          new_field.obj_class_display,
                                          new_field.ref_type, value))
            except IDDError:
                self.assign_idd()

            # Save the new object to the IDF
            self.idf.add_objects(obj_class, idf_object, update=False)

            # Reset variables for next object
            fields = StringIO()
            comment_list = list()
            comment_list_special = list()

            # Yield the current progress for progress bars
            yield math.ceil(100.0 * total_read / total_size)

        # Be sure we're finished at this point (bytes read is not always accurate!)
        yield 100.0

        # Execute the SQL to insert the new objects
        if file_path is not None:
            insert_operation = "INSERT INTO idf_objects VALUES (?,?,?,?,?)"
            self.idf.db.executemany(insert_operation, field_objects)
            self.idf.db.commit()

        log.info('Parsing IDF complete!')