Compare commits
3
Commits
06a4ea6870
...
v0.2.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e227a0802 | ||
|
|
4604f6923a | ||
|
|
73b9ae4dd6 |
@@ -5,6 +5,34 @@
|
|||||||
Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.0.0/),
|
Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.0.0/),
|
||||||
и этот проект придерживается [Semantic Versioning](https://semver.org/lang/ru/).
|
и этот проект придерживается [Semantic Versioning](https://semver.org/lang/ru/).
|
||||||
|
|
||||||
|
## [0.2.9] - 2026-10-07
|
||||||
|
|
||||||
|
### Добавлено
|
||||||
|
- Оптимизация SEO и поисковой семантики: добавлены целевые мета-теги (`title`, `description`, `keywords`, `og:title`, `og:description`) с релевантными ключевыми запросами для веб-типографики.
|
||||||
|
- Индексация главной страницы (`/`) в `sitemap.xml` через добавление `StaticViewSitemap` с приоритетом 1.0.
|
||||||
|
- Развернутый справочно-информационный раздел на главной странице:
|
||||||
|
- 9 карточек возможностей типографа (неразрывные пробелы с обработкой сквозь скобки и кавычки, кавычки-ёлочки и лапки, длинные/средние тире и числовые интервалы с римскими/арабскими числами, инициалы и сокращения организаций/титулов, висячая пунктуация, HTML-санитайзер и спецсимволы, мягкие переносы `­`, форматы вывода Unicode/HTML/Mixed, бесплатный и безлимитный доступ без сохранения текстов на сервере).
|
||||||
|
- Секция целевой аудитории (копирайтеры, верстальщики, контент-менеджеры, дизайнеры).
|
||||||
|
- Блок интеграции открытой Python-библиотеки `etpgrf` со сниппетами установки и использования.
|
||||||
|
- Справочный аккордеон с ответами на частые вопросы (FAQ).
|
||||||
|
- Сворачивание информационного SEO-блока в компактный спойлер (Bootstrap Collapse) для сохранения чистоты и минимализма главного экрана сервиса.
|
||||||
|
- Интерактивный hover-эффект для карточек возможностей с адаптивной подсветкой (светлая тень в тёмной теме оформления и глубокая тень в светлой теме).
|
||||||
|
- Автоматический тест валидации метаданных SEO и наличия корня сайта в карте сайта `sitemap.xml`.
|
||||||
|
|
||||||
|
### Исправлено
|
||||||
|
- Формирование канонических URL (`rel="canonical"`): очистка от случайных query-параметров и меток сессий.
|
||||||
|
|
||||||
|
## [0.2.8] - 2026-10-04
|
||||||
|
|
||||||
|
### Добавлено
|
||||||
|
- Локальная статика для внешних фронтенд-библиотек (Bootstrap 5, Bootstrap Icons, HTMX, Alpine.js, CodeMirror 6) для работы интерфейса при недоступности CDN.
|
||||||
|
- Стратегия fallback-подключения в шаблонах: автоматическое переключение на локальную статику при сбое или блокировке CDN.
|
||||||
|
- Скрипт копирования production-ассетов `frontend-assembly/copy-vendor.js` и обновление сборки через `frontend-assembly/assembly_via_npm.sh`.
|
||||||
|
- Автоматические тесты проверки наличия статических вендорных ассетов и корректности рендеринга fallback-подключений в `typograph/tests.py`.
|
||||||
|
|
||||||
|
### Изменено
|
||||||
|
- Обновлен и структурирован `.gitignore` с подробными комментариями.
|
||||||
|
|
||||||
## [0.2.7] - 2025-03-22
|
## [0.2.7] - 2025-03-22
|
||||||
### Изменено
|
### Изменено
|
||||||
- Обновление всех пактов, включая `etpgrf` (v0.1.5 → v0.1.6.post1).
|
- Обновление всех пактов, включая `etpgrf` (v0.1.5 → v0.1.6.post1).
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Основные возможности:
|
Основные возможности:
|
||||||
- Веб-интерфейс для ввода текста и настройки параметров типографики.
|
- Веб-интерфейс для ввода текста и настройки параметров типографики.
|
||||||
"""
|
"""
|
||||||
__version__ = "0.2.7"
|
__version__ = "0.2.9"
|
||||||
__author__ = "Sergei Erjemin"
|
__author__ = "Sergei Erjemin"
|
||||||
__email__ = "erjemin@gmail.com"
|
__email__ = "erjemin@gmail.com"
|
||||||
__license__ = "MIT"
|
__license__ = "MIT"
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
from django.contrib.sitemaps import Sitemap
|
from django.contrib.sitemaps import Sitemap
|
||||||
|
from django.urls import reverse
|
||||||
from .models import Post
|
from .models import Post
|
||||||
|
|
||||||
|
class StaticViewSitemap(Sitemap):
|
||||||
|
"""Карта сайта для статических страниц (главная и др.)."""
|
||||||
|
priority = 1.0
|
||||||
|
changefreq = "daily"
|
||||||
|
|
||||||
|
def items(self):
|
||||||
|
return ['index']
|
||||||
|
|
||||||
|
def location(self, item):
|
||||||
|
return reverse(item)
|
||||||
|
|
||||||
class PostSitemap(Sitemap):
|
class PostSitemap(Sitemap):
|
||||||
changefreq = "weekly" # Как часто меняются страницы
|
changefreq = "weekly" # Как часто меняются страницы
|
||||||
priority = 0.9 # Приоритет (от 0.0 до 1.0)
|
priority = 0.9 # Приоритет (от 0.0 до 1.0)
|
||||||
|
|||||||
@@ -4,175 +4,314 @@
|
|||||||
{% block title %}Песочница верстки — ETPGRF{% endblock %}
|
{% block title %}Песочница верстки — ETPGRF{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
{# Левая колонка: Дата и Картинка #}
|
<div class="row align-items-start">
|
||||||
<div class="col-lg-2 align-self-start text-end mb-4">
|
<div class="col border-end">
|
||||||
<p class="small align-self-end">
|
<h3>Сайт typograph.cube2.ru</h3>
|
||||||
<small class="bg-secondary bg-opacity-10 p-2 text-nowrap">
|
|
||||||
12.фев.2026
|
<p>Исходный код сайта онланй-типографа <tt>etpgrf</tt> доступен в нескольких репози­ториях (<a
|
||||||
</small>
|
href="https://github.com/erjemin/etpgrf-site" target="_blank">GitHub</a>, <a
|
||||||
</p>
|
href="https://gitverse.ru/erjemin/etpgrf-site" target="_blank">GitVerse</a> и <a
|
||||||
<p>
|
href="https://git.cube2.ru/erjemin/2026-etpgrf-site" target="_blank">Сube2</a>. Наиболее заметные изменения
|
||||||
<img src="{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}" class="w-100" alt="Django Admin" />
|
сайта онлайн-типографа приводятся ниже в хронологическом порядке. Для полного списка изменений,
|
||||||
</p>
|
включая мелкие фиксы и коммиты, пожалуйста, обратитесь к истории репозитория.</p>
|
||||||
|
|
||||||
|
<h4>[0.2.8] - 2026–10–04</h4>
|
||||||
|
<h5>Добавлено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Локальная статика для внешних фронтенд-библиотек (Bootstrap 5, Bootstrap Icons, HTMX, Alpine.js,
|
||||||
|
CodeMirror 6) для работы интер­фейса при недос­тупности CDN.
|
||||||
|
</li>
|
||||||
|
<li>Стратегия fallback-подключения в шаблонах: автома­тическое переклю­чение на локальную
|
||||||
|
статику при сбое или блокировке CDN.
|
||||||
|
</li>
|
||||||
|
<li>Скрипт копиро­вания production-ассетов <code>frontend-assembly/copy-vendor.js</code>
|
||||||
|
и обновление сборки через <code>frontend-assembly/assembly_via_npm.sh</code>.
|
||||||
|
</li>
|
||||||
|
<li>Автомати­ческие тесты проверки наличия стати­ческих вендорных ассетов и коррект­ности
|
||||||
|
ренде­ринга fallback-подключений в <code>typograph/tests.py</code>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Изменено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Обновлен и структу­рирован <code>.gitignore</code> с подроб­ными коммен­тариями.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>[0.2.7] - 2026–03–22</h4>
|
||||||
|
<h5>Изменено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Обновление всех пактов, включая <tt>etpgrf</tt> (v0.1.5 → v0.1.6.post1).</li>
|
||||||
|
</ul>
|
||||||
|
<h4>Исправлено</h4>
|
||||||
|
<ul>
|
||||||
|
<li><code>canonical</code> для страниц учитывает https/http.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>[0.2.6] - 2026–02–28</h4>
|
||||||
|
<h5>Изменено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Подключена новая версия библиотеки <tt>etpgrf</tt> (v0.1.4 → v0.1.5).</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>[0.2.5] — 2026–02–13</h4>
|
||||||
|
<h5>Добавлено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Редизайн списка постов в блоге: шахматный порядок, вертикальные разделители, улучшенная адаптивность
|
||||||
|
для мобильных
|
||||||
|
устройств.
|
||||||
|
</li>
|
||||||
|
<li>Поле <code>updated_at</code> (<em>Дата обновления</em>) в модели, админке, блогах, страницах, <tt>sitemaps.xml</tt>
|
||||||
|
и микро­разметке <tt>Schema.org</tt> для улучшения <abbr
|
||||||
|
title="Search Engine Optimization / Поисковая оптимизация">SEO</abbr>, <abbr
|
||||||
|
title="Generative Engine Optimization / Оптимизацию для Генеративных нейросетей и ИИ-поисковиков">GEO</abbr>
|
||||||
|
и <abbr title="Large Language Model Optimization / Оптимизация под Большие Языковые Модели">LLMO</abbr>.
|
||||||
|
</li>
|
||||||
|
<li><tt>README.md</tt> с описанием проекта онлайн-типографа, его особенностей, технического стека и инструкциями
|
||||||
|
по установке и запуску.
|
||||||
|
</li>
|
||||||
|
<li>Отображение заголовков постов в списке админки без HTML-мнемоник (декоди­рование <code>&nbsp;</code>
|
||||||
|
и др.).
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Изменено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Исправлены ошибки в шаблоне <tt>post_list.html</tt> (и полностью переработан дизайн в целом).
|
||||||
|
<ul>
|
||||||
|
<li>Улучшено отображение даты и скрытие декоративных изображений в списке постов на мобильных
|
||||||
|
устройствах.
|
||||||
|
</li>
|
||||||
|
<li>Оптими­зированы отступы и типографика в списке постов.</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>Формирование <code>slug</code> из <code>title</code> при сохранении поста или страницы с исполь­зованием
|
||||||
|
библиотеки <code>pytils</code> для трансли­терации с очистикой от HTML-мнемоник и создания
|
||||||
|
<abbr title="Uniform Resource Locator / Унифицированный Указатель Ресурса">URL</abbr>-дружес­твенных
|
||||||
|
строк.
|
||||||
|
</li>
|
||||||
|
<li>Дизайн и вёрстка страниц для постов блога и вспомо­гательных страниц для мобильных
|
||||||
|
устройств (адаптивность, скрытие картинки-обложки).
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>[0.2.4] — 2026–02–12</h4>
|
||||||
|
<h5>Добавлено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Микро­разметка <code>Schema.org</code> (JSON-LD) для постов и страниц для улучшения
|
||||||
|
SEO и понимания контента поисковиками и ИИ.
|
||||||
|
</li>
|
||||||
|
<li>Файл <code>llms.txt</code> для предоста­вления информации о сайте и API для больших
|
||||||
|
языковых моделей (<abbr title="Large Language Model">LLM</abbr>) в соответвиии со стандартом GEO
|
||||||
|
и LLMO.
|
||||||
|
</li>
|
||||||
|
<li>Кастомный фильтр <code>unescape</code> для очистки мета-тегов от HTML-сущностей и переводов
|
||||||
|
строк.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Исправлено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Исправлена ошибка, при которой счетчик символов не обновлялся при восста­новлении вкладки
|
||||||
|
из истории браузера.
|
||||||
|
</li>
|
||||||
|
<li>Исправлена ошибка экрани­рования кавычек в <tt>JSON-LD</tt>, <tt>Title</tt>
|
||||||
|
и <tt>Description</tt>.
|
||||||
|
</li>
|
||||||
|
<li>В <code>docker-compose.yaml</code> додавлен перезапуск <code>watchtower</code> при его
|
||||||
|
остановке.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h4>[0.2.3] — 2026–02–11</h4>
|
||||||
|
<h5>Изменено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Добавлена кнопка очистки текста во вводном поле и счетчик вводимых символов.</li>
|
||||||
|
</ul>
|
||||||
|
<h4>[0.2.2] — 2026–02–03</h4>
|
||||||
|
<h5>Изменено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>В онлайн-типографе подключена новая версия библиотеки <tt>etpgrf</tt> (v0.1.3 → v0.1.4).</li>
|
||||||
|
<li>Незначи­тельные улучшения в оформлении.</li>
|
||||||
|
</ul>
|
||||||
|
<h4>v0.2.1 — 2026–01–30</h4>
|
||||||
|
<h5>Исправлено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Исправление ошибок при формировании мета-тегов, картинок, <code>alt</code> под картинками
|
||||||
|
и т. п.
|
||||||
|
</li>
|
||||||
|
<li>Исправлена ошибка в настройках nginx внутри docker-контейнера возникавшая при отдаче
|
||||||
|
media-файлов.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Изменено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>При создании записи в блог или страницы «тизер» обязателен!</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>v0.2.0 — 2026–01–28</h4>
|
||||||
|
<h5>Добавлено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Приложение blog (для страниц и постов) и соответ­ствующие изменения в моделях базы,
|
||||||
|
добавление новых view и шаблонов.
|
||||||
|
</li>
|
||||||
|
<li>Песочница (шаблон <code>blog/templates/blog/tmp.html</code> для тестирования верстки (доступен только в режиме
|
||||||
|
debug).
|
||||||
|
</li>
|
||||||
|
<li>Динамическое создание sitemap.xml</li>
|
||||||
|
<li>robots.txt</li>
|
||||||
|
<li>Изменения в шапке сайта (меню и бургер)</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Изменено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Спрятан <code>url</code> админки типографа. Его расположение теперь задается через переменные окружения
|
||||||
|
в <code>.env</code>.
|
||||||
|
</li>
|
||||||
|
<li><code>favicon.ico</code> оптими­зирована для Яндекс (120х120px).</li>
|
||||||
|
<li>Исправлено поведение шапки и логотива для мобильных устройств.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>v0.1.8 — 2026–01–23</h4>
|
||||||
|
Коммит:
|
||||||
|
846c066
|
||||||
|
|
||||||
|
<h4>v0.1.7 — 2026–01–23</h4>
|
||||||
|
Коммит:
|
||||||
|
d74bee2
|
||||||
|
|
||||||
|
<h4>v0.1.6 — 2026–01–23</h4>
|
||||||
|
Коммит:
|
||||||
|
6b4dbaf
|
||||||
|
|
||||||
|
<h4>v0.1.5 — 2026–01–21</h4>
|
||||||
|
Коммит:
|
||||||
|
78174a8
|
||||||
|
|
||||||
|
<h4>v0.1.4 — 2026–01–20</h4>
|
||||||
|
Коммит:
|
||||||
|
2d09aef
|
||||||
|
|
||||||
|
<h4>v0.1.3 — 2026–01–19</h4>
|
||||||
|
Коммит:
|
||||||
|
66f2228
|
||||||
|
|
||||||
|
<h4>v0.1.2 — 2026–01–18</h4>
|
||||||
|
Коммит:
|
||||||
|
92711f5
|
||||||
|
|
||||||
|
<h4>v0.1.1 — 2026–01–16</h4>v0.1.1
|
||||||
|
Коммит:
|
||||||
|
5d5d48d
|
||||||
|
|
||||||
|
<h4>v0.1.0 — 2026–01–16</h4>
|
||||||
|
Коммит:
|
||||||
|
3a7bb29
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col border-start">
|
||||||
|
<h3>Библиотека etpgrf</h3>
|
||||||
|
<p>
|
||||||
|
Исходный код etpgrf-типографа доступен в нескольких репози­ториях (<a
|
||||||
|
href="https://github.com/erjemin/etpgrf" target="_blank">GitHub</a>, <a
|
||||||
|
href="https://gitverse.ru/erjemin/etpgrf" target="_blank">GitVerse</a>, <a
|
||||||
|
href="https://git.cube2.ru/erjemin/2025-etpgrf" target="_blank">Сube2</a>, и <a
|
||||||
|
href="https://pypi.org/project/etpgrf/" target="_blank">PyPI</a>), распрос­траняется под лицензией <a
|
||||||
|
href="https://opensource.org/licenses/MIT" target="_blank">MIT</a>, может быть установлен локально, на ваш
|
||||||
|
сайт или интегри­рован в ваши проекты как Python-библиотека.</p>
|
||||||
|
|
||||||
{# Правая колонка: Контент #}
|
<h4>[0.1.6.post1] - 2026–03–19</h4>
|
||||||
<div class="col-lg-10 border-start ps-lg-4 post-page-content">
|
<h5>Изменено</h5>
|
||||||
|
<ul>
|
||||||
<h1>Массовая типографика контента в Django: пользова­тельские команды управления (Custom Management Commands)</h1>
|
<li>Новый алгоритм «висячей пунктуации» (<em>HangingPunctuationProcessor</em>). Добавлены компенсирующие
|
||||||
|
пробелы для висячих символов, чтобы избежать наложения на соседние слова. Теперь «висячие символы» (кавычки,
|
||||||
<div class="lead bg-secondary bg-opacity-10 p-3 rounded">
|
скобки и т. п.) оборачиваются в <code><span></code> вместе с ближайшим словом и пробелом, что
|
||||||
|
обеспечивает отсутствие визуальных смещений внутри строки. Режим <code>both</code> (для одновременного
|
||||||
<p>Если вы работали с Django, вы наверняка использовали команду <code>manage.py</code> сотни раз: <code>python manage.py runserver</code>, <code>migrate</code>, <code>createsuperuser</code>.</p>
|
вывешивания в обе стороны) отключен из-за потенциальных конфликтов компенсирующих пробелов и проблем
|
||||||
<p>Но знаете ли вы, что можете легко создавать <strong>свои собственные</strong> команды? Это один из самых мощных и недоо­цененных инструментов Django для автома­тизации рутины. В этом посте я расскажу, что такое <strong>Management Command</strong>, зачем она нужна и как её использовать на примере реальной задачи: массовой типографики контента в базе данных.</p>
|
с выравни­ванием при исполь­зовании CSS <code>text-justify</code>.</li>
|
||||||
|
</ul>
|
||||||
</div>
|
<h4>Исправлено</h4>
|
||||||
<hr>
|
<ul>
|
||||||
<div class="post-content mt-4">
|
<li>Автоматическая замена символов <code>x</code>, <code>X</code>, <code>х</code>, <code>Х</code>, стоящих между
|
||||||
|
числами, на знак умножения <code>×</code>, чтобы выражения вида <code>100x100</code> или <code>100 х 100</code>
|
||||||
<h2>Что это такое?</h2>
|
корректно обрабатывались и выглядели типографски правильными (<code>100×100</code> или <code>100&nbsp;×&nbsp;100</code>).
|
||||||
<p><strong>Management Command</strong> — это обычный Python-скрипт, который «встраивается» в экосистему Django. Он имеет доступ к моделям, настройкам (settings.py) и базе данных, но запускается из консоли через <code>python manage.py ...</code>.</p>
|
</li>
|
||||||
<h2>Зачем это нужно?</h2>
|
</ul>
|
||||||
<p>Представьте ситуации:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Нужно пересчитать рейтинг для десятки тысяч товаров на сайте или сделать типографику всех публикаций после подключения etpgrf-типографа (или любого другого).</li>
|
|
||||||
<li>Необходимо обновить цены для всех товарных карточек в соответствии с таблицей excel или по API (или спарсить данные с внешнего сайта).</li>
|
|
||||||
<li>Нужно отправить e-mail рассылку пользо­вателям.</li>
|
|
||||||
<li>Нужно пометить комментарии или публикации как архивные в соответствии с правилом или почистить базу от старых логов.</li>
|
|
||||||
</ul>
|
|
||||||
<p>Делать это через <tt>views.py</tt> (views) — плохая идея (страница может отвалиться по тайм-ауту). Писать отделный скрипт рядом <tt>manage.py</tt> — неудобно (нужно вручную настраивать `DJANGO_SETTINGS_MODULE`). Встроенные команды решают эти проблемы элегантно.</p>
|
|
||||||
<h2>Как это работает?</h2>
|
|
||||||
<p>Django использует систему «автообна­ружения» (auto-discovery). Чтобы ваша команда появилась в списке `manage.py`, нужно соблюсти строгую иерархию папок внутри вашего приложения (например, <tt>web</tt>):</p>
|
|
||||||
<pre class="border p-3 my-3 rounded bg-secondary bg-opacity-25">web/
|
|
||||||
├── __init__.py
|
|
||||||
├── models.py
|
|
||||||
└── management/ <-- 1. Создаем папку management
|
|
||||||
├── __init__.py
|
|
||||||
└── nginx/
|
|
||||||
├── commands/ <-- 2. Внутри неё папку commands
|
|
||||||
├── __init__.py
|
|
||||||
└── my_cool_script.py <-- 3. Наш файл с командой</pre>
|
|
||||||
<p>Файл <tt>my_cool_script.py</tt> автома­тически превратится в команду: <code>python manage.py my_cool_script</code>. Django будет искать его в папках <tt>management/commands/</tt> внутри каждого устано­вленного приложения. Это позволяет легко организовать код и держать все «команды обслуживания» в одном месте.</p>
|
|
||||||
<p><strong>Для справки:</strong> Механизм Custom Management Commands появился еще в Django 0.96 (в глубокой древности) и с тех пор является стандартом де-факто для написания скриптов обслуживания.</p>
|
|
||||||
<h2>Анатомия команды</h2>
|
|
||||||
<p>Вот пример простейшей команды. Мы наследуемся от класса <code>BaseCommand</code> и переопре­деляем метод <code>handle</code>:</p>
|
|
||||||
<pre class="border p-3 my-3 rounded bg-secondary bg-opacity-25">
|
|
||||||
# web/management/commands/hello.py
|
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = 'Выводит приветствие' # Описание для --help
|
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
|
||||||
# Можно добавлять аргументы, как в argparse
|
|
||||||
parser.add_argument('name', type=str, help='Имя пользователя')
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
# Главная логика
|
|
||||||
name = options['name']
|
|
||||||
self.stdout.write(f"Привет, {name}!")</pre>
|
|
||||||
<p>Запуск:</p>
|
|
||||||
<pre class="border p-3 my-3 rounded bg-secondary bg-opacity-25">
|
|
||||||
python manage.py hello Иван</pre>
|
|
||||||
<p>Увидим в консоли:</p>
|
|
||||||
<pre class="border p-3 my-3 rounded bg-secondary bg-opacity-25">
|
|
||||||
Привет, Иван!</pre>
|
|
||||||
<h2>Реальный пример: Массовая типографика через etpgrf</h2>
|
|
||||||
<p>В моём проекте, <a href="https://dq.cube2.ru/" target="_blank">DQ – коллекция цитат: место для вдумчивого чтения</a>, контент был частично обработан с помощью Типографа Муравьёа, частично вручную. И вот я установил и настроил etpgrf (тем более что Типограф Муравьёва «почил в бозе», да и до того не обновлялся с 2018 года). Все новые цитаты типогра­фируются через etpgrf, но что делать со старыми? Их сотни, и открывать и «пересо­хранаять» каждую вручную — это адский труд. Вот тут на помощь и приходит Custom Management Command.</p>
|
|
||||||
<p>Вот как я это реализовал. В <tt>web/management/commands/reprocess_typography.py</tt>:</p>
|
|
||||||
<pre class="border p-3 my-3 rounded bg-secondary bg-opacity-25">
|
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
from web.models import TbDictumAndQuotes
|
|
||||||
# Импорты библиотеки etpgrf
|
|
||||||
from etpgrf.typograph import Typographer
|
|
||||||
from etpgrf.sanitizer import SanitizerProcessor
|
|
||||||
# ... другие процессоры, если нужно ...
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = 'Переобрабатывает все цитаты'
|
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
|
||||||
# Добавляем флаг "сухой прогон"
|
|
||||||
parser.add_argument(
|
|
||||||
'--dry-run',
|
|
||||||
action='store_true',
|
|
||||||
help='Запустить без сохранения изменений',
|
|
||||||
)
|
|
||||||
# Ограничение количества
|
|
||||||
parser.add_argument('--limit', type=int, help='Сколько записей обработать')
|
|
||||||
# Смещение (для пагинации)
|
|
||||||
parser.add_argument('--offset', type=int, default=0, help='Сколько пропустить')
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
# 1. Настраиваем типограф
|
|
||||||
# ... инициализация процессоров ...
|
|
||||||
settings = {
|
|
||||||
# ...
|
|
||||||
'sanitizer': SanitizerProcessor(mode="html"), # Режим HTML-очистки
|
|
||||||
'hanging_punctuation': 'left', # Режим висячей пунктуации
|
|
||||||
# ...
|
|
||||||
}
|
|
||||||
typographer = Typographer(**settings)
|
|
||||||
|
|
||||||
# 2. Получаем записи из базы с учетом limit/offset
|
|
||||||
qs = TbDictumAndQuotes.objects.all().order_by('id')
|
|
||||||
|
|
||||||
start = options['offset']
|
|
||||||
if options['limit']:
|
|
||||||
qs = qs[start : start + options['limit']]
|
|
||||||
else:
|
|
||||||
qs = qs[start:]
|
|
||||||
|
|
||||||
self.stdout.write(f"Найдено {qs.count()} цитат...")
|
|
||||||
|
|
||||||
# 3. Бежим по циклу с прогресс-баром (tqdm если есть)
|
|
||||||
try:
|
|
||||||
from tqdm import tqdm
|
|
||||||
iterator = tqdm(qs)
|
|
||||||
except ImportError:
|
|
||||||
iterator = qs
|
|
||||||
|
|
||||||
for dq in iterator:
|
|
||||||
try:
|
|
||||||
# Обрабатываем текст
|
|
||||||
new_html = typographer.process(dq.szContent)
|
|
||||||
|
|
||||||
if options['dry_run']:
|
|
||||||
self.stdout.write(f"[{dq.id}] Предпросмотр: {new_html[:50]}...")
|
|
||||||
else:
|
|
||||||
dq.szContentHTML = new_html
|
|
||||||
dq.save(update_fields=['szContentHTML'])
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.stdout.write(self.style.ERROR(f"Ошибка id={dq.id}: {e}"))
|
|
||||||
|
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS(f"\nГотово!"))</pre>
|
|
||||||
<p>Возможно вы заметили, что в моем проекте для контента есть два поля: <code>szContent</code> и <code>szContentHTML</code>. В первом хранится «сырой» текст, во втором — результат типогра­фирования. В вашем проекте, скорее всего, только одно поле для контента, но по сути это ничего не меняет.</p>
|
|
||||||
<p>Еще интересные фишки, которые исполь­зовались при типогра­фировании:</p>
|
|
||||||
<ol>
|
|
||||||
<li><code>self.stdout.write</code> вместо <code>print</code> – позволяет Django перехва­тывать вывод (например, для тестов) и корректно работать с кодировками.</li>
|
|
||||||
<li><code>self.style.SUCCESS</code> и <code>self.style.ERROR</code> – раскрашивает текст в консоли (зеленый/красный) и это очень удобно для визуального восприятия логов.</li>
|
|
||||||
<li>Аргумент <tt>--dry-run</tt> — позволяют безопасно тестировать скрипт на продакшене перед тем, как реально менять данные.</li>
|
|
||||||
<li>Аргументы <tt>--limit</tt> и <tt>--offset</tt> — позволяют обрабатывать базу «порциями», что полезно для больших объемов данных (можно запустить несколько процессов параллельно с разными <tt>offset</tt>).</li>
|
|
||||||
<li>Используется <code>update_fields</code> — позволяет переза­писывать не всю модель целиком (что могло бы затереть изменения, сделанные кем-то другим в ту же секунду), а обновляем только конкретные поля.</li>
|
|
||||||
</ol>
|
|
||||||
<p>Теперь, чтобы типогра­фировать весь контент, нам достаточно одной строку в терминале, и Django сделает всю грязную работу за нас (не забудьте инициировать виртуальное окружение вашего проекта).</p>
|
|
||||||
<p>Тестовый прогон (безопасно, ничего не сохраняет):</p>
|
|
||||||
<pre class="border p-3 my-3 rounded bg-secondary bg-opacity-25">
|
|
||||||
python manage.py reprocess_typography --dry-run</pre>
|
|
||||||
<p>Боевой запуск, с изменениями данные в базе:</p>
|
|
||||||
<pre class="border p-3 my-3 rounded bg-secondary bg-opacity-25">
|
|
||||||
python manage.py reprocess_typography</pre>
|
|
||||||
<p>Если у вас очень много записей, можно запускать по частям:</p>
|
|
||||||
<pre class="border p-3 my-3 rounded bg-secondary bg-opacity-25">
|
|
||||||
python manage.py reprocess_typography --limit 1000 --offset 0
|
|
||||||
python manage.py reprocess_typography --limit 1000 --offset 1000
|
|
||||||
python manage.py reprocess_typography --limit 1000 --offset 2000</pre>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4>[0.1.5] - 2026–02–27</h4>
|
||||||
|
<h5>Исправлено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Исправлена ошибка, из-за которой <code>&amp;</code> в исходном тексте некорректно преобразовывался
|
||||||
|
в <code>&</code>. Теперь <code>&amp;</code>; и его маскирование (<code>&amp;amp;</code>,
|
||||||
|
<code>&amp;lt;</code> и т. д.) сохраняются в итоговом HTML.
|
||||||
|
</li>
|
||||||
|
<li>Исправлена (частично) расстановка неразрывных пробелов <code>&nbsp;</code> на границах закрывающих
|
||||||
|
тегов (например, <code><b>Текст</b> — слово</code> теперь корректно обрабатывается, в <code><b>Текст</b>&nbsp;—
|
||||||
|
слово)</code>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h4>[0.1.4] - 2026–02–03</h4>
|
||||||
|
<h5>Изменено</h5>
|
||||||
|
<ul>
|
||||||
|
<li><b>Архите­ктурное улучшение:</b> Полностью переработан механизм обработки HTML.</li>
|
||||||
|
<li>Внедрены <b>маркеры границ узлов</b> (<code>\uFFFF</code>) при сборке текста. Это позволяет корректно
|
||||||
|
восста­навливать структуру HTML даже если длина текста изменилась в процессе обработки (например, при удалении лишних
|
||||||
|
пробелов).
|
||||||
|
</li>
|
||||||
|
<li>Внедрены <b>плейсхолдеры</b> (<code>\uFFFC</code>) для защищенных тегов (<code><code></code>,
|
||||||
|
<code><script></code> и др.). Теперь содержимое этих тегов физически изолируется перед обработкой,
|
||||||
|
что предот­вращает «протекание» контекста (например, склеивание слов, разделенных кодом).
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Исправлено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Исправлена ошибка смещения текста при наличии спецсимволов (мнемоник) или при изменении длины
|
||||||
|
строки.
|
||||||
|
</li>
|
||||||
|
<li>Исправлена обработка кавычек, стоящих вплотную к границам тегов (например, <code>"<b>Текст</b>"</code>).
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h4>v0.1.3 — 2026–01–11</h4>
|
||||||
|
<h5>Исправлено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Исправлена проблема с появлением лишних тегов <code><html></code>
|
||||||
|
и <code><body></code> при обработке фрагментов HTML (когда исполь­зуется парсер
|
||||||
|
<code>lxml</code>). Теперь типограф автома­тически определяет, был ли на входе полноценный
|
||||||
|
документ или фрагмент, и возвращает соответ­ствующий результат.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h4>v0.1.2 — 2025–12–27</h4>
|
||||||
|
<h5>Исправлено</h5>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Критическое исправление:</strong> Добавлена отсут­ствующая зависимость <code>regex</code> в <code>pyproject.toml</code>.
|
||||||
|
Без неё библиотека падала при импорте.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h4>v0.1.1 — 2025–12–23</h4>
|
||||||
|
<h5>Добавлено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Ссылки на зеркала репозитория (GitVerse, Gitea) в <code>pyproject.toml</code> и <code>README.md</code>.
|
||||||
|
</li>
|
||||||
|
<li>Раздел Credits в документации.</li>
|
||||||
|
</ul>
|
||||||
|
<h4>v0.1.0 — 2025–12–23</h4>
|
||||||
|
<h5>Добавлено</h5>
|
||||||
|
<ul>
|
||||||
|
<li>Первый публичный релиз библиотеки <code>etpgrf</code>.</li>
|
||||||
|
<li>
|
||||||
|
Основные модули:
|
||||||
|
<ul>
|
||||||
|
<li><code>Typographer</code>: основной класс-оркестратор.</li>
|
||||||
|
<li><code>Hyphenator</code>: расстановка мягких переносов (алгоритм Ляна-Кнута).</li>
|
||||||
|
<li><code>QuotesProcessor</code>: замена кавычек («ёлочки», „лапки“).</li>
|
||||||
|
<li><code>Unbreakables</code>: неразрывные пробелы для предлогов, союзов и частиц.</li>
|
||||||
|
<li><code>LayoutProcessor</code>: типографика тире, инициалов, акронимов, единиц измерения, устойчивых
|
||||||
|
сокращений (постпо­зиционных и препози­ционных).</li>
|
||||||
|
<li><code>SymbolsProcessor</code>: псевдо­графика (тире, стрелочки, копирайт и т. п.)</li>
|
||||||
|
<li><code>HangingPunctuationProcessor</code>: висячая пунктуация.</li>
|
||||||
|
<li><code>SanitizerProcessor</code>: очистка HTML перед обработкой.</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>Поддержка русского, русского дорефо­рменного и английского языков.</li>
|
||||||
|
<li>Поддержка обработки HTML (через BeautifulSoup).</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ from django.conf import settings
|
|||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
from django.contrib.sitemaps.views import sitemap # Импортируем view для sitemap
|
from django.contrib.sitemaps.views import sitemap # Импортируем view для sitemap
|
||||||
from blog import views as blog_views
|
from blog import views as blog_views
|
||||||
from blog.sitemaps import PostSitemap # Импортируем наш класс Sitemap
|
from blog.sitemaps import PostSitemap, StaticViewSitemap # Импортируем классы Sitemap
|
||||||
|
|
||||||
# Словарь с картами сайта
|
# Словарь с картами сайта
|
||||||
sitemaps = {
|
sitemaps = {
|
||||||
|
'static': StaticViewSitemap,
|
||||||
'posts': PostSitemap,
|
'posts': PostSitemap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,25 +3,25 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
{# SEO & Meta Tags #}<title>{% block title %}ETPGRF — единая типографика для веба{% endblock %}</title>
|
{# SEO & Meta Tags #}<title>{% block title %}Онлайн-типограф для текста и HTML — ETPGRF{% endblock %}</title>
|
||||||
<meta name="description" content="{% block description %}Бесплатный онлайн-типограф для подготовки текстов к публикации в вебе. Расставка неразрывных пробелов, правильных кавычек («ёлочки»), тире, спецсимволы, отбивка, компоновка, висячая пунктуация. Идеально для верстки сайтов, статей и постов.{% endblock %}">
|
<meta name="description" content="{% block description %}Онлайн-типограф для подготовки HTML и публикаций на русском и английском: кавычки «ёлочки» и “лапки”, длинное тире, неразрывные пробелы (&nbsp;), отбивка предлогов, артиклей и чисел, висячая пунктуация…{% endblock %}">
|
||||||
<meta name="keywords" content="{% block keywords %}типограф, типографика, онлайн типограф, подготовка текста для веба, html типограф, неразрывные пробелы, кавычки елочки, длинное тире, очистка текста от мусора, интернет верстка, муравьев{% endblock %}">
|
<meta name="keywords" content="{% block keywords %}онлайн типограф, типографика онлайн, типограф для текста, html типограф, неразрывные пробелы, кавычки елочки, длинное тире, подготовка текста для веба, висячая пунктуация, python etpgrf, типограф лебедева муравьева{% endblock %}">
|
||||||
<meta name="author" content="Sergei Erjemin">
|
<meta name="author" content="Sergei Erjemin">
|
||||||
{# Schema.org (JSON-LD) #}{% block schema %}{% endblock %}
|
{# Schema.org (JSON-LD) #}{% block schema %}{% endblock %}
|
||||||
{# Open Graph (Facebook, VK, LinkedIn, Telegram) #}<meta property="og:type" content="website" />
|
{# Open Graph (Facebook, VK, LinkedIn, Telegram) #}<meta property="og:type" content="website" />
|
||||||
<meta property="og:site_name" content="ETPGRF" />
|
<meta property="og:site_name" content="ETPGRF" />
|
||||||
<meta property="og:url" content="{{ request.build_absolute_uri }}" />
|
<meta property="og:url" content="{% block og_url %}{{ request.scheme }}://{{ request.get_host }}{{ request.path }}{% endblock %}" />
|
||||||
<meta property="og:title" content="{% block og_title %}ETPGRF — единая типографика для веба{% endblock %}" />
|
<meta property="og:title" content="{% block og_title %}Онлайн-типограф для текста и HTML — ETPGRF{% endblock %}" />
|
||||||
<meta property="og:description" content="{% block og_description %}Сделайте ваш текст профессиональным и готовым к публикации в интернете за один клик. Умная типографика для веб-дизайнеров, редакторов и контент-менеджеров.{% endblock %}" />
|
<meta property="og:description" content="{% block og_description %}Бесплатный онлайн-типограф для подготовки текстов и HTML к публикации: кавычки «ёлочки», неразрывные пробелы, длинное тире и висячая пунктуация.{% endblock %}" />
|
||||||
<meta property="og:image" content="{% block og_image %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endblock %}" />
|
<meta property="og:image" content="{% block og_image %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endblock %}" />
|
||||||
<meta property="og:image:width" content="1200" />
|
<meta property="og:image:width" content="1200" />
|
||||||
<meta property="og:image:height" content="630" />
|
<meta property="og:image:height" content="630" />
|
||||||
{# --- Twitter Cards (X) --- #}<meta name="twitter:card" content="summary_large_image">
|
{# --- Twitter Cards (X) --- #}<meta name="twitter:card" content="summary_large_image">
|
||||||
<meta name="twitter:title" content="{% block twitter_title %}ETPGRF — единая типографика для веба{% endblock %}" />
|
<meta name="twitter:title" content="{% block twitter_title %}Онлайн-типограф для текста и HTML — ETPGRF{% endblock %}" />
|
||||||
<meta name="twitter:description" content="{% block twitter_description %}Сделайте ваш текст профессиональным и готовым к публикации в интернете за один клик.{% endblock %}" />
|
<meta name="twitter:description" content="{% block twitter_description %}Бесплатный онлайн-типограф для подготовки текстов и HTML к публикации в вебе.{% endblock %}" />
|
||||||
<meta name="twitter:image" content="{% block twitter_image %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endblock %}" />
|
<meta name="twitter:image" content="{% block twitter_image %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endblock %}" />
|
||||||
{# Шутка #}<meta name="generator" content="Microsoft FrontPage 1.0"/>
|
{# Шутка #}<meta name="generator" content="Microsoft FrontPage 1.0"/>
|
||||||
{# Canonical #}<link rel="canonical" href="{{ request.build_absolute_uri }}"/>
|
{# Canonical #}<link rel="canonical" href="{% block canonical %}{{ request.scheme }}://{{ request.get_host }}{{ request.path }}{% endblock %}"/>
|
||||||
{# Favicons #}<link rel="icon" href="{{ request.scheme }}://{{ request.get_host }}{% static 'favicon.ico' %}" type="image/x-icon" />
|
{# Favicons #}<link rel="icon" href="{{ request.scheme }}://{{ request.get_host }}{% static 'favicon.ico' %}" type="image/x-icon" />
|
||||||
{# Favicons #}<link rel="icon" type="image/png" href="{% static 'favicon-96x96.png' %}" />
|
{# Favicons #}<link rel="icon" type="image/png" href="{% static 'favicon-96x96.png' %}" />
|
||||||
{# Favicons #}<link rel="icon" href="{% static 'favicon-light.svg' %}" type="image/svg+xml" media="(prefers-color-scheme: light)" />
|
{# Favicons #}<link rel="icon" href="{% static 'favicon-light.svg' %}" type="image/svg+xml" media="(prefers-color-scheme: light)" />
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
<div class="container d-flex justify-content-between align-items-center">
|
<div class="container d-flex justify-content-between align-items-center">
|
||||||
<span class="text-muted small nowrap me-2">© Sergei Erjemin, 2025–{% now 'Y' %}.</span>
|
<span class="text-muted small nowrap me-2">© Sergei Erjemin, 2025–{% now 'Y' %}.</span>
|
||||||
<nobr class="text-muted small mx-2">
|
<nobr class="text-muted small mx-2">
|
||||||
<i class="bi bi-tags me-1" title="Версия библиотеки etpgrf / Версия сайта"></i><a href="/changelog">v0.1.6.post1 / v0.2.7</a>
|
<i class="bi bi-tags me-1" title="Версия библиотеки etpgrf / Версия сайта"></i><a href="/changelog">v0.1.6.post1 / v0.2.9</a>
|
||||||
</nobr>
|
</nobr>
|
||||||
{# Сводная статистика (HTMX) #}<span class="text-muted small ms-2" hx-get="{% url 'stats_summary' %}" hx-trigger="load">
|
{# Сводная статистика (HTMX) #}<span class="text-muted small ms-2" hx-get="{% url 'stats_summary' %}" hx-trigger="load">
|
||||||
...
|
...
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{% extends 'typograph/base.html' %}
|
{% extends 'typograph/base.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block title %}ETPGRF — единая типографика для веба{% endblock %}
|
{% block title %}Онлайн-типограф для текста и HTML — ETPGRF: кавычки «ёлочки», тире, неразрывные пробелы{% endblock %}
|
||||||
|
|
||||||
{% block description %}Бесплатный онлайн-типограф для подготовки публикаций в вебе: кавычки «ёлочки», тире, неразрывные пробелы, спецсимволы, отбивка/компоновка, висячая пунктуация… Идеально для верстки сайтов, статей и постов.{% endblock %}
|
{% block og_title %}Онлайн-типограф для текста и HTML — ETPGRF{% endblock %}
|
||||||
|
|
||||||
{% block keywords %}типограф, типографика, онлайн типограф, подготовка текста для веба, html типограф, неразрывные пробелы, кавычки елочки, длинное тире, очистка текста от мусора, интернет верстка, муравьев{% endblock %}
|
{% block og_description %}Бесплатный умный онлайн-типограф для подготовки текстов, статей и верстки к публикации в вебе за один клик.{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
{# SEO-текст и описание #}
|
{# SEO-текст и описание #}
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<h1 class="h3 mb-3">Онлайн-типограф для веба</h1>
|
<h1 class="h3 mb-3">Онлайн-типограф для текста и веб-публикаций</h1>
|
||||||
<p>
|
<p>
|
||||||
Интернет-типограф <strong>etpgrf</strong> — инструмент для подготовки текста для публикации
|
Интернет-типограф <strong>etpgrf</strong> — инструмент для подготовки текста для публикации
|
||||||
в вебе. Расстановка неразрывных пробелов перед союзами и предлогами, замена кавычек
|
в вебе. Расстановка неразрывных пробелов перед союзами и предлогами, замена кавычек
|
||||||
@@ -298,6 +298,192 @@
|
|||||||
<div id="cm-result-wrapper" class="result-box p-0"></div>
|
<div id="cm-result-wrapper" class="result-box p-0"></div>
|
||||||
<div id="result-area" style="display: none;"></div>
|
<div id="result-area" style="display: none;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{# ИНФОРМАЦИОННЫЙ И SEO-БЛОК #}
|
||||||
|
<div class="col-md-12 mt-4 pt-3 border-top">
|
||||||
|
<div class="text-center mb-3">
|
||||||
|
<button class="btn btn-sm btn-outline-secondary rounded-pill px-3 py-1 shadow-sm collapse-toggle-btn collapsed"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#seoDetailsCollapse"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="seoDetailsCollapse">
|
||||||
|
<i class="bi bi-info-circle me-1"></i> О сервисе, возможности и правила
|
||||||
|
<i class="bi bi-chevron-down ms-1 collapse-icon"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="collapse" id="seoDetailsCollapse">
|
||||||
|
<section class="mb-5 pt-3">
|
||||||
|
<h2 class="h4 mb-4">Возможности онлайн-типографа etpgrf</h2>
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<div class="p-3 border rounded bg-body-tertiary h-100 feature-card">
|
||||||
|
<h3 class="h6 fw-bold"><i class="bi bi-distribute-horizontal me-1"></i> Неразрывные пробелы (&nbsp;)</h3>
|
||||||
|
<p class="small text-muted mb-0">
|
||||||
|
Автоматическая привязка предлогов, союзов, частиц («в», «на», «с», «и», «не», «бы»…) и артиклей („the“, „a“, „is“…). Парсер «видит» и связывает их со словами даже сквозь кавычки и скобки (например, <code>и&nbsp;«Беда» села на мель</code>).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<div class="p-3 border rounded bg-body-tertiary h-100 feature-card">
|
||||||
|
<h3 class="h6 fw-bold"><i class="bi bi-quote me-1"></i> Кавычки «ёлочки» и „лапки“</h3>
|
||||||
|
<p class="small text-muted mb-0">
|
||||||
|
Замена машинных прямых кавычек (<code>"</code>) на правильные русские «ёлочки» и французские „лапки“ для вложенных цитат. Поддержка английской, современной русской и русской дореформенной типографики («безъ», «въ», «надъ», «сiя»…).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<div class="p-3 border rounded bg-body-tertiary h-100 feature-card">
|
||||||
|
<h3 class="h6 fw-bold"><i class="bi bi-dash-lg me-1"></i> Тире, числа и диапазоны</h3>
|
||||||
|
<p class="small text-muted mb-0">
|
||||||
|
Замена дефисов на длинное тире (—) с языковой отбивкой, среднее тире (–) в числовых интервалах (1941–1945, 10–20 кг). Корректная обработка арабских и римских чисел (например, XXI век, III в. н. э.).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<div class="p-3 border rounded bg-body-tertiary h-100 feature-card">
|
||||||
|
<h3 class="h6 fw-bold"><i class="bi bi-person-badge me-1"></i> Инициалы и сокращения</h3>
|
||||||
|
<p class="small text-muted mb-0">
|
||||||
|
Связывание неразрывными пробелами инициалов (А.С. Пушкин), устойчивых сокращений (ЗАО «Рога и Копыта», др. Барменталь), сокращений («и т.д.», «и др.»), знаков (§ 5, № 12) и единиц (100 руб., 50 м²).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<div class="p-3 border rounded bg-body-tertiary h-100 feature-card">
|
||||||
|
<h3 class="h6 fw-bold"><i class="bi bi-layout-text-sidebar me-1"></i> Висячая пунктуация</h3>
|
||||||
|
<p class="small text-muted mb-0">
|
||||||
|
Оптическое выравнивание левого и правого края текстового блока за счет выноса кавычек, скобок и тире за границу полосы набора. Идеально для крупных заголовков, цитат, выносов и врезок.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<div class="p-3 border rounded bg-body-tertiary h-100 feature-card">
|
||||||
|
<h3 class="h6 fw-bold"><i class="bi bi-shield-check me-1"></i> Санитайзер и спецсимволы</h3>
|
||||||
|
<p class="small text-muted mb-0">
|
||||||
|
Встроенный HTML-санитайзер: очистка текста от мусорных тегов при копировании из Word/Google Docs, сброс разметки висячей пунктуации. Обработка псевдографики (<code>(c)</code> → ©, <code>(tm)</code> → ™, <code>+/-</code> → ±).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<div class="p-3 border rounded bg-body-tertiary h-100 feature-card">
|
||||||
|
<h3 class="h6 fw-bold"><i class="bi bi-fonts me-1"></i> Переносы в словах</h3>
|
||||||
|
<p class="small text-muted mb-0">
|
||||||
|
Автоматическая расстановка мягких переносов (<code>&shy;</code>) в длинных словах с настраиваемым порогом длины (от 12 символов). Колонки текста аккуратно перено­сятся в узких блоках и на мобильных экранах без разрыва вёрстки.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<div class="p-3 border rounded bg-body-tertiary h-100 feature-card">
|
||||||
|
<h3 class="h6 fw-bold"><i class="bi bi-code-slash me-1"></i> Форматы вывода и гибкие правила</h3>
|
||||||
|
<p class="small text-muted mb-0">
|
||||||
|
Поддержка HTML с мнемониками, смешанного режима и Unicode (UTF-8) для постов в соцсетях (VK, Telegram, Reddit). Любые группы правил можно гибко комбинировать, включать или отключать под конкретную задачу или проект.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<div class="p-3 border rounded bg-body-tertiary h-100 feature-card">
|
||||||
|
<h3 class="h6 fw-bold"><i class="bi bi-infinity me-1"></i> Бесплатно и безлимитно</h3>
|
||||||
|
<p class="small text-muted mb-0">
|
||||||
|
Полная свобода использования без ограничений по объёму текста, без регистрации, подписок и рекламы. Доступен как онлайн-сервис, так и в виде открытой библиотеки для Python по лицензии MIT.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<hr />
|
||||||
|
<section class="mb-5">
|
||||||
|
<h2 class="h4 mb-3">Кому подходит типограф ETPGRF</h2>
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="border-start border-3 border-secondary ps-3">
|
||||||
|
<h3 class="h6 fw-bold">Контент-менеджерам и дизайнерам</h3>
|
||||||
|
<p class="small text-muted">
|
||||||
|
Профессиональный визуальный ритм и читаемость текста в веб-интерфейсах, блогах, SMM-публикациях в соцсетях и электронных рассылках.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="border-start border-3 border-secondary ps-3">
|
||||||
|
<h3 class="h6 fw-bold">Веб-разработчикам и верстальщикам</h3>
|
||||||
|
<p class="small text-muted">
|
||||||
|
Получайте чистый, валидный HTML-код с неразрывными пробелами и мнемониками, готовый к вставке в шаблоны, CMS и лендинги.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="border-start border-3 border-secondary ps-3">
|
||||||
|
<h3 class="h6 fw-bold">Редакторам и копирайтерам</h3>
|
||||||
|
<p class="small text-muted">
|
||||||
|
Быстрая автоматическая подготовка статей, колонок и постов для публикации без рутинной ручной расстановки спецсимволов.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<hr />
|
||||||
|
<section class="mb-5">
|
||||||
|
<h2 class="h4 mb-3">Интеграция в проекты: Python-библиотека etpgrf</h2>
|
||||||
|
<p class="small text-muted">
|
||||||
|
Вы можете свободно использовать движок etpgrf-типографа офлайн в своих Python-скриптах, Django-, FastAPI- или Flask-приложениях и сайтах:
|
||||||
|
</p>
|
||||||
|
<div class="bg-body-tertiary p-3 rounded border font-monospace small mb-3">
|
||||||
|
<div class="text-secondary mb-1"># Установка через pip:</div>
|
||||||
|
<div class="text-success fw-bold mb-3">pip install etpgrf</div>
|
||||||
|
<div class="text-secondary mb-1"># Пример использования в коде:</div>
|
||||||
|
<div><span class="text-primary">from</span> etpgrf.main <span class="text-primary">import</span> Typographer</div>
|
||||||
|
<div class="mt-1">tp = Typographer(lang=<span class="text-success">'ru'</span>, mode=<span class="text-success">'mixed'</span>)</div>
|
||||||
|
<div>formatted_html = tp.process(<span class="text-success">'Правильная вёрстка "кавычек" и отбивка тире -- улучшают читаемость текста на сайте!'</span>)</div>
|
||||||
|
</div>
|
||||||
|
<p class="small text-muted mb-0">
|
||||||
|
Подробная документация и исходный код библиотеки открыты на <a href="https://github.com/erjemin/etpgrf" target="_blank" rel="noopener">GitHub</a> и <a href="https://pypi.org/project/etpgrf/" target="_blank" rel="noopener">PyPI</a>.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<hr />
|
||||||
|
<section class="mb-5">
|
||||||
|
<h2 class="h4 mb-3">Часто задаваемые вопросы (FAQ)</h2>
|
||||||
|
<div class="accordion" id="faqAccordion">
|
||||||
|
<div class="accordion-item bg-body-tertiary">
|
||||||
|
<h3 class="accordion-header" id="faqHeadingOne">
|
||||||
|
<button class="accordion-button collapsed bg-body-tertiary" type="button" data-bs-toggle="collapse" data-bs-target="#faqOne" aria-expanded="false" aria-controls="faqOne">
|
||||||
|
Зачем типографировать текст перед публикацией на сайте?
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<div id="faqOne" class="accordion-collapse collapse" aria-labelledby="faqHeadingOne" data-bs-parent="#faqAccordion">
|
||||||
|
<div class="accordion-body small text-muted">
|
||||||
|
В вебе строки переносятся динамически в зависимости от экрана устройства. Без неразрывных пробелов предлоги и союзы часто «висят» на концах строк, а дефисы путаются с длинным тире. Типографика делает текст легким для восприятия и профессиональным.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="accordion-item bg-body-tertiary">
|
||||||
|
<h3 class="accordion-header" id="faqHeadingTwo">
|
||||||
|
<button class="accordion-button collapsed bg-body-tertiary" type="button" data-bs-toggle="collapse" data-bs-target="#faqTwo" aria-expanded="false" aria-controls="faqTwo">
|
||||||
|
Чем ETPGRF отличается от Типографа Студии Лебедева или Типографа Муравьёва?
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<div id="faqTwo" class="accordion-collapse collapse" aria-labelledby="faqHeadingTwo" data-bs-parent="#faqAccordion">
|
||||||
|
<div class="accordion-body small text-muted">
|
||||||
|
ETPGRF разработан на современной открытой кодовой базе Python 3, активно поддерживается, умеет корректно обрабатывать смешанные русско-английские тексты (ru+en), имеет гибкий выбор режима кодирования (Unicode/HTML-мнемоники), настраиваемую висячую пунктуацию и может работать как локальный модуль или веб-микросервис.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="accordion-item bg-body-tertiary">
|
||||||
|
<h3 class="accordion-header" id="faqHeadingThree">
|
||||||
|
<button class="accordion-button collapsed bg-body-tertiary" type="button" data-bs-toggle="collapse" data-bs-target="#faqThree" aria-expanded="false" aria-controls="faqThree">
|
||||||
|
Сохраняются ли введённые тексты на сервере?
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<div id="faqThree" class="accordion-collapse collapse" aria-labelledby="faqHeadingThree" data-bs-parent="#faqAccordion">
|
||||||
|
<div class="accordion-body small text-muted">
|
||||||
|
Нет, сервис соблюдает полную конфиденциальность. Тексты обрабатываются исключительно в памяти в момент отправки запроса и не сохраняются в базу данных и логи.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -100,3 +100,20 @@ class TypographViewsTestCase(TestCase):
|
|||||||
"""Проверка эндпоинта сводной статистики."""
|
"""Проверка эндпоинта сводной статистики."""
|
||||||
response = self.client.get(reverse('stats_summary'))
|
response = self.client.get(reverse('stats_summary'))
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_seo_metadata_and_sitemap(self):
|
||||||
|
"""Проверка мета-тегов на главной странице и генерации sitemap.xml."""
|
||||||
|
response = self.client.get(reverse('index'))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
content = response.content.decode('utf-8')
|
||||||
|
self.assertIn('<title>Онлайн-типограф', content)
|
||||||
|
self.assertIn('name="description"', content)
|
||||||
|
self.assertIn('name="keywords"', content)
|
||||||
|
self.assertIn('rel="canonical"', content)
|
||||||
|
|
||||||
|
# Проверка sitemap.xml
|
||||||
|
sitemap_res = self.client.get('/sitemap.xml')
|
||||||
|
self.assertEqual(sitemap_res.status_code, 200)
|
||||||
|
sitemap_content = sitemap_res.content.decode('utf-8')
|
||||||
|
self.assertIn('<loc>', sitemap_content)
|
||||||
|
self.assertIn('http://testserver/', sitemap_content)
|
||||||
|
|||||||
@@ -410,3 +410,44 @@ footer.footer a:hover {
|
|||||||
border-right: 1px solid var(--bs-border-color) !important;
|
border-right: 1px solid var(--bs-border-color) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- Карточки возможностей (hover-эффект без потери читаемости) --- */
|
||||||
|
.feature-card {
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
border-color: rgba(var(--bs-primary-rgb), 0.5) !important;
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.09), 0 2px 6px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-bs-theme="dark"] .feature-card:hover {
|
||||||
|
border-color: rgba(176, 190, 197, 0.5) !important;
|
||||||
|
background-color: rgba(255, 255, 255, 0.03) !important;
|
||||||
|
box-shadow: 0 6px 22px rgba(255, 255, 255, 0.15), 0 2px 8px rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root:not([data-bs-theme="light"]) .feature-card:hover {
|
||||||
|
border-color: rgba(176, 190, 197, 0.5) !important;
|
||||||
|
background-color: rgba(255, 255, 255, 0.03) !important;
|
||||||
|
box-shadow: 0 6px 22px rgba(255, 255, 255, 0.15), 0 2px 8px rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Кнопка раскрытия информационного блока --- */
|
||||||
|
.collapse-toggle-btn {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-toggle-btn .collapse-icon {
|
||||||
|
display: inline-block;
|
||||||
|
transition: transform 0.25s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-toggle-btn:not(.collapsed) .collapse-icon {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "2026-etpgrf-site"
|
name = "2026-etpgrf-site"
|
||||||
version = "0.2.7"
|
version = "0.2.9"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["erjemin <erjemin@gmail.com>"]
|
authors = ["erjemin <erjemin@gmail.com>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
Reference in New Issue
Block a user