Giter Club home page Giter Club logo

import_me's People

Contributors

arondit avatar dodgyturtle avatar faunris avatar ivan-feofanov avatar korneevm avatar kvbrg avatar lerikp avatar lighter97 avatar mcproger avatar melevir avatar rapiskal avatar vladimir-kotikov avatar vquadx avatar winazar avatar yakovistomin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

import_me's Issues

BaseProcessor validators

Необходимо добавить необязательный параметр validators: Optional[List[Callable]] в BaseProcessor

Пример использования:

IntegerProcessor(…., validators=[lambda x: x > 10, lambda x: x <= 1000])
StringProcessor(..., validators=[lambda x: x.startswith('a')])
DateProcessor(..., validators=[lambda x: x.year == 2000])

Валидируем значение, возвращаемое process_value. Если хоть один из валидаторов вернул False или вызвал исключение, то ColumnError(f'{value} is not valid')

Add Column.header validator

Сейчас заголовок колонки проверяется на полное соответствие значению Column.header.
Необходимо добавить возможность указания функции для валидации заголовка.

Пример1:

def test_validate_headers(column):
    class Parser(BaseXLSXParser):
        columns = [
            Column('column_name', header='№'),
        ]

    xlsx_file = xlsx_file_factory(header=['№'], data=[])
    parser = Parser(file_path=xlsx_file.file)

    parser()

    assert parser.has_errors is False



def test_validate_headers_with_validator(column):
    class Parser(BaseXLSXParser):
        columns = [
            Column('column_name', header='№', header_validator=lambda x: x in ['№', 'Номер', '№пп']),
        ]

    xlsx_file = xlsx_file_factory(header=['№пп'], data=[])
    parser = Parser(file_path=xlsx_file.file)

    parser()

    assert parser.has_errors is False



def test_validate_headers_error(header):
    class Parser(BaseXLSXParser):
        columns = [
            Column('column_name', header='№'),
        ]

    xlsx_file = xlsx_file_factory(header=['fail header'], data=[])
    parser = Parser(file_path=xlsx_file.file)

    parser()

    assert parser.has_errors is True
    assert parser.errors == (
        "Incorrect column names in the file. "
        "Columns in file: ['fail header']. "
        "Expected columns: ['№']."
    )



def test_validate_headers_with_validator_error(header):
    class Parser(BaseXLSXParser):
        columns = [
            Column('column_name', header='№', header_validator=lambda x: x in ['№', 'Номер', '№пп']),
        ]

    xlsx_file = xlsx_file_factory(header=['fail header'], data=[])
    parser = Parser(file_path=xlsx_file.file)

    parser()

    assert parser.has_errors is True
    assert parser.errors == (
        "Incorrect column names in the file. "
        "Columns in file: ['fail header']. "
        "Expected columns: ['№']."
    )

Add Column.name validation

Название колонок:

  • должно состоять из символов английского алфавита, цифр и знаков подчеркивания
  • не может начинаться с подчеркивания
  • не должно быть пустым
  • название всех колонок должны быть уникальными

Parser context

Добавить возможность передавать в парсер контекст.
Контекст должен быть доступен в любой момент при обработке данных, а также прокидываться колонки и обработчики

class MyProcessor(IntegerProcessor):
    def process_value(value, context):
        value = super().process_value(value, context)
        value += context.get(“i”, 10):
        return value

class MyParser(BaseXLSXParser):
    columns = [
        Column(“column1”, processor=MyProcessor())
    ]

    def clean_column_column1(self, value):
        value += self.context.get(“j”, 2)
        return value

context = {“i”: 10, “j”: 20}

parser = MyParser(filr_path, context=context)
        

Разделить Parser и ParserResult

Сейчас парсер инстанцируется с файлом для импорта, а потом начинает содержать в себе ошибки и данные. Это, может быть, вдохновлено Serializer'ами из DRF, но в целом не сильно удобно и смешивает сущности.

>>> parser = XLSXParser(file_path=xlsx_filepath)
>>> parser()
>>> print(parser.has_errors)  # False
>>> pprint(parser.cleaned_data)

Почему бы не передавать файл для импорта в метод parse(), отдавая некий ParseResult?

>>> parser = XLSXParser()
>>> result = parser.parse(file_path=xlsx_filepath)
>>> print(result.has_errors)  # False
>>> pprint(result.cleaned_data)

Это позволит отделить настройки парсера от результата, собрать в парсере только методы для импорта файлов, файлов по шаблону и т.п.

Sorted errors wanted

When you customize your parser it is very likely that the order of raised errors will be broken.
Say you have some column that is required=True and you need some overall post-validation.
row1 pass requirement validation, but fails on post-validation
row 2 fails requirement validation
Thus in self.errors you will get messages in order: row2, row1

Maybe storing errors as objects with some attributes will make sorting simple, however it will require some additional memory.

Disallow multiple Parser calls

При повторном вызове метода call парсера необходимо вызывать исключение.

Пример:

def test_parser_multiple_call():
    parser = Parser()

    parser()
    with pytest.raises(ParserError):
        parser()

ClassifierProcessor

Добавить обработчик, позволяющий классифицировать данные

Пример:

processor = ClassifierProcessor(
    choices={
        'a': lambda x: 0 <= x <= 10,
        'b': lambda x: 10 <= x <= 100,
        'c': lambda x: isinstance(x, str),
    })

assert processor(3) == 'a'
assert processor('test') == 'c'

Feature request: EnumerateProcessor

csv/xlsx data often has enum fields, that are stored as integers or strings. Now we should parse raw data, than map them to appropriate enum.

It would be nice to have processor, that does all that mapping logic.

Add import encoding parametrization

Now when parsing win1251 csv/xlsx file, parser raises ParserError: 'utf-8' codec can't decode byte 0xc4 in position 1: invalid continuation byte.

It would be nice to:

  1. Supply file encoding as unrequired parameter.
  2. Try to guess file encoding.

Feature request: strip whitespaces in BaseProcessor

Proposition:
I propose to strip whitespaces in BaseProcessor and assume whitespaced values as None.

Reason:
Cells with whitespaces in large files are hard to find for users and displayed error is not clear to them.

Expected behavior:

def test_integer_processor_with_whitespace():
    processor = IntegerProcessor()

    assert processor(' ') is None

Current result:

import_me.exceptions.ColumnError:   is not an integer.

Стратеги поиска данных в файле

Проблема 1:
Сейчас парсер получает данные из первого (активного) листа xlsx файла.

Не обработаны случаи:

  • Файл содержит несколько листов (worksheet) с данными и все их нужно обработать.
  • Файл содержит несколько листов и данные находятся на каком-то конкретном листе.
  • Файл содержит несколько листов и не все они с данными. Нужно обработать несколько листов

Проблема 2:
Сейчас заголовок таблицы ищется в строке header_row_index страницы.

Не обработаны случаи:

  • header_row_index изначально неизвестен. Допустим в файле сначала идет шапка документа произвольной высоты, а дальше таблица с данными, которые нужно получить.
  • на разных страницах документа заголовок таблицы находится на разных строках
  • для разных колонок заголовок находится на разных стоках (заголовок многострочный)

Проблема 3:
Под таблицей с данными может быть колонтитул (например ячейки с местами для подписи).

Не решена проблема определения последней строки с данными.
Сейчас можно или указать last_row_index, или завязаться на наличие значение в какой-то из колонок и делать SkipRow для всех строк, у которых в этой ячейке нет данных.
Кажется, что этого мало.

Решение
Предлагаю ввести понятия:

  • Стратегия поиска листа с данными
  • Стратегия поиска заголовка таблицы с данными
  • Стратегия поиска первой и последней строки с данными.

Стратегия поиска листа с данными

Актуально только для xlsx документов

  • активный лист (default) - парсер получает данные с активного листа файла.
  • все листы файла - парсер получает данные со всех листов файла
  • список листов - указываем названия или индексы листов, парсер собирает данные только из них
  • все листы, на которых были найдены таблицы с данными - перебираем все листы, проверяем наличие правильного заголовка таблицы (подумать на какие еще признаки можно опереться)

Стратегия поиска заголовка таблицы с данными

  • по header_row_index (default) - данные лежат в конкретной строке, индекс которой заранее известен
  • первая строка, значения ячеек в которой совпадают с ожидаемым заголовком таблицы - перербираем построчно файл, каждую строку сравниваем с ожидаемым заголовком. Первое совпадение - строка с заголовком таблицы

Стратегия поиска первой строки с данными

  • по first_data_row_index - данные лежат начиная со строки, индекс которой заранее известен
  • первая непустая строка после заголовка
  • n строка после заголовка
  • первая строка удовлетворяющая условию

Стратегия поиска последней строки с данными

  • конец файла - все строки до конца файла содержат данные
  • по last_data_row_index
  • первая строка удовлетворяющая условию

Replace __ call__ method with parse_data

Необходимо заменить метод call на parse_data

Было

parser = MyParser()
parser()

Стало

parser = MyParser()
parser.parse_data()

Для обратной совместимости можно ставить оба метода

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.