diff --git a/back/object/admin.py b/back/object/admin.py
index c39d1a8..625f10b 100644
--- a/back/object/admin.py
+++ b/back/object/admin.py
@@ -1,15 +1,152 @@
from django.contrib import admin
+from django.db import models
from import_export.admin import ImportExportModelAdmin
from .models import ClickableArea, Element3D, Environment, Scene3D
-class Scene3DAdmin(ImportExportModelAdmin, admin.ModelAdmin):
- filter_horizontal = ("elements",)
+# Админ-класс для Scene3D
+@admin.register(Scene3D)
+class Scene3DAdmin(admin.ModelAdmin):
+ list_display = ('name', 'description', 'element_count')
+ search_fields = ('name', 'description')
+ autocomplete_fields = ('elements',) # Используем автодополнение
-class ImportExportBtnAdmin(ImportExportModelAdmin, admin.ModelAdmin):
- pass
+ # Метод для подсчета количества связанных элементов
+ @admin.display(description='Количество элементов')
+ def element_count(self, obj):
+ return obj.elements.count()
-admin.site.register(Environment, ImportExportBtnAdmin)
-admin.site.register(Scene3D, Scene3DAdmin)
-admin.site.register(Element3D, ImportExportBtnAdmin)
-admin.site.register(ClickableArea, ImportExportBtnAdmin)
+
+@admin.register(Environment)
+class EnvironmentAdmin(admin.ModelAdmin):
+ list_display = ("id", "clear_color_display", "clear_color_to_display", "file_count")
+ list_filter = ("clear_color", "clear_color_to")
+ search_fields = ("id",)
+ readonly_fields = ("file_preview",)
+
+ # Метод для отображения цвета clear_color
+ @admin.display(description="Цвет очистки (начальный)", empty_value="-")
+ def clear_color_display(self, obj):
+ if obj.clear_color:
+ return f'
'
+ return "-"
+
+ clear_color_display.allow_tags = True
+
+ # Метод для отображения цвета clear_color_to
+ @admin.display(description="Цвет очистки (конечный)", empty_value="-")
+ def clear_color_to_display(self, obj):
+ if obj.clear_color_to:
+ return f''
+ return "-"
+
+ clear_color_to_display.allow_tags = True
+
+ # Метод для подсчета количества загруженных файлов
+ @admin.display(description="Количество файлов", empty_value="0")
+ def file_count(self, obj):
+ return sum(
+ bool(getattr(obj, field.name))
+ for field in obj._meta.fields
+ if isinstance(field, models.FileField)
+ )
+
+ # Метод для предпросмотра файлов
+ @admin.display(description="Предпросмотр файлов")
+ def file_preview(self, obj):
+ preview_html = []
+ if obj.hdr_gainmap:
+ preview_html.append(
+ f'Просмотр Gainmap'
+ )
+ if obj.hdr_json:
+ preview_html.append(
+ f'Просмотр JSON'
+ )
+ if obj.hdr_webp:
+ preview_html.append(
+ f'Просмотр WEBP'
+ )
+ return "
".join(preview_html) or "-"
+
+ file_preview.allow_tags = True
+
+
+@admin.register(Element3D)
+class Element3DAdmin(admin.ModelAdmin):
+ list_display = (
+ "name",
+ "model_file_display",
+ "is_enabled",
+ "can_not_disable",
+ "position_display",
+ "scenes_count",
+ )
+ list_filter = ("is_enabled", "can_not_disable")
+ search_fields = ("name", "description")
+ readonly_fields = ("model_file_preview",)
+
+ # Метод для отображения пути к файлу модели
+ @admin.display(description="Файл модели", empty_value="-")
+ def model_file_display(self, obj):
+ return obj.model_file.name if obj.model_file else "-"
+
+ # Метод для отображения позиции элемента
+ @admin.display(description="Позиция (X, Y, Z)")
+ def position_display(self, obj):
+ return f"({obj.x_pos}, {obj.y_pos}, {obj.z_pos})"
+
+ # Метод для подсчета количества связанных сцен
+ @admin.display(description="Количество сцен", empty_value="0")
+ def scenes_count(self, obj):
+ return obj.scene3d_set.count()
+
+ # Метод для предпросмотра файла модели
+ @admin.display(description="Предпросмотр файла")
+ def model_file_preview(self, obj):
+ if obj.model_file:
+ return f'Просмотр файла'
+ return "-"
+
+ model_file_preview.allow_tags = True
+
+ # Декоратор для регистрации модели ClickableArea
+
+
+@admin.register(ClickableArea)
+class ClickableAreaAdmin(admin.ModelAdmin):
+ list_display = (
+ "name",
+ "description_shortened",
+ "source_element",
+ "target_scene",
+ "object_name",
+ )
+ list_filter = ("source__name", "target__name")
+ search_fields = (
+ "name",
+ "description",
+ "object_name",
+ "source__name",
+ "target__name",
+ )
+ autocomplete_fields = ("source", "target")
+
+ # Метод для отображения укороченного описания
+ @admin.display(description="Описание", empty_value="-")
+ def description_shortened(self, obj):
+ return (
+ (obj.description[:50] + "...")
+ if len(obj.description) > 50
+ else obj.description
+ )
+
+ # Метод для отображения связанного элемента (source)
+ @admin.display(description="Элемент 3D", empty_value="-")
+ def source_element(self, obj):
+ return obj.source.name if obj.source else "-"
+
+ # Метод для отображения связанной сцены (target)
+ @admin.display(description="Сцена", empty_value="-")
+ def target_scene(self, obj):
+ return obj.target.name if obj.target else "-"
diff --git a/back/object/management/commands/glb_import.py b/back/object/management/commands/glb_import.py
index 8b6e923..03a0fc8 100644
--- a/back/object/management/commands/glb_import.py
+++ b/back/object/management/commands/glb_import.py
@@ -1,23 +1,69 @@
-from django.core.management.base import BaseCommand, CommandError
-from django.core.files import File
-import glob
import os
+import glob
+from django.core.management.base import BaseCommand
+from django.core.files import File
import logging
-from object.models import Element3D, Scene3D
-logger = logging.getLogger("root")
+from object.models import Scene3D, Element3D
+
+logger = logging.getLogger(__name__) # Инициализация логгера
class Command(BaseCommand):
+ help = "Import GLB files into the database and associate them with a specific Scene3D object."
+
def handle(self, *args, **options):
- root_directory = "object/management/commands/data"
-
- files = glob.glob("*.glb", recursive=True, root_dir=root_directory)
- hv = Scene3D.objects.get(id=48)
+ logger.info("Starting the import process...")
+
+ # Определение корневой директории
+ base_dir = os.path.dirname(os.path.abspath(__file__)) # Получаем путь к текущему файлу команды
+ root_directory = os.path.join(base_dir, "../../data") # Переходим на уровень выше и указываем папку data
+ root_directory = os.path.normpath(root_directory) # Нормализуем путь для кроссплатформенности
+
+ if not os.path.exists(root_directory):
+ logger.error(f"The directory {root_directory} does not exist.")
+ return
+
+ logger.info(f"Using root directory: {root_directory}")
+
+ # Поиск файлов .glb
+ try:
+ files = glob.glob("*.glb", recursive=True, root_dir=root_directory)
+ if not files:
+ logger.warning("No .glb files found in the specified directory.")
+ return
+
+ logger.info(f"Found {len(files)} .glb files: {', '.join(files)}")
+ except Exception as e:
+ logger.error(f"Error while searching for .glb files: {e}")
+ return
+
+ # Получение объекта Scene3D
+ try:
+ hv = Scene3D.objects.get(id=56)
+ logger.info(f"Retrieved Scene3D object with ID 56: {hv}")
+ except Scene3D.DoesNotExist:
+ logger.error("Scene3D object with ID 56 does not exist.")
+ return
+ except Exception as e:
+ logger.error(f"Error while retrieving Scene3D object: {e}")
+ return
+
+ # Обработка каждого файла
for f in files:
- with open(os.path.join(root_directory, f), 'rb') as file:
- el = Element3D(name=f)
- el.model_file = File(file, f)
- el.save()
- logger.info(el)
- hv.elements.add(el)
- logger.info(hv.elements.count())
-
\ No newline at end of file
+ file_path = os.path.join(root_directory, f)
+ logger.info(f"Processing file: {file_path}")
+
+ try:
+ with open(file_path, 'rb') as file:
+ el = Element3D(name=f)
+ el.model_file = File(file, f)
+ el.save()
+ logger.info(f"Successfully saved Element3D object: {el}")
+
+ hv.elements.add(el)
+ logger.info(f"Added Element3D {el} to Scene3D {hv}. Total elements count: {hv.elements.count()}")
+ except FileNotFoundError:
+ logger.error(f"File not found: {file_path}")
+ except Exception as e:
+ logger.error(f"Error while processing file {f}: {e}")
+
+ logger.info("Import process completed successfully.")
\ No newline at end of file
diff --git a/back/object/models.py b/back/object/models.py
index e80e3e0..2532e26 100644
--- a/back/object/models.py
+++ b/back/object/models.py
@@ -15,50 +15,6 @@ def group_based_upload_to(instance, filename):
)
-class Environment(models.Model):
- clear_color = ColorField(blank=True, null=True)
- clear_color_to = ColorField(blank=True, null=True)
- hdr_gainmap = models.FileField(
- upload_to=group_based_upload_to, blank=True, null=True
- )
- hdr_json = models.FileField(upload_to=group_based_upload_to, blank=True, null=True)
- hdr_webp = models.FileField(upload_to=group_based_upload_to, blank=True, null=True)
-
-
-class Element3D(models.Model):
- model_file = models.FileField(upload_to=group_based_upload_to)
- name = models.CharField(max_length=255)
- description = models.TextField(blank=True, null=True)
- is_enabled = models.BooleanField(default=True)
- can_not_disable = models.BooleanField(default=False)
-
- x_pos = models.IntegerField(default=0)
- y_pos = models.IntegerField(default=0)
- z_pos = models.IntegerField(default=0)
-
- def __str__(self):
- return self.name
-
-
-class Scene3D(models.Model):
- name = models.CharField(max_length=120)
- description = models.TextField(blank=True, null=True)
- elements = models.ManyToManyField(Element3D)
- env = models.ForeignKey(Environment, models.RESTRICT, blank=True, null=True)
-
- min_distance = models.IntegerField(
- default=10,
- validators=[MinValueValidator(1), MaxValueValidator(600)],
- )
- max_distance = models.IntegerField(
- default=20,
- validators=[MinValueValidator(2), MaxValueValidator(1000)],
- )
-
- def __str__(self):
- return self.name
-
-
def maximum_size_validator(image):
max_width = 512
max_height = 512
@@ -68,6 +24,117 @@ def maximum_size_validator(image):
raise ValidationError("Height or Width is larger than what is allowed")
+class Environment(models.Model):
+ clear_color = ColorField(
+ blank=True,
+ null=True,
+ verbose_name="Цвет очистки (начальный)"
+ )
+ clear_color_to = ColorField(
+ blank=True,
+ null=True,
+ verbose_name="Цвет очистки (конечный)"
+ )
+ hdr_gainmap = models.FileField(
+ upload_to=group_based_upload_to,
+ blank=True,
+ null=True,
+ verbose_name="HDR Gainmap файл"
+ )
+ hdr_json = models.FileField(
+ upload_to=group_based_upload_to,
+ blank=True,
+ null=True,
+ verbose_name="HDR JSON файл"
+ )
+ hdr_webp = models.FileField(
+ upload_to=group_based_upload_to,
+ blank=True,
+ null=True,
+ verbose_name="HDR WEBP файл"
+ )
+
+ def __str__(self):
+ return f"Среда #{self.id}"
+
+ class Meta:
+ verbose_name = "Среда"
+ verbose_name_plural = "Среды"
+
+
+class Element3D(models.Model):
+ model_file = models.FileField(
+ upload_to=group_based_upload_to,
+ verbose_name="Файл модели"
+ )
+ name = models.CharField(
+ max_length=255,
+ verbose_name="Название элемента"
+ )
+ description = models.TextField(
+ blank=True,
+ null=True,
+ verbose_name="Описание элемента"
+ )
+ is_enabled = models.BooleanField(
+ default=True,
+ verbose_name="Включен"
+ )
+ can_not_disable = models.BooleanField(
+ default=False,
+ verbose_name="Невозможно отключить"
+ )
+ x_pos = models.IntegerField(
+ default=0,
+ verbose_name="Позиция X"
+ )
+ y_pos = models.IntegerField(
+ default=0,
+ verbose_name="Позиция Y"
+ )
+ z_pos = models.IntegerField(
+ default=0,
+ verbose_name="Позиция Z"
+ )
+
+ def __str__(self):
+ return self.name
+
+ class Meta:
+ verbose_name = "Элемент 3D"
+ verbose_name_plural = "Элементы 3D"
+
+
+class Scene3D(models.Model):
+ name = models.CharField(max_length=120, verbose_name="Название сцены")
+ description = models.TextField(blank=True, null=True, verbose_name="Описание сцены")
+ elements = models.ManyToManyField("Element3D", verbose_name="Элементы 3D")
+ env = models.ForeignKey(
+ "Environment",
+ on_delete=models.RESTRICT,
+ blank=True,
+ null=True,
+ verbose_name="Среда",
+ )
+ min_distance = models.IntegerField(
+ default=10,
+ validators=[MinValueValidator(1), MaxValueValidator(600)],
+ verbose_name="Минимальное расстояние",
+ )
+ max_distance = models.IntegerField(
+ default=20,
+ validators=[MinValueValidator(2), MaxValueValidator(1000)],
+ verbose_name="Максимальное расстояние",
+ )
+
+ def __str__(self):
+ return self.name
+
+ class Meta:
+ verbose_name = "Сцена 3D"
+ verbose_name_plural = "Сцены 3D"
+
+
class ClickableArea(models.Model):
name = models.CharField(
"Название",
diff --git a/front/package-lock.json b/front/package-lock.json
index dd8b980..6649ae5 100644
--- a/front/package-lock.json
+++ b/front/package-lock.json
@@ -807,6 +807,7 @@
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/@tresjs/cientos/-/cientos-3.9.0.tgz",
"integrity": "sha512-LAtMveKlecKvWh7TNWdwEs3nQUYMLqz9DZy0YhSZ6OVTfL2vevx2K4sH9744UME8OedUf4fkFFkX4OWQRHaDRQ==",
+ "license": "MIT",
"dependencies": {
"@tresjs/core": "3.9.0",
"@vueuse/core": "^10.9.0",
diff --git a/front/src/components/Promo/load_models.vue b/front/src/components/Promo/load_models.vue
index 782b8ed..dcfdc91 100644
--- a/front/src/components/Promo/load_models.vue
+++ b/front/src/components/Promo/load_models.vue
@@ -130,6 +130,7 @@ const loadModels = async () => {
item.modelFile = loaded_scene
item.id = element.id
item.name = element.name
+ console.log(item)
if (!element.is_enabled) {
item.modelFile.visible = false