from django.core.exceptions import PermissionDenied
from django.http import Http404
from django.utils.encoding import force_str
from rest_framework import exceptions, serializers
from rest_framework.metadata import SimpleMetadata
from rest_framework.request import clone_request
from rest_framework.utils.field_mapping import ClassLookupDict


class CustomMetadata(SimpleMetadata):


    label_lookup = ClassLookupDict(
        {
            serializers.Field: "field",
            serializers.BooleanField: "boolean",
            serializers.CharField: "string",
            serializers.UUIDField: "string",
            serializers.URLField: "url",
            serializers.EmailField: "email",
            serializers.RegexField: "regex",
            serializers.SlugField: "slug",
            serializers.IntegerField: "integer",
            serializers.FloatField: "float",
            serializers.DecimalField: "decimal",
            serializers.DateField: "date",
            serializers.DateTimeField: "datetime",
            serializers.TimeField: "time",
            serializers.DurationField: "duration",
            serializers.ChoiceField: "choice",
            serializers.MultipleChoiceField: "multiple choice",
            serializers.FileField: "file upload",
            serializers.ImageField: "image upload",
            serializers.ListField: "list",
            serializers.DictField: "nested object",
            serializers.Serializer: "nested object",
        }
    )

    def determine_metadata(self, request, view):

        metadata = {
            "name": view.get_view_name(),
            "description": view.get_view_description(),
            "renders": [renderer.media_type for renderer in view.renderer_classes],
            "parses": [parser.media_type for parser in view.parser_classes],
        }
        if hasattr(view, "get_serializer"):
            actions = self.determine_actions(request, view)
            if actions:
                metadata["actions"] = actions
        return metadata

    def determine_actions(self, request, view):

        actions = {}

        for method in {"PUT", "POST", "GET"} & set(view.allowed_methods):

            view.request = clone_request(request, method)
            try:

                if hasattr(view, "check_permissions"):
                    view.check_permissions(view.request)

                if method == "PUT" and hasattr(view, "get_object"):
                    view.get_object()
            except (exceptions.APIException, PermissionDenied, Http404):

                pass
            else:

                serializer = view.get_serializer()
                actions[method] = self.get_serializer_info(serializer)
            finally:

                view.request = request

        return actions

    def get_serializer_info(self, serializer):

        if hasattr(serializer, "child"):

            serializer = serializer.child
        return {
            field_name: self.get_field_info(field)
            for field_name, field in serializer.fields.items()
            if not isinstance(field, serializers.HiddenField)
        }

    def get_field_info(self, field):

        field_info = {
            "type": self.label_lookup[field],
            "required": getattr(field, "required", False),
        }


        attrs = [
            "read_only",
            "label",
            "help_text",
            "min_length",
            "max_length",
            "min_value",
            "max_value",
            "max_digits",
            "decimal_places",
        ]

        for attr in attrs:
            value = getattr(field, attr, None)
            if value is not None and value != "":
                field_info[attr] = force_str(value, strings_only=True)


        if hasattr(field, "child"):
            field_info["child"] = self.get_field_info(field.child)
        elif hasattr(field, "fields"):
            field_info["children"] = self.get_serializer_info(field)


        if (
            not field_info.get("read_only")
            and not isinstance(field, (serializers.RelatedField, serializers.ManyRelatedField))
            and hasattr(field, "choices")
        ):
            field_info["choices"] = [
                {"value": choice_value, "display_name": force_str(choice_name, strings_only=True)}
                for choice_value, choice_name in field.choices.items()
            ]

        return field_infoclass AudioMaterial(BaseMaterial):
    title = models.CharField("Название", max_length=512, blank=True)
    text_author = models.CharField("Автор текста", max_length=256, blank=True)
    performer = models.CharField("Исполнитель", max_length=256, blank=True)
    music_author = models.CharField("Автор музыки", max_length=256, blank=True)
    arrangement = models.CharField("Аранжировка", max_length=256, blank=True)
    conductor = models.CharField("Дирижер", max_length=256, blank=True)
    art_director = models.CharField("Художественный руководитель", max_length=256, blank=True)
    language = models.CharField("Язык", max_length=256, blank=True)
    choir = models.CharField("Сопровождение (хор)", max_length=256, blank=True)
    orchestra = models.CharField("Сопровождение (инструменты)", max_length=256, blank=True)
    montage_sheet = models.TextField("Монтажный лист", blank=True)

    class Meta:
        verbose_name = "Фонодокумент"
        verbose_name_plural = "Фонодокументы"
        db_table = "archive_audio_material"
        ordering = ("id",)


class VideoMaterial(BaseMaterial):
    title = models.CharField("Название", max_length=512, blank=True)
    description = models.TextField("Описание сюжета", blank=True)
    montage_sheet = models.TextField("Монтажный лист", blank=True)
    roles = models.TextField("В ролях", blank=True)
    scriptwriter = models.CharField("Сценарист", max_length=512, blank=True)
    director = models.CharField("Режиссер", max_length=512, blank=True)
    cameraman = models.CharField("Оператор", max_length=512, blank=True)
    sound_engineer = models.CharField("Звукооператор", max_length=512, blank=True)
    painter = models.CharField("Художник", max_length=512, blank=True)
    composer = models.CharField("Композитор", max_length=512, blank=True)
    studio = models.CharField("Студия", max_length=512, blank=True)
    role_dubbing = models.CharField("Роли озвучивали", max_length=1024, blank=True)
    video_creators = models.CharField("Над фильмом работали", max_length=1024, blank=True)
    video_type = models.CharField("Вид документа", max_length=256, blank=True)
    color = models.CharField("Цветность", max_length=256, blank=True)
    language = models.CharField("Язык", max_length=256, blank=True)
    literature_source = models.CharField("Литературный первоисточник", max_length=512, blank=True)
    voice_type = models.CharField("Озвученность", max_length=256, blank=True)

    class Meta:
        verbose_name = "Киновидеодокумент"
        verbose_name_plural = "Киновидеодокументы"
        db_table = "archive_video_material"
        ordering = ("id",)


class ImageMaterial(BaseMaterial):
    author = models.CharField("Автор", max_length=256, blank=True)
    photo_type = models.CharField("Вид фотодокумента", max_length=256, blank=True)
    color = models.CharField("Цветность", max_length=256, blank=True)

    class Meta:
        verbose_name = "Фотодокумент"
        verbose_name_plural = "Фотодокументы"
        db_table = "archive_image_material"
        ordering = ("id",)from django.db.models import Q
from rest_framework import viewsets
from rest_framework.response import Response

from backend.apps.archive.models import AudioMaterial, ImageMaterial, VideoMaterial

from . import serializers


class BaseMaterialViewSet(viewsets.ReadOnlyModelViewSet):
    def get_common_search_lookup(self, common_search_value):
        lookup = Q()
        for field in self.common_search_fields:
            lookup |= Q(**{f"{field}__icontains": common_search_value})
        return lookup

    def get_queryset(self):
        queryset = super().get_queryset()
        common_search_query = self.request.GET.get("common_search_query")

        if common_search_query:
            query_lookup = self.get_common_search_lookup(common_search_query)
            queryset = queryset.filter(query_lookup)

        return queryset

    def list(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        serializer = self.get_serializer(self.queryset, many=True)
        return self.get_paginated_response(serializer.data) if page is not None else Response(serializer.data)


class AudioMaterialViewSet(BaseMaterialViewSet):
    queryset = AudioMaterial.objects.all()
    serializer_class = serializers.AudioMaterialListSerializer
    common_search_fields = ["keywords", "annotation", "persons", "title"]


class ImageMaterialViewSet(BaseMaterialViewSet):
    queryset = ImageMaterial.objects.all()
    serializer_class = serializers.ImageMaterialListSerializer
    common_search_fields = ["keywords", "annotation", "persons", "author"]

    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)


class VideoMaterialViewSet(BaseMaterialViewSet):
    queryset = VideoMaterial.objects.all()
    serializer_class = serializers.VideoMaterialListSerializer
    common_search_fields = ["keywords", "annotation", "persons", "title", "description"]import api from "@/plugins/axios.js";

function recordBase(method, options) {
  const defaultAxios = {
    method: method
  }
  return function (queryParams) {
    return api({ ...defaultAxios, ...options, ...queryParams })
  };
}

export function getRecordList(options) {
  return recordBase("get", options)
}

export function getRecordDetail(options) {
  return recordBase("get", options)
}

export function getOptions(options) {
  return recordBase("options", options)
}
<script setup>
import { onBeforeMount, ref } from 'vue'
import { getRecordList, getOptions } from '@/api/default_api_promises.js'
import { useRoute, useRouter } from 'vue-router'
import Paginator from '@/components/common/Paginator.vue'
import DynamicDataList from '@/components/DynamicDataList.vue'

let route = useRoute();
let router = useRouter();
console.log(route.query)
let currentTableTab = ref(route.query.material_type)

let items = ref([]);
let totalCount = ref(0);
let pageCount = ref(0);
function getItems() {
  let urlSearchParams = new URLSearchParams(route.query);
  getRecordList({ url: `/api/${currentTableTab.value}-materials/` })({ params: urlSearchParams })
    .then(response => {
      totalCount.value = response.data.count;
      items.value = response.data.results;
      pageCount.value = Math.ceil(totalCount.value / route.query.page_size);
    })
  getOptions({ url: `/api/${currentTableTab.value}-materials/` })({ params: urlSearchParams })
    .then(response => {
      console.log(response)
    })
}

function updateTab() {
  let query = { ...route.query, material_type: currentTableTab.value }
  router.push({ name: route.name, query: query, params: { ...route.params } })
}

onBeforeMount(getItems)
</script>

<template>
  <v-tabs
    v-model="currentTableTab"
    align-tabs="center"
    color="primary-blue"
    @update:model-value="updateTab"
  >
    <v-tab value="image">Фото</v-tab>
    <v-tab value="audio">Фоно</v-tab>
    <v-tab value="video">Видео</v-tab>
  </v-tabs>

  <dynamic-data-list :items="items" :material-type="currentTableTab"/>
  <paginator v-if="items.length" :page-count="pageCount"/>
</template>


<style scoped lang="scss">
</style>
<script setup>
import { ref, onBeforeMount, watch } from 'vue';
import { getOptions } from '@/api/default_api_promises.js';
import { useRoute } from 'vue-router';

const props = defineProps({
  materialType: String,
  items: Array
});

const route = useRoute();
const fields = ref([]);

const iconMap = {
  audio: 'mdi-volume-high',
  image: 'mdi-image-outline',
  video: 'mdi-video-outline'
};

const fetchFieldsMetadata = () => {
  const urlSearchParams = new URLSearchParams(route.query);
  getOptions({ url: `/api/${props.materialType}-materials/` })({ params: urlSearchParams })
    .then(response => {
      const allFields = response.data.actions.GET;
      fields.value = Object.keys(allFields).map(fieldName => ({
        name: fieldName,
        label: allFields[fieldName].label || fieldName
      }));
      console.log('Fields:', fields.value);
    })
    .catch(error => {
      console.error('Error fetching field metadata:', error);
    });
};

onBeforeMount(fetchFieldsMetadata);

watch(() => props.materialType, fetchFieldsMetadata);
</script>

<template>
  <v-data-iterator :items="items" class="d-flex justify-center">
    <template v-slot:default="{ items }">
      <template v-for="(item, index) in items" :key="index">
        <div class="item">
          <div class="item-icon">
            <v-icon size="110" color="primary-violet">
              {{ iconMap[props.materialType] || 'mdi-file-document-outline' }}
            </v-icon>
          </div>
          <div class="item-content">
            <div v-if="fields.length === 0">No fields defined</div>
            <div v-for="field in fields" :key="field.name" class="item-field">
              <span class="field-label">{{ field.label }}:</span>
              <span class="field-value">
                {{ item.raw[field.name] || 'No value' }}
              </span>
            </div>
          </div>
        </div>
      </template>
    </template>
  </v-data-iterator>
</template>

<style scoped lang="scss">
.item {
  width: 900px;
  padding: 15px;
  margin-top: 15px;
  margin-bottom: 15px;
  border: 1px solid darkgray;
  border-radius: 8px;
  display: flex;
  overflow: hidden;
}

.item-icon {
  flex-shrink: 0;
  width: 110px;
}

.item-content {
  flex-grow: 1;
  margin-left: 20px;
  overflow: hidden;
}

.item-field {
  display: flex;
  align-items: center;
  margin-bottom: 3px;
  line-height: 1.2;
}

.field-label {
  flex-shrink: 0;
  font-size: 12px;
  font-weight: bold;
  color: gray;
  margin-right: 5px;
}

.field-value {
  font-size: 12px;
  font-weight: normal;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
</style> ПОЧЕМУ В КОНСОЛИ ВЫДАЮТСЯ КОРРЕКТНЫЕ РЕЗУЛЬТАТЫ ПОИСКА 123 {'archive_number': 'CD00004-058', 'title': 'Муз. Г. Пукста. Сюиты на темы современных белорусских песен.', 'annotation': '"Узышоу свецел месяц".', 'performer': 'секстет домр', 'creation_date': '01.01.1967', 'language': 'не определен'}
456 {'Архивный номер': 'CD00004-058', 'Название': 'Муз. Г. Пукста. Сюиты на темы современных белорусских песен.', 'Аннотация': '"Узышоу свецел месяц".', 'Исполнитель': 'секстет домр', 'Дата создания': '01.01.1967', 'Язык': 'не определен'}
123 {'archive_number': 'ML01438', 'title': '"Старонкi выбранай лiрыкi". Стихи М. Богдановича.', 'annotation': '"С песней белорусского мужика".', 'performer': 'Захаревич М. Г.', 'creation_date': '27.07.1981', 'language': 'не определен'}
456 {'Архивный номер': 'ML01438', 'Название': '"Старонкi выбранай лiрыкi". Стихи М. Богдановича.', 'Аннотация': '"С песней белорусского мужика".', 'Исполнитель': 'Захаревич М. Г.', 'Дата создания': '27.07.1981', 'Язык': 'не определен'}
123 {'archive_number': 'ML03623-002', 'title': '"Рампа дружбы", радиорассказ о 7-м фестивале народных театров Прибалтики и Белорусской ССР.', 'annotation': 'Беседа журналиста Л. Батюшковой со вторым режиссером народного театра Каунасского политехнического института Витавтасом Григулисом о репертуаре и представленном спектакле по роману Казиса Борутаса "Млын Палтарагiса".', 'performer': 'Батюшкова Л., Григулис В.', 'creation_date': '14.08.1986', 'language': 'рус.'}
456 {'Архивный номер': 'ML03623-002', 'Название': '"Рампа дружбы", радиорассказ о 7-м фестивале народных театров Прибалтики и Белорусской ССР.', 'Аннотация': 'Беседа журналиста Л. Батюшковой со вторым режиссером народного театра Каунасского политехнического института Витавтасом Григулисом о репертуаре и представленном спектакле по роману Казиса Борутаса "Млын Палтарагiса".', 'Исполнитель': 'Батюшкова Л., Григулис В.', 'Дата создания': '14.08.1986', 'Язык': 'рус.'}
123 {'archive_number': 'PL01401', 'title': 'Читает Константин Симонов. Стихи. Из записок Лопатина (фрагмент).', 'annotation': '"Красное и белое", стихотворение.', 'performer': 'Симонов К.', 'creation_date': '01.01.1990', 'language': 'не определен'}
456 {'Архивный номер': 'PL01401', 'Название': 'Читает Константин Симонов. Стихи. Из записок Лопатина (фрагмент).', 'Аннотация': '"Красное и белое", стихотворение.', 'Исполнитель': 'Симонов К.', 'Дата создания': '01.01.1990', 'Язык': 'не определен'} А НА САМОМ САЙТЕ НАПИСАНО NO FIELDS DEFINED? НАЗВАНИЯ ПОЛЕЙ И ИХ ЗНАЧЕНИЯ ДОЛЖНЫЫ ВЫДАВАТЬСЯ В САМИХ БЛОКАХ