From 4498fed2473f217c0d283f63c02913a69b910014 Mon Sep 17 00:00:00 2001 From: Marina Date: Fri, 2 Jun 2023 17:52:39 +0300 Subject: [PATCH 01/19] add empty_slide_check --- app/main/check_packs/pack_config.py | 3 ++- app/main/checks/__init__.py | 3 ++- .../checks/presentation_checks/__init__.py | 1 + .../presentation_checks/empty_slide_check.py | 23 +++++++++++++++++++ 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 app/main/checks/presentation_checks/empty_slide_check.py diff --git a/app/main/check_packs/pack_config.py b/app/main/check_packs/pack_config.py index cf0b85b2..52758e9c 100644 --- a/app/main/check_packs/pack_config.py +++ b/app/main/check_packs/pack_config.py @@ -14,7 +14,8 @@ ['pres_right_words'], ['pres_image_share'], ['future_dev'], - ['pres_banned_words_check'] + ['pres_banned_words_check'], + ['pres_empty_slide'], ] BASE_REPORT_CRITERION = [ ["simple_check"], diff --git a/app/main/checks/__init__.py b/app/main/checks/__init__.py index 8f65c507..9a6fa009 100644 --- a/app/main/checks/__init__.py +++ b/app/main/checks/__init__.py @@ -17,7 +17,8 @@ PresRightWordsCheck.id: PresRightWordsCheck, PresImageShareCheck.id: PresImageShareCheck, FurtherDev.id: FurtherDev, - PresBannedWordsCheck.id: PresBannedWordsCheck + PresBannedWordsCheck.id: PresBannedWordsCheck, + PresEmptySlideCheck.id: PresEmptySlideCheck, }, 'report': { ReportSimpleCheck.id: ReportSimpleCheck, diff --git a/app/main/checks/presentation_checks/__init__.py b/app/main/checks/presentation_checks/__init__.py index 81ec9cbd..d9df81ca 100644 --- a/app/main/checks/presentation_checks/__init__.py +++ b/app/main/checks/presentation_checks/__init__.py @@ -10,3 +10,4 @@ from .pres_right_words import PresRightWordsCheck from .image_share import PresImageShareCheck from .banned_words import PresBannedWordsCheck +from .empty_slide_check import PresEmptySlideCheck diff --git a/app/main/checks/presentation_checks/empty_slide_check.py b/app/main/checks/presentation_checks/empty_slide_check.py new file mode 100644 index 00000000..5758e2bf --- /dev/null +++ b/app/main/checks/presentation_checks/empty_slide_check.py @@ -0,0 +1,23 @@ + +from app.utils.parse_for_html import format_header +from ..base_check import BasePresCriterion, answer + + +class PresEmptySlideCheck(BasePresCriterion): + description = "Проверка наличия пустых слайдов в презентации" + id = 'pres_empty_slide' + + def __init__(self, file_info): + super().__init__(file_info) + + def check(self): + empty_pages = [] + for page, slide in enumerate(self.file.get_text_from_slides(), 1): + if not slide.strip(): + empty_pages.append(page) + if empty_pages: + empty_pages = self.format_page_link(empty_pages) + return answer(False, format_header( + 'Не пройдена, обнаружены пустые слайды: {}'.format(', '.join(map(str, empty_pages))))) + else: + return answer(True, "Пройдена!") From 8b41857e6a3fb3313066bfa432e6ef6656727ed6 Mon Sep 17 00:00:00 2001 From: Marina Date: Tue, 13 Jun 2023 11:21:52 +0300 Subject: [PATCH 02/19] optimize script --- .../checks/presentation_checks/empty_slide_check.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/main/checks/presentation_checks/empty_slide_check.py b/app/main/checks/presentation_checks/empty_slide_check.py index 5758e2bf..64c4952e 100644 --- a/app/main/checks/presentation_checks/empty_slide_check.py +++ b/app/main/checks/presentation_checks/empty_slide_check.py @@ -11,13 +11,9 @@ def __init__(self, file_info): super().__init__(file_info) def check(self): - empty_pages = [] - for page, slide in enumerate(self.file.get_text_from_slides(), 1): - if not slide.strip(): - empty_pages.append(page) + empty_pages = [page for page, slide in enumerate(self.file.get_text_from_slides(), 1) if not slide.strip()] if empty_pages: - empty_pages = self.format_page_link(empty_pages) return answer(False, format_header( - 'Не пройдена, обнаружены пустые слайды: {}'.format(', '.join(map(str, empty_pages))))) + 'Не пройдена, обнаружены пустые слайды: {}'.format(', '.join(self.format_page_link(empty_pages))))) else: - return answer(True, "Пройдена!") + return answer(True, "Пройдена!") \ No newline at end of file From 0aee23a221e3b4fc89c655116e83ec9cbf71953e Mon Sep 17 00:00:00 2001 From: Marina Date: Wed, 5 Jul 2023 22:22:48 +0300 Subject: [PATCH 03/19] fix empty slide with title --- .../presentation_checks/empty_slide_check.py | 49 ++++++++++++++++--- app/main/presentations/pptx/slide_pptx.py | 2 + app/main/presentations/slide_basic.py | 6 ++- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/app/main/checks/presentation_checks/empty_slide_check.py b/app/main/checks/presentation_checks/empty_slide_check.py index 64c4952e..a065b768 100644 --- a/app/main/checks/presentation_checks/empty_slide_check.py +++ b/app/main/checks/presentation_checks/empty_slide_check.py @@ -7,13 +7,48 @@ class PresEmptySlideCheck(BasePresCriterion): description = "Проверка наличия пустых слайдов в презентации" id = 'pres_empty_slide' - def __init__(self, file_info): + def __init__(self, file_info, status=False): super().__init__(file_info) + self.status = status def check(self): - empty_pages = [page for page, slide in enumerate(self.file.get_text_from_slides(), 1) if not slide.strip()] - if empty_pages: - return answer(False, format_header( - 'Не пройдена, обнаружены пустые слайды: {}'.format(', '.join(self.format_page_link(empty_pages))))) - else: - return answer(True, "Пройдена!") \ No newline at end of file + result_str = '' + page_titles = {} + full_pages = {} + empty_pages = [] + pages_with_title = [] + + pages_with_images = [page for page, slide in enumerate(self.file.slides, 1) + if slide.get_images() or slide.get_table()] + + for page, slide in enumerate(self.file.get_text_from_slides(), 1): + slide_without_page = slide.replace("\n", " ").replace(str(page), '') + full_pages[str(page)] = ''.join(char for char in slide_without_page.strip() if char.isprintable()) + if not full_pages[str(page)]: + empty_pages.append(page) + + for page, slide in enumerate(self.file.get_titles(), 1): + page_titles[str(page)] = slide + if slide == full_pages[str(page)] and page not in pages_with_images and page not in empty_pages: + pages_with_title.append(page) + + if empty_pages and not pages_with_title: + result_str += format_header( + 'Не пройдена! Обнаружены пустые слайды: {}'.format( + ', '.join(self.format_page_link(empty_pages))) + ) + if pages_with_title and not empty_pages: + result_str += format_header( + 'Не пройдена! Обнаружены слайды, в которых присутствует только заголовок: {}'.format( + ', '.join(self.format_page_link(pages_with_title))) + ) + if empty_pages and pages_with_title: + result_str += format_header( + 'Не пройдена! Обнаружены пустые слайды: {}, также обнаружены слайды, в которых присутствует только заголовок: {}'.format( + ', '.join(self.format_page_link(empty_pages)), ', '.join(self.format_page_link(pages_with_title))) + ) + if not result_str: + self.status = True + result_str = 'Пройдена!' + + return answer(self.status, result_str) diff --git a/app/main/presentations/pptx/slide_pptx.py b/app/main/presentations/pptx/slide_pptx.py index e7172fe8..46681cdd 100644 --- a/app/main/presentations/pptx/slide_pptx.py +++ b/app/main/presentations/pptx/slide_pptx.py @@ -26,6 +26,8 @@ def __init__(self, container, w, h, index=-1): self.images.append(shape) if hasattr(shape, "text"): self.text += "\n" + shape.text + if shape.has_table: + self.table.append(shape) def __str__(self): return super().__str__() diff --git a/app/main/presentations/slide_basic.py b/app/main/presentations/slide_basic.py index 20997874..17114320 100644 --- a/app/main/presentations/slide_basic.py +++ b/app/main/presentations/slide_basic.py @@ -5,6 +5,7 @@ def __init__(self, container): # Extracting only the properties we need! self.page_number = [-1, -1, -1] self.dimensions = [-1, -1] self.images = [] + self.table = [] def get_title(self): return self.title @@ -16,7 +17,10 @@ def get_page_number(self): return self.page_number def get_images(self): - return self.images + return self.images + + def get_table(self): + return self.table def __str__(self): return f"\tTitle: {self.title}.\n\tText: {self.text}.\n\tPage_num: {self.page_number}" From c69166ae8f52d7c0f0fddd709c474ff28d8a9f3b Mon Sep 17 00:00:00 2001 From: Marina Date: Fri, 7 Jul 2023 09:58:31 +0300 Subject: [PATCH 04/19] change replace(page) to regex --- app/main/checks/presentation_checks/empty_slide_check.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/main/checks/presentation_checks/empty_slide_check.py b/app/main/checks/presentation_checks/empty_slide_check.py index a065b768..813fe5ef 100644 --- a/app/main/checks/presentation_checks/empty_slide_check.py +++ b/app/main/checks/presentation_checks/empty_slide_check.py @@ -1,3 +1,4 @@ +import re from app.utils.parse_for_html import format_header from ..base_check import BasePresCriterion, answer @@ -22,7 +23,9 @@ def check(self): if slide.get_images() or slide.get_table()] for page, slide in enumerate(self.file.get_text_from_slides(), 1): - slide_without_page = slide.replace("\n", " ").replace(str(page), '') + slide_string = ''.join(slide.replace("\n", " ")) + slide_without_page = re.sub(r'\d+(?=\s*$)', '', slide_string) + print(slide_without_page) full_pages[str(page)] = ''.join(char for char in slide_without_page.strip() if char.isprintable()) if not full_pages[str(page)]: empty_pages.append(page) From eb7c976350e3a409d09a48043133e53383c611cc Mon Sep 17 00:00:00 2001 From: Marina Date: Thu, 13 Jul 2023 12:54:53 +0300 Subject: [PATCH 05/19] different check for pptx and odp --- .../presentation_checks/empty_slide_check.py | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/app/main/checks/presentation_checks/empty_slide_check.py b/app/main/checks/presentation_checks/empty_slide_check.py index 813fe5ef..9e4c210c 100644 --- a/app/main/checks/presentation_checks/empty_slide_check.py +++ b/app/main/checks/presentation_checks/empty_slide_check.py @@ -25,7 +25,7 @@ def check(self): for page, slide in enumerate(self.file.get_text_from_slides(), 1): slide_string = ''.join(slide.replace("\n", " ")) slide_without_page = re.sub(r'\d+(?=\s*$)', '', slide_string) - print(slide_without_page) + print(slide_without_page, self.file.get_text_from_slides) full_pages[str(page)] = ''.join(char for char in slide_without_page.strip() if char.isprintable()) if not full_pages[str(page)]: empty_pages.append(page) @@ -35,21 +35,30 @@ def check(self): if slide == full_pages[str(page)] and page not in pages_with_images and page not in empty_pages: pages_with_title.append(page) - if empty_pages and not pages_with_title: - result_str += format_header( - 'Не пройдена! Обнаружены пустые слайды: {}'.format( - ', '.join(self.format_page_link(empty_pages))) - ) - if pages_with_title and not empty_pages: - result_str += format_header( - 'Не пройдена! Обнаружены слайды, в которых присутствует только заголовок: {}'.format( - ', '.join(self.format_page_link(pages_with_title))) - ) - if empty_pages and pages_with_title: - result_str += format_header( - 'Не пройдена! Обнаружены пустые слайды: {}, также обнаружены слайды, в которых присутствует только заголовок: {}'.format( - ', '.join(self.format_page_link(empty_pages)), ', '.join(self.format_page_link(pages_with_title))) - ) + if self.file.presentation_name.endswith('.ppt') or self.file.presentation_name.endswith('.pptx'): + + if empty_pages and not pages_with_title: + result_str += format_header( + 'Не пройдена! Обнаружены пустые слайды: {}'.format( + ', '.join(self.format_page_link(empty_pages))) + ) + if pages_with_title and not empty_pages: + result_str += format_header( + 'Не пройдена! Обнаружены слайды, в которых присутствует только заголовок: {}'.format( + ', '.join(self.format_page_link(pages_with_title))) + ) + if empty_pages and pages_with_title: + result_str += format_header( + 'Не пройдена! Обнаружены пустые слайды: {}, также обнаружены слайды, в которых присутствует только заголовок: {}'.format( + ', '.join(self.format_page_link(empty_pages)), ', '.join(self.format_page_link(pages_with_title))) + ) + elif self.file.presentation_name.lower().endswith('.odp'): + if empty_pages: + result_str += format_header( + 'Не пройдена! Обнаружены пустые слайды или слайды, в которых присутствует только заголовок: {}'.format( + ', '.join(self.format_page_link(empty_pages))) + ) + if not result_str: self.status = True result_str = 'Пройдена!' From 79387b9111fe288f0a6605b118ac41d6caebb20b Mon Sep 17 00:00:00 2001 From: Marina Date: Thu, 13 Jul 2023 17:30:32 +0300 Subject: [PATCH 06/19] =?UTF-8?q?fix=20check=20'=D0=97=D0=B0=D0=BF=D0=B0?= =?UTF-8?q?=D1=81=D0=BD=D1=8B=D0=B5=20=D1=81=D0=BB=D0=B0=D0=B9=D0=B4=D1=8B?= =?UTF-8?q?'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/main/checks/presentation_checks/empty_slide_check.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/main/checks/presentation_checks/empty_slide_check.py b/app/main/checks/presentation_checks/empty_slide_check.py index 9e4c210c..958fff08 100644 --- a/app/main/checks/presentation_checks/empty_slide_check.py +++ b/app/main/checks/presentation_checks/empty_slide_check.py @@ -25,15 +25,15 @@ def check(self): for page, slide in enumerate(self.file.get_text_from_slides(), 1): slide_string = ''.join(slide.replace("\n", " ")) slide_without_page = re.sub(r'\d+(?=\s*$)', '', slide_string) - print(slide_without_page, self.file.get_text_from_slides) full_pages[str(page)] = ''.join(char for char in slide_without_page.strip() if char.isprintable()) if not full_pages[str(page)]: empty_pages.append(page) for page, slide in enumerate(self.file.get_titles(), 1): page_titles[str(page)] = slide - if slide == full_pages[str(page)] and page not in pages_with_images and page not in empty_pages: - pages_with_title.append(page) + if slide != "Запасные слайды": + if slide == full_pages[str(page)] and page not in pages_with_images and page not in empty_pages: + pages_with_title.append(page) if self.file.presentation_name.endswith('.ppt') or self.file.presentation_name.endswith('.pptx'): From a5f0bc72ec8473309d0edadc0bb00dc2406c2c1d Mon Sep 17 00:00:00 2001 From: Marina Date: Tue, 19 Sep 2023 11:17:10 +0300 Subject: [PATCH 07/19] change upload.js --- assets/scripts/upload.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/scripts/upload.js b/assets/scripts/upload.js index cf152e27..cb5ca78d 100644 --- a/assets/scripts/upload.js +++ b/assets/scripts/upload.js @@ -7,6 +7,8 @@ const file_input = $("#upload_file"); const file_label = $("#upload_file_label"); const return_file_label = file_label.text(); +const pt = $("#upload_file_pdf"); +pdf_uploaded = pt.length > 0 ? !!pt.prop("files")[0] : false; //проверка наличия pdf const pdf_file_input = $("#upload_file_pdf"); const pdf_file_label = $("#upload_file_label_pdf"); const return_pdf_file_label = pdf_file_label.text(); @@ -58,7 +60,7 @@ const resetFileUpload = () => { const changeUploadButton = () => { if (pdf_uploaded || file_uploaded) { - const pdf_size = pdf_file_input.prop("files")[0]?.size || 0; + const pdf_size = pdf_uploaded ? (pt.prop("files")[0]?.size || 0) : 0; const file_size = file_input.prop("files")[0]?.size || 0; if (pdf_size + file_size <= file_upload_limit) { if (file_uploaded) From 1aab2c61dbae9676bd27b80833a8bc74a2a9347d Mon Sep 17 00:00:00 2001 From: Marina Date: Tue, 19 Sep 2023 14:47:20 +0300 Subject: [PATCH 08/19] fix button bug --- app/main/check_packs/pack_config.py | 5 ++++- app/servants/pre_luncher.py | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/main/check_packs/pack_config.py b/app/main/check_packs/pack_config.py index 0639689b..9740df11 100644 --- a/app/main/check_packs/pack_config.py +++ b/app/main/check_packs/pack_config.py @@ -45,6 +45,7 @@ DEFAULT_TYPE = 'pres' REPORT_TYPES = ('LR', 'VKR') DEFAULT_REPORT_TYPE_INFO = {'type': 'report', 'report_type': REPORT_TYPES[1]} +# DEFAULT_REPORT_TYPE_INFO = {'type': 'report', 'report_type': REPORT_TYPES[0]} DEFAULT_PRES_TYPE_INFO = {'type': 'pres'} DEFAULT_TYPE_INFO = DEFAULT_PRES_TYPE_INFO @@ -52,5 +53,7 @@ 'pres': BaseCriterionPack(BASE_PRES_CRITERION, DEFAULT_PRES_TYPE_INFO, min_score=1.0, name="BasePresentationCriterionPack"), 'report': BaseCriterionPack(BASE_REPORT_CRITERION, DEFAULT_REPORT_TYPE_INFO, min_score=1.0, - name="BaseReportCriterionPack") + name="BaseReportCriterionPack ") + # 'report': BaseCriterionPack(BASE_REPORT_CRITERION, DEFAULT_REPORT_TYPE_INFO, min_score=1.0, + # name="LabReportPack ") } diff --git a/app/servants/pre_luncher.py b/app/servants/pre_luncher.py index 31164022..47836e2a 100644 --- a/app/servants/pre_luncher.py +++ b/app/servants/pre_luncher.py @@ -3,6 +3,7 @@ from db.db_methods import add_user, get_user, get_client, edit_user, save_criteria_pack from main.check_packs.pack_config import BASE_PACKS, DEFAULT_REPORT_TYPE_INFO +# LabReportPack from pymongo.errors import ConnectionFailure from server import ALLOWED_EXTENSIONS @@ -40,6 +41,7 @@ def init(app, debug): user.criteria = BASE_PACKS[file_type].name user.formats = list(ALLOWED_EXTENSIONS.get(file_type)) user.two_files = True + # user.two_files = False edit_user(user) logger.info(f"Создан администратор по умолчанию: логин: {user.username}, пароль уточняйте у разработчика") From aa6754f2a1c3e1123baf0b0e448133f8d33f51dd Mon Sep 17 00:00:00 2001 From: Marina Date: Wed, 20 Sep 2023 18:56:32 +0300 Subject: [PATCH 09/19] first example md_parser --- app/main/reports/md_parser/example.md | 158 ++++++++++++++++++++++++ app/main/reports/md_parser/md_parser.py | 64 ++++++++++ 2 files changed, 222 insertions(+) create mode 100644 app/main/reports/md_parser/example.md create mode 100644 app/main/reports/md_parser/md_parser.py diff --git a/app/main/reports/md_parser/example.md b/app/main/reports/md_parser/example.md new file mode 100644 index 00000000..d243a05d --- /dev/null +++ b/app/main/reports/md_parser/example.md @@ -0,0 +1,158 @@ +# Описание бенчмарка для сравнения влияния алгоритмов выявления особых RGB-точек в изображении на точность локализации мобильного робота. + +Ключевые слова: алгоритмы выявления особых RGB-точек(признаков), визуальная одометрия, бенчмарк. + +## Аннотация + +В статье представлен обзор существующих алгоритмов выявлениях особых RGB-точек(признаков). Сформированы требования к бенчмарку для сравнения влияния алгоритмов выявления признаков на точность локализации мобильного робота. Кратко изложены этапы вычисления местоположения робота методом визуальной одометрии, которые будут использоваться в бенчмарке. Приведены инструменты для реализации бенчмарка. + +## Введение + + С быстрым развитием области мобильной робототехники, возрастает необходимость в точной локализации роботов. Одним из методов отслеживания движения является визуальная одометрия, которая использует поток изображений с камеры робота. Выявление и описание признаков в изображении является одним из первых и важнейших этапов визуальной одометрии [2]. Необходимо провести исследования влияния алгоритмов выявления признаков в изображении на точность локализации мобильного робота. + +Целью данной работы является разработать архитектуру бенчмарка для исследования алгоритмов выявления особых RGB-точек(признаков) изображений на точность локализации мобильного робота. + +Для достижения цели были поставлены следующие задачи: + +1. Изучить существующие алгоритмы выявления признаков. +2. Определить метод сравнения алгоритмов выявления признаков. +3. Описать способ реализации бенчмарка алгоритмов выявления признаков. + +Объектом исследования являются алгоритмы выявления признаков. + +Предметом исследования являются влияния алгоритмов выявления признаков на точность локализации мобильного робота. + +## Обзор предметной области + +### Принцип отбора аналогов + +Для создания бенчмарка необходимо провести обзор существующих алгоритмов выявления признаков. Для поиска алгоритмов выявления признаков использовался следующий запрос: feature detection and description algorithms. + +#### SIFT + +Один из популярнейших алгоритмов выявления и описания признаков в изображении, предложенный в 2004 году[3]. Состоит из нескольких этапов: + +1. **Поиск особых точек.** Для достижения инвариантности относительно масштаба строятся пирамиды гауссианов. Гауссиан - это изображение после применения гауссового фильтра[4]. Далее из разностей соседних гауссианов в пирамиде получают пирамиду разности гауссиан. После ищутся локальные экстремумы в пирамиде разностей гауссиан. +2. **Локализация ключевых точек.** Используется разложение масштабного пространства в ряд Тейлора для получения более точных значений экстремумов. +3. **Поиск ориентации ключевых точек**. Для инвариантности к вращению ключевым точкам назначается ориентация. Направление ключевой точки находиться исходя из её окрестности - для соседних точек вычисляется величина и направление градиента. +4. **Описание ключевой точки**. Описание (дескриптор) состоит из окрестности 16x16 вокруг ключевой точки. Также принимаются некоторые меры для обеспечения устойчивости к освещению, вращению и т.п. + +#### SURF + +Модификация SIFT, нацеленная на ускорение вычислений[5]. Вместо разности гауссианов используется коробчатый фильтр (Box Filter), уменьшающий количество вычислений и позволяющий выполнять их параллельно для разных масштабов. Также для обнаружения особых точек используется детерминант матрицы Гессе, что дополнительно снижает траты на вычисления. За счёт улучшений SURF требует гораздо меньше времени на вычисления, чем SIFT, но при этом немного теряет в устойчивости перед изменением освещения и сменой точки обзора. + +#### KAZE + +Метод выявления и описания признаков, работающий в нелинейном масштабном пространстве [6]. Построение нелинейного масштабного пространства происходит с помощью техник AOS[7]. Далее обнаруживаются максимумы с помощью детерминанта матрицы Гессе, нормализованный по масштабу. На последнем шаге вычисляется ориентация ключевых точек (как в SURF) и получают описание(дескриптор) размером 24x24. Алгоритм KAZE уменьшает шум и имеет более высокую точность (чем SIFT и SURF) за счёт небольшого увеличения вычислений. + +#### BRISK + +Был предложен в 2011 году как альтернатива SURF с более низкими вычислительными затратами[8]. Алгоритм основан на алгоритме обнаружения углов AGAST [9] (который является модификацией FAST[10]). Описание (дескриптор) состоит из бинарной битовой строки, соответствующей расстоянию Хэмминга, что позволяет ускорить вычисления. + +#### ORB + +Алгоритм основан на модификациях алгоритма выявления особых точек FAST[10] и алгоритма описания признаков BRIEF[11] и предложен в 2011 году[12]. Вычисляются особые точки с помощью алгоритма FAST, затем точки фильтруются алгоритмом обнаружения углов Харриса[13]. Далее вычисляется ориентация ключевой точки. После используется модифицированный алгоритм BRIEF - точки, предназначенные для сравнения, поворачиваются на угол ориентации ключевой точки (таким образом лишая недостатка BRIEF в неустойчивости к вращениям). + +### Сравнение аналогов + +Были выбраны следующие основные параметры сравнения: + +1. Повторяемость - среднее количество признаков, обнаруженных на изображениях после применения различных геометрических и фотометрических преобразований. В данном обзоре рассмотрены вращение и масштабирование. Высокое значение данного параметра позволяет говорить о том, что найденные признаки будут обнаружены и на других изображениях. +2. Точность при сопоставлении изображений. Исключение выбросов увеличит точность локализации мобильного робота. +3. Время выявления и описания признаков. Вычислительно эффективные алгоритмы позволяют обработать большее количество данных. + +В табл.1 представлено сравнение алгоритмов по данным из статьи[14]. + +Таблица 1 - Сравнительный анализ алгоритмов по количественным критериям + +| Алгоритмы | Повторяемость, % | Повторяемость, % | Повторяемость, % | Повторяемость, % | Повторяемость, % | Процент ошибки, % | Процент ошибки, % | Процент ошибки, % | Процент ошибки, % | Процент ошибки, % | Среднее время выявления и описания признаков, c | +| --------- | ---------------- | ---------------- | ---------------- | ---------------- | ---------------- | ----------------- | ----------------- | ----------------- | ----------------- | ----------------- | ----------------------------------------------- | +| | Масштаб | Масштаб | Масштаб | Вращение | Вращение | Масштаб | Масштаб | Масштаб | Вращение | Вращение | | +| | 50% | 150% | 200% | 30° | 60° | 50% | 150% | 200% | 30° | 60° | | +| SIFT | 78 | 80 | 82 | 78 | 78 | 0 | 0 | 0 | 0 | 0 | 0.198 | +| SURF | 81 | 77 | 83 | 73 | 72 | 0.08 | 0.18 | 0.22 | 0 | 0.01 | 0.138 | +| KAZE | 70 | 70 | 73 | 88 | 88 | 0.17 | 0 | 0.3 | 0 | 0 | 0.242 | +| BRISK | 81 | 83 | 85 | 82 | 84 | 0 | 0 | 0.2 | 0.01 | 0.01 | 0.045 | +| ORB | 85 | 83 | 66 | 89 | 89 | 0.2 | 0.1 | 0.5 | 0.01 | 0.01 | 0.01 | + + + +В среднем повторяемость выше у BRISK и ORB, но при этом у ORB сильно снижается значения при увеличении масштаба более чем на 150%. У KAZE высокие значения для вращений. Самым точным алгоритмом является SIFT. ORB имеет наибольший процент ошибок. Самым вычислительно трудоёмким является KAZE, самым эффективным ORB, за ним по эффективности идёт BRISK. + +## Выбор метода решения + +Бенчмарк должен предоставлять выбор различных алгоритмов выявления и описания признаков. Пользователю предоставляется возможность загрузить набор изображений и файлы с эталонными значениям и местоположения. Пользователь может узнать промежуточные значения работы бенчмарка, например, количество найденных признаков. По набору изображений будут найдены признаки и их описание, далее на основе этих признаков бенчмарк будет определять текущее местоположение мобильного робота. + +Исходя из обзора алгоритмов, бенчмарк должен: + +1. Определять точность локализации мобильного робота. Бенчмарк выдаёт результат в виде графиков, по которым можно отследить ошибку определения местоположения. +2. Предоставлять возможность применять геометрические и фотометрические преобразования над изображениями для оценивания инвариантности алгоритмов. Например, к части изображений может быть применён эффект размытия или добавлен шум. +3. Рассчитывать время выполнения алгоритмов. По окончанию работы бенчмарк выдаёт общее время на расчёт определения местоположения. + +## Описание метода решения + +Для исследования влияния алгоритмов выявления признаков на точность локализации мобильного робота, необходимо воспользоваться методом визуальной одометрии и рассчитать местоположение робота. Метод визуальной одометрии, который будет реализован в бенчмарке, основывается на статьях [1] и [2]. Ниже приведены подробности данного метода: + +1. **Выявление и описание признаков**. На данном этапе работает один из алгоритмов, описанных в разделе "Cравнение аналогов". Для повышения точности дальнейших расчётов признаки должны быть равномерно распределены по всему изображению. Для этого изображения разбивают на сетку, в каждом фрагменте ищется определенное количество признаков. +2. **Отслеживание признаков**. После обнаружения признаков на первом изображении, необходимо найти эти признаки на остальных изображениях, таким образом отслеживая перемещение признаков между изображениями. Одним из быстрых и эффективных методов отслеживания является трекер Канаде-Лукаса-Томаси[15]. +3. **Расчёт движения**. На основе информации о том, как менялись признаки на изображении, можно рассчитать движение самого мобильного робота. Для расчёта движения используется существенная матрица, описывающая геометрическую связь между двумя изображениями[16]. Из существенной матрицы можно получить значения вращение и перемещение между изображениями. На основании данной информации можно высчитать координаты перемещения. +4. **Удаление выбросов**. Обычно в изображениях содержится множество выбросов, которые не учитываются алгоритмами выявления и описания признаков. С удалением выбросов хорошо справляется алгоритм RANSAC[17]. В данном случае для алгоритма RANSAC моделью является существенная матрица, а параметрами - признаки на изображении. +5. **Оптимизация**. При расчёте местоположения со временем будет накапливаться ошибка("scale drift") [18]. Одной из оптимизаций является обнаружение циклов[19]. Для этого возвращаются к предыдущим признакам, которые давно не появлялись на изображениях, и сравнивают с текущими. Если признаки совпали, применяют глобальную оптимизацию и исправляют траекторию движения робота. + +В качестве результата выполнения приведенного выше метода, будут доступны график с вычисленной траекторией и настоящей, и график, показывающий накопления ошибки с течением времени. По данным графиков можно будет оценить степень влияния алгоритмов выявления признаков на точность локализации мобильного робота. Также отдельно будет выводиться время выполнения алгоритмов для оценки их эффективности. + +В качестве основного инструмента реализации бенчмарка была выбрана библиотека OpenCV[20]. В неё включены все приведенные в обзоре алгоритмы выявления и описания признаков, а так же в ней есть множество функций по обработке изображений, которые будут необходимы для вычисления местоположения мобильного робота. В качестве языка программирования был выбран Python, из-за простого синтаксиса языка. + +Бенчмарк будет представлять из себя консольное приложение. Пользователь может выбрать один из предложенных алгоритмов выявления и описания признаков и выбрать папку с набором изображений (если у пользователя нет набора изображений, программа может использовать свой внутренний). Далее пользователь может выбрать, будет ли данные изображения подвергаться различными преобразованиям (например, добавления в изображение размытия или шума). После начинает работу алгоритм, вычисляющий местоположение робота. По завершению бенчмарк выведет количество времени, потраченное на вычисления и создаст два файла с графиками. + +## Заключение + +В ходе работы был проведён обзор существующих алгоритмов выявления признаков(особых RGB-точек), в ходе которого были выявлены основные требования к бенчмарку. Был представлен метод сравнения алгоритмов выявления признаков - непосредственное вычисление методом визуальной одометрии местоположение робота и дальнейшее сравнение результатов для различных алгоритмов выявления признаков. Также были предложены инструменты для реализации бенчмарка и описан способ реализации бенчмарка - консольное приложение, принимающие на вход набор изображение и выдающее график с траекторией мобильного робота. + +В дальнейшем следует расширять возможности бенчмарка: добавить больше алгоритмов выявления признаков и увеличить количество различных преобразований над набором изображений. Также в дальнейших работах можно расширить существующее решение, чтобы исследовать влияние алгоритмов вычисления признаков на SLAM[21]. + + + +## Список литературы + +[1] Fraundorfer, Friedrich ; Scaramuzza, Davide. Visual Odometry [Tutorial] // IEEE Robotics & Automation Magazine 18, 2011. + +[2] Fraundorfer, Friedrich ; Scaramuzza, Davide. Visual Odometry: Part II - Matching, Robustness, and Applications // IEEE Robotics & Automation Magazine 19, 2012. + +[3]David G. Lowe. Distinctive Image Features from Scale-Invariant Keypoints // International Journal of Computer Vision 60, 2004. + +[4]Mark Nixon, Alberto Aguado. Feature Extraction and Image Processing for Computer Vision, 2002, C.86-88. + +[5]Herbert Bay, Tinne Tuytelaars, Luc Van Gool. SURF: Speeded up robust features // Proceedings of the 9th European conference on Computer Vision - Volume Part I, 2006. + +[6]Pablo Fernández Alcantarilla. KAZE Features // European Conference on Computer Vision (ECCV), 2012. + +[7]J. Weickert; B.M.T.H. Romeny; M.A. Viergever. Efficient and reliable schemes for nonlinear diffusion filtering // IEEE, 1998. + +[8]Stefan Leutenegger,Margarita Chli, Roland Y.Siegwart. BRISK: Binary Robust Invariant Scalable Keypoints // IEEE International Conference on Computer Vision, 2011. + +[9]E. Mair,G. D. Hager,D. Burschka, M.Suppa, and G. Hirzinger. Adaptiveand generic corner detection based on the accelerated segment test // European Conference on Computer Vision, 2010. + +[10]Edward Rosten and Tom Drummond. Machine learning for high-speed corner detection // 9th European Conference on Computer Vision, 2006. + +[11]Michael Calonder, Vincent Lepetit, Christoph Strecha, Pascal Fua. "BRIEF: Binary Robust Independent Elementary Features" // 11th European Conference on Computer Vision (ECCV), 2010. + +[12]Ethan Rublee. ORB: an efficient alternative to SIFT or SURF // IEEE International Conference on Computer Vision, 2011. + +[13]Chris Harris, Mike Stephens. A Combined Corner and Edge Detector // Alvey Vision Conference, 1988. + +[14]Shaharyar Ahmed Khan Tareen, Zahra Saleem. A comparative analysis of SIFT, SURF, KAZE, AKAZE, ORB, and BRISK // 2018 International Conference on Computing, Mathematics and Engineering Technologies, 2018. + +[15]C. Tomasi and J. Shi, "Good features to track" // *Proc. CVPR*, 1994. + +[16]H. C. Longuet-Higgins. A computer algorithm for reconstructing a scene from two projections. // Nature, 293:133–135, 1981. + +[17]Martin A. Fischler and Robert C. Bolles. Random Sample Consensus: A Paradigm for Model Fitting with Applications to Image Analysis and Automated Cartography // Comm. Of the ACM, 1981. + +[18]L. Matthies and S. Shafer, "Error modeling in stereo navigation" // IEEE J. Robot. Automat., vol. 3, no. 3, 1987. + +[19] T. Bailey and H. Durrant-Whyte, "Simultaneous localisation and mapping (SLAM): Part II. State of the art" // IEEE Robot. Automat. Mag., vol. 13, no. 3, 2006. + +[20] OpenCV [Электронный ресурс]. URL: https://opencv.org/ (дата обращения 19.12.2022). + +[21] Alif Ridzuan Khairuddin, Mohamad Shukor Talib, Habibollah Haron. Review on simultaneous localization and mapping (SLAM) // IEEE International Conference on Control System, Computing and Engineering, 2015. \ No newline at end of file diff --git a/app/main/reports/md_parser/md_parser.py b/app/main/reports/md_parser/md_parser.py new file mode 100644 index 00000000..c9a36929 --- /dev/null +++ b/app/main/reports/md_parser/md_parser.py @@ -0,0 +1,64 @@ +import markdown +import re + +class MdParser: + def __init__(self): + self.path_to_md_file = '/home/marina/src/2/mse_auto_checking_slides_vaganov/app/main/reports/md_parser/example.md' + self.headers = [] + self.chapters = [] + self.paragraphs = [] + self.html_text = '' + self.tables = [] + self.chapter_with_text = [] + self.literature_header = [] + self.headers_page = 0 + + def get_html_from_md(self): + with open(self.path_to_md_file, "r", encoding="utf-8") as f: + text = f.read() + self.html_text = markdown.markdown(text) + self.paragraphs = self.html_text.split('\n') + return(self.paragraphs, self.html_text) + + def get_headers(self): + header_regex = "

(.*?)<\/h1>" + self.headers = re.findall(header_regex, self.html_text) + return self.headers + + def get_chapters(self): + chapter_regex = "

(.*?)<\/h2>" + self.chapters = re.findall(chapter_regex, self.html_text) + return(self.chapters) + + def get_chapter_with_text(self): + text = self.html_text + chapter_name = '' + for chapter in self.chapters: + self.split_chapter = text.split("

" + chapter + "

") + self.chapter_with_text.append(chapter_name + self.split_chapter[-2]) + chapter_name = chapter + text = self.split_chapter[-1] + if chapter == self.chapters[len(self.chapters)-1]: + self.chapter_with_text.append(chapter_name + text) + return self.chapter_with_text + + def get_tables_size(self): + count_table_line = 0 + count_paragraph = len(self.paragraphs) + for line in self.paragraphs: + if "|" in line: + count_table_line +=1 + return (count_table_line, count_table_line/count_paragraph) + + + +file = MdParser() +file.get_html_from_md() +# print(file.get_headers()) +# print(file.get_chapters()) +# print(file.get_chapter_with_text()) +print(file.get_tables_size()) + +# if __name__ == '__main__':e +# get_html_from_md() + From 526060124cafa81dfd68d18d98f180df33e2d86c Mon Sep 17 00:00:00 2001 From: Marina Date: Wed, 20 Sep 2023 18:58:51 +0300 Subject: [PATCH 10/19] md_parser_example --- app/main/reports/md_parser/md_parser.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/main/reports/md_parser/md_parser.py b/app/main/reports/md_parser/md_parser.py index c9a36929..0bc68f1b 100644 --- a/app/main/reports/md_parser/md_parser.py +++ b/app/main/reports/md_parser/md_parser.py @@ -3,7 +3,7 @@ class MdParser: def __init__(self): - self.path_to_md_file = '/home/marina/src/2/mse_auto_checking_slides_vaganov/app/main/reports/md_parser/example.md' + self.path_to_md_file = '' #insert path to file self.headers = [] self.chapters = [] self.paragraphs = [] @@ -58,7 +58,3 @@ def get_tables_size(self): # print(file.get_chapters()) # print(file.get_chapter_with_text()) print(file.get_tables_size()) - -# if __name__ == '__main__':e -# get_html_from_md() - From f80330a989ba61583956a92a1e62fc28fdc4894e Mon Sep 17 00:00:00 2001 From: Marina Date: Thu, 21 Sep 2023 11:10:07 +0300 Subject: [PATCH 11/19] change structure --- app/main/reports/md_parser/md_parser.py | 45 +++++++++++++------------ 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/app/main/reports/md_parser/md_parser.py b/app/main/reports/md_parser/md_parser.py index 0bc68f1b..d14a4fd4 100644 --- a/app/main/reports/md_parser/md_parser.py +++ b/app/main/reports/md_parser/md_parser.py @@ -2,33 +2,32 @@ import re class MdParser: - def __init__(self): - self.path_to_md_file = '' #insert path to file + def __init__(self, path_to_md_file): + self.path_to_md_file = path_to_md_file self.headers = [] self.chapters = [] self.paragraphs = [] self.html_text = '' self.tables = [] self.chapter_with_text = [] - self.literature_header = [] - self.headers_page = 0 + # self.literature_header = '' - def get_html_from_md(self): + def read_md_file(self): with open(self.path_to_md_file, "r", encoding="utf-8") as f: - text = f.read() - self.html_text = markdown.markdown(text) + md_text = f.read() + return md_text + + def get_html_from_md(self, md_text): + self.html_text = markdown.markdown(md_text) self.paragraphs = self.html_text.split('\n') - return(self.paragraphs, self.html_text) def get_headers(self): header_regex = "

(.*?)<\/h1>" self.headers = re.findall(header_regex, self.html_text) - return self.headers def get_chapters(self): chapter_regex = "

(.*?)<\/h2>" self.chapters = re.findall(chapter_regex, self.html_text) - return(self.chapters) def get_chapter_with_text(self): text = self.html_text @@ -38,9 +37,7 @@ def get_chapter_with_text(self): self.chapter_with_text.append(chapter_name + self.split_chapter[-2]) chapter_name = chapter text = self.split_chapter[-1] - if chapter == self.chapters[len(self.chapters)-1]: - self.chapter_with_text.append(chapter_name + text) - return self.chapter_with_text + self.chapter_with_text.append(chapter_name + text) def get_tables_size(self): count_table_line = 0 @@ -48,13 +45,17 @@ def get_tables_size(self): for line in self.paragraphs: if "|" in line: count_table_line +=1 - return (count_table_line, count_table_line/count_paragraph) - + return count_table_line/count_paragraph + + def parse_md_file(self): + md_text = self.read_md_file() + self.get_html_from_md(md_text) + self.get_headers() + self.get_chapters() + self.get_chapter_with_text() + self.get_tables_size() + return f"{self.headers} \n {self.chapters} \n {self.chapter_with_text} \n {self.get_tables_size()}" - -file = MdParser() -file.get_html_from_md() -# print(file.get_headers()) -# print(file.get_chapters()) -# print(file.get_chapter_with_text()) -print(file.get_tables_size()) + +md_file = MdParser('') +print(md_file.parse_md_file()) From 8061d8dba3d0983564bb7d27b09ac92dbb325730 Mon Sep 17 00:00:00 2001 From: Marina Date: Thu, 21 Sep 2023 12:00:01 +0300 Subject: [PATCH 12/19] delete changes from previos branch --- app/main/check_packs/pack_config.py | 3 --- app/servants/pre_luncher.py | 4 ++-- assets/scripts/upload.js | 4 +--- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/app/main/check_packs/pack_config.py b/app/main/check_packs/pack_config.py index 9740df11..1def2e45 100644 --- a/app/main/check_packs/pack_config.py +++ b/app/main/check_packs/pack_config.py @@ -45,7 +45,6 @@ DEFAULT_TYPE = 'pres' REPORT_TYPES = ('LR', 'VKR') DEFAULT_REPORT_TYPE_INFO = {'type': 'report', 'report_type': REPORT_TYPES[1]} -# DEFAULT_REPORT_TYPE_INFO = {'type': 'report', 'report_type': REPORT_TYPES[0]} DEFAULT_PRES_TYPE_INFO = {'type': 'pres'} DEFAULT_TYPE_INFO = DEFAULT_PRES_TYPE_INFO @@ -54,6 +53,4 @@ name="BasePresentationCriterionPack"), 'report': BaseCriterionPack(BASE_REPORT_CRITERION, DEFAULT_REPORT_TYPE_INFO, min_score=1.0, name="BaseReportCriterionPack ") - # 'report': BaseCriterionPack(BASE_REPORT_CRITERION, DEFAULT_REPORT_TYPE_INFO, min_score=1.0, - # name="LabReportPack ") } diff --git a/app/servants/pre_luncher.py b/app/servants/pre_luncher.py index 47836e2a..9e1b3331 100644 --- a/app/servants/pre_luncher.py +++ b/app/servants/pre_luncher.py @@ -3,7 +3,7 @@ from db.db_methods import add_user, get_user, get_client, edit_user, save_criteria_pack from main.check_packs.pack_config import BASE_PACKS, DEFAULT_REPORT_TYPE_INFO -# LabReportPack + from pymongo.errors import ConnectionFailure from server import ALLOWED_EXTENSIONS @@ -41,7 +41,7 @@ def init(app, debug): user.criteria = BASE_PACKS[file_type].name user.formats = list(ALLOWED_EXTENSIONS.get(file_type)) user.two_files = True - # user.two_files = False + edit_user(user) logger.info(f"Создан администратор по умолчанию: логин: {user.username}, пароль уточняйте у разработчика") diff --git a/assets/scripts/upload.js b/assets/scripts/upload.js index cb5ca78d..cf152e27 100644 --- a/assets/scripts/upload.js +++ b/assets/scripts/upload.js @@ -7,8 +7,6 @@ const file_input = $("#upload_file"); const file_label = $("#upload_file_label"); const return_file_label = file_label.text(); -const pt = $("#upload_file_pdf"); -pdf_uploaded = pt.length > 0 ? !!pt.prop("files")[0] : false; //проверка наличия pdf const pdf_file_input = $("#upload_file_pdf"); const pdf_file_label = $("#upload_file_label_pdf"); const return_pdf_file_label = pdf_file_label.text(); @@ -60,7 +58,7 @@ const resetFileUpload = () => { const changeUploadButton = () => { if (pdf_uploaded || file_uploaded) { - const pdf_size = pdf_uploaded ? (pt.prop("files")[0]?.size || 0) : 0; + const pdf_size = pdf_file_input.prop("files")[0]?.size || 0; const file_size = file_input.prop("files")[0]?.size || 0; if (pdf_size + file_size <= file_upload_limit) { if (file_uploaded) From 00e1b63c60e8983284d5190922b78f0268eedece Mon Sep 17 00:00:00 2001 From: Marina Date: Thu, 21 Sep 2023 12:20:49 +0300 Subject: [PATCH 13/19] add folder 'playground' --- {app/main/reports => playground}/md_parser/example.md | 0 {app/main/reports => playground}/md_parser/md_parser.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename {app/main/reports => playground}/md_parser/example.md (100%) rename {app/main/reports => playground}/md_parser/md_parser.py (94%) diff --git a/app/main/reports/md_parser/example.md b/playground/md_parser/example.md similarity index 100% rename from app/main/reports/md_parser/example.md rename to playground/md_parser/example.md diff --git a/app/main/reports/md_parser/md_parser.py b/playground/md_parser/md_parser.py similarity index 94% rename from app/main/reports/md_parser/md_parser.py rename to playground/md_parser/md_parser.py index d14a4fd4..38a8ce17 100644 --- a/app/main/reports/md_parser/md_parser.py +++ b/playground/md_parser/md_parser.py @@ -57,5 +57,5 @@ def parse_md_file(self): return f"{self.headers} \n {self.chapters} \n {self.chapter_with_text} \n {self.get_tables_size()}" -md_file = MdParser('') +md_file = MdParser('/home/marina/src/2/mse_auto_checking_slides_vaganov/playground/md_parser/example.md') print(md_file.parse_md_file()) From 5a55f6cb559dafce0b45666cc778048c43e7337c Mon Sep 17 00:00:00 2001 From: Marina Date: Fri, 22 Sep 2023 11:43:47 +0300 Subject: [PATCH 14/19] add if-name-main-construction --- playground/md_parser/md_parser.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/playground/md_parser/md_parser.py b/playground/md_parser/md_parser.py index 38a8ce17..510db381 100644 --- a/playground/md_parser/md_parser.py +++ b/playground/md_parser/md_parser.py @@ -45,7 +45,7 @@ def get_tables_size(self): for line in self.paragraphs: if "|" in line: count_table_line +=1 - return count_table_line/count_paragraph + return round(count_table_line/count_paragraph, 4) def parse_md_file(self): md_text = self.read_md_file() @@ -56,6 +56,6 @@ def parse_md_file(self): self.get_tables_size() return f"{self.headers} \n {self.chapters} \n {self.chapter_with_text} \n {self.get_tables_size()}" - -md_file = MdParser('/home/marina/src/2/mse_auto_checking_slides_vaganov/playground/md_parser/example.md') -print(md_file.parse_md_file()) +if __name__ == "__main__": + md_file = MdParser('playground/md_parser/example.md') + print(md_file.parse_md_file()) From b0872746288893d6d96caf66691606c5da4f5207 Mon Sep 17 00:00:00 2001 From: Marina Date: Fri, 22 Sep 2023 15:57:28 +0300 Subject: [PATCH 15/19] improved view --- app/main/check_packs/pack_config.py | 2 +- playground/md_parser/md_parser.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/main/check_packs/pack_config.py b/app/main/check_packs/pack_config.py index 1def2e45..0639689b 100644 --- a/app/main/check_packs/pack_config.py +++ b/app/main/check_packs/pack_config.py @@ -52,5 +52,5 @@ 'pres': BaseCriterionPack(BASE_PRES_CRITERION, DEFAULT_PRES_TYPE_INFO, min_score=1.0, name="BasePresentationCriterionPack"), 'report': BaseCriterionPack(BASE_REPORT_CRITERION, DEFAULT_REPORT_TYPE_INFO, min_score=1.0, - name="BaseReportCriterionPack ") + name="BaseReportCriterionPack") } diff --git a/playground/md_parser/md_parser.py b/playground/md_parser/md_parser.py index 510db381..0c6a1b95 100644 --- a/playground/md_parser/md_parser.py +++ b/playground/md_parser/md_parser.py @@ -1,4 +1,4 @@ -import markdown +import markdown #installation: pip install markdown import re class MdParser: @@ -54,7 +54,7 @@ def parse_md_file(self): self.get_chapters() self.get_chapter_with_text() self.get_tables_size() - return f"{self.headers} \n {self.chapters} \n {self.chapter_with_text} \n {self.get_tables_size()}" + return f"Заголовки:\n{self.headers}\n\nГлавы:\n{self.chapters}\n\nГлавы с текстом:\n{self.chapter_with_text}\n\nДоля таблиц в тексте:\n{self.get_tables_size()}" if __name__ == "__main__": md_file = MdParser('playground/md_parser/example.md') From 48cbea1e90bc0cb6caec3ce1876989607fc0d9f1 Mon Sep 17 00:00:00 2001 From: Marina Date: Tue, 3 Oct 2023 15:57:03 +0300 Subject: [PATCH 16/19] first-step package md_uploader --- app/main/reports/README.md | 11 +++- app/main/reports/md_uploader/__init__.py | 1 + app/main/reports/md_uploader/__main__.py | 21 ++++++++ app/main/reports/md_uploader/md_uploader.py | 60 +++++++++++++++++++++ 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 app/main/reports/md_uploader/__init__.py create mode 100644 app/main/reports/md_uploader/__main__.py create mode 100644 app/main/reports/md_uploader/md_uploader.py diff --git a/app/main/reports/README.md b/app/main/reports/README.md index 16904d47..4c591479 100644 --- a/app/main/reports/README.md +++ b/app/main/reports/README.md @@ -1,6 +1,6 @@ # Запуск и тестирование -Пререквизиты: `argparse`, `python-docx`, `docx2python`, `re`, `subprocess`. Для парсинга `.doc`-файлов потребуется +Пререквизиты: `argparse`, `python-docx`, `docx2python`, `re`, `subprocess`, `markdown`. Для парсинга `.doc`-файлов потребуется LibreOffice. Здесь и далее считается, что корневая директория репозитория добавлена в `PYTHONPATH`. @@ -65,3 +65,12 @@ Proof-of-concept парсинг файлов `.docx` с выводом стру ```bash $ python3 -m app.main.mse22.pdf_document text_from_pages --filename path_to_file ``` + +## `MD` + +Парсинг файлов `.md` с выводом структуры файла в текстовом виде в stdout. + +```bash +$ python3 -m app.main.reports.md_uploader md_parser --mdfile path_to_md_file +``` + diff --git a/app/main/reports/md_uploader/__init__.py b/app/main/reports/md_uploader/__init__.py new file mode 100644 index 00000000..ef406550 --- /dev/null +++ b/app/main/reports/md_uploader/__init__.py @@ -0,0 +1 @@ +from .md_uploader import MdUpload \ No newline at end of file diff --git a/app/main/reports/md_uploader/__main__.py b/app/main/reports/md_uploader/__main__.py new file mode 100644 index 00000000..f69ae975 --- /dev/null +++ b/app/main/reports/md_uploader/__main__.py @@ -0,0 +1,21 @@ +import argparse + +from .md_uploader import main as md_uploader_main + + +def parse_args(): + parser = argparse.ArgumentParser(description='File md parser') + subparsers = parser.add_subparsers() + md_parser = subparsers.add_parser('md_parser', help='md document') + md_parser.add_argument('--mdfile', type=str, required=True, help='path to md file') + md_parser.set_defaults(func=md_uploader_main) + return parser.parse_args() + + +def main(): + args = parse_args() + args.func(args) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/app/main/reports/md_uploader/md_uploader.py b/app/main/reports/md_uploader/md_uploader.py new file mode 100644 index 00000000..1df1284a --- /dev/null +++ b/app/main/reports/md_uploader/md_uploader.py @@ -0,0 +1,60 @@ +import markdown #installation: pip install markdown +import re + +class MdUpload: + def __init__(self, path_to_md_file): + self.path_to_md_file = path_to_md_file + self.headers = [] + self.chapters = [] + self.paragraphs = [] + self.html_text = '' + self.tables = [] + self.chapter_with_text = [] + + def read_md_file(self): + with open(self.path_to_md_file, "r", encoding="utf-8") as f: + md_text = f.read() + return md_text + + def get_html_from_md(self, md_text): + self.html_text = markdown.markdown(md_text) + self.paragraphs = self.html_text.split('\n') + + def get_headers(self): + header_regex = "

(.*?)<\/h1>" + self.headers = re.findall(header_regex, self.html_text) + + def get_chapters(self): + chapter_regex = "

(.*?)<\/h2>" + self.chapters = re.findall(chapter_regex, self.html_text) + + def get_chapter_with_text(self): + text = self.html_text + chapter_name = '' + for chapter in self.chapters: + self.split_chapter = text.split("

" + chapter + "

") + self.chapter_with_text.append(chapter_name + self.split_chapter[-2]) + chapter_name = chapter + text = self.split_chapter[-1] + self.chapter_with_text.append(chapter_name + text) + + def get_tables_size(self): + count_table_line = 0 + count_paragraph = len(self.paragraphs) + for line in self.paragraphs: + if "|" in line: + count_table_line +=1 + return round(count_table_line/count_paragraph, 4) + + def parse_md_file(self): + md_text = self.read_md_file() + self.get_html_from_md(md_text) + self.get_headers() + self.get_chapters() + self.get_chapter_with_text() + self.get_tables_size() + return f"Заголовки:\n{self.headers}\n\nГлавы:\n{self.chapters}\n\nГлавы с текстом:\n{self.chapter_with_text}\n\nДоля таблиц в тексте:\n{self.get_tables_size()}" + +def main(args): + md_file = MdUpload(args.mdfile) + print(md_file.parse_md_file()) From 9019fc96f59507bdb96e2994904a24306b2f8b89 Mon Sep 17 00:00:00 2001 From: Marina Date: Tue, 3 Oct 2023 16:02:27 +0300 Subject: [PATCH 17/19] add empty string --- app/main/reports/md_uploader/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/main/reports/md_uploader/__init__.py b/app/main/reports/md_uploader/__init__.py index ef406550..31cb93c1 100644 --- a/app/main/reports/md_uploader/__init__.py +++ b/app/main/reports/md_uploader/__init__.py @@ -1 +1 @@ -from .md_uploader import MdUpload \ No newline at end of file +from .md_uploader import MdUpload From 63193e346dcda2091def01bf8ab16649b335dfdb Mon Sep 17 00:00:00 2001 From: Marina Date: Tue, 3 Oct 2023 16:03:10 +0300 Subject: [PATCH 18/19] add empty string2 --- app/main/reports/md_uploader/md_uploader.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/main/reports/md_uploader/md_uploader.py b/app/main/reports/md_uploader/md_uploader.py index 1df1284a..926052ea 100644 --- a/app/main/reports/md_uploader/md_uploader.py +++ b/app/main/reports/md_uploader/md_uploader.py @@ -58,3 +58,4 @@ def parse_md_file(self): def main(args): md_file = MdUpload(args.mdfile) print(md_file.parse_md_file()) + From 5c09bc20fc99bf13794fb50b2b1d0f0311244161 Mon Sep 17 00:00:00 2001 From: Marina Date: Thu, 5 Oct 2023 15:06:05 +0300 Subject: [PATCH 19/19] add markdown in requirements --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 6ec862a4..0d87cf6c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,3 +27,4 @@ pdfplumber==0.6.1 pytest~=7.1.2 filetype==1.2.0 language-tool-python==2.7.1 +markdown==3.4.4