Remove old bulk create view.

This commit is contained in:
SergeantPanda 2025-09-14 12:19:23 -05:00
parent 8ab9a508d4
commit 4f49636899
2 changed files with 3 additions and 263 deletions

View file

@ -698,266 +698,6 @@ class ChannelViewSet(viewsets.ModelViewSet):
return Response(serializer.data, status=status.HTTP_201_CREATED)
@swagger_auto_schema(
method="post",
operation_description=(
"Bulk create channels from existing streams. For each object, if 'channel_number' is provided, "
"it is used (if available); otherwise, the next available number is auto-assigned. "
"Each object must include 'stream_id' and 'name'. "
"Supports single profile ID or array of profile IDs in 'channel_profile_ids'."
),
request_body=openapi.Schema(
type=openapi.TYPE_ARRAY,
items=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=["stream_id"],
properties={
"stream_id": openapi.Schema(
type=openapi.TYPE_INTEGER,
description="ID of the stream to link",
),
"channel_number": openapi.Schema(
type=openapi.TYPE_NUMBER,
description="(Optional) Desired channel number. Must not be in use.",
),
"name": openapi.Schema(
type=openapi.TYPE_STRING, description="Desired channel name"
),
"channel_profile_ids": openapi.Schema(
type=openapi.TYPE_ARRAY,
items=openapi.Items(type=openapi.TYPE_INTEGER),
description="(Optional) Channel profile ID(s) to add the channel to. Can be a single ID or array of IDs."
),
},
),
),
responses={201: "Bulk channels created"},
)
@action(detail=False, methods=["post"], url_path="from-stream/bulk")
def from_stream_bulk(self, request):
data_list = request.data
if not isinstance(data_list, list):
return Response(
{"error": "Expected a list of channel objects"},
status=status.HTTP_400_BAD_REQUEST,
)
created_channels = []
errors = []
# Gather current used numbers once.
used_numbers = set(
Channel.objects.all().values_list("channel_number", flat=True)
)
next_number = 1
def get_auto_number():
nonlocal next_number
while next_number in used_numbers:
next_number += 1
used_numbers.add(next_number)
return next_number
logos_to_create = []
channels_to_create = []
streams_map = []
logo_map = []
profile_map = [] # Track which profiles each channel should be added to
for item in data_list:
stream_id = item.get("stream_id")
if not stream_id:
errors.append(
{
"item": item,
"error": "Missing required field: stream_id is required.",
}
)
continue
try:
stream = get_object_or_404(Stream, pk=stream_id)
except Exception as e:
errors.append({"item": item, "error": str(e)})
continue
name = item.get("name")
if name is None:
name = stream.name
channel_group = stream.channel_group
stream_custom_props = stream.custom_properties or {}
channel_number = None
if "tvg-chno" in stream_custom_props:
channel_number = float(stream_custom_props["tvg-chno"])
elif "channel-number" in stream_custom_props:
channel_number = float(stream_custom_props["channel-number"])
elif "num" in stream_custom_props:
channel_number = float(stream_custom_props["num"])
# Get the tvc_guide_stationid from custom properties if it exists
tvc_guide_stationid = None
if "tvc-guide-stationid" in stream_custom_props:
tvc_guide_stationid = stream_custom_props["tvc-guide-stationid"]
# Determine channel number: if provided, use it (if free); else auto assign.
if channel_number is None:
provided_number = item.get("channel_number")
if provided_number is None:
channel_number = get_auto_number()
else:
try:
channel_number = float(provided_number)
except ValueError:
errors.append(
{
"item": item,
"error": "channel_number must be a number.",
}
)
continue
if (
channel_number in used_numbers
or Channel.objects.filter(
channel_number=channel_number
).exists()
):
errors.append(
{
"item": item,
"error": f"Channel number {channel_number} is already in use.",
}
)
continue
used_numbers.add(channel_number)
channel_data = {
"channel_number": channel_number,
"name": name,
"tvc_guide_stationid": tvc_guide_stationid,
"tvg_id": stream.tvg_id,
}
# Only add channel_group_id if the stream has a channel group
if channel_group:
channel_data["channel_group_id"] = channel_group.id
# Attempt to find existing EPGs with the same tvg-id
epgs = EPGData.objects.filter(tvg_id=stream.tvg_id)
if epgs:
channel_data["epg_data_id"] = epgs.first().id
serializer = self.get_serializer(data=channel_data)
if serializer.is_valid():
validated_data = serializer.validated_data
channel = Channel(**validated_data)
channels_to_create.append(channel)
streams_map.append([stream_id])
# Store which profiles this channel should be added to - normalize to array
channel_profile_ids = item.get("channel_profile_ids")
if channel_profile_ids is not None:
# Normalize single ID to array
if not isinstance(channel_profile_ids, list):
channel_profile_ids = [channel_profile_ids]
profile_map.append(channel_profile_ids)
if stream.logo_url:
logos_to_create.append(
Logo(
url=stream.logo_url,
name=stream.name or stream.tvg_id,
)
)
logo_map.append(stream.logo_url)
else:
logo_map.append(None)
else:
errors.append({"item": item, "error": serializer.errors})
if logos_to_create:
Logo.objects.bulk_create(logos_to_create, ignore_conflicts=True)
channel_logos = {
logo.url: logo
for logo in Logo.objects.filter(
url__in=[url for url in logo_map if url is not None]
)
}
# Get all profiles for default assignment
all_profiles = ChannelProfile.objects.all()
channel_profile_memberships = []
if channels_to_create:
with transaction.atomic():
created_channels = Channel.objects.bulk_create(channels_to_create)
update = []
for channel, stream_ids, logo_url, channel_profile_ids in zip(
created_channels, streams_map, logo_map, profile_map
):
if logo_url:
channel.logo = channel_logos[logo_url]
update.append(channel)
# Handle channel profile membership based on channel_profile_ids
if channel_profile_ids:
# Add channel only to the specified profiles
try:
specific_profiles = ChannelProfile.objects.filter(id__in=channel_profile_ids)
channel_profile_memberships.extend([
ChannelProfileMembership(
channel_profile=profile,
channel=channel,
enabled=True
)
for profile in specific_profiles
])
except Exception:
# If profiles don't exist, add to all profiles as fallback
channel_profile_memberships.extend([
ChannelProfileMembership(
channel_profile=profile,
channel=channel,
enabled=True
)
for profile in all_profiles
])
else:
# Default behavior: add to all profiles
channel_profile_memberships.extend([
ChannelProfileMembership(
channel_profile=profile,
channel=channel,
enabled=True
)
for profile in all_profiles
])
# Bulk create profile memberships
if channel_profile_memberships:
ChannelProfileMembership.objects.bulk_create(
channel_profile_memberships
)
# Update logos
if update:
Channel.objects.bulk_update(update, ["logo"])
# Set stream relationships
for channel, stream_ids in zip(created_channels, streams_map):
channel.streams.set(stream_ids)
response_data = {"created": ChannelSerializer(created_channels, many=True).data}
if errors:
response_data["errors"] = errors
return Response(response_data, status=status.HTTP_201_CREATED)
@swagger_auto_schema(
method="post",
operation_description=(
@ -983,8 +723,8 @@ class ChannelViewSet(viewsets.ModelViewSet):
),
responses={202: "Task started successfully"},
)
@action(detail=False, methods=["post"], url_path="from-stream/bulk-async")
def from_stream_bulk_async(self, request):
@action(detail=False, methods=["post"], url_path="from-stream/bulk")
def from_stream_bulk(self, request):
from .tasks import bulk_create_channels_from_streams
stream_ids = request.data.get("stream_ids", [])

View file

@ -560,7 +560,7 @@ export default class API {
}
const response = await request(
`${host}/api/channels/channels/from-stream/bulk-async/`,
`${host}/api/channels/channels/from-stream/bulk/`,
{
method: 'POST',
body: requestBody,