Site/programmer/models.py
2026-04-09 19:09:19 +03:00

219 lines
9.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logging
from django.contrib.auth.models import User
from django.db import models
from django.urls import reverse
from django.utils import timezone
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.core.cache import cache
from .utils.email_notifications import send_callback_notification
from django.utils.text import slugify
logger = logging.getLogger(__name__)
class Recall(models.Model):
title = models.CharField(max_length=255, verbose_name='Организация')
content = models.TextField(blank=True, verbose_name='Отзыв')
scan = models.ImageField(upload_to="scan/%Y/%m/%d/", blank=True, null=True, verbose_name='Фото')
time_create = models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')
time_update = models.DateTimeField(auto_now=True, verbose_name='Дата изменения')
is_published = models.BooleanField(default=True, verbose_name='Опубликован')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('recall_detail', kwargs={'pk': self.pk})
class Meta:
verbose_name = 'Отзыв'
verbose_name_plural = 'Отзывы'
ordering = ['time_create', 'title']
def get_seo_title(self):
return f"Отзыв от {self.title} | Программист 1С"
def get_seo_description(self):
if self.content:
clean_content = self.content[:160].replace('\n', ' ').strip()
return f"Отзыв о работе программиста 1С от {self.title}. {clean_content}..."
return f"Отзыв клиента {self.title} о работе программиста 1С Николая Сердюк"
class Competence(models.Model):
title = models.CharField(max_length=255, verbose_name='Программист')
content = models.TextField(blank=True, verbose_name='Компетенция')
photo = models.ImageField(upload_to="photos/%Y/%m/%d/", blank=True, null=True, verbose_name='Фото')
time_create = models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')
time_update = models.DateTimeField(auto_now=True, verbose_name='Дата изменения')
is_published = models.BooleanField(default=True, verbose_name='Опубликован')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('competence_detail', kwargs={'pk': self.pk})
class Meta:
verbose_name = 'Компетенция'
verbose_name_plural = 'Компетенции'
ordering = ['time_create', 'title']
class Solution(models.Model):
title = models.CharField(max_length=255, verbose_name='Наименование')
description = models.TextField(blank=True, verbose_name='Описание')
implementation = models.TextField(blank=True, verbose_name='Реализация')
closing = models.TextField(blank=True, verbose_name='Заключение')
time_create = models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')
time_update = models.DateTimeField(auto_now=True, verbose_name='Дата изменения')
is_published = models.BooleanField(default=True, verbose_name='Опубликован')
slug = models.SlugField(
max_length=255,
unique=True,
db_index=True,
verbose_name='URL-идентификатор',
blank=True,
)
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse('solution_detail', kwargs={'slug': self.slug})
class Meta:
verbose_name = 'Проекты'
verbose_name_plural = 'Проекты'
ordering = ['time_create', 'title']
def get_seo_title(self):
return f"Проект: {self.title} | Автоматизация 1С"
def get_seo_description(self):
# Используем closing если есть, иначе description
source = self.closing or self.description
if source:
clean = source[:160].replace('\n', ' ').strip()
return f"Проект автоматизации: {self.title}. {clean}..."
return f"Реализация проекта {self.title} - программист 1С Николай Сердюк"
def get_meta_keywords(self):
base_keywords = ["проект 1С", "автоматизация 1С", "внедрение 1С"]
title_words = self.title.lower().split()
return base_keywords + title_words
class Home(models.Model):
title = models.CharField(max_length=255, verbose_name='Наименование')
content = models.TextField(blank=True, verbose_name='Статья')
home_image = models.ImageField(upload_to="home_image/%Y/%m/%d/", blank=True, null=True, verbose_name='Фото')
time_create = models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')
time_update = models.DateTimeField(auto_now=True, verbose_name='Дата изменения')
is_published = models.BooleanField(default=True, verbose_name='Опубликован')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('home')
class Meta:
verbose_name = 'Главная страница'
verbose_name_plural = 'Главная страница'
ordering = ['time_create', 'title']
class CallbackRequest(models.Model):
name = models.CharField(max_length=100, verbose_name='Имя')
phone = models.CharField(max_length=20, verbose_name='Телефон')
email = models.EmailField(blank=True, null=True, verbose_name='Электронная почта')
question = models.TextField(blank=True, verbose_name='Ваш вопрос')
time_create = models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')
is_processed = models.BooleanField(default=False, verbose_name='Обработано')
is_read = models.BooleanField(default=False, verbose_name='Прочитано')
notification_sent = models.BooleanField(default=False, verbose_name='Уведомление отправлено')
def __str__(self):
return f"{self.name} - {self.phone}"
class Meta:
verbose_name = 'Заявка на звонок'
verbose_name_plural = 'Заявки на звонок'
ordering = ['-time_create']
@receiver(post_save, sender=CallbackRequest)
def send_callback_email_notification(sender, instance, created, **kwargs):
if created and not instance.notification_sent:
try:
success = send_callback_notification(instance)
if success:
sender.objects.filter(pk=instance.pk).update(notification_sent=True)
except Exception:
# Логируем ошибку, но не прерываем сохранение объекта
logger.exception(
'Не удалось отправить email-уведомление для заявки pk=%s', instance.pk
)
class PageView(models.Model):
url = models.CharField(max_length=500)
timestamp = models.DateTimeField(default=timezone.now)
ip_address = models.GenericIPAddressField()
user_agent = models.TextField(blank=True)
referer = models.CharField(max_length=500, blank=True)
class Meta:
indexes = [
models.Index(fields=['url', 'timestamp']),
models.Index(fields=['timestamp']),
]
class Visitor(models.Model):
ip_address = models.GenericIPAddressField()
first_visit = models.DateTimeField(default=timezone.now)
last_visit = models.DateTimeField(default=timezone.now)
visit_count = models.IntegerField(default=1)
class Meta:
indexes = [
models.Index(fields=['ip_address']),
]
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, verbose_name='Пользователь')
phone = models.CharField(max_length=20, blank=True, verbose_name='Телефон')
company = models.CharField(max_length=255, blank=True, verbose_name='Компания')
specialization = models.CharField(max_length=100, blank=True, verbose_name='Специализация')
avatar = models.ImageField(upload_to='avatars/%Y/%m/%d/', blank=True, verbose_name='Аватар')
email_notifications = models.BooleanField(default=True, verbose_name='Получать уведомления')
def __str__(self):
return f'Профиль {self.user.username}'
class Meta:
verbose_name = 'Профиль'
verbose_name_plural = 'Профили'
@receiver(post_save, sender=User)
def create_or_save_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.get_or_create(user=instance)
@receiver([post_save, post_delete], sender=Home)
@receiver([post_save, post_delete], sender=Solution)
@receiver([post_save, post_delete], sender=Competence)
@receiver([post_save, post_delete], sender=Recall)
def clear_sitemap_cache(sender, **kwargs):
cache.delete('sitemap_cache')