Skip to content

Commit

Permalink
Merge pull request #36 from Studio-Yandex-Practicum/feature/union_doc…
Browse files Browse the repository at this point in the history
…x_&_xlxs_parsers

Исправил конфликты, добавил poetry run
  • Loading branch information
Andrewprvrzv authored Nov 17, 2023
2 parents 8e1e3d3 + 86cc511 commit 9eab507
Show file tree
Hide file tree
Showing 10 changed files with 298 additions and 220 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,4 @@ cython_debug/
# Static
static/

/adaptive_hockey_federation/parser/Именная заявка/
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,16 @@ docker-compose up -d --build
```

Django-проект и Nginx запустятся в контейнерах, при помощи инструкций в entrypoint.sh через 10 секунд добавится статика


## Парсинг файлов

```shell
poetry run parser --path </путь до папки с файлами>
```
Запуск парсера документов для заполнения БД.
Опциональные параметры:
--result Вывод в консоль извлеченных данных и статистики
--help Вызов справки

!!!Необходимо скопировать папку с файлами в контейнер или примонтировать через -volume
50 changes: 32 additions & 18 deletions adaptive_hockey_federation/core/user_card.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dataclasses import dataclass
from datetime import date
from typing import Optional, Union
from typing import Union


@dataclass
Expand All @@ -11,25 +11,39 @@ class BaseUserInfo:
surname: Union[str, None]
date_of_birth: Union[date, None]
team: Union[str, None]
position: Union[str, None]
player_number: Union[int, None]


@dataclass
class HockeyData(BaseUserInfo):
"""Класс с необязательными полями из документов формата docx.
"""
patronymic: Union[str, None]
birth_certificate: Union[str, None]
passport: Union[str, None]
position: Union[str, None]
numeric_status: Union[int, None]
patronymic: Union[str, None] = None
birth_certificate: Union[str, None] = None
passport: Union[str, None] = None
classification: Union[float, None] = None
revision: Union[str, None] = None
is_assistant: bool = False
is_captain: bool = False

def __eq__(self, other):
return all(
getattr(self, attr) == getattr(other, attr) for attr in vars(self)
)

@dataclass
class ExcelData(BaseUserInfo):
"""Класс с необязательными полями из таблицы
"Реестр классов ХДН.xlsx".
"""
classification: Union[float, None]
numeric_status: Optional[int] = None
def __hash__(self):
return hash(
(
self.name,
self.surname,
self.date_of_birth,
self.team,
self.player_number,
self.position,
self.numeric_status,
self.patronymic,
self.birth_certificate,
self.passport,
self.classification,
self.revision,
self.is_assistant,
self.is_captain,

)
)
57 changes: 15 additions & 42 deletions adaptive_hockey_federation/parser/docx_parser.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import os
import re
from datetime import date
from typing import Optional

import docx # type: ignore

from adaptive_hockey_federation.core.user_card import ( # type: ignore
HockeyData,
)
from adaptive_hockey_federation.core.user_card import BaseUserInfo

NAME = '[И|и][М|м][Я|я]'
SURNAME = '[Ф|ф][А|а][М|м][И|и][Л|л][И|и][Я|я]'
Expand Down Expand Up @@ -314,10 +311,14 @@ def numeric_status_check(
return None


def parser(file: docx, numeric_statuses: list[list[str]]) -> list[HockeyData]:
def docx_parser(
path: str,
numeric_statuses: list[list[str]]
) -> list[BaseUserInfo]:
"""Функция собирает все данные об игроке
и передает их в dataclass.
"""
file = docx.Document(path)
columns_from_file = read_file_columns(file)
text_from_file = read_file_text(file)
names = find_names(columns_from_file, NAME)
Expand All @@ -332,48 +333,20 @@ def parser(file: docx, numeric_statuses: list[list[str]]) -> list[HockeyData]:
positions = find_positions(columns_from_file, POSITION)

return [
HockeyData(
names[index],
surnames[index],
patronymics[index],
dates_of_birth[index],
team,
players_number[index],
positions[index],
numeric_status_check(
BaseUserInfo(
name=names[index],
surname=surnames[index],
date_of_birth=dates_of_birth[index],
team=team,
player_number=players_number[index],
position=positions[index],
numeric_status=numeric_status_check(
names[index],
surnames[index],
patronymics[index],
numeric_statuses,
),
patronymic=patronymics[index],
)
for index in range(len(names))
]


if __name__ == '__main__':
files_dir = '/Users/frost/dev/adaptive_hockey_federation/Именная заявка/'
numeric_status_file = (
'/Users/frost/dev/adaptive_hockey_federation/'
'Числовые статусы следж-хоккей 02.10.203.docx'
)
numeric_statuses = find_numeric_statuses(
docx.Document(numeric_status_file)
)
for root, directories, files in os.walk(files_dir):
for file in files:
if file.startswith('~'):
pass
else:
if (
file.endswith('.docx')
and file != 'На мандатную комиссию.docx'
and file != (
'Именная заявка следж-хоккей Энергия Жизни Сочи.docx'
)
and file != 'ФАХ Сияжар Взрослые.docx'
):
parser(
docx.Document(os.path.join(root, file)),
numeric_statuses,
)
93 changes: 93 additions & 0 deletions adaptive_hockey_federation/parser/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import os
from pprint import pprint

import click
import docx # type: ignore

from adaptive_hockey_federation.parser.docx_parser import (
docx_parser,
find_numeric_statuses,
)
from adaptive_hockey_federation.parser.xlsx_parser import xlsx_parser

NUMERIC_STATUSES = 'Числовые статусы следж-хоккей 02.10.203.docx'
FILES_BLACK_LIST = [
'На мандатную комиссию',
'Именная заявка следж-хоккей Энергия Жизни Сочи',
'ФАХ Сияжар Взрослые',
'Числовые статусы следж-хоккей 02.10.203',
]
FILES_EXTENSIONS = [
'.docx',
'.xlsx',
]
NUMERIC_STATUSES_FILE_ERROR = ('Не могу найти {}. Без него не'
' получиться загрузить именные заявки.'
' Файл должен находиться в директории с'
' файлами для парсинга')


@click.command()
@click.option(
'-p',
'--path',
required=True,
help='Путь до папки с файлами для парсинга',
)
@click.option(
'-r',
'--result',
is_flag=True,
help='Вывод в консоль извлеченных данных и статистики',
)
def parsing_file(path: str, result: bool) -> None:
"""Функция запускает парсинг файлов в рамках проекта.
Запуск через командную строку:
'python parser.py -p(--path) путь_до_папки_с_файлами'
Вызов справки 'python parser.py -h(--help)'
"""
results_list = []
files, numeric_statuses_file = get_all_files(path)
if numeric_statuses_file is None:
click.echo(NUMERIC_STATUSES_FILE_ERROR.format(NUMERIC_STATUSES))
return
numeric_statuses = find_numeric_statuses(
docx.Document(numeric_statuses_file)
)
click.echo(f'Найдено {len(files)} файлов.')
for file in files:
if file.endswith('docx'):
results_list.extend(docx_parser(file, numeric_statuses))
else:
results_list.extend(xlsx_parser(file)) # type: ignore
if result:
for data in results_list:
pprint(data)
results_list = list(set(results_list))
click.echo(f'Успешно обработано {len(files)} файлов.')
click.echo(f'Извлечено {len(results_list)} уникальных записей')


def get_all_files(path: str) -> tuple[list[str], str | None]:
"""Функция извлекает из папки, в том числе вложенных,
список всех файлов и отдельно путь до файла с числовыми статусами.
Извлекаются только файлы с расширениями указанными в константе
FILES_EXTENSIONS (по умолчанию docx, xlsx) и не извлекает файлы, название
которых без расширения указано в списке FILES_BLACK_LIST.
"""
files = []
numeric_statuses_filepath = None
for dirpath, dirnames, filenames in os.walk(path):
for filename in filenames:
if filename == NUMERIC_STATUSES:
numeric_statuses_filepath = os.path.join(dirpath, filename)
file, extension = os.path.splitext(filename)
if (not file.startswith('~')
and extension in FILES_EXTENSIONS
and file not in FILES_BLACK_LIST):
files.append(os.path.join(dirpath, filename))
return files, numeric_statuses_filepath


if __name__ == '__main__':
parsing_file()
59 changes: 0 additions & 59 deletions adaptive_hockey_federation/parser/parser_1.py

This file was deleted.

31 changes: 31 additions & 0 deletions adaptive_hockey_federation/parser/xlsx_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import openpyxl

from adaptive_hockey_federation.core.user_card import BaseUserInfo


def xlsx_parser(path: str) -> list[BaseUserInfo]:
"""Функция парсит xlsx файлы и возвращает
игроков в виде dataclass ExcelData.
"""
players = []
sheet_data = []
workbook = openpyxl.load_workbook(path)
sheet = workbook.active
header = [cell.value for cell in sheet[1]] # type: ignore
for row in sheet.iter_rows(min_row=2, values_only=True): # type: ignore
sheet_data.append(dict(zip(header, row)))
for data in sheet_data:
if data.get('ФИО игрока') is not None:
player = BaseUserInfo(
team=data.get('Команда'),
name=data.get('ФИО игрока').split()[0], # type: ignore
surname=data.get('ФИО игрока').split()[1], # type: ignore
date_of_birth=data.get('Дата рождения'),
player_number=data.get('Номер игрока'),
position=data.get('Позиция'),
classification=data.get('Класс'),
revision=data.get('Пересмотр (начало сезона)'),
numeric_status=None
)
players.append(player)
return players
Binary file not shown.
Loading

0 comments on commit 9eab507

Please sign in to comment.