def get_shape_type(input_filename):
def get_lines(input_filename, shape_type):
def get_open_polylines(input_filename):
def get_polygons(input_filename):
def get_all_fields(input_filename):
def get_attribute_names(input_filename):
def get_numeric_attribute_names(input_filename):
def get_points(input_filename, indices=None, with_z=False):
def write_shp_points_z(output_filename, z_name, points):
def write_shp_lines(output_filename, shape_type, lines, attribute_name): class SerafinData:
    def __init__(self, job_id, filename, language):
        self.job_id = job_id
        self.language = language
        self.filename = filename
        self.index = None
        self.triangles = {}
        self.header = None
        self.time = []  # <[float]>
        self.time_second = []  # <[datetime.timedelta]>  FIXME: should be renamed differently!
        self.start_time = None

        self.selected_vars = []
        self.selected_vars_names = {}
        self.selected_time_indices = []
        self.equations = []
        self.us_equation = None
        self.to_single = False

        self.operator = None
        self.metadata = {}
        # * select single layer: 'layer_selection': an integer
        # * vertical aggregation: 'vertical_operator': 'Min', 'Max' or 'Mean'

    def read(self):
        try:
            with Serafin.Read(self.filename, self.language) as input_stream:
                input_stream.read_header()
                input_stream.get_time()

                self.header = input_stream.header.copy()
                self.time = input_stream.time[:]
        except PermissionError:
            raise Serafin.SerafinRequestError('Permission denied (Is the file opened by another application?).')

        if self.header.date is not None:
            try:
                year, month, day, hour, minute, second = self.header.date
                self.start_time = datetime.datetime(year, month, day, hour, minute, second)
            except ValueError:
                logger.warning('Date seems invalid, replaced by default date.')
        if self.start_time is None:
            self.start_time = datetime.datetime(1900, 1, 1, 0, 0, 0)
        self.time_second = list(map(lambda x: datetime.timedelta(seconds=x), self.time))
        self.selected_vars = self.header.var_IDs[:]
        self.selected_vars_names = {var_id: (var_name, var_unit) for (var_id, var_name, var_unit)
                                    in zip(self.header.var_IDs, self.header.var_names, self.header.var_units)}
        self.selected_time_indices = list(range(len(self.time)))
        return self.header.is_2d

    def copy(self):
        copy_data = SerafinData(self.job_id, self.filename, self.language)
        copy_data.index = self.index
        copy_data.triangles = self.triangles
        copy_data.header = self.header
        copy_data.time = self.time
        copy_data.start_time = self.start_time
        copy_data.time_second = self.time_second
        copy_data.metadata = self.metadata

        copy_data.selected_vars = self.selected_vars[:]
        copy_data.selected_vars_names = deepcopy(self.selected_vars_names)
        copy_data.selected_time_indices = self.selected_time_indices[:]
        copy_data.equations = self.equations[:]
        copy_data.us_equation = self.us_equation
        copy_data.to_single = self.to_single
        copy_data.operator = self.operator
        return copy_data

    def default_output_header(self):
        output_header = self.header.copy()
        output_header.empty_variables()
        for var_ID in self.selected_vars:
            var_name, var_unit = self.selected_vars_names[var_ID]
            output_header.add_variable(var_ID, var_name, var_unit)
        if self.to_single:
            output_header.to_single_precision()
        return output_header

    def build_2d_output_header(self):
        output_header = self.header.copy_as_2d()
        output_header.empty_variables()
        for var_ID in self.selected_vars:
            var_name, var_unit = self.selected_vars_names[var_ID]
            output_header.add_variable(var_ID, var_name, var_unit)
        if self.to_single:
            output_header.to_single_precision()
        return output_header

    def transform_mesh(self, transformations):
        """!
        @brief Apply transformations on mesh nodes (only in 2D)
        @param transformations <[geom.transformation.Transformation]>: list of successive transformations
        """
        self.header.transform_mesh(transformations)
        self.index = None
        self.triangles = {} class ComplexExpressionMultiPool:
    def __init__(self):
        self.input_data = []
        self.nb_pools = 0
        self.pools = []
        self.representative = None

    def clear(self):
        self.input_data = []
        self.nb_pools = 0
        self.pools = []
        self.representative = None

    def get_data(self, input_data):
        """!
        @param input_data <slf.datatypes.SerafinData>: input SerafinData stream
        """
        self.input_data = input_data
        self.nb_pools = len(input_data)

        common_vars = set(input_data[0].header.var_IDs)
        for data in input_data[1:]:
            common_vars.intersection_update(data.header.var_IDs)
        variables = [var for var in input_data[0].header.var_IDs if var in common_vars]
        names = [name.decode(SLF_EIT).strip() for (var, name)
                 in zip(input_data[0].header.var_IDs, input_data[0].header.var_names) if var in common_vars]

        for data in input_data:
            pool = ComplexExpressionPool(variables, names, data.header.x, data.header.y)
            self.pools.append(pool)
        self.representative = self.pools[0]

    def add_polygonal_mask(self, polygons, attribute_index):
        for pool in self.pools:
            pool.add_polygonal_mask(polygons, attribute_index)

    def add_simple_expression(self, literal_expression):
        success_code = self.representative.add_simple_expression(literal_expression)
        if success_code not in (0, 1):
            return success_code
        for pool in self.pools[1:]:
            pool.add_simple_expression(literal_expression)
        return success_code

    def add_conditional_expression(self, condition, true_expression, false_expression):
        success_code = self.representative.add_conditional_expression(condition, true_expression, false_expression)
        if success_code not in (0, 1):
            return success_code
        for pool in self.pools[1:]:
            pool.add_conditional_expression(condition, true_expression, false_expression)
        return success_code

    def add_max_min_expression(self, first_expression, second_expression, is_max):
        success_code = self.representative.add_max_min_expression(first_expression, second_expression, is_max)
        if success_code not in (1, 2):
            return success_code
        for pool in self.pools[1:]:
            pool.add_max_min_expression(first_expression, second_expression, is_max)
        return success_code

    def add_masked_expression(self, inside_expression, outside_expression):
        for pool in self.pools:
            pool.add_masked_expression(inside_expression, outside_expression)

    def add_condition(self, expression, comparator, threshold):
        for pool in self.pools:
            pool.add_condition(expression, comparator, threshold)

    def add_and_or_condition(self, first_condition, second_condition, is_and):
        success_code = self.representative.add_and_or_condition(first_condition, second_condition, is_and)
        if success_code not in (0, 1):
            return success_code
        for pool in self.pools[1:]:
            pool.add_and_or_condition(first_condition, second_condition, is_and)
        return success_code

    def vars(self):
        return self.representative.vars

    def var_names(self):
        return self.representative.var_names

    def nb_expressions(self):
        return self.representative.nb_expressions

    def expressions(self):
        return self.representative.expressions

    def nb_masks(self):
        return self.representative.nb_masks

    def masks(self):
        return self.representative.masks

    def nb_conditions(self):
        return self.representative.nb_conditions

    def conditions(self):
        return self.representative.conditions

    def get_expression(self, text):
        return self.representative.get_expression(text)

    def get_condition(self, text):
        return self.representative.get_condition(text)

    def get_mask(self, text):
        return self.representative.get_mask(text)

    def ready_for_conditional_expression(self):
        return self.representative.ready_for_conditional_expression()

    def ready_for_max_min_expression(self):
        return self.representative.ready_for_max_min_expression()

    def ready_for_masked_expression(self):
        return self.representative.ready_for_masked_expression()

    def evaluable_expressions(self):
        for code, text in self.representative.evaluable_expressions():
            yield code, text

    def output_headers(self, selected_names):
        for data in self.input_data:
            output_header = data.header.copy()
            output_header.empty_variables()
            for name in selected_names:
                output_header.add_variable_str('DUMMY', name, '')
            yield output_header

    def build_augmented_path(self, selected_expressions):
        augmented_path = self.representative.get_dependence(selected_expressions[0])
        for expr in selected_expressions[1:]:
            path = self.representative.get_dependence(expr)
            for node in path:
                if node not in augmented_path:
                    augmented_path.append(node)
        return augmented_path

    def evaluate_iterator(self, selected_names):
        for data, output_header, pool in zip(self.input_data, self.output_headers(selected_names), self.pools):
            yield data.filename, data.header, output_header, pool class Serafin:
    """!
    @brief A Serafin object corresponds to a single Serafin in file IO stream

    # Attributes:
    - language <str>: language for variables ('fr' or 'en')
    - mode <str>: file open mode
    - file <_io.BufferedReader>: input Serafin stream
    - filename <str>: path to file
    - file_size <int>: size of file
    """
    def __init__(self, filename, mode, language):
        self.language = language
        self.mode = mode

        self.file = None  # will be opened when called using `with` statement
        self.filename = filename
        self.file_size = None

    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()
        return False class MultiLoadSerafinDialog(MultiLoadDialog):
    def __init__(self, old_options):
        super().__init__('Serafin', settings.SERAFIN_EXT, old_options) class MultiSaveDialog(QDialog):
    def __init__(self, suffix):
        super().__init__()
        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
                                   Qt.Horizontal, self)
        self.panel = OutputOptionPanel([suffix, True, '', False, False])

        buttons.accepted.connect(self.check)
        buttons.rejected.connect(self.reject)

        layout = QVBoxLayout()
        layout.addWidget(self.panel)
        layout.addStretch()
        layout.addWidget(buttons)
        self.setLayout(layout)

        self.setWindowTitle('Save multiple files')
        self.resize(500, 400)

    def check(self):
        success_code = self.panel.check()
        if success_code == 2:
            self.accept()
            return
        elif success_code == 1:
            return
        self.reject() def process_output_options(input_file, job_id, extension, suffix, in_source_folder, dir_path, double_name):
    input_path, input_name = os.path.split(input_file)
    input_rootname = os.path.splitext(input_name)[0]
    if double_name:
        output_name = input_rootname + '_' + job_id + suffix + extension
    else:
        output_name = input_rootname + suffix + extension
    if in_source_folder:
        filename = os.path.join(input_path, output_name)
    else:
        filename = os.path.join(dir_path, output_name)
    return filename class OutputProgressDialog(QProgressDialog):
    def __init__(self, message='Output in progress', title='Writing the output...', parent=None):
        super().__init__(message, 'OK', 0, 100, parent)
        self.setMinimumDuration(0)

        self.cancelButton = QPushButton('Cancel')
        self.setCancelButton(self.cancelButton)
        self.canceled.connect(self.cancel)

        self.setAutoReset(False)
        self.setAutoClose(False)

        self.setWindowTitle(title)
        self.setWindowFlags(Qt.WindowTitleHint)
        self.setFixedSize(300, 150)
        self.thread = None

        self.open()
        self.setValue(0)
        QApplication.processEvents()

    def outputFinished(self):
        self.setValue(100)
        self.setCancelButtonText('OK')
        logging.info('Finished writing the output.')

    def connectToThread(self, thread):
        self.thread = thread
        thread.tick.connect(self.setValue)

    def cancel(self):
        if self.cancelButton.text() == 'Cancel':
            if not self.thread.canceled:
                self.thread.canceled = True
                logging.info('Output canceled.') class OutputThread(QThread):
    tick = pyqtSignal(int, name='changed')

    def __init__(self):
        super().__init__()
        self.canceled = False class PyTelToolWidget(QWidget):
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.input = None
        if parent is None:
            self.language = settings.LANG
            self.csv_separator = settings.CSV_SEPARATOR
            self.fmt_float = settings.FMT_FLOAT
            self.logging_level = settings.LOGGING_LEVEL
        else:
            self.language = parent.language
            self.csv_separator = parent.csv_separator
            self.fmt_float = parent.fmt_float
            self.logging_level = parent.logging_level
        self.setMinimumWidth(600)
        self.setWindowFlags(self.windowFlags() | Qt.CustomizeWindowHint)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    def inDialog(self):
        if self.parent is not None:
            self.parent.inDialog()
        else:
            self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
            self.setEnabled(False)
            self.show()

    def outDialog(self):
        if self.parent is not None:
            self.parent.outDialog()
        else:
            self.setWindowFlags(self.windowFlags() | Qt.WindowCloseButtonHint)
            self.setEnabled(True)
            self.show()

    def switch_language(self, language):
        if language == 'fr':
            self.input.frenchButton.setChecked(True)
        else:
            self.input.englishButton.setChecked(True) def test_open(filename):
    try:
        with open(filename, 'rb'):
            pass
    except PermissionError:
        QMessageBox.critical(None, 'Permission denied',
                             'Permission denied. (Is the file opened by another application?).',
                             QMessageBox.Ok, QMessageBox.Ok)
        return False
    return True
import os
import struct
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QAbstractItemView, QApplication, QComboBox, QDialog, QDialogButtonBox,
                             QFileDialog, QGridLayout, QGroupBox, QHeaderView, QHBoxLayout,
                             QLabel, QLineEdit, QListWidget, QListWidgetItem, QMessageBox,
                             QPushButton, QRadioButton, QSpacerItem, QStackedLayout, QStyle,
                             QTableWidget, QTableWidgetItem, QTabWidget, QTextEdit,
                             QVBoxLayout, QWidget)
from pyteltools.geom import Shapefile
from pyteltools.slf.datatypes import SerafinData
from pyteltools.slf.expression.pool import ComplexExpressionMultiPool
from pyteltools.slf import Serafin
from pyteltools.workflow.util import MultiLoadSerafinDialog, MultiSaveDialog, process_output_options
from .util import OutputProgressDialog, OutputThread, PyTelToolWidget, test_open




class WriteVariableThread(OutputThread):
    def __init__(self, pool, selected_expressions, selected_names, output_names, overwrite):
        super().__init__()
        self.pool = pool
        self.inv_nb_files = 1 / pool.nb_pools

        self.selected_expressions = selected_expressions
        self.augmented_path = self.pool.build_augmented_path(self.selected_expressions)
        self.selected_names = selected_names
        self.output_names = output_names
        self.overwrite = overwrite

    def run_single(self, input_name, input_header, output_name, output_header, pool):
        i = 0
        try:
            with Serafin.Read(input_name, input_header.language) as input_stream:
                input_stream.header = input_header
                input_stream.get_time()
                inv_nb_frames = len(input_stream.time)

                with Serafin.Write(output_name, input_header.language) as output_stream:
                    output_stream.write_header(output_header)

                    for time_value, value_array in pool.evaluate_expressions(self.augmented_path,
                                                                             input_stream, self.selected_expressions):
                        if self.canceled:
                            return
                        i += 1
                        output_stream.write_entire_frame(output_header, time_value, value_array)

                        self.tick.emit(100 * i * self.inv_nb_files * inv_nb_frames)
                        QApplication.processEvents()

        except (Serafin.SerafinRequestError, Serafin.SerafinValidationError) as e: