diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml new file mode 100644 index 0000000..263bf3a --- /dev/null +++ b/.github/workflows/ci-workflow.yml @@ -0,0 +1,36 @@ +name: Checks workflow + +on: ['push', 'pull_request'] + +jobs: + check-toc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Run + run: make toc-check + + release: + needs: check-toc + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Create epub + uses: docker://pandoc/core:2.14 + with: + args: --toc --toc-depth=6 -o questions.epub metadata.txt questions.md + + - name: Get TAG + if: startsWith(github.ref, 'refs/tags/') + run: echo "TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV + - name: Write build number + if: startsWith(github.ref, 'refs/tags/') + run: sed -i "s/build 1/$TAG/g" metadata.txt + - name: Test + run: cat metadata.txt + - name: Release + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v1 + with: + files: questions.epub diff --git a/Makefile b/Makefile index fa186b1..63ad5d9 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,10 @@ questions.epub: questions.md metadata.txt book_res/* attachments/* Makefile ## Generate epub book pandoc --toc --toc-depth=6 -o questions.epub metadata.txt questions.md + +.PHONY: toc +toc: ## Generate TOC from questions.md + python3 generate_toc.py + +.PHONY: toc +toc-check: ## Check that toc is actual + python3 generate_toc.py --check diff --git a/README.md b/README.md index 699ea5b..23d8a58 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,10 @@ ### Нашел ошибку/опечатку/бред в ответах или хочу добавить еще вопросов -Молодец, создавай PR. Не забудь добавить себя в [contributors.md](contributors.md) +Молодец, создавай PR. + +- После того как что-то изменил в [questions.md](questions.md) запусти `make toc` чтобы обновить [Список вопросов](#Список-вопросов) +- Не забудь добавить себя в [metadata.txt](metadata.txt) в раздел `author` ### Не хочу читать в Markdown, хочу читать в электронной книге @@ -60,7 +63,7 @@ + [Что возвращает метод items](questions.md/#Что-возвращает-метод-items) + [Как отсортировать список словарей по определенному полю](questions.md/#Как-отсортировать-список-словарей-по-определенному-полю) + [Что может являться ключом словаря. Что не может. Почему](questions.md/#Что-может-являться-ключом-словаря-Что-не-может-Почему) - + [Есть два списка – ключи и значения. Как составить из них словарь](questions.md/#Есть-два-списка-–-ключи-и-значения-Как-составить-из-них-словарь) + + [Есть два списка – ключи и значения. Как составить из них словарь](questions.md/#Есть-два-списка--ключи-и-значения-Как-составить-из-них-словарь) + [Как работает хэш-таблица](questions.md/#Как-работает-хэш-таблица) + [Что такое коллизия](questions.md/#Что-такое-коллизия) + [Где будет быстрее поиск, а где перебор и почему: dict, list, set, tuple](questions.md/#Где-будет-быстрее-поиск-а-где-перебор-и-почему-dict-list-set-tuple) @@ -183,7 +186,7 @@ + [В чем отличие тредов от мультипроцессинга](questions.md/#В-чем-отличие-тредов-от-мультипроцессинга) + [Какие задачи хорошо параллелятся, какие плохо](questions.md/#Какие-задачи-хорошо-параллелятся-какие-плохо) + [Нужно посчитать 100 уравнений. Делать это в тредах или нет](questions.md/#Нужно-посчитать-100-уравнений-Делать-это-в-тредах-или-нет) - + [Треды в Питоне — это нативные треды или нет](questions.md/#Треды-в-Питоне-—это-нативные-треды-или-нет) + + [Треды в Питоне — это нативные треды или нет](questions.md/#Треды-в-Питоне--это-нативные-треды-или-нет) + [Что такое гринлеты. Общее понятие. Примеры реализаций](questions.md/#Что-такое-гринлеты-Общее-понятие-Примеры-реализаций) * [Какие варианты реализации шаблона Singleton на питоне](questions.md/#Какие-варианты-реализации-шаблона-Singleton-на-питоне) * [Какие вы знаете инструменты для проверки кодстайл](questions.md/#Какие-вы-знаете-инструменты-для-проверки-кодстайл) @@ -199,7 +202,7 @@ * [Опишите алгоритм работы CSRF middleware](questions.md/#Опишите-алгоритм-работы-CSRF-middleware) * [Что такое сигналы? Зачем нужны? Назовите основные](questions.md/#Что-такое-сигналы-Зачем-нужны-Назовите-основные) * [Как реализуется связь m2m на уровне базы данных](questions.md/#Как-реализуется-связь-m2m-на-уровне-базы-данных) - * [Чем лучше отправлять форму — GET или POST](questions.md/#Чем-лучше-отправлять-форму-—-GET-или-POST) + * [Чем лучше отправлять форму — GET или POST](questions.md/#Чем-лучше-отправлять-форму--GET-или-POST) * [Как работает Serializer в Django REST Framework](questions.md/#Как-работает-Serializer-в-Django-REST-Framework) * [Что такое Meta в классах Django и для чего нужен](questions.md/#Что-такое-Meta-в-классах-Django-и-для-чего-нужен) * [За что отвечает Meta в сериализаторе](questions.md/#За-что-отвечает-Meta-в-сериализаторе) @@ -232,7 +235,7 @@ + [Наследование](questions.md/#Наследование) + [Полиморфизм](questions.md/#Полиморфизм) + [Абстракция](questions.md/#Абстракция) - * [Какие принципы программирования вы знаете](questions.md/#Какиe-принципы-программирования-вы-знаете) + * [Какие принципы программирования вы знаете](questions.md/#Какие-принципы-программирования-вы-знаете) + [KISS](questions.md/#KISS) + [DRY](questions.md/#DRY) + [YAGNI](questions.md/#YAGNI) diff --git a/contributors.md b/contributors.md deleted file mode 100644 index 08b91c7..0000000 --- a/contributors.md +++ /dev/null @@ -1,2 +0,0 @@ -- yakimka: https://github.com/yakimka -- pavlenk0: https://github.com/pavlenk0 diff --git a/generate_toc.py b/generate_toc.py new file mode 100644 index 0000000..da1897a --- /dev/null +++ b/generate_toc.py @@ -0,0 +1,118 @@ +import dataclasses +import re +import sys +from typing import List + + +@dataclasses.dataclass() +class Header: + name: str + level: int + + @property + def slug(self): + text = self.name.replace(' ', '-') + # single chars that are removed + text = re.sub(r'[`~!@#$%^&*()+=<>?,./:;"\'|{}\[\]\\–—]', '', text) + # CJK punctuations that are removed + text = re.sub(r'[ 。?!,、;:“”【】()〔〕[]﹃﹄“”‘’﹁﹂—…-~《》〈〉「」]', '', text) + return text + + +class TOCMaker: + def __init__( + self, + *, + max_depth=6, + link_prefix='', + indentation_size=2, + list_bullets=('-', '*', '+', '-'), + header_class=Header, + ): + self.max_depth = max_depth + self.link_prefix = link_prefix + self.indentation_size = indentation_size + self.list_bullets = list_bullets + self.header_class = header_class + + def make(self, text): + headers = self._collect_headers(text) + return self._make_toc(headers) + + def make_from_file(self, fp): + return self.make(fp.read()) + + def _collect_headers(self, text): + headers = [] + + code_blocks = 0 + for line in text.splitlines(): + line = line.strip() + code_blocks += line.count('```') % 2 + if code_blocks % 2 == 0 and line.startswith('#'): + header = self._parse_header_from_line(line) + if header.level <= self.max_depth: + headers.append(self._parse_header_from_line(line)) + + return headers + + def _make_toc(self, headers: List[Header]): + toc = [] + for header in headers: + indentation = ' ' * ((header.level - 1) * self.indentation_size) + bullet = self._get_bullet(header.level) + toc.append(f'{indentation}{bullet} [{header.name}]({self.link_prefix}#{header.slug})') + return '\n'.join(toc) + + def _get_bullet(self, level): + if level > len(self.list_bullets): + return self.list_bullets[-1] + return self.list_bullets[level - 1] + + def _parse_header_from_line(self, line): + level = 0 + name = '' + for char in line: + if char == '#': + level += 1 + else: + name = line[level + 1:].strip() + break + + return self.header_class( + name=name, + level=level + ) + + +def paste_after(delimiter, content, text): + result = [] + for line in text.splitlines(): + if line.strip() != delimiter: + result.append(line) + else: + result.append(f'{delimiter}\n') + result.append(f'{content}\n') + return '\n'.join(result) + + raise ValueError(f"Can't find delimiter '{delimiter}'") + + +if __name__ == '__main__': + with open('questions.md') as fp: + maker = TOCMaker(link_prefix='questions.md/') + toc = maker.make_from_file(fp) + + with open('README.md', 'r') as fp: + original = fp.read() + changed = paste_after('', toc, original) + + if '--check' in sys.argv: + if original != changed: + print('Error') + sys.exit(1) + else: + with open('README.md', 'w') as fp: + fp.write(changed) + + print('Done') diff --git a/metadata.txt b/metadata.txt index d6b2617..3b2b20f 100644 --- a/metadata.txt +++ b/metadata.txt @@ -4,9 +4,9 @@ title: text: Вопросы для подготовки к Python Developer интервью - type: edition text: build 1 -creator: -- role: author - text: yakimka +author: +- yakimka +- pavlenk0 toc-title: Содержание language: ru-RU stylesheet: book_res/style.css