models.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. from django.conf import settings
  2. from urllib.parse import parse_qs, urlparse
  3. from django.conf import settings as django_settings
  4. from django.core.exceptions import ValidationError
  5. from django.db import models
  6. from landing.rich_text import render_rich_html, sanitize_rich_html
  7. class LandingPage(models.Model):
  8. title = models.CharField(max_length=150)
  9. slug = models.SlugField(unique=True)
  10. content = models.TextField(blank=True, null=True)
  11. def clean(self):
  12. super().clean()
  13. self.content = sanitize_rich_html(self.content)
  14. def save(self, *args, **kwargs):
  15. self.content = sanitize_rich_html(self.content)
  16. return super().save(*args, **kwargs)
  17. @property
  18. def rendered_content(self):
  19. return render_rich_html(self.content)
  20. def __str__(self) -> str:
  21. return self.title
  22. class Meta:
  23. verbose_name = 'Простая страница'
  24. verbose_name_plural = 'Простые страницы'
  25. class FAQmodel(models.Model):
  26. question = models.TextField(blank=True)
  27. answer = models.TextField(blank=True)
  28. def __str__(self) -> str:
  29. return self.question
  30. class Meta:
  31. verbose_name = 'FAQ'
  32. class LandingMainPageSettings(models.Model):
  33. title = models.CharField(
  34. 'Название набора настроек',
  35. max_length=150,
  36. default='Главная страница',
  37. )
  38. is_active = models.BooleanField(
  39. 'Активно',
  40. default=True,
  41. )
  42. hero_title = models.CharField(
  43. 'Главный заголовок',
  44. max_length=255,
  45. blank=True,
  46. default='Цифровой сервис для управления услугами',
  47. )
  48. hero_text = models.TextField(
  49. 'Текст главного блока',
  50. blank=True,
  51. default='Подключайте услуги, управляйте предложениями и показывайте клиентам понятную информацию о сервисе.',
  52. )
  53. hero_image = models.ImageField(
  54. 'Базовое изображение лендинга',
  55. upload_to='landing/main/',
  56. blank=True,
  57. null=True,
  58. )
  59. services_title = models.CharField(
  60. 'Заголовок блока услуг',
  61. max_length=255,
  62. blank=True,
  63. default='Наши услуги',
  64. )
  65. services_text = models.TextField(
  66. 'Текст блока услуг',
  67. blank=True,
  68. default='Раздел объединяет основные категории услуг сервиса и помогает пользователям быстро перейти к подходящему предложению.',
  69. )
  70. about_title = models.CharField(
  71. 'Заголовок блока о компании',
  72. max_length=255,
  73. blank=True,
  74. default='О компании',
  75. )
  76. about_text = models.TextField(
  77. 'Текст блока о компании',
  78. blank=True,
  79. default='Опишите здесь компанию, ее сервис, подход к работе и преимущества для пользователей и партнеров.',
  80. )
  81. video_url = models.URLField(
  82. 'Ссылка для видео',
  83. max_length=500,
  84. blank=True,
  85. )
  86. extra_section_title = models.CharField(
  87. 'Заголовок дополнительного раздела',
  88. max_length=255,
  89. blank=True,
  90. default='Дополнительные возможности сервиса',
  91. )
  92. image_block_title = models.CharField(
  93. 'Подзаголовок блока с изображением',
  94. max_length=255,
  95. blank=True,
  96. default='Как это работает',
  97. )
  98. image_block_text = models.TextField(
  99. 'Текст блока с изображением',
  100. blank=True,
  101. default='Используйте этот блок, чтобы рассказать о подключении, особенностях работы сервиса или преимуществах для партнеров и клиентов.',
  102. )
  103. image_block_image = models.ImageField(
  104. 'Изображение дополнительного блока',
  105. upload_to='landing/main/',
  106. blank=True,
  107. null=True,
  108. )
  109. faq_csv = models.FileField(
  110. 'CSV-файл с вопросами и ответами FAQ',
  111. upload_to='landing/faq/',
  112. blank=True,
  113. null=True,
  114. help_text=(
  115. 'Необязательный файл для раздела FAQ. Рекомендуемый формат CSV: '
  116. 'question;answer;keywords. Первая строка может быть заголовком. '
  117. 'question — вопрос, answer — готовый ответ, keywords — ключевые слова '
  118. 'через запятую или точку с запятой. Если подходящий ответ в файле не найден, '
  119. 'будет использован ответ нейросети.'
  120. ),
  121. )
  122. team_title = models.CharField(
  123. 'Заголовок блока команды',
  124. max_length=255,
  125. blank=True,
  126. default='Наша команда',
  127. )
  128. team_text = models.TextField(
  129. 'Текст блока команды',
  130. blank=True,
  131. default='Добавьте участников команды, которые отвечают за развитие, поддержку и сопровождение сервиса.',
  132. )
  133. updated_at = models.DateTimeField(
  134. 'Обновлено',
  135. auto_now=True,
  136. )
  137. @classmethod
  138. def get_current(cls):
  139. return (
  140. cls.objects
  141. .filter(is_active=True)
  142. .order_by('-updated_at', '-id')
  143. .first()
  144. )
  145. @property
  146. def video_embed_url(self):
  147. video_url = str(self.video_url or '').strip()
  148. if not video_url:
  149. return ''
  150. parsed_url = urlparse(video_url)
  151. hostname = parsed_url.hostname or ''
  152. path_parts = [part for part in parsed_url.path.split('/') if part]
  153. if 'youtube.com' in hostname:
  154. if path_parts[:1] == ['embed'] and len(path_parts) > 1:
  155. return video_url
  156. video_id = parse_qs(parsed_url.query).get('v', [''])[0]
  157. if video_id:
  158. return f'https://www.youtube.com/embed/{video_id}'
  159. if 'youtu.be' in hostname and path_parts:
  160. return f'https://www.youtube.com/embed/{path_parts[0]}'
  161. if 'rutube.ru' in hostname and len(path_parts) >= 2 and path_parts[0] == 'video':
  162. return f'https://rutube.ru/play/embed/{path_parts[1]}'
  163. return video_url
  164. @property
  165. def header_title(self):
  166. return self.hero_title
  167. @property
  168. def header_slogan(self):
  169. return self.hero_text
  170. @property
  171. def header_image(self):
  172. return self.hero_image
  173. @property
  174. def about_video_url(self):
  175. return self.video_url
  176. @property
  177. def show_about_video(self):
  178. return bool(self.video_url)
  179. @property
  180. def about_video_embed_url(self):
  181. return self.video_embed_url
  182. @property
  183. def extra_title(self):
  184. return self.image_block_title
  185. @property
  186. def extra_text(self):
  187. return self.image_block_text
  188. def __str__(self):
  189. return self.title
  190. class Meta:
  191. verbose_name = 'Настройки главной страницы лендинга'
  192. verbose_name_plural = 'Настройки главной страницы лендинга'
  193. class LandingTeamMember(models.Model):
  194. user = models.ForeignKey(
  195. settings.AUTH_USER_MODEL,
  196. on_delete=models.CASCADE,
  197. related_name='landing_team_members',
  198. verbose_name='Пользователь',
  199. )
  200. caption = models.CharField(
  201. 'Подпись',
  202. max_length=255,
  203. blank=True,
  204. )
  205. sort_order = models.PositiveIntegerField(
  206. 'Порядок',
  207. default=100,
  208. )
  209. is_active = models.BooleanField(
  210. 'Показывать на лендинге',
  211. default=True,
  212. )
  213. @property
  214. def display_name(self):
  215. full_name = ' '.join(
  216. value
  217. for value in [
  218. getattr(self.user, 'last_name', ''),
  219. getattr(self.user, 'first_name', ''),
  220. ]
  221. if str(value or '').strip()
  222. ).strip()
  223. return full_name or str(self.user)
  224. @property
  225. def avatar_src(self):
  226. avatar_base64 = str(getattr(self.user, 'avatar_base64', '') or '').strip()
  227. if not avatar_base64:
  228. return ''
  229. if avatar_base64.startswith('data:image'):
  230. return avatar_base64
  231. return f'data:image/jpeg;base64,{avatar_base64}'
  232. @property
  233. def full_name(self):
  234. return self.display_name
  235. @property
  236. def position(self):
  237. return self.caption
  238. def __str__(self):
  239. return self.display_name
  240. class Meta:
  241. verbose_name = 'Участник команды лендинга'
  242. verbose_name_plural = 'Участники команды лендинга'
  243. ordering = ('sort_order', 'id')