best-doctor / import_me Goto Github PK
View Code? Open in Web Editor NEWPython library to simplify importing data from xls/xlsx
License: MIT License
Python library to simplify importing data from xls/xlsx
License: MIT License
Необходимо добавить необязательный параметр 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')
Now validations error are in russian, we should translate it.
Сейчас заголовок колонки проверяется на полное соответствие значению 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: ['№']."
)
Название колонок:
String data often has trash start, like last_card_num_digits *1234
(*
should be removed).
It would be nice if StringProcessor
could automatically remove specified prefixes.
Добавить возможность передавать в парсер контекст.
Контекст должен быть доступен в любой момент при обработке данных, а также прокидываться колонки и обработчики
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)
Сейчас парсер инстанцируется с файлом для импорта, а потом начинает содержать в себе ошибки и данные. Это, может быть, вдохновлено 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)
Это позволит отделить настройки парсера от результата, собрать в парсере только методы для импорта файлов, файлов по шаблону и т.п.
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.
При повторном вызове метода call парсера необходимо вызывать исключение.
Пример:
def test_parser_multiple_call():
parser = Parser()
parser()
with pytest.raises(ParserError):
parser()
Now formats are required argument when using DateProcessor/DatetimeProcessor, that makes parser looks more verbose.
It would be nice if DatetimeProcessor
without formats
argument tried to guess datetime format.
Добавить обработчик, позволяющий классифицировать данные
Пример:
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'
Docs should be autogenerated from dosctrings and be released at readthedocs.
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.
Description tbd.
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:
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 файла.
Не обработаны случаи:
Проблема 2:
Сейчас заголовок таблицы ищется в строке header_row_index страницы.
Не обработаны случаи:
Проблема 3:
Под таблицей с данными может быть колонтитул (например ячейки с местами для подписи).
Не решена проблема определения последней строки с данными.
Сейчас можно или указать last_row_index, или завязаться на наличие значение в какой-то из колонок и делать SkipRow для всех строк, у которых в этой ячейке нет данных.
Кажется, что этого мало.
Решение
Предлагаю ввести понятия:
Стратегия поиска листа с данными
Актуально только для xlsx документов
Стратегия поиска заголовка таблицы с данными
Стратегия поиска первой строки с данными
Стратегия поиска последней строки с данными
Необходимо заменить метод call на parse_data
Было
parser = MyParser()
parser()
Стало
parser = MyParser()
parser.parse_data()
Для обратной совместимости можно ставить оба метода
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.