(self, request)
| 671 | ) |
| 672 | @action(detail=False, methods=['get'], url_path='search', permission_classes=[IsStandardUser]) |
| 673 | def search(self, request): |
| 674 | params = request.query_params |
| 675 | |
| 676 | # Build base queryset with prefetching |
| 677 | queryset = ProgramData.objects.select_related( |
| 678 | 'epg', 'epg__epg_source' |
| 679 | ).prefetch_related( |
| 680 | 'epg__channels', 'epg__channels__channel_group', |
| 681 | 'epg__channels__streams', 'epg__channels__streams__channel_group', |
| 682 | 'epg__channels__streams__m3u_account', |
| 683 | ) |
| 684 | |
| 685 | filters = Q() |
| 686 | |
| 687 | # Text filters |
| 688 | title = params.get('title') |
| 689 | if title: |
| 690 | title_regex = params.get('title_regex', '').lower() in ('true', '1', 'yes') |
| 691 | title_whole_words = params.get('title_whole_words', '').lower() in ('true', '1', 'yes') |
| 692 | filters &= parse_text_query('title', title, use_regex=title_regex, whole_words=title_whole_words) |
| 693 | |
| 694 | description = params.get('description') |
| 695 | if description: |
| 696 | desc_regex = params.get('description_regex', '').lower() in ('true', '1', 'yes') |
| 697 | desc_whole_words = params.get('description_whole_words', '').lower() in ('true', '1', 'yes') |
| 698 | filters &= parse_text_query('description', description, use_regex=desc_regex, whole_words=desc_whole_words) |
| 699 | |
| 700 | # Time filters with validation |
| 701 | start_after = params.get('start_after') |
| 702 | if start_after: |
| 703 | dt = parse_datetime(start_after) |
| 704 | if dt is None: |
| 705 | return Response( |
| 706 | {"error": f"Invalid datetime format for start_after: {start_after}. Use ISO 8601 format (e.g., 2026-02-14T18:00:00Z)"}, |
| 707 | status=status.HTTP_400_BAD_REQUEST |
| 708 | ) |
| 709 | filters &= Q(start_time__gte=dt) |
| 710 | |
| 711 | start_before = params.get('start_before') |
| 712 | if start_before: |
| 713 | dt = parse_datetime(start_before) |
| 714 | if dt is None: |
| 715 | return Response( |
| 716 | {"error": f"Invalid datetime format for start_before: {start_before}. Use ISO 8601 format."}, |
| 717 | status=status.HTTP_400_BAD_REQUEST |
| 718 | ) |
| 719 | filters &= Q(start_time__lte=dt) |
| 720 | |
| 721 | end_after = params.get('end_after') |
| 722 | if end_after: |
| 723 | dt = parse_datetime(end_after) |
| 724 | if dt is None: |
| 725 | return Response( |
| 726 | {"error": f"Invalid datetime format for end_after: {end_after}. Use ISO 8601 format."}, |
| 727 | status=status.HTTP_400_BAD_REQUEST |
| 728 | ) |
| 729 | filters &= Q(end_time__gte=dt) |
| 730 |
no test coverage detected