import json
import math

from django.db.models import Count
from rest_framework import status
from rest_framework.exceptions import ValidationError
from django.db import connection
from api.models import TtModule, TtActivityTemplate, TtActivity, TtPos, TtSetting
from api.models.tt_location import TtLocation
from api.models.tt_staff import TtStaff
from api.models.tt_student_set import TtStudentSet
from api.translation import __
from api.utils import log_critical_error, get_exception_detail
from api.validator import BaseValidator
from api.views.admin.base import AdminApiBase

class ScheduleList(AdminApiBase):
    def validate_request(self,request):
        rules = {
            "academic_term_id": "required|exists:api.TtAcademicTerm,id",
            "type": "required",
        }
        # if need rename field, can put here
        attribute = {
            # "email": __("attr.email"),
        }
        # validate id first, cause need get pos by id, if wrong id direct return error
        validator = BaseValidator(request.data,rules,attribute)
        error = validator.validate()
        if error:
            raise ValidationError(error)

    def post(self, request):
        try:
            self.api_log_skip_outgoing_data=True
            self.validate_request(request)
            setting_params = {
                "violation_key",
            }
            settings = TtSetting.get_multiple_setting(setting_params)
            violation_key = json.loads(settings["violation_key"])
            type = request.data.get("type")
            # data = []
            match type:
                case "pos":
                    data = self.get_data_by_pos(request)
                case "module" | "activity_template":
                    data = self.get_data(request)
                case "staff" | "location" | "student_set":
                    data = self.get_data_by_staff_location_student_set(request)
                case _:
                    # return invalid type error
                    raise ValidationError({
                        "error": __("validation.invalid", field=__("attr.type")),
                        "errors": {"type": __("validation.invalid", field=__("attr.type"))}
                    })

            response = {
                "data": data,
                "violation_key":violation_key
            }
            # log_critical_error(user_id=None,descr=len(connection.queries),url="testing")

            return self.api_response(data=response)
        except ValidationError as e:
            first_message = e.detail['error']
            errors = e.detail['errors']
            return self.api_response(error=first_message,errors=errors,code=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            e_details = get_exception_detail(e)
            log_critical_error(user_id=None,descr=e_details['descr'],url=e_details['url'],trace=e_details['trace'])
            return self.api_response(error=__("message.internal_server_error"),code=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def get_data(self, request):
        activities = (
            TtActivity.objects
            .filter(academic_term_id=request.data.get('academic_term_id'))
            .exclude(is_jta=1, jta_parent_id__isnull=False)
            .select_related(
                "activity_template", "department", "academic_term", "module",
                "week_pattern", "activity_type", "zone", "availability",
                "start_preference", "usage_preference"
            )
            .prefetch_related(
                "week", "week_pattern__week", "tag__tag", "staff", "staff_preset",
                "staff_suitability", "location", "location_preset",
                "location_suitability", "student_set"
            )
            .order_by(self.list_default_order_column)
        )

        return_data = {}
        method_name = f"get_{request.data.get("type")}_data"
        datas = getattr(self, method_name)(request)

        for val in activities:
            group_obj = getattr(val, request.data.get("type"))
            if not group_obj:
                continue

            data = next((m for m in datas if m['id'] == group_obj.id), {})
            return_data.setdefault(
                group_obj.id,
                {
                    **data,
                    "activities": []
                }
            )
            activity_data = self.get_activity_data(val)
            return_data[group_obj.id]['activities'].append(activity_data)
        # sort by name
        return_data = sorted(return_data.values(), key=lambda x: x.get(self.list_default_order_column, ''))
        return list(return_data)


    def get_data_by_pos(self, request):
        # from pos get module_ids need to filter from activity table
        module_ids = set()
        module_to_pos = {}

        pos_list = (
            TtPos.objects
            .filter(academic_term_id=request.data.get("academic_term_id"))
            .select_related("department","academic_term")
            .prefetch_related(
                "ttposmodulegroup_set__ttposmodulegroupmodule_set__module",
                "module_group",
                "tag__tag" #tags means the relation add in models, __tag means the relation table tag_id get the tag object
            )
            .order_by(self.list_default_order_column)
        )
        return_data = {}

        for pos in pos_list:
            real_size = 0
            ignored = []
            for pos_module_group in pos.ttposmodulegroup_set.all():
                for pos_module_group_module in pos_module_group.ttposmodulegroupmodule_set.all():
                    if pos_module_group_module.module and pos_module_group_module.module.ttactivity_set.all():
                        for activity in pos_module_group_module.module.ttactivity_set.all():
                            for sset in activity.student_set.all():
                                if(sset.id not in ignored):
                                    real_size += (sset.planned_size or 0)
                                    ignored.append(sset.id)


            pos_data = self.get_pos_data(pos)
            return_data.setdefault(
                pos.id,
                {
                    **pos_data,
                    "real_size": real_size,
                    "activities": []
                }
            )
            for pos_module_group in pos.ttposmodulegroup_set.all():
                for pos_module_group_module in pos_module_group.ttposmodulegroupmodule_set.all():
                    module_id = pos_module_group_module.module_id
                    module_ids.add(module_id)
                    module_to_pos.setdefault(module_id, set()).add(pos.id)

        # so if isset of module_id, check got what pos_id append to all
        activities = (
            TtActivity.objects
            .filter(module_id__in=module_ids)
            .exclude(is_jta=1, jta_parent_id__isnull=False)
            .select_related(
                "activity_template", "department", "academic_term", "module",
                "week_pattern", "activity_type", "zone", "availability",
                "start_preference", "usage_preference"
            )
            .prefetch_related(
                "week", "week_pattern__week", "tag__tag", "staff", "staff_preset",
                "staff_suitability", "location", "location_preset",
                "location_suitability", "student_set"
            )
            .order_by(self.list_default_order_column)
        )

        for val in activities:
            activity_data = self.get_activity_data(val)
            target_pos_ids = module_to_pos.get(val.module_id, [])
            for pos_id in target_pos_ids:
                # double check isset of pos_id
                if pos_id in return_data:
                    return_data[pos_id]['activities'].append(activity_data)

        return list(return_data.values())

    def get_data_by_staff_location_student_set(self, request):
        group_type = request.data.get("type")  # 'staff' or 'location'
        preset_type = f"{group_type}_preset"  # 'staff_preset' or 'location_preset'
        activities = (
            TtActivity.objects
            .filter(academic_term_id=request.data.get('academic_term_id'))
            .exclude(is_jta=1, jta_parent_id__isnull=False)
            .select_related(
                "activity_template", "department", "academic_term", "module",
                "week_pattern", "activity_type", "zone", "availability",
                "start_preference", "usage_preference"
            )
            .prefetch_related(
                "week", "week_pattern__week", "tag__tag", "staff", "staff_preset",
                "staff_suitability", "location", "location_preset",
                "location_suitability", "student_set"
            )
            .order_by(self.list_default_order_column)
        )

        return_data = {}

        method_name = f"get_{request.data.get("type")}_data"
        datas = getattr(self, method_name)(request)

        for val in activities:
            main_collection = getattr(val, group_type).all()
            preset_collection = []
            # student set no preset, so no need have preset collection
            if request.data.get("type") != "student_set":
                preset_collection = getattr(val, preset_type).all()

            if not main_collection and not preset_collection:
                continue

            activity_data = self.get_activity_data(val)

            for obj in main_collection:
                data = next((m for m in datas if m['id'] == obj.id), {})
                return_data.setdefault(obj.id, {
                    **data ,"activities": []
                })
                return_data[obj.id]['activities'].append(activity_data)

            for obj in preset_collection:
                if obj.id not in return_data:
                    data = next((m for m in datas if m['id'] == obj.id), {})
                    return_data[obj.id] = {
                       **data, "activities": []
                    }
                return_data[obj.id]['activities'].append(activity_data)
        # sort by name
        return_data = sorted(return_data.values(), key=lambda x: x.get(self.list_default_order_column, ''))
        return list(return_data)

    def get_activity_data(self,val):
        # Basic Data
        data = {
            "id": val.id,
            "code": val.code,
            "name": val.name,
            "desc": val.desc,
            "activity_template": val.activity_template.name if val.activity_template else None,
            "activity_template_id": val.activity_template_id,
            "department": val.department.name if val.department else None,
            "department_id": val.department_id,
            "academic_term": val.academic_term.name if val.academic_term else None,
            "academic_term_id": val.academic_term_id,
            "activity_type": val.activity_type.name if val.activity_type else None,
            "activity_type_color": val.activity_type.color if val.activity_type else None,
            "activity_type_id": val.activity_type_id,
            "zone": val.zone.name if val.zone else None,
            "zone_id": val.zone_id,
            "module": val.module.name if val.module else None,
            "module_id": val.module_id,
            "duration": val.duration,
            "slot_required": val.slot_required,
            "is_jta": val.is_jta,
            "jta_parent_id": val.jta_parent_id,
            "is_variant": val.is_variant,
            "variant_parent_id": val.variant_parent_id,
            "scheduled_start_time": val.scheduled_start_time,
            "scheduled_day": __("attr.days_name." + str(val.scheduled_day)) if val.scheduled_day is not None else None,
            "suggested_day": __("attr.days_name." + str(val.suggested_day)) if val.suggested_day is not None else None,
            "suggested_time": val.suggested_time,
            "suggested_time_slot": val.suggested_time_slot,
            "staff_requirement_type": __("attr.staff_requirement_type_name." + str(val.staff_requirement_type)),
            "staff_requirement": val.staff_requirement,
            "location_requirement_type": __("attr.location_requirement_type_name." + str(val.location_requirement_type)),
            "location_requirement": val.location_requirement,
            "planned_size": val.planned_size,
            "real_size": sum((student.planned_size or 0) for student in val.student_set.all()),
            "module_size": val.module.planned_size if val.module else 0,
            "scheduled": val.scheduled,
        }

        # Week Pattern Logic
        weeks = val.week_pattern.week.all() if val.week_pattern else val.week.all()
        data["week_pattern"] = [{"id": w.id, "week": w.week, "start_date": w.start_date} for w in weeks]
        data["week_pattern_id"] = val.week_pattern.id if val.week_pattern else None
        data["week_pattern_name"] = val.week_pattern.name if val.week_pattern else "-"

        # Preference Patterns
        for pref in ['availability', 'start_preference', 'usage_preference']:
            obj = getattr(val, pref)
            data[f"{pref}_id"] = obj.id if obj else None
            data[f"{pref}_name"] = obj.name if obj else "-"
            data[f"{pref}_pattern"] = obj.pattern if obj else getattr(val, f"{pref}_pattern")

        # Many-to-Many Relationships
        m2m_fields = ['staff', 'staff_preset', 'staff_suitability', 'location', 'location_preset',
                      'location_suitability', 'student_set']
        for field in m2m_fields:
            data[field] = [{"id": o.id, "name": o.name, "code": o.code} for o in getattr(val, field).all()]

        # Tags (Special case)
        data["tag"] = [{"id": t.tag.id, "name": t.tag.name} for t in val.tag.all()]

        # Extra Fields
        for i in range(1, 11):
            field_name = f"extra_data_{i}"
            data[field_name] = getattr(val, field_name, None)

        return data

    def get_module_data(self, request):
        modules = (
            TtModule.objects
            .filter(academic_term_id=request.data.get('academic_term_id'))
            .select_related("department","academic_term","week_pattern","availability","start_preference","usage_preference")
            .prefetch_related(
                "week",
                "week_pattern__week",
                "tag__tag", #tags means the relation add in models, __tag means the relation table tag_id get the tag object
                "ttactivity_set__student_set"
            )
            .order_by(self.list_default_order_column)
        )
        return_data=[]

        for val in modules:
            real_size = 0
            ignored = []
            if val.ttactivity_set.all():
                for act in val.ttactivity_set.all():
                    if act.student_set.all():
                        for sset in act.student_set.all():
                            if(sset.id not in ignored):
                                real_size += (sset.planned_size or 0)
                                ignored.append(sset.id)

            data={}
            data["id"] = val.id
            data["code"] = val.code
            data["name"] = val.name
            data['desc'] = val.desc
            data['department'] = val.department.name if val.department else None
            data['department_id'] = val.department_id
            data['academic_term'] = val.academic_term.name if val.academic_term else None
            data['academic_term_id'] = val.academic_term_id
            data['planned_size'] = val.planned_size
            data['real_size'] = real_size
            data['credit_provided'] = val.credit_provided

            data["week_pattern_id"] = val.week_pattern.id if val.week_pattern else None
            data["week_pattern_name"] = val.week_pattern.name if val.week_pattern else "-"
            if val.week_pattern:
                data["week_pattern"] = [
                    {"id": week.id, "week": week.week, "start_date": week.start_date}
                    for week in val.week_pattern.week.all()
                ]
            else:
                data["week_pattern"] = [
                    {"id": week.id, "week": week.week, "start_date": week.start_date}
                    for week in val.week.all()
                ]
            data["availability_id"] = val.availability.id if val.availability else None
            data["availability_name"] = val.availability.name if val.availability else "-"
            data["availability_pattern"] = val.availability.pattern if val.availability else val.availability_pattern
            data["start_preference_id"] = val.start_preference.id if val.start_preference else None
            data["start_preference_name"] = val.start_preference.name if val.start_preference else "-"
            data["start_preference_pattern"] = val.start_preference.pattern if val.start_preference else val.start_preference_pattern
            data["usage_preference_id"] = val.usage_preference.id if val.usage_preference else None
            data["usage_preference_name"] = val.usage_preference.name if val.usage_preference else "-"
            data["usage_preference_pattern"] = val.usage_preference.pattern if val.usage_preference else val.usage_preference_pattern

            data["tag"] = [
                {"id": tag.tag.id, "name": tag.tag.name}
                for tag in val.tag.all()
            ]

            for i in range(1,11):
                extra_data_field = f"extra_data_{i}"
                data[extra_data_field] = getattr(val,extra_data_field,None)

            return_data.append(data)

        return return_data
    
    def get_activity_template_data(self, request):
        activity_templates = (
            TtActivityTemplate.objects
            .filter(academic_term_id=request.data.get('academic_term_id'))
            .select_related("department", "academic_term", "module", "week_pattern","activity_type", "zone", "availability", "start_preference", "usage_preference")
            .prefetch_related(
                "staff",
                "staff_suitability",
                "location",
                "location_suitability",
                "week",
                "week_pattern__week",
                "tag__tag"
                # tags means the relation add in models, __tag means the relation table tag_id get the tag object #2
            )
            .annotate(activity_count=Count("ttactivity"))
            .order_by(self.list_default_order_column)
        )
        return_data=[]

        for val in activity_templates:
            data={}
            data["id"] = val.id
            data["code"] = val.code
            data["name"] = val.name
            data['desc'] = val.desc
            data['department'] = val.department.name if val.department else None
            data['department_id'] = val.department_id
            data['academic_term'] = val.academic_term.name if val.academic_term else None
            data['academic_term_id'] = val.academic_term_id
            data['activity_type'] = val.activity_type.name if val.activity_type else None
            data['activity_type_color'] = val.activity_type.color if val.activity_type else None
            data['activity_type_id'] = val.activity_type_id
            data['zone'] = val.zone.name if val.zone else None
            data['zone_id'] = val.zone_id
            data['module'] = val.module.name if val.module else None
            data['module_id'] = val.module_id
            data['duration'] = val.duration

            data['staff_requirement_type'] = __("attr.staff_requirement_type_name."+str(val.staff_requirement_type))
            data['staff_requirement'] = val.staff_requirement
            data['location_requirement_type'] = __("attr.location_requirement_type_name."+str(val.location_requirement_type))
            data['location_requirement'] = val.location_requirement
            data['planned_size'] = val.planned_size

            # module size, extising activity, required activity, need to generate
            data['module_size'] = val.module.planned_size if val.module else 0
            data['required_activity'] = math.ceil(data['module_size'] / data['planned_size']) if data['module_size'] > 0 and data['planned_size'] > 0 else 0
            data['existing_activity'] = val.activity_count

            data['need_to_generate'] = max(data['required_activity'] - data['existing_activity'],0)

            data["week_pattern_id"] = val.week_pattern.id if val.week_pattern else None
            data["week_pattern_name"] = val.week_pattern.name if val.week_pattern else "-"
            if val.week_pattern:
                data["week_pattern"] = [
                    {"id": week.id, "week": week.week, "start_date": week.start_date}
                    for week in val.week_pattern.week.all()
                ]
            else:
                data["week_pattern"] = [
                    {"id": week.id, "week": week.week, "start_date": week.start_date}
                    for week in val.week.all()
                ]
            data["availability_id"] = val.availability.id if val.availability else None
            data["availability_name"] = val.availability.name if val.availability else "-"
            data["availability_pattern"] = val.availability.pattern if val.availability else val.availability_pattern
            data["start_preference_id"] = val.start_preference.id if val.start_preference else None
            data["start_preference_name"] = val.start_preference.name if val.start_preference else "-"
            data["start_preference_pattern"] = val.start_preference.pattern if val.start_preference else val.start_preference_pattern
            data["usage_preference_id"] = val.usage_preference.id if val.usage_preference else None
            data["usage_preference_name"] = val.usage_preference.name if val.usage_preference else "-"
            data["usage_preference_pattern"] = val.usage_preference.pattern if val.usage_preference else val.usage_preference_pattern

            data['staff_suitability'] = [
                {"id": suit.id, "name": suit.name}
                for suit in val.staff_suitability.all()
            ]
            data['staff'] = [
                {"id": staff.id, "name": staff.name}
                for staff in val.staff.all()
            ]
            data['location_suitability'] = [
                {"id": location.id, "name": location.name}
                for location in val.location_suitability.all()
            ]
            data['location'] = [
                {"id": location.id, "name": location.name}
                for location in val.location.all()
            ]
            data["tag"] = [
                {"id": tag.tag.id, "name": tag.tag.name}
                for tag in val.tag.all()
            ]

            for i in range(1,11):
                extra_data_field = f"extra_data_{i}"
                data[extra_data_field] = getattr(val,extra_data_field,None)

            return_data.append(data)

        return return_data
    
    def get_pos_data(self, pos):
        data={}
        data["id"] = pos.id
        data["code"] = pos.code
        data["name"] = pos.name
        data['desc'] = pos.desc
        data['department'] = pos.department.name if pos.department else None
        data['department_id'] = pos.department_id
        data['academic_term'] = pos.academic_term.name if pos.academic_term else None
        data['academic_term_id'] = pos.academic_term_id
        data['planned_size'] = pos.planned_size
        data['max_credit'] = pos.max_credit

        data['module_group'] = [
            {"id": module_group.id, "name": module_group.name}
            for module_group in pos.module_group.all()
        ]
        data["tag"] = [
            {"id": tag.tag.id, "name": tag.tag.name}
            for tag in pos.tag.all()
        ]

        for i in range(1,11):
            extra_data_field = f"extra_data_{i}"
            data[extra_data_field] = getattr(pos,extra_data_field,None)

        return data
    
    def get_staff_data(self, request):
        staffs = (
            TtStaff.objects
            .select_related("department","zone","availability","start_preference","usage_preference")
            .prefetch_related(
                "shared_with_department",
                "suitability",
                "constraint_profile",
                "avoid_concurrency",
                "tag__tag" #tags means the relation add in models, __tag means the relation table tag_id get the tag object
            )
            .order_by(self.list_default_order_column)
        )
        return_data=[]

        for val in staffs:
            data={}
            data["id"] = val.id
            data["code"] = val.code
            data["name"] = val.name
            data['desc'] = val.desc
            data['department'] = val.department.name if val.department else None
            data['department_id'] = val.department_id
            data['zone'] = val.zone.name if val.zone else None
            data['zone_id'] = val.zone_id
            data['email'] = val.email
            data['is_part_time'] = val.is_part_time
            data['maximum_period'] = val.maximum_period
            data['contract_period'] = val.contract_period

            data["availability_id"] = val.availability.id if val.availability else None
            data["availability_name"] = val.availability.name if val.availability else "-"
            data["availability_pattern"] = val.availability.pattern if val.availability else val.availability_pattern
            data["start_preference_id"] = val.start_preference.id if val.start_preference else None
            data["start_preference_name"] = val.start_preference.name if val.start_preference else "-"
            data["start_preference_pattern"] = val.start_preference.pattern if val.start_preference else val.start_preference_pattern
            data["usage_preference_id"] = val.usage_preference.id if val.usage_preference else None
            data["usage_preference_name"] = val.usage_preference.name if val.usage_preference else "-"
            data["usage_preference_pattern"] = val.usage_preference.pattern if val.usage_preference else val.usage_preference_pattern

            data['suitability'] = [
                {"id": suit.id, "name": suit.name}
                for suit in val.suitability.all()
            ]
            data['shared_with_department'] = [
                {"id": dept.id, "name": dept.name}
                for dept in val.shared_with_department.all()
            ]
            data['avoid_concurrency'] = [
                {"id": avoid.id, "name": avoid.name}
                for avoid in val.avoid_concurrency.all()
            ]
            data['shared_with_all_department'] = val.shared_with_all_department
            data["tag"] = [
                {"id": tag.tag.id, "name": tag.tag.name}
                for tag in val.tag.all()
            ]

            for i in range(1,11):
                extra_data_field = f"extra_data_{i}"
                data[extra_data_field] = getattr(val,extra_data_field,None)

            return_data.append(data)

        return return_data
    
    def get_location_data(self, request):
        locations = (
            TtLocation.objects
            .select_related("department","zone","availability","start_preference","usage_preference")
            .prefetch_related(
                "shared_with_department",
                "suitability",
                "constraint_profile",
                "avoid_concurrency",
                "tag__tag" #tags means the relation add in models, __tag means the relation table tag_id get the tag object
            )
            .order_by(self.list_default_order_column)
        )
        return_data=[]

        for val in locations:
            data={}
            data["id"] = val.id
            data["code"] = val.code
            data["name"] = val.name
            data['desc'] = val.desc
            data['department'] = val.department.name if val.department else None
            data['department_id'] = val.department_id
            data['zone'] = val.zone.name if val.zone else None
            data['zone_id'] = val.zone_id
            data['capacity'] = val.capacity
            data['area'] = val.area
            data['maximum_period'] = val.maximum_period
            data['contract_period'] = val.contract_period

            data["availability_id"] = val.availability.id if val.availability else None
            data["availability_name"] = val.availability.name if val.availability else "-"
            data["availability_pattern"] = val.availability.pattern if val.availability else val.availability_pattern
            data["start_preference_id"] = val.start_preference.id if val.start_preference else None
            data["start_preference_name"] = val.start_preference.name if val.start_preference else "-"
            data["start_preference_pattern"] = val.start_preference.pattern if val.start_preference else val.start_preference_pattern
            data["usage_preference_id"] = val.usage_preference.id if val.usage_preference else None
            data["usage_preference_name"] = val.usage_preference.name if val.usage_preference else "-"
            data["usage_preference_pattern"] = val.usage_preference.pattern if val.usage_preference else val.usage_preference_pattern

            data['suitability'] = [
                {"id": suit.id, "name": suit.name}
                for suit in val.suitability.all()
            ]
            data['shared_with_department'] = [
                {"id": dept.id, "name": dept.name}
                for dept in val.shared_with_department.all()
            ]
            data['avoid_concurrency'] = [
                {"id": avoid.id, "name": avoid.name}
                for avoid in val.avoid_concurrency.all()
            ]
            data['shared_with_all_department'] = val.shared_with_all_department
            data["tag"] = [
                {"id": tag.tag.id, "name": tag.tag.name}
                for tag in val.tag.all()
            ]
            for i in range(1,11):
                extra_data_field = f"extra_data_{i}"
                data[extra_data_field] = getattr(val,extra_data_field,None)

            return_data.append(data)

        return return_data
    
    def get_student_set_data(self, request):
        student_sets = (
            TtStudentSet.objects
            .select_related("department","pos","academic_term","availability","start_preference","usage_preference")
            .prefetch_related(
                "module",
                "constraint_profile",
                "tag__tag" #tags means the relation add in models, __tag means the relation table tag_id get the tag object
            )
            .filter(academic_term_id=request.data.get("academic_term_id"))
            .order_by(self.list_default_order_column)
        )
        return_data=[]

        for val in student_sets:
            data={}
            data["id"] = val.id
            data["code"] = val.code
            data["name"] = val.name
            data['desc'] = val.desc
            data['department'] = val.department.name if val.department else None
            data['department_id'] = val.department_id
            data['academic_term'] = val.academic_term.name if val.academic_term else None
            data['academic_term_id'] = val.academic_term_id
            data['pos'] = val.pos.name if val.pos else None
            data['pos_id'] = val.pos_id
            data['planned_size'] = val.planned_size
            data['real_size'] = val.real_size

            data["availability_id"] = val.availability.id if val.availability else None
            data["availability_name"] = val.availability.name if val.availability else "-"
            data["availability_pattern"] = val.availability.pattern if val.availability else val.availability_pattern
            data["start_preference_id"] = val.start_preference.id if val.start_preference else None
            data["start_preference_name"] = val.start_preference.name if val.start_preference else "-"
            data["start_preference_pattern"] = val.start_preference.pattern if val.start_preference else val.start_preference_pattern
            data["usage_preference_id"] = val.usage_preference.id if val.usage_preference else None
            data["usage_preference_name"] = val.usage_preference.name if val.usage_preference else "-"
            data["usage_preference_pattern"] = val.usage_preference.pattern if val.usage_preference else val.usage_preference_pattern

            data['module'] = [
                {"id": module.id, "code": module.code, "name": module.name}
                for module in val.module.all()
            ]
            data["tag"] = [
                {"id": tag.tag.id, "name": tag.tag.name}
                for tag in val.tag.all()
            ]
            for i in range(1,11):
                extra_data_field = f"extra_data_{i}"
                data[extra_data_field] = getattr(val,extra_data_field,None)

            return_data.append(data)

        return return_data