U
    h                    @   s  d dl Z d dlZd dlZd dlZd dlZd dlZd dlmZ d dlm	Z	 d dl
mZ d dlmZ d dlmZ d dlmZmZ d dlmZmZmZmZ d d	lmZmZ d d
lmZ d dlmZ d dlmZ d dl m!Z!m"Z" d dl#m$Z$ d dl%m&Z& d dl'm(Z( d dl)m*Z*m+Z+m,Z, d dl-m.Z.m/Z/m0Z0 d dl1m2Z2 d dl3m4Z4 d dl5m6Z6m7Z7 d dl8m9Z9m:Z:m;Z;m<Z< d dl=m>Z> G dd de$eZ?G dd de$eZ@G dd de$eZAG dd de$eZBG d d! d!e$eZCG d"d# d#e$eZDdS )$    N)View)
connectiontimezone)transaction)messages)reverse_lazyreverse)QCountMaxMin)HttpResponseJsonResponse)PermissionDenied)method_decorator)csrf_protect)ListView
UpdateView)LoginRequiredMixin)login_required)require_http_methods)get_object_or_404renderredirect)	Paginator	EmptyPagePageNotAnInteger)APIView)get_client_ip)ChannelChannelZone)	PlaylistsWindowsAvailsAdspotsInAvail)PlaylistXMLGeneratorc                   @   sr   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dd Z
dd Zdd Zdd ZdddZdd ZdS )PlaylistCreateViewu  
        Django class-based view for comprehensive playlist creation and management operations.
        
        This view provides a complete interface for playlist creation, supporting both immediate
        playlist activation and draft playlist functionality. It handles the full lifecycle
        of playlist creation from initial form display through validation, processing, and
        final activation or storage.
        
        The view integrates with Django's authentication system to ensure only authenticated
        users can create playlists, and implements proper data isolation so users can only
        work with their own channels and zones.
        
        Inheritance:
            LoginRequiredMixin: Ensures only authenticated users can access this view
                            Automatically redirects unauthenticated users to login page
            View: Django's base class-based view providing HTTP method dispatch
        
        Supported HTTP Methods:
            GET: Renders the playlist creation form with user's available channels
            POST: Processes form submission for playlist creation (apply or draft)
        
        Features:
            - User authentication and authorization
            - Channel and zone validation
            - Date parsing and validation
            - Dual submission modes (immediate apply vs draft)
            - Comprehensive error handling with user feedback
            - Data integrity validation
            - Secure data isolation per user
        
        URL Pattern:
            Typically mapped to: /playlists/create/ or similar
            
        Template:
            Uses: playlists/new.html
            
        Context Data:
            - channels: QuerySet of user's available channels
            
        Form Processing Flow:
            1. User accesses GET endpoint → Display creation form
            2. User fills form and clicks button → POST to same endpoint
            3. System validates all input data → Extract common data
            4. Route to appropriate handler → Process apply or draft
            5. Redirect to success page → Display confirmation message
            
        Security Considerations:
            - LoginRequiredMixin prevents unauthorized access
            - Channel filtering by user ownership
            - CSRF protection through Django middleware
            - Input validation and sanitization
            - Error message sanitization to prevent XSS
            
        Error Handling:
            - Invalid channel/zone combinations
            - Malformed date inputs
            - Missing required fields
            - Database connectivity issues
            - User permission violations
            
        Dependencies:
            - Django authentication system
            - Channels model with user relationship
            - ChannelsZone model with channel relationship
            - Django messaging framework
            - Template rendering system
            
        Example Usage:
            # URL configuration
            path('create/', PlaylistCreateView.as_view(), name='new_playlist')
            
            # Template usage
            <form method="post">
                {% csrf_token %}
                <select name="channel_id">
                    {% for channel in channels %}
                        <option value="{{ channel.id_channel }}">{{ channel.name }}</option>
                    {% endfor %}
                </select>
                <input type="text" name="zonename" required>
                <input type="number" name="numofwin" required>
                <input type="date" name="day" required>
                <button type="submit" name="apply_btn">Submit Playlist</button>
                <button type="submit" name="draft_btn">Save as Draft</button>
            </form>
            
        Related Models:
            - Channels: Stores channel information with user relationships
            - ChannelsZone: Defines zones within channels
            - User: Django's authentication user model
            - Playlist: The target model for created playlists (implied)
            
        Performance Considerations:
            - Database queries are filtered and optimized
            - Single query for channels per GET request
            - Lazy evaluation of QuerySets
            - Proper indexing assumed on foreign key relationships
            
        Extensibility:
            - Additional validation can be added to _extract_common_data
            - New submission types can be added alongside apply/draft
            - Template can be customized for different UI frameworks
            - Additional context data can be passed to template
            
        Version History:
        - Initial implementation: Basic playlist creation
        - Added draft functionality: Save incomplete playlists
        - Enhanced validation: Comprehensive input checking
        - Improved error handling: User-friendly error messages
    c                 C   s(   |j }tjj|d}d|i}t|d|S )a	  
            Handle GET requests for playlist creation page rendering.
            
            This method processes GET requests to display the playlist creation form.
            It retrieves all channels associated with the authenticated user and prepares
            the context data needed for rendering the playlist creation interface.
            
            The method ensures users can only see and work with channels they own,
            maintaining proper data isolation and security. The rendered page will
            contain form elements populated with the user's available channels.
            
            Args:
                request (HttpRequest): Django HTTP request object containing:
                                    - User authentication information
                                    - Session data
                                    - GET parameters (if any)
                                    - HTTP headers and metadata
            
            Returns:
                HttpResponse: Rendered HTML response containing:
                    - Playlist creation form
                    - User's available channels in dropdown/selection elements
                    - Associated JavaScript and CSS for form functionality
                    - CSRF tokens for secure form submission
                    
            Raises:
                AttributeError: If request.user is not available (authentication middleware issue)
                TemplateDoesNotExist: If 'playlists/new.html' template is missing
                
            Template Context:
                channels (QuerySet): Filtered list of Channel objects owned by current user
                
            Example:
                GET /playlist/create/
                
                Response: HTML page with form containing:
                - Channel selection dropdown
                - Zone selection (populated via AJAX based on channel)
                - Date picker for playlist scheduling
                - Number of windows input
                - Submit buttons (Apply/Draft)
                
            Security:
            - Channels are filtered by user ownership (id_user=user)
            - Only authenticated users can access this view
            - CSRF protection enabled through Django middleware
        id_userchannelszplaylists/new.html)userr    objectsfilterr   )selfrequestr+   r*   context r1   //var/www/html/Focus/src/apps/playlists/views.pyget   s    8		 zPlaylistCreateView.getc                 C   sR   |j }|jd}|jd}|r.| ||S |r>| ||S t|d tdS )a	  
            Handle POST requests for playlist creation and management operations.
            
            This method serves as the main entry point for processing playlist form submissions.
            It determines the user's intended action (apply or draft) based on which submit button
            was clicked, then delegates to the appropriate handler method for processing.
            
            The method supports two primary workflows:
            1. Apply Playlist: Immediately processes and activates the playlist
            2. Draft Playlist: Saves the playlist as a draft for later modification
            
            Args:
                request (HttpRequest): Django HTTP request object containing:
                                    - POST data with form fields
                                    - User authentication information
                                    - Session data
                                    Expected POST fields:
                                    - 'apply_btn': Present if "Submit Playlist" button clicked
                                    - 'draft_btn': Present if "Save as Draft" button clicked
                                    - Additional playlist data fields
            
            Returns:
                HttpResponseRedirect: Redirect response to appropriate page:
                    - On apply action: Redirects based on _handle_apply_playlist result
                    - On draft action: Redirects based on _handle_draft_playlist result  
                    - On invalid submission: Redirects to playlist list with error message
                    
            Raises:
                AttributeError: If request.user is not available (should not occur in normal flow)
                
            Example:
                POST /playlist/create/
                Form data: {
                    'apply_btn': 'Submit Playlist',
                    'channel_id': '123',
                    'zonename': 'zone1',
                    'numofwin': '5',
                    'day': '07/30/2025'
                }
                
            Flow:
            1. Extract user from request
            2. Determine which button was clicked
            3. Route to appropriate handler method
            4. Return redirect response
        	apply_btn	draft_btnzRInvalid submission. Please use either 'Save as Draft' or 'Submit Playlist' button.playlists:list_playlist)r+   POSTr3   _handle_apply_playlist_handle_draft_playlistr   errorr   )r.   r/   r+   r4   r5   r1   r1   r2   post   s    6zPlaylistCreateView.postc           	   
   C   s  z8|j d}|s(t|d tdW S tjj|d}W nl tjk
rb   t|d td Y S  tk
r } z&t|dt	|  td W Y S d}~X Y nX z@|j dd	
 }|st|d
 tdW S tjj||d}W np tjk
r   t|d td Y S  tk
rV } z&t|dt	|  td W Y S d}~X Y nX z4|j d}|st|d tdW S t| W nn tk
r   t|d td Y S  tk
r } z&t|dt	|  td W Y S d}~X Y nX z:|j d}|s&t|d tdW S tj|d}W nn tk
r`   t|d td Y S  tk
r } z&t|dt	|  td W Y S d}~X Y nX |||||dS )a@	  
            Extract and validate common data from POST request for playlist operations.
            
            This method processes form data submitted via POST request and validates each field
            to ensure data integrity before proceeding with playlist operations. It handles
            channel selection, zone configuration, window count, and date formatting with
            comprehensive error handling and user feedback.
            
            Args:
                request (HttpRequest): Django HTTP request object containing POST data
                                    Expected POST fields:
                                    - 'channel_id': Unique identifier for the channel
                                    - 'zonename': Zone identifier within the channel
                                    - 'numofwin': Number of windows (must be numeric)
                                    - 'day': Date in MM/DD/YYYY format
                
            Returns:
                dict or HttpResponseRedirect: On success, returns dictionary with validated data:
                    {
                        'channel_id': str - The channel identifier
                        'channel': Channels - Channel model instance
                        'channel_zone': ChannelsZone - Channel zone model instance  
                        'number_of_windows': str - Number of windows value
                        'daydate': datetime - Parsed datetime object
                    }
                    On error, returns redirect to 'playlists:new_playlist' with error message
                    
            Raises:
                Channels.DoesNotExist: When specified channel doesn't exist in database
                ChannelsZone.DoesNotExist: When specified zone doesn't exist for the channel
                ValueError: When date format is invalid or number_of_windows is not numeric
                Exception: For any other unexpected errors during processing
                
            Example:
            >>> data = self._extract_common_data(request)
            >>> if isinstance(data, dict):
            ...     # Process successful data extraction
            ...     channel = data['channel']
            ...     date = data['daydate']
            >>> else:
            ...     # Handle redirect response
            ...     return data
        
channel_idzChannel ID is required.zplaylists:new_playlist
id_channelz Selected channel does not exist.zError retrieving channel: Nzonename zChannel Zone name is required.r>   id_zone_channelz.Selected zone does not exist for this channel.zError retrieving channel zone: numofwinzNumber of windows is required.z)Number of windows must be a valid number.z$Error processing number of windows: dayzDate is required.%m/%d/%Yz2Invalid date format. Please use MM/DD/YYYY format.zError processing date: )r<   channelchannel_zonenumber_of_windowsdaydate)r7   r3   r   r:   r   r    r,   DoesNotExist	Exceptionstrstripr!   int
ValueErrordatetimestrptime)	r.   r/   r<   rF   er?   rG   rH   rI   r1   r1   r2   _extract_common_dataQ  sr    1




	z'PlaylistCreateView._extract_common_datac           	      C   s   t   }| |}|jd}tjj|d d|d |d jddddtj	j
d|d jddddtj	j
d|d	 d
dd}| |||d |d  | j|t| dddtt   | d d t }|j|d}t| td|jdS )a`  
            Handle immediate playlist application and activation (non-draft processing).
            
            This method processes a complete playlist creation workflow including validation,
            database record creation, structural processing, activity logging, XML generation,
            and optional FTP deployment. It creates an active playlist that is immediately
            ready for broadcast scheduling.
            
            The method performs the complete playlist lifecycle in a single transaction-like
            operation, ensuring data consistency and providing comprehensive error handling
            with detailed logging for troubleshooting and auditing purposes.
            
            Args:
                request (HttpRequest): Django HTTP request object containing:
                                    - POST data with validated form fields
                                    - User session information
                                    - CSRF tokens and security headers
                                    - Optional draft_version for playlist updates
                user (User): Django User model instance representing the authenticated user
                            Used for permissions, logging, and audit trail creation
            
            Returns:
                HttpResponseRedirect: Redirect to playlist detail page showing:
                    - Created playlist information
                    - Generated schedule details
                    - XML output preview
                    - Success confirmation message
                    URL pattern: "playlists:playlist_detail" with playlist ID parameter
                    
            Raises:
                ValidationError: If _extract_common_data validation fails
                IntegrityError: If playlist creation violates database constraints
                AttributeError: If required model relationships are missing
                Exception: For XML generation or FTP upload failures
                
            Database Operations:
                - Creates new Playlists record with version 1
                - Processes playlist structure (windows, avails, ads)
                - Logs activity record for audit trail
                - Maintains referential integrity across related models
                
            Processing Flow:
                1. Start performance timer for monitoring
                2. Extract and validate common form data
                3. Create main playlist database record
                4. Process detailed playlist structure
                5. Log successful creation activity
                6. Generate XML schedule document
                7. Optional FTP upload (currently disabled)
                8. Redirect to playlist detail view
                
            Performance Monitoring:
                - Tracks total processing time in milliseconds
                - Logs duration for performance analysis
                - Enables identification of bottlenecks
                
            Security Features:
                - User authentication required (enforced at view level)
                - Data validation through _extract_common_data
                - Audit logging for all playlist operations
                - CSRF protection for form submissions
                
            Example Usage:
                # Called from POST method when apply_btn is clicked
                return self._handle_apply_playlist(request, user)
                
                # Results in playlist creation and redirect to:
                # /playlists/detail/123/ (where 123 is the new playlist ID)
        draft_versionrF      rI   r   )hourminutesecondmicrosecondtzinfo   ;   rG   F)rF   versionbroadcast_date
start_dateend_datezone_channelis_draftrT   rH   r/   NTi  )playlistr/   is_successfulduration_ms)rc   playlists:playlist_detailid_playlist)timerS   r7   r3   r"   r,   createreplacerP   r   utc_process_playlist_structure_log_activitygetattrrN   r&   generate_scheduleprintr   id)	r.   r/   r+   
start_timedatarT   rc   	generatorxml_playlist_resr1   r1   r2   r8     sR    M	

z)PlaylistCreateView._handle_apply_playlistc                 C   s   |  |}tdtjdS )aW  
            Handle draft playlist creation and management for incremental playlist development.
            
            This method processes playlist creation in draft mode, allowing users to save
            incomplete or work-in-progress playlists for later modification and eventual
            activation. Draft playlists are not immediately broadcast-ready and require
            additional processing before they can be applied to live broadcast schedules.
            
            The draft functionality enables users to:
                - Save incomplete playlist configurations
                - Iteratively develop complex playlists over multiple sessions
                - Collaborate on playlist creation with approval workflows
                - Test playlist configurations before activation
                - Maintain version history of playlist development
            
            Currently, the core implementation is commented out pending infrastructure
            setup for draft storage and management. The method maintains the interface
            contract while preserving the implementation logic for future activation.
            
            Args:
                request (HttpRequest): Django HTTP request object containing:
                                - POST data with form fields (same as apply playlist)
                                - User session and authentication information
                                - CSRF tokens for secure form submission
                                - Optional existing draft ID for updates
                user (User): Django User model instance representing the authenticated user
                            Used for draft ownership, permissions, and audit trail creation
            
            Returns:
                HttpResponseRedirect: Redirect to draft playlist management page showing:
                    - Saved draft playlist information
                    - Options for further editing
                    - Convert to active playlist option
                    - Draft version history
                    URL pattern: "playlists:draft_playlist" with playlist ID parameter
                    
            Raises:
                ValidationError: If _extract_common_data validation fails
                IntegrityError: If draft playlist creation violates database constraints
                AttributeError: If required model relationships are missing
                NotImplementedError: Currently raised due to commented implementation
                
            Database Operations (When Implemented):
                - Creates new Playlists record with is_draft=True
                - Sets draft version for tracking incremental changes
                - Processes playlist structure in draft mode
                - Maintains audit trail for draft operations
                
            Draft vs Apply Differences:
                - is_draft flag set to True
                - draft_version incremented for tracking
                - No XML generation for broadcast systems
                - No FTP upload to production servers
                - Relaxed validation for incomplete data
                - Additional save/resume functionality
                
            Processing Flow (When Active):
                1. Extract and validate form data (same as apply)
                2. Create draft playlist database record
                3. Process playlist structure in draft mode
                4. Skip XML generation and FTP deployment
                5. Redirect to draft management interface
                
            Implementation Status:
                CURRENT: Core logic commented out pending infrastructure setup
                PLANNED: Full draft functionality with version control
                PRESERVED: Implementation logic maintained for easy activation
                
            Future Enhancements:
                - Draft-specific validation rules (more permissive)
                - Version comparison and diff functionality
                - Draft sharing and collaboration features
                - Automatic draft cleanup policies
                - Draft to production promotion workflows
                
            Security Considerations:
                - Draft ownership restricted to creating user
                - Same authentication requirements as apply playlist
                - Draft data isolated from production playlists
                - Audit logging for draft operations
                
            Example Usage:
                # Called from POST method when draft_btn is clicked
                return self._handle_draft_playlist(request, user)
                
                # Results in draft creation and redirect to:
                # /playlists/draft/123/ (where 123 is the draft playlist ID)
                
            Related Functionality:
                - _extract_common_data: Common validation logic
                - _process_playlist_structure: Structure generation
                - _handle_draft_xml_generation: Draft-specific XML processing
                - Draft management views: Edit, delete, promote drafts
        playlists:draft_playlistrg   )rS   r   rc   rr   )r.   r/   r+   rt   r1   r1   r2   r9     s    g
3z)PlaylistCreateView._handle_draft_playlistc              	   C   s   d}|rt |dkr|S tt |D ]}|jd| d}|sBq$|jd| d}|jd| d}	| ||	}
t|||	|
d}|  | ||||t ||}q$|S )a  
            Process the complex nested structure of playlist components including windows, avails, and advertisements.
            
            This method orchestrates the creation of the complete playlist hierarchy by processing
            time windows and their associated advertisement availability slots. It handles the
            iterative creation of Windows records and delegates the processing of nested avails
            and advertisements to specialized handler methods.
            
            The method implements a hierarchical data processing pattern where:
                1. Playlist contains multiple Windows
                2. Each Window contains multiple Avails (advertisement slots)
                3. Each Avail contains multiple Ads (specific advertisements)
                
            The processing maintains referential integrity across all levels and provides
            comprehensive validation to ensure data consistency and broadcast schedule accuracy.
            
            Args:
                request (HttpRequest): Django HTTP request object containing:
                                    - POST data with nested form arrays for windows/avails
                                    - Form fields indexed by window number (e.g., windowstart[0])
                                    - User session and authentication information
                playlist (Playlists): Playlist model instance to associate windows with
                                    Must be a saved instance with valid primary key
                daydate (datetime): Formatted datetime object representing the broadcast date
                                Used for scheduling calculations and time validation
                number_of_windows (str): String representation of total windows to process
                                        Must be convertible to positive integer
            
            Returns:
                int: Cumulative traffic counter value representing total processed advertisements
                    Used for billing, reporting, and capacity planning purposes
                    Incremented by each processed avail and advertisement
                    
            Raises:
                ValueError: If number_of_windows cannot be converted to integer
                AttributeError: If playlist instance is invalid or unsaved
                IntegrityError: If window creation violates database constraints
                KeyError: If expected form fields are missing from request.POST
                
            Database Operations:
                - Creates Windows records linked to the playlist
                - Calculates and stores window duration based on start/end times
                - Delegates avail and advertisement creation to _process_avails
                - Maintains foreign key relationships across the hierarchy
                
            Form Data Structure Expected:
                - windowstart[0], windowstart[1], ... : Start times for each window
                - windowend[0], windowend[1], ... : End times for each window  
                - numofavails[0], numofavails[1], ... : Avail counts per window
                - Additional nested data processed by _process_avails method
                
            Processing Flow:
                1. Initialize traffic counter for advertisement tracking
                2. Validate number_of_windows parameter
                3. Iterate through each window index
                4. Extract window-specific form data
                5. Calculate window duration from start/end times
                6. Create and save Windows database record
                7. Process nested avails for current window
                8. Accumulate traffic counter from avail processing
                9. Return final traffic count
                
            Performance Considerations:
                - Processes windows sequentially to maintain order
                - Single database query per window creation
                - Bulk processing delegated to _process_avails method
                - Early termination for invalid window configurations
                
            Data Validation:
                - Validates number_of_windows is positive integer
                - Skips windows with missing or invalid avail counts
                - Ensures window start/end times are present
                - Validates window duration calculations
                
            Error Handling Strategy:
                - Graceful handling of missing form fields
                - Continues processing valid windows despite individual failures
                - Returns partial results when possible
                - Delegates detailed error handling to specialized methods
                
            Integration Points:
                - _calculate_window_duration: Computes time spans
                - _process_avails: Handles nested avail/ad processing
                - Windows model: Database representation of time windows
                - Form validation: Client-side and server-side validation
                
            Example Usage:
                traffic_count = self._process_playlist_structure(
                    request=request,
                    playlist=playlist_instance,
                    daydate=datetime(2025, 7, 30),
                    number_of_windows='3'
                )
                
            Business Logic:
                - Each window represents a scheduled time slot for advertisements
                - Windows must have valid start/end times for broadcast scheduling
                - Traffic counter enables revenue tracking and capacity management
                - Window duration affects advertisement slot availability
        r   numofavails[]windowstart[
windowend[)rc   window_start
window_endwindow_duration)rN   ranger7   r3   _calculate_window_durationr#   save_process_avails)r.   r/   rc   rI   rH   trafficinumofavailsr|   r}   r~   windowr1   r1   r2   rm   D  s4    o		z.PlaylistCreateView._process_playlist_structurec              	   C   s   t |D ]|}d| d| d}|j|}	|	s0q|jd| d| d}
t||	t|d d}|  |
r| ||||t|
|}q|S )u  
            Process advertisement availability slots (avails) within a specific broadcast window.
            
            This method handles the second level of the playlist hierarchy, processing avails
            which represent discrete advertisement opportunity slots within a broadcast window.
            Each avail defines a specific time slot where advertisements can be scheduled,
            and contains one or more individual advertisement records.
            
            The method iterates through all avails for a given window, creates the corresponding
            database records, and delegates the processing of nested advertisements to the
            specialized _process_ads method. It maintains the hierarchical relationship
            between windows and avails while accumulating traffic statistics.
            
            Avails serve as the fundamental scheduling unit for advertisement placement,
            providing the granular time slots that advertisers can purchase for their
            campaigns. Each avail is uniquely identified within its parent window and
            maintains precise timing information for broadcast coordination.
            
            Args:
                request (HttpRequest): Django HTTP request object containing:
                                    - Nested POST data with double-indexed form arrays
                                    - Form fields indexed by window and avail position
                                    - Advertisement data for each avail slot
                                    - User session and authentication information
                window (Windows): Window model instance that will contain the avails
                                Must be a saved instance with valid primary key
                                Represents the parent time window for these avails
                daydate (datetime): Formatted datetime object for the broadcast date
                                Used for scheduling context and time calculations
                                Provides date reference for avail timing
                window_index (int): Zero-based index of the current window being processed
                                Used to access correct form fields in nested arrays
                                Corresponds to window position in the playlist
                numofavails (int): Total number of avails to process for this window
                                Must be a positive integer value
                                Determines the iteration range for avail processing
                traffic (int): Current cumulative traffic counter from previous processing
                            Represents total advertisement count processed so far
                            Will be incremented by advertisements in processed avails
            
            Returns:
                int: Updated traffic counter including advertisements from all processed avails
                    Accumulates advertisement counts from nested _process_ads calls
                    Used for billing calculations and capacity reporting
                    Maintains running total across all playlist components
                    
            Raises:
                ValueError: If numofavails is not a valid positive integer
                AttributeError: If window instance is invalid or unsaved
                IntegrityError: If avail creation violates database constraints
                KeyError: If expected nested form fields are missing
                
            Database Operations:
                - Creates Avails records linked to the parent window
                - Sets avail position within window (availinwindow field)
                - Maintains foreign key relationship to Windows model
                - Delegates advertisement creation to _process_ads method
                
            Form Data Structure Expected:
                - availstart[window_index][avail_index]: Start time for each avail
                - numofads[window_index][avail_index]: Advertisement count per avail
                - Additional nested advertisement data processed by _process_ads
                
            Processing Flow:
                1. Iterate through each avail index from 0 to numofavails-1
                2. Extract avail start time from double-indexed form field
                3. Validate avail data and skip if invalid
                4. Extract advertisement count for current avail
                5. Create and save Avails database record
                6. Process nested advertisements for current avail
                7. Accumulate traffic counter from advertisement processing
                8. Continue to next avail or return final traffic count
                
            Data Validation:
                - Validates avail start time is present in form data
                - Skips avails with missing or invalid start times
                - Handles optional advertisement count gracefully
                - Ensures avail position numbering starts from 1
                
            Performance Considerations:
                - Sequential processing maintains avail order within window
                - Single database query per avail creation
                - Efficient nested form field access using computed keys
                - Early continuation for invalid avail configurations
                
            Business Logic:
                - Avails represent purchasable advertisement time slots
                - Each avail has a unique position within its parent window
                - Avail timing must align with window boundaries
                - Traffic counter enables revenue and capacity tracking
                
            Integration Points:
                - _process_ads: Handles nested advertisement processing
                - Avails model: Database representation of advertisement slots
                - Windows model: Parent relationship for hierarchical structure
                - Form validation: Client-side and server-side data validation
                
            Example Usage:
                updated_traffic = self._process_avails(
                    request=request,
                    window=window_instance,
                    daydate=datetime(2025, 7, 30),
                    window_index=0,
                    numofavails=5,
                    traffic=current_traffic_count
                )
                
            Avail Positioning:
                - availinwindow field uses 1-based indexing for business logic
                - Loop uses 0-based indexing for programming convenience
                - Position mapping: loop index j → avail position (j + 1)
                - Maintains consistent ordering for broadcast scheduling
        availstart[][ry   	numofads[rU   )r   avail_startavailinwindow)r   r7   r3   r$   rL   r   _process_adsrN   )r.   r/   r   rI   window_indexr   r   jZavail_start_keyav_startnumber_of_adsavailr1   r1   r2   r     s,    z

z"PlaylistCreateView._process_availsc                 C   sd   t |D ]V}d| d| d| d}|j|}	|	s6q|d7 }t||	t|d |d}
|
  q|S )u  
            Process individual advertisement records within a specific advertisement availability slot.
            
            This method handles the deepest level of the playlist hierarchy, processing individual
            advertisements that will be scheduled within specific avail slots. Each advertisement
            represents a concrete commercial spot with specific content, duration, and positioning
            requirements for broadcast scheduling.
            
            The method creates AdspotsInAvail records that link specific advertisement content
            to precise time slots within the broadcast schedule. It maintains the complete
            hierarchical relationship from playlist → window → avail → advertisement and
            provides the granular scheduling data required for broadcast automation systems.
            
            This is the final stage of playlist structure processing, where abstract time
            slots are populated with actual advertisement content. The traffic counter is
            incremented for each successfully processed advertisement, providing accurate
            counts for billing, reporting, and compliance purposes.
                
            Args:
                request (HttpRequest): Django HTTP request object containing:
                                    - Triple-indexed POST data with nested form arrays
                                    - Advertisement IDs for each position within avails
                                    - Form fields indexed by window, avail, and ad position
                                    - User session and authentication information
                avail (Avails): Avail model instance that will contain the advertisements
                            Must be a saved instance with valid primary key
                            Represents the parent advertisement slot for these ads
                window_index (int): Zero-based index of the parent window being processed
                                Used to access correct form fields in nested arrays
                                Corresponds to window position in the playlist
                avail_index (int): Zero-based index of the parent avail being processed
                                Used to access correct form fields in nested arrays
                                Corresponds to avail position within the window
                number_of_ads (int): Total number of advertisements to process for this avail
                                    Must be a positive integer value
                                    Determines the iteration range for advertisement processing
                traffic (int): Current cumulative traffic counter from previous processing
                            Represents total advertisement count processed so far
                            Will be incremented by each advertisement processed here
            
            Returns:
                int: Updated traffic counter including all advertisements processed in this avail
                    Incremented by one for each successfully processed advertisement
                    Used for billing calculations, reporting, and compliance tracking
                    Maintains accurate count across entire playlist hierarchy
                    
            Raises:
                ValueError: If number_of_ads is not a valid positive integer
                AttributeError: If avail instance is invalid or unsaved
                IntegrityError: If advertisement creation violates database constraints
                KeyError: If expected triple-indexed form fields are missing
                
            Database Operations:
                - Creates AdspotsInAvail records linking advertisements to avail slots
                - Sets advertisement position within avail (positioninavail field)
                - Assigns unique traffic ID for each advertisement record
                - Maintains foreign key relationship to Avails model
                
            Form Data Structure Expected:
                - ad[window_index][avail_index][ad_index]: Advertisement ID for each position
                - Triple indexing enables precise advertisement placement
                - Form structure mirrors the complete playlist hierarchy
                
            Processing Flow:
                1. Iterate through each advertisement index from 0 to number_of_ads-1
                2. Extract advertisement ID from triple-indexed form field
                3. Validate advertisement data and skip if invalid
                4. Increment traffic counter for billing and reporting
                5. Create and save AdspotsInAvail database record
                6. Continue to next advertisement or return final traffic count
                
            Traffic Counter Logic:
                - Incremented once per successfully processed advertisement
                - Provides accurate count for revenue calculation
                - Enables compliance reporting and audit trails
                - Supports capacity planning and inventory management
                
            Advertisement Positioning:
                - positioninavail field uses 1-based indexing for business logic
                - Loop uses 0-based indexing for programming convenience
                - Position mapping: loop index k → advertisement position (k + 1)
                - Maintains precise ordering for broadcast playback sequence
                
            Data Validation:
                - Validates advertisement ID is present in form data
                - Skips advertisements with missing or invalid IDs
                - Handles sparse advertisement configurations gracefully
                - Ensures traffic counter accuracy despite validation failures
                
            Performance Considerations:
                - Sequential processing maintains advertisement order within avail
                - Single database query per advertisement creation
                - Efficient triple-indexed form field access using computed keys
                - Early continuation for invalid advertisement configurations
                
            Business Logic:
                - Each advertisement represents billable inventory
                - Advertisement positioning affects broadcast scheduling
                - Traffic IDs enable precise advertisement tracking
                - Position within avail determines playback sequence
                
            Integration Points:
                - AdspotsInAvail model: Database representation of scheduled advertisements
                - Avails model: Parent relationship for hierarchical structure
                - Advertisement inventory system: Source of advertisement content
                - Billing system: Uses traffic counter for revenue calculation
                
            Example Usage:
                final_traffic = self._process_ads(
                    request=request,
                    avail=avail_instance,
                    window_index=0,
                    avail_index=2,
                    number_of_ads=3,
                    traffic=current_traffic_count
                )
                
            Broadcast Integration:
                - Created records drive automated broadcast systems
                - Traffic IDs enable real-time playback tracking
                - Position data ensures correct advertisement sequencing
                - Timing data coordinates with broadcast schedules
        ad[r   ry   rU   )r   	adspot_idpositioninavail	trafficid)r   r7   r3   r%   rL   r   )r.   r/   r   r   Zavail_indexr   r   kZad_keyadspotads_in_availr1   r1   r2   r     s     		

zPlaylistCreateView._process_adsc                 C   sP   t j t j t j |d }t j t j t j |d }|| }|S )a  
            Calculate the duration of a broadcast window for scheduling and billing purposes.
            
            This method computes the time span between window start and end times, handling
            time format parsing and duration calculation for broadcast scheduling systems.
            The calculated duration is used for advertisement slot allocation, billing
            calculations, and broadcast automation timing requirements.
            
            The method currently contains placeholder implementation with the core logic
            commented out pending finalization of duration format requirements and
            edge case handling strategies. The preserved implementation shows the
            intended calculation approach using datetime arithmetic.
            
            Key considerations for duration calculation:
                - Handles standard HH:MM time format input
                - Manages day boundary crossings (e.g., 23:30 to 01:30)
                - Provides duration in format suitable for broadcast systems
                - Validates input times and handles parsing errors
                - Supports both same-day and cross-midnight windows
                
            Args:
                window_start (str): Window start time in HH:MM format
                                Examples: "09:30", "14:15", "23:45"
                                Must be valid 24-hour time format
                                Used as the beginning of the broadcast window
                window_end (str): Window end time in HH:MM format
                                Examples: "10:00", "14:45", "00:15"
                                Must be valid 24-hour time format
                                Used as the conclusion of the broadcast window
            
            Returns:
                str: Formatted duration string suitable for broadcast systems
                    Format depends on business requirements (pending implementation)
                    Examples might include: "00:30:00", "1.5 hours", "90 minutes"
                    Currently returns None due to commented implementation
                    
            Raises:
                ValueError: If time strings cannot be parsed in HH:MM format
                TypeError: If input parameters are not strings
                AttributeError: If datetime operations fail due to invalid data
                
            Implementation Status:
                CURRENT: Core logic commented out pending format specification
                PLANNED: Full duration calculation with multiple output formats
                PRESERVED: Implementation approach maintained for easy activation
                
            Time Handling Considerations:
                - Same-day windows: start < end (e.g., 09:00 to 17:00)
                - Cross-midnight windows: start > end (e.g., 23:00 to 01:00)
                - Zero-duration windows: start == end (special case handling)
                - Invalid ranges: end before start on same day
                
            Business Logic Applications:
                - Advertisement slot duration for billing
                - Broadcast schedule timing validation
                - Capacity planning and inventory management
                - Automated playlist generation timing
                - Compliance reporting for broadcast regulations
                
            Example Usage:
                duration = self._calculate_window_duration("09:30", "10:15")
                # Expected result: "00:45:00" or equivalent format
                
                duration = self._calculate_window_duration("23:30", "01:15")
                # Expected result: "01:45:00" (cross-midnight calculation)
                
            Future Enhancements:
                - Multiple output format support (HH:MM:SS, minutes, decimal hours)
                - Timezone handling for multi-region broadcasts
                - Business day vs calendar day duration options
                - Duration validation against minimum/maximum window constraints
                - Integration with broadcast system time standards
                
            Integration Points:
                - Windows model: Duration field for database storage
                - Broadcast scheduling: Timing coordination
                - Billing system: Duration-based pricing calculations
                - Playlist validation: Window timing consistency checks
        %H:%M)rP   combinedatetodayrQ   ri   )r.   r|   r}   durationr1   r1   r2   r     s    Wz-PlaylistCreateView._calculate_window_durationc                 C   s   dS )a  
            Handle automated FTP upload of generated XML playlist files to broadcast servers.
            
            This method manages the deployment of generated playlist XML files to remote
            broadcast automation systems via FTP protocol. It provides the critical link
            between playlist creation and broadcast system integration, ensuring that
            created playlists are automatically deployed to the appropriate servers
            for immediate broadcast availability.
            
            The method implements a complete FTP deployment workflow including configuration
            retrieval, connection establishment, file transfer, and error handling. It
            supports multiple broadcast environments and provides robust error recovery
            for network connectivity issues.
            
            Currently, the implementation is disabled pending infrastructure configuration
            and security review. The preserved logic demonstrates the intended workflow
            and can be activated when deployment requirements are finalized.
            
            Args:
                xml_playlist_res (dict): XML generation result containing:
                                        - 'status' (bool): Success status of XML generation
                                        - 'file' (str): Path to generated XML file
                                        - 'filename' (str): Name of the XML file
                                        - 'errors' (list): Any generation errors
                                        - 'metadata' (dict): Additional file information
                playlist (Playlists): Playlist model instance for configuration context
                                    Used to determine target FTP server and deployment path
                                    Contains channel and zone information for routing
            
            Returns:
                None: Method performs side effects (file upload) without return value
                    Success/failure communicated through logging and exception handling
                    
            Raises:
                ConnectionError: If FTP server connection cannot be established
                AuthenticationError: If FTP credentials are invalid or expired
                FileNotFoundError: If generated XML file is missing or inaccessible
                PermissionError: If insufficient permissions for file upload
                TimeoutError: If FTP transfer exceeds configured timeout limits
                
            Implementation Status:
                CURRENT: Implementation disabled with pass statement
                PLANNED: Full FTP deployment with error handling and logging
                PRESERVED: Core logic maintained for easy activation
                SECURITY: Pending security review for credential management
                
            FTP Deployment Workflow (When Active):
                1. Validate XML generation was successful
                2. Retrieve FTP configuration for target playlist
                3. Initialize FTP connector with server credentials
                4. Establish secure connection to broadcast server
                5. Upload XML file to designated remote path
                6. Verify successful transfer and file integrity
                7. Log deployment status for audit trail
                8. Clean up temporary files and connections
                
            Configuration Requirements:
                - FTP server hostnames and ports
                - Authentication credentials (username/password or key-based)
                - Remote directory paths for different channels/zones
                - Transfer mode settings (binary/ASCII)
                - Timeout and retry configurations
                - SSL/TLS encryption settings for secure transfers
                
            Security Considerations:
                - Credential storage and encryption
                - Secure FTP (SFTP) or FTP over SSL/TLS
                - Network access control and firewall rules
                - File permission validation on remote servers
                - Audit logging for compliance and monitoring
                
            Error Handling Strategy:
                - Retry logic for temporary network failures
                - Fallback servers for high availability
                - Graceful degradation when FTP is unavailable
                - Detailed error logging for troubleshooting
                - User notification for critical deployment failures
                
            Performance Optimization:
                - Connection pooling for multiple uploads
                - Compression for large XML files
                - Parallel uploads to multiple servers
                - Transfer progress monitoring and reporting
                - Bandwidth throttling for network management
                
            Integration Points:
                - _get_ftp_config: Retrieves server-specific configuration
                - FTPConnector: Handles low-level FTP operations
                - Logging system: Records deployment activities
                - Monitoring system: Tracks deployment success rates
                - Alert system: Notifies on deployment failures
                
            Example Usage (When Implemented):
                xml_result = generator.generate_schedule(playlist)
                self._handle_ftp_upload(xml_result, playlist)
                
                # Results in XML file deployed to:
                # ftp://broadcast-server/playlists/channel_123/playlist_456.xml
                
            Business Impact:
                - Enables automated broadcast deployment
                - Reduces manual intervention and errors
                - Supports real-time playlist updates
                - Ensures consistent deployment processes
                - Facilitates compliance with broadcast schedules
                
            Monitoring and Analytics:
                - Upload success/failure rates
                - Transfer duration and performance metrics
                - Server availability and connectivity status
                - File integrity verification results
                - Deployment frequency and patterns
        Nr1   )r.   rv   rc   r1   r1   r2   _handle_ftp_upload  s    yz%PlaylistCreateView._handle_ftp_uploadNTc                 C   s6  ddl m}m} z|jjdd}W n |jk
r<   d}Y nX d|jrN|jjnd }	|jrr|	d|j	d	 7 }	t
|j|jr|jjnd|jr|jjnd|jr|j nd|jr|j nd|jr|j nd|jr|jjnd|j|j|jd

}
|j|jdd|jr|jjnd d|t||jdd|
d dS )a=  
        Log comprehensive playlist creation activity for audit trail, compliance, and monitoring.
        
        This method creates detailed audit records for all playlist operations, providing
        complete traceability for compliance, debugging, and business intelligence purposes.
        It captures user actions, system performance, and contextual metadata to enable
        comprehensive analysis of playlist creation patterns and system behavior.
        
        The logging system integrates with the centralized activity tracking framework
        to maintain consistent audit trails across all system operations. This supports
        regulatory compliance, security monitoring, and operational analytics for
        broadcast scheduling systems.
        
        The method handles both successful and failed operations, capturing appropriate
        context and error information to support troubleshooting and system optimization.
        It also records performance metrics to enable capacity planning and performance
        monitoring across the playlist creation workflow.
        
        Args:
            playlist (Playlists): The Playlist model instance that was created or attempted
                                Contains all playlist metadata and relationships
                                Used to extract contextual information for logging
            request (HttpRequest, optional): Django request object containing:
                                            - User authentication information
                                            - Client IP address and user agent
                                            - Session data and request metadata
                                            - HTTP headers for security analysis
            is_successful (bool, optional): Success status of the playlist operation
                                        Defaults to True for successful operations
                                        Used to categorize and filter activity logs
            error_message (str, optional): Detailed error message for failed operations
                                        Provides debugging context for failures
                                        Stored in activity metadata for analysis
            duration_ms (int, optional): Processing duration in milliseconds
                                        Used for performance monitoring and optimization
                                        Enables identification of slow operations
        
        Returns:
            None: Method performs logging side effects without return value
                Success/failure communicated through logging system
                
        Raises:
            AttributeError: If playlist instance lacks required attributes
            ImportError: If Activity or ActivityCategory models are unavailable
            DatabaseError: If activity logging fails due to database issues
            
        Database Operations:
            - Retrieves or handles missing ActivityCategory for 'PLAYLIST'
            - Creates comprehensive Activity record with full metadata
            - Stores user information, timing data, and system context
            - Maintains audit trail for compliance and security monitoring
            
        Metadata Structure:
            - playlist_id: Unique identifier for created playlist
            - channel_id: Channel identifier for scheduling context
            - channel_name: Human-readable channel name
            - broadcast_date: Scheduled broadcast date in ISO format
            - start_date: Window start time in ISO format
            - end_date: Window end time in ISO format
            - zone_channel: Geographic or programming zone identifier
            - version: Playlist version for change tracking
            - is_draft: Draft status for workflow management
            - draft_version: Draft iteration for version control
            
        Security and Compliance:
            - Records user identity and authentication status
            - Captures client IP address for security monitoring
            - Logs user agent for client analysis and security
            - Maintains tamper-evident audit trails
            - Supports regulatory compliance requirements
            
        Performance Monitoring:
            - Records operation duration for optimization
            - Enables identification of performance bottlenecks
            - Supports capacity planning and resource allocation
            - Provides data for system performance analysis
            
        Example Usage:
            # Successful playlist creation
            self._log_activity(
                playlist=created_playlist,
                request=request,
                is_successful=True,
                duration_ms=1250
            )
            
            # Failed playlist creation
            self._log_activity(
                playlist=partial_playlist,
                request=request,
                is_successful=False,
                error_message="Invalid channel configuration",
                duration_ms=450
            )
        r   )ActivityActivityCategoryZPLAYLIST)codeNzCreated playlist for zUnknown Channelz scheduled for %Y-%m-%d)
playlist_idr<   channel_namer^   r_   r`   ra   r]   rb   rT   CREATEUser Systemz performed create_playlistHTTP_USER_AGENTr@   )r+   actiondescriptionr/   
ip_address
user_agentmetadata)apps.activities.modelsr   r   r,   r3   rJ   rF   r   r^   strftimerL   rr   r>   	isoformatr_   r`   ra   regionr]   rb   rT   log_activityr+   emailr   META)r.   rc   r/   rd   error_messagere   r   r   categoryr   r   r1   r1   r2   rn     s8    g
	z PlaylistCreateView._log_activityc                 C   s   dS )a  
        Generate XML preview files for draft playlists without production deployment.
        
        This method creates XML representations of draft playlists for preview,
        validation, and testing purposes without deploying to production broadcast
        systems. It enables users to review the generated schedule format and
        validate playlist structure before committing to live broadcast deployment.
        
        Draft XML generation serves multiple purposes:
        - Preview generation for user review and approval workflows
        - Format validation before production deployment
        - Testing and development of playlist structures
        - Compliance verification without affecting live broadcasts
        - Template generation for repeated playlist patterns
        
        The method currently contains placeholder implementation with core logic
        commented out pending infrastructure setup for draft XML processing and
        storage systems. The preserved logic demonstrates the intended workflow
        for generating and managing draft XML files.
        
        Args:
            data (dict): Validated playlist data dictionary containing:
                        - 'daydate': Formatted broadcast date for scheduling
                        - 'channel_id': Channel identifier for routing
                        - 'channel_zone': Zone configuration for regional settings
                        - Additional scheduling and configuration parameters
            playlist (Playlists): Draft playlist model instance containing:
                                - Playlist structure and timing information
                                - Channel and zone relationships
                                - Draft-specific metadata and version information
        
        Returns:
            None: Method performs XML generation side effects without return value
                Generated files managed through file system or storage service
                Success/failure communicated through logging and exception handling
                
        Raises:
            AttributeError: If required data fields are missing or invalid
            FileNotFoundError: If XML generation templates are unavailable
            PermissionError: If insufficient permissions for file creation
            ValidationError: If playlist data fails XML schema validation
            
        Implementation Status:
            CURRENT: Core logic commented out pending infrastructure setup
            PLANNED: Full draft XML generation with preview capabilities
            PRESERVED: Implementation approach maintained for easy activation
            INTEGRATION: Pending storage and preview system configuration
            
        Draft XML Workflow (When Active):
            1. Extract playlist scheduling parameters from data dictionary
            2. Initialize XML generator with draft-specific configuration
            3. Generate XML content from playlist structure and timing
            4. Validate generated XML against broadcast system schemas
            5. Store XML file in draft storage location for preview
            6. Create preview URLs or file references for user access
            7. Log generation activity for audit and monitoring
            
        XML Generation Parameters:
            - daydate: Target broadcast date for scheduling context
            - channel_id: Channel identifier for content routing
            - zone_channel_id: Zone identifier for regional configuration
            - version: Draft version identifier for tracking
            
        Storage Considerations:
            - Draft XML files stored separately from production files
            - Temporary storage with configurable retention policies
            - Access control for draft file security and privacy
            - Version tracking for iterative draft development
            - Cleanup policies for abandoned drafts
            
        Preview Integration:
            - Web interface for XML preview and validation
            - Download capabilities for offline review
            - Diff comparison with previous draft versions
            - Integration with approval and collaboration workflows
            - Format validation and compliance checking
            
        Business Applications:
            - Client approval processes for broadcast schedules
            - Compliance review before production deployment
            - Testing new playlist configurations safely
            - Training and education on playlist structures
            - Template development for repeated patterns
            
        Example Usage (When Implemented):
            validated_data = self._extract_common_data(request)
            self._handle_draft_xml_generation(validated_data, draft_playlist)
            
            # Results in draft XML file created at:
            # /drafts/xml/channel_123/draft_playlist_456_v1.xml
            
        Integration Points:
            - GenerateXMLfromDatabase: Core XML generation functionality
            - Draft storage system: File management and retention
            - Preview interface: User access to generated XML
            - Validation system: Schema and compliance checking
            - Logging system: Activity tracking and monitoring
        Nr1   )r.   rt   rc   r1   r1   r2   _handle_draft_xml_generation<  s    jz/PlaylistCreateView._handle_draft_xml_generation)NTNN)__name__
__module____qualname____doc__r3   r;   rS   r8   r9   rm   r   r   r   r   rn   r   r1   r1   r1   r2   r'   $   s*   oVg " 7  M C As 
 )r'   c                   @   s8   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d ZdS )PlaylistDetailViewzB
    Detailed view for individual playlist with related data.
    c                 C   sr   zHt jddj||jd}|| || || |d}t|d|W S  t j	k
rl   t|ddd Y S X d	S )
z
        Display detailed playlist information.
        
        Args:
            request: Django HTTP request object
            id_playlist: Playlist ID
            
        Returns:
            HttpResponse: Rendered template
        rF   ra   rr   channel__id_user)rc   windowsstatstimelinezplaylists/detail.htmlzplaylists/not_found.htmli  )statusN)
r"   r,   select_relatedr3   r+   _get_playlist_windows_get_detailed_stats_get_playlist_timeliner   rJ   )r.   r/   rh   rc   r0   r1   r1   r2   r3     s     
	zPlaylistDetailView.getc              	   C   s   g }|j  dD ]p}g }|j dD ]F}g }|j dD ]}||j|j|jd qD|||d q,|||d q|S )z
        Get organized window data for the playlist.
        
        Args:
            playlist: Playlist model instance
            
        Returns:
            list: Organized window data
        r|   r   r   )r   position
traffic_id)r   ads)r   avails)	r   allorder_byr   adspots_in_availappendr   r   r   )r.   rc   Zwindows_datar   Zavails_datar   Zads_dataadr1   r1   r2   r     s(    



z(PlaylistDetailView._get_playlist_windowsc                 C   s^   |j  }tdd |D }tdd |D }| |||dkrNt|| dnd| |dS )z
        Get detailed statistics for the playlist.
        
        Args:
            playlist: Playlist model instance
            
        Returns:
            dict: Detailed statistics
        c                 s   s   | ]}|j  V  qd S N)r   count).0r   r1   r1   r2   	<genexpr>  s     z9PlaylistDetailView._get_detailed_stats.<locals>.<genexpr>c                 s   s(   | ] }|j  D ]}|j V  qqd S r   )r   r   r   r   )r   r   r   r1   r1   r2   r     s    r      )Ztotal_windowstotal_avails	total_adsZavg_ads_per_availZduration_info)r   r   sumr   round_calculate_total_duration)r.   rc   r   r   r   r1   r1   r2   r     s    

z&PlaylistDetailView._get_detailed_statsc                 C   sL   d}|D ]}|j r||j  7 }q|d }t|dt|d dt|ddS )z
        Calculate total duration information.
        
        Args:
            windows: Window queryset
            
        Returns:
            dict: Duration information
        r   <   r   )total_minutesZtotal_hourstotal_seconds)r~   r   r   )r.   r   r   r   r   r1   r1   r2   r   '  s    
z,PlaylistDetailView._calculate_total_durationc           
   	   C   s   ddl m }m}m} ddlm} d|jdd|j ddg}|j 	d	D ]}t
|j|rt|d
| }|||j}	||	r||	}	n|j}	||	r||	}	|d|	dd|j  dd qLt|dd dS )z
        Get timeline information for the playlist.
        
        Args:
            playlist: Playlist model instance
            
        Returns:
            list: Timeline events
        r   )rP   r   ri   r   zPlaylist CreatedcreationzPlaylist version z created)eventrP   typer   r|   r   zWindow Scheduledr   zWindow with z availsc                 S   s   | d S )NrP   r1   )xr1   r1   r2   <lambda>n      z;PlaylistDetailView._get_playlist_timeline.<locals>.<lambda>)key)rP   r   ri   django.utilsr   
created_atr]   r   r   r   
isinstancer|   ro   r   r   is_naive
make_awarer   r   r   sorted)
r.   rc   rP   r   ri   r   r   r   playlist_dateZwindow_datetimer1   r1   r2   r   @  s0    





z)PlaylistDetailView._get_playlist_timelineN)	r   r   r   r   r3   r   r   r   r   r1   r1   r1   r2   r     s   )%r   c                   @   s   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zd d! Zd"d# Zd$d% Zd&d' Zd(d) Zd*d+ Zd,d- Zd.d/ Zd0d1 Zd2d3 Zd4d5 Zd6d7 Zd8S )9PlaylistListViewz
    Class-based view for listing and filtering playlists using base View class.
    Supports search, filtering, sorting, and pagination with full control.
    c                 C   s   |j ddkr| |S | |}t|| |}|jdd}z||}W n< tk
rn   |d}Y n  t	k
r   ||j
}Y nX | |||}t|d|S )z
        Handle GET requests for playlist listing.
        
        Args:
            request: Django HTTP request object
            
        Returns:
            HttpResponse or JsonResponse for AJAX requests
        X-Requested-WithXMLHttpRequestpagerU   zplaylists/list.html)headersr3   _handle_ajax_request_get_filtered_querysetr   _get_items_per_pageGETget_pager   r   	num_pages_build_contextr   )r.   r/   queryset	paginatorpage_numberpage_objr0   r1   r1   r2   r3   w  s    

zPlaylistListView.getc                 C   st   |j d}|j d}|s*tdddS |dkr>| ||S |dkrR| ||S |dkrf| ||S tdd	dS )
z
        Handle POST requests for bulk operations.
        
        Args:
            request: Django HTTP request object
            
        Returns:
            JsonResponse with operation results
        r   playlist_idsFzNo playlists selected.successmessageZbulk_deleteZbulk_exportZbulk_status_changezInvalid action.)r7   r3   getlistr   _handle_bulk_delete_handle_bulk_export_handle_bulk_status_change)r.   r/   r   r   r1   r1   r2   r;     s"    
zPlaylistListView.postc                 C   s   |j }tjdddddjtdddtdddtddddj|d	}| ||}| 	||}| 
||}| ||}| ||}| ||}|S )
z
        Get filtered and sorted queryset based on request parameters.
        
        Args:
            request: Django HTTP request object
            
        Returns:
            QuerySet: Filtered playlist queryset
        rF   ra   r   Zwindows__avails!windows__avails__adspots_in_availT)distinct)window_countr   r   r   )r+   r"   r,   r   prefetch_relatedannotater   r-   _apply_search_filter_apply_channel_filter_apply_date_filter_apply_status_filter_apply_zone_filter_apply_sorting)r.   r/   r+   r   r1   r1   r2   r     s,    



z'PlaylistListView._get_filtered_querysetc                 C   sR   |j dd }|rN|t|dt|dB t|dB t|dB t|dB }|S )z
        Apply search filter based on search query.
        
        Args:
            request: Django HTTP request object
            queryset: Base queryset
            
        Returns:
            QuerySet: Filtered queryset
        searchr@   )Z channel__channel_name__icontains)Z$zone_channel__networkname__icontains)Zzone_channel__region__icontains)Zbroadcast_date__icontains)Zversion__icontains)r   r3   rM   r-   r
   )r.   r/   r   search_queryr1   r1   r2   r	    s    z%PlaylistListView._apply_search_filterc              	   C   sN   |j d}|rJ|dkrJzt|}|j|d}W n ttfk
rH   Y nX |S )z
        Apply channel filter.
        
        Args:
            request: Django HTTP request object
            queryset: Base queryset
            
        Returns:
            QuerySet: Filtered queryset
        rF   r   )Zchannel__id_channelr   r3   rN   r-   rO   	TypeError)r.   r/   r   r<   r1   r1   r2   r
    s    z&PlaylistListView._apply_channel_filterc                 C   s*  |j d}|j d}|j d}|rt  }|dkrN|j|d}nX|dkrv|tjdd }|j|d}n0|d	kr|tj| d }|j|d
}n|dkr|tj| d d }|tjdd }	|j||	d}n|dkr|j	dd}
|j|
d
}n|dkr|j
dkr4|j	|jd ddd}n|j	|j
d dd}|j
dkrx|j	|jd dddtjdd }n |j	|j
d ddtjdd }|j||d}|rz"tj|d }|j|d
}W n tk
r   Y nX |r&z"tj|d }|j|d}W n tk
r$   Y nX |S )z
        Apply date range filter with predefined and custom ranges.
        
        Args:
            request: Django HTTP request object
            queryset: Base queryset
            
        Returns:
            QuerySet: Filtered queryset
        	date_fromdate_to
date_ranger   r^   	yesterdayrU   days	this_week)broadcast_date__gteZ	last_week      )r  broadcast_date__lte
this_monthrD   
last_month   )yearmonthrD   )r$  rD   r   )r  )r   r3   r   nowr   r-   rP   	timedeltaweekdayrk   r$  r#  rQ   rO   )r.   r/   r   r  r  r  r   r  
week_startweek_endmonth_startr!  	month_endZdate_from_parsedZdate_to_parsedr1   r1   r2   r  	  s^    

$ z#PlaylistListView._apply_date_filterc                 C   s:   |j d}|dkr"|jdd}n|dkr6|jdd}|S )z
        Apply status filter (draft/published).
        
        Args:
            request: Django HTTP request object
            queryset: Base queryset
            
        Returns:
            QuerySet: Filtered queryset
        r   draft1rb   	published0)r   r3   r-   )r.   r/   r   r   r1   r1   r2   r  T	  s    z%PlaylistListView._apply_status_filterc              	   C   sN   |j d}|rJ|dkrJzt|}|j|d}W n ttfk
rH   Y nX |S )z
        Apply zone/region filter.
        
        Args:
            request: Django HTTP request object
            queryset: Base queryset
            
        Returns:
            QuerySet: Filtered queryset
        zoner   )Zzone_channel__id_zone_channelr  )r.   r/   r   zone_idr1   r1   r2   r  h	  s    z#PlaylistListView._apply_zone_filterc                 C   sT   |j dd}dddddddd	d
dddddddg}||krF||}n
|d}|S )z
        Apply sorting based on sort parameter.
        
        Args:
            request: Django HTTP request object
            queryset: Base queryset
            
        Returns:
            QuerySet: Sorted queryset
        sort_by-created_atr   r^   -broadcast_datechannel__channel_name-channel__channel_namer]   z-versionr  -window_countr   
-total_adsr   -total_availsrb   	-is_draft)r   r3   r   )r.   r/   r   r3  valid_sort_fieldsr1   r1   r2   r  ~	  s,            
zPlaylistListView._apply_sortingc              	   C   sD   z$t |jdd}tdt|dW S  ttfk
r>   Y dS X dS )z
        Get number of items per page from request or default.
        
        Args:
            request: Django HTTP request object
            
        Returns:
            int: Items per page
        per_page   
   d   N)rN   r   r3   maxminrO   r  )r.   r/   r=  r1   r1   r2   r   	  s
    
z$PlaylistListView._get_items_per_pagec                 C   sX   |j }|j|||jdk| || || ||  | ||  |j	|j
|jd}|S )a  
        Build template context with all necessary data.
        
        Args:
            request: Django HTTP request object
            page_obj: Paginated page object
            paginator: Paginator instance
            
        Returns:
            dict: Template context
        rU   )	playlistsr   r   is_paginatedr*   zonesfilter_paramssort_optionsr   r  total_resultscurrent_pagetotal_pages)r+   object_listr   _get_user_channels_get_available_zones_get_current_filters_get_sort_options_get_playlist_stats_get_date_ranger   number)r.   r/   r   r   r+   r0   r1   r1   r2   r   	  s     zPlaylistListView._build_contextc                 C   s   t jj|ddS )z
        Get channels available to the current user.
        
        Args:
            user: Current user object
            
        Returns:
            QuerySet: User's channels
        r(   r   )r    r,   r-   r   r.   r+   r1   r1   r2   rL  	  s    
z#PlaylistListView._get_user_channelsc                 C   s   t jj|d ddS )z
        Get zones available to the current user.
        
        Args:
            user: Current user object
            
        Returns:
            QuerySet: Available zones
        )id_channel__id_usernetworknamer   )r!   r,   r-   r  r   rS  r1   r1   r2   rM  	  s    

 z%PlaylistListView._get_available_zonesc                 C   sr   |j dd|j dd|j dd|j dd|j dd|j dd|j d	d|j d
d|j ddd	S )z
        Get current filter parameters for template.
        
        Args:
            request: Django HTTP request object
            
        Returns:
            dict: Current filter values
        r  r@   rF   r   r1  r   r  r  r  r3  r4  r=  20)	r  rF   r1  r   r  r  r  r3  r=  )r   r3   )r.   r/   r1   r1   r2   rN  	  s    z%PlaylistListView._get_current_filtersc                 C   s\   ddddddddddd	dd
ddddddddddddddddddddgS )zv
        Get available sorting options.
        
        Returns:
            list: Sort options for template
        r4  zNewest First)valuelabelr   zOldest Firstr5  zBroadcast Date (Newest)r^   zBroadcast Date (Oldest)r6  zChannel Name (A-Z)r7  zChannel Name (Z-A)r8  zMost Windowsr9  zMost Adsr:  zMost Availsrb   zPublished Firstr;  zDrafts Firstr1   )r.   r1   r1   r2   rO  
  s    z"PlaylistListView._get_sort_optionsc              	   C   s   t jj|d}t  }|tjdd }| |jdd |jdd |j|d |jt tjdd d |j|dd	 |j|d
 dS )z
            Get playlist statistics for dashboard.

            Args:
            user: Current user object

            Returns:
            dict: Playlist statistics
        r  r  r  r-  r.  r0  r  )created_at__gte)Zbroadcast_date__gtrb   )broadcast_date__lt)total_playlistsdraft_playlistspublished_playlistsZtodays_playlistsZrecent_playlistsZupcoming_playlistsZpast_playlists)	r"   r,   r-   r   r%  r   rP   r&  r   )r.   r+   base_querysetr   week_agor1   r1   r2   rP  
  s$    z$PlaylistListView._get_playlist_statsc              	   C   sZ   t   }||tjdd |tj| d |jdd|tjdd |tjdd dS )zz
        Get date range information for filtering.
        
        Returns:
            dict: Date range options
        rU   r  r   im  )r   r  r(  r*  min_datemax_date)r   r%  r   rP   r&  r'  rk   )r.   r   r1   r1   r2   rQ  ;
  s    
z PlaylistListView._get_date_rangec                 C   s^   |j d}|dkr| |S |dkr0| |S |dkrB| |S |dkrT| |S | |S )z
        Handle AJAX requests for dynamic updates.
        
        Args:
            request: Django HTTP request object
            
        Returns:
            JsonResponse: JSON response with data
        r   	get_zonesquick_statsexport_data	load_more)r   r3   _get_zones_for_channel_ajax_get_quick_stats_ajax_export_playlist_data_ajax_load_more_playlists_ajax_get_filtered_playlists_ajax)r.   r/   r   r1   r1   r2   r   M
  s    




z%PlaylistListView._handle_ajax_requestc              	   C   s~   |j d}|stdddS z4t|}tjj|dddd}td	t|d
W S  t	t
fk
rx   tddd Y S X dS )z
        Get zones for a specific channel via AJAX.
        
        Args:
            request: Django HTTP request object
            
        Returns:
            JsonResponse: JSON response with zone data
        r<   FzChannel ID requiredr   r:   )id_channel_idrB   rU  r   T)r   rE  zInvalid channel IDN)r   r3   r   rN   r!   r,   r-   valueslistrO   r  )r.   r/   r<   rE  r1   r1   r2   rf  e
  s$    
  
z,PlaylistListView._get_zones_for_channel_ajaxc                 C   s   |j }| |}td|dS )z
        Get quick statistics via AJAX.
        
        Args:
            request: Django HTTP request object
            
        Returns:
            JsonResponse: JSON response with statistics
        T)r   r   )r+   rP  r   )r.   r/   r+   r   r1   r1   r2   rg  
  s    

z&PlaylistListView._get_quick_stats_ajaxc                 C   s,   |  |}tdd|  dd| dS )z
        Export playlist data via AJAX.
        
        Args:
            request: Django HTTP request object
            
        Returns:
            JsonResponse: JSON response with export status
        TExport prepared for 
 playlists#)r   r   download_urlr   )r   r   r   )r.   r/   r   r1   r1   r2   rh  
  s    
z+PlaylistListView._export_playlist_data_ajaxc           	      C   s"  |j dd}| |}| |}t||}z||}W n& ttfk
r`   tddd Y S X g }|j	D ]}|
|j|jj|jj d|jj |jd|j|jdkt|d	d
t|dd
t|dd
|jdd|j dd|j dd qltd|| | r| nd|j|jdS )z
        Load more playlists for infinite scroll.
        
        Args:
            request: Django HTTP request object
            
        Returns:
            JsonResponse: JSON response with more playlists
        r   rU   FzInvalid pagerk   - r   r-  r  r   r   r   %Y-%m-%d %H:%Mz/playlists//z/edit/)rr   r   r1  r^   r]   rb   r  r   r   creation_dateZ
detail_urlZedit_urlTN)r   rC  has_next	next_pagerJ  rI  )r   r3   r   r   r   r   r   r   r   rK  r   rh   r>   r   rB   rU  r   r^   r   r]   rb   ro   r   rw  next_page_numberr   rR  )	r.   r/   r   r=  r   r   r   playlists_datarc   r1   r1   r2   ri  
  s@    










z*PlaylistListView._load_more_playlists_ajaxc                 C   s   |  |}tt|jddd}|d| }g }|D ]b}||j|jj|j	j
 d|j	j |jd|j|jdkt|dd	t|d
d	|jdd	 q6td|| t|dS )z
        Get filtered playlists for AJAX requests.
        
        Args:
            request: Django HTTP request object
            
        Returns:
            JsonResponse: JSON response with filtered playlists
        limit2   r@  Nrs  r   r-  r  r   r   rt  )	rr   r   r1  r^   r]   rb   r  r   rv  T)r   rC  total_countZdisplayed_count)r   rB  rN   r   r3   r   rh   r>   r   rB   rU  r   r^   r   r]   rb   ro   r   r   r   len)r.   r/   r   r{  rC  rz  rc   r1   r1   r2   rj  
  s,    






z-PlaylistListView._get_filtered_playlists_ajaxc           
      C   s6  z|j }d}g }|D ]}zXtjj||d}tj }|j|kr\|jdkr\|	d|  W q| 
| |d7 }W q tjk
r   |	d| d Y q tk
r }	 z|	d| d	t|	  W 5 d
}	~	X Y qX qtd||d| ddW S  tk
r0 }	 z tddt|	 d W Y S d
}	~	X Y nX d
S )a  
        Handle bulk deletion of playlists.
        
        Args:
            request: Django HTTP request object
            playlist_ids: List of playlist IDs to delete
            
        Returns:
            JsonResponse: Result of bulk operation
        r   rh   rT  r0  zCannot delete active playlist rU   z	Playlist z
 not foundzError deleting playlist z: NTzSuccessfully deleted rp  )r   deleted_counterrorsr   FzBulk delete failed: r   )r+   r"   r,   r3   rP   r   r   r^   rb   r   _delete_playlist_cascaderJ   rK   rL   r   )
r.   r/   r   r+   r  r  r   rc   r   rR   r1   r1   r2   r   
  s>    

.

z$PlaylistListView._handle_bulk_deletec                 C   s   t ddt| dddS )a  
        Handle bulk export of playlists.
        
        Args:
            request: Django HTTP request object
            playlist_ids: List of playlist IDs to export
            
        Returns:
            JsonResponse: Result of bulk operation
        Tro  rp  rq  )r   r   rr  )r   r~  )r.   r/   r   r1   r1   r2   r  0  s
    z$PlaylistListView._handle_bulk_exportc           
   
   C   s   |j d}|dkr"tdddS zh|j}d}tjj||d}|D ]}||_|  |d7 }qB|d	krjd
nd}td|d| d| dW S  t	k
r }	 z tddt
|	 d W Y S d}	~	X Y nX dS )a  
        Handle bulk status change (draft/published).
        
        Args:
            request: Django HTTP request object
            playlist_ids: List of playlist IDs to update
            
        Returns:
            JsonResponse: Result of bulk operation
        
new_status)r0  r-  FzInvalid status valuer   r   )id_playlist__inrT  rU   r-  r,  r/  TzSuccessfully updated z playlists to )r   updated_countr   zBulk status update failed: N)r7   r3   r   r+   r"   r,   r-   rb   r   rK   rL   )
r.   r/   r   r  r+   r  rC  rc   Zstatus_namerR   r1   r1   r2   r  B  s8    

z+PlaylistListView._handle_bulk_status_changec                 C   sV   |j  D ]0}|j D ]}|j   q|j   q
|j    |  dS z
        Delete playlist and all related objects in proper order.
        
        Args:
            playlist: Playlist model instance
        Nwindows_setr   
avails_setadspotsinAvail_setdeleter.   rc   r   r   r1   r1   r2   r  q  s    z)PlaylistListView._delete_playlist_cascadeN)r   r   r   r   r3   r;   r   r	  r
  r  r  r  r  r   r   rL  rM  rN  rO  rP  rQ  r   rf  rg  rh  ri  rj  r   r  r  r  r1   r1   r1   r2   r   q  s8   !(G ' /%3/r   c                   @   sh   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dd Z
dd Zdd Zdd Zdd ZdS )PlaylistEditViewz
    Class-based view for editing playlists with form handling.
    Combines the functionality of the original new_playlist view but for editing.
    c                 C   s   t tjddd||jd}| |sBt|d t	d|dS t
jj|jd}tjj|jd	}| |}||d
|i|dd}t|d|S )a  
        Handle GET requests - render the edit form with existing data.
        
        Args:
            request: Django HTTP request object
            playlist_id: ID of the playlist to edit
            
        Returns:
            Rendered edit form template
        rF   ra   r  r   This playlist cannot be edited.rf   rg   r(   r=   r*   T)rc   playlist_datart   rE  Zis_editzplaylists/edit.html)r   r"   r,   r   r  r+   _can_edit_playlistr   r:   r   r    r-   r!   rF   _structure_playlist_datar   )r.   r/   r   rc   r*   rE  r  r0   r1   r1   r2   r3     s.    


zPlaylistEditView.getc                 C   s   t tjdd||jd}| |s<t|d td|dS |j	
d}|j	
d}|rd| ||S |rt| ||S td	|d
S )z
        Handle POST requests - update the playlist.
        
        Args:
            request: Django HTTP request object
            playlist_id: ID of the playlist to edit
            
        Returns:
            Redirect to appropriate page
        rF   ra   r   r  rf   rg   r4   r5   playlists:edit_playlistr   )r   r"   r,   r   r+   r  r   r:   r   r7   r3   _handle_update_playlist_handle_save_as_draft)r.   r/   r   rc   r4   r5   r1   r1   r2   r;     s    
zPlaylistEditView.postc                 C   s@   t j }|jrdS |j|ks8t j   t ddkr<dS dS )z
        Check if playlist can be edited.
        
        Args:
            playlist: Playlist model instance
            
        Returns:
            bool: True if playlist can be edited
        Tr      F)rP   r   r   rb   r^   r%  ri   r.   rc   r   r1   r1   r2   r    s    
$z#PlaylistEditView._can_edit_playlistc              
   C   s   g |j j|jj|jd|j|jdkd}|j	 
dD ]}|j|jd|jdg d}|j	 
dD ]d}|j|jd|jg d}|j	 
d	D ]&}|d
 |j|jj|j|jd q|d | qn|d | q:|S )z
        Structure existing playlist data for form rendering.
        
        Args:
            playlist: Playlist model instance
            
        Returns:
            dict: Structured playlist data
        rE   r-  )r   r<   r2  r^   r]   rb   r   r   )rr   rs   end_timer   r   )rr   rs   r   r   r   r   )rr   r   r   r   r   r   )rF   r>   ra   rB   r^   r   r]   rb   r   r   r   rr   r|   r}   r   r   r   r   r   r   r   r   )r.   rc   rt   r   Zwindow_datar   Z
avail_datar   r1   r1   r2   r    s:    
	



z)PlaylistEditView._structure_playlist_datac              
   C   s   zht  > | || | | | || t|}| |j| W 5 Q R X t	|d t
d|jdW S  tk
r } z,t|dt|  t
d|jd W Y S d}~X Y nX dS )z
        Handle playlist update (apply changes).
        
        Args:
            request: Django HTTP request object
            playlist: Playlist model instance
            
        Returns:
            HttpResponse redirect
        zPlaylist updated successfully.rf   rg   zError updating playlist: r  r  N)r   atomic_update_playlist_basic_info_clear_playlist_structure_recreate_playlist_structuregenerateSchedule_log_update_activityr+   r   r   r   rr   rK   r:   rL   )r.   r/   rc   rv   rR   r1   r1   r2   r    s    

z(PlaylistEditView._handle_update_playlistc              
   C   s   z|t  X d|_tt|jd |_| || | | | 	|| |
  | |j| W 5 Q R X t|d tdW S  tk
r } z,t|dt|  td|jd W Y S d}~X Y nX dS )	z
        Handle saving playlist as draft.
        
        Args:
            request: Django HTTP request object
            playlist: Playlist model instance
            
        Returns:
            HttpResponse redirect
        r-  rU   zPlaylist saved as draft.rw   zError saving draft: r  r  N)r   r  rb   rL   rN   r]   rT   r  r  r  r   _log_draft_activityr+   r   r   r   rK   r:   rh   )r.   r/   rc   rR   r1   r1   r2   r  =  s    


z&PlaylistEditView._handle_save_as_draftc                 C   s   |j d}|rJtjt|d}|d}||_| d|_| d|_|j dd	 }|rxt
jj|j|d}||_|  d	S )
z
        Update basic playlist information.
        
        Args:
            request: Django HTTP request object
            playlist: Playlist model instance
        rD   rE   r   T00:01:00+00:00T23:59:00+00:00r?   r@   rA   N)r7   r3   rP   rQ   rL   r   r^   r_   r`   rM   r!   r,   rF   rB   r   )r.   r/   rc   rI   r?   rG   r1   r1   r2   r  a  s    	
z,PlaylistEditView._update_playlist_basic_infoc                 C   sN   |j  D ]0}|j D ]}|j   q|j   q
|j    dS )z
        Clear existing playlist structure (windows, avails, ads).
        
        Args:
            playlist: Playlist model instance
        N)r   r   r   r   r  r  r1   r1   r2   r  ~  s
    z*PlaylistEditView._clear_playlist_structurec                 C   s&  |j d}|jd}d}|r"t|dkr"tt|D ]}|j d| dr<|j d| d}|j d| d}| d| d}|j d	| d}	| d|	 d}	d
}
tj|	|
tj||
 }tjt|d}|d}t	|||	|d}|
  tt|D ]
}|j d| d| dr|j d| d| d}| d| d}|j d| d| d}t||t|d tj d}|
  tt|D ]p}|j d| d| d| dr|j d| d| d| d}|d7 }t||t|d |d}|
  qqq<dS )z
        Recreate playlist structure from form data.
        Uses the same logic as the original new_playlist view.
        
        Args:
            request: Django HTTP request object
            playlist: Playlist model instance
        rC   r   r   rx   ry   rz    z:00r{   %Y-%m-%d %H:%M:%Sz%H:%M:%Sz%H%M%S00rh   r|   r}   r~   r   r   r   rU   	id_windowr   r   rP   r   )id_availid_adspot_idr   r   N)r7   r3   r^   r   rN   r   rP   rQ   rL   r#   r   r$   r%  r%   )r.   r/   rc   rH   rI   r   r   r   r|   r}   FMTr~   r   r   r   r   r   r   r   r   r1   r1   r2   r    sZ    	

" 
z-PlaylistEditView._recreate_playlist_structurec              	   C   sL   z:t dtj d|j d|j d|jj d}|  W n   Y nX dS )z
        Log playlist update activity.
        
        Args:
            user: User who updated the playlist
            playlist: Updated playlist instance
        zUpdate Playlistr   z updated playlist ID:  for channel activityr   r   N)r   rP   r%  usernamerh   r>   r   r   r.   r+   rc   r  r1   r1   r2   r    s    z%PlaylistEditView._log_update_activityc                 C   sD   z2t dtj d|j d|j dd}|  W n   Y nX dS )z
        Log playlist draft save activity.
        
        Args:
            user: User who saved the draft
            playlist: Draft playlist instance
        zSave Playlist Draftr   z saved playlist ID: z	 as draftr  N)r   rP   r%  r  rh   r   r  r1   r1   r2   r    s    z$PlaylistEditView._log_draft_activityN)r   r   r   r   r3   r;   r  r  r  r  r  r  r  r  r  r1   r1   r1   r2   r    s   ,!1#$Er  c                   @   sH   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dd Z
dS )PlaylistDeleteViewz~
    Class-based view for deleting playlists with proper authorization.
    Supports both AJAX and regular HTTP requests.
    c              
   C   s*  zt tjd||jd}| |s2| |dW S |j|jj	|j
|jd}t  | | W 5 Q R X | |j| |jddkrtdd|d  d	d
W S t|d|d  d	 tdW S W n^ tjk
r   | |d Y S  tk
r$ } z| |dt|  W Y S d}~X Y nX dS )z
        Handle playlist deletion.
        
        Args:
            request: Django HTTP request object
            playlist_id: ID of the playlist to delete
            
        Returns:
            JsonResponse or HttpResponse redirect
        rF   r  zMCannot delete this playlist. It may be currently active or have dependencies.)rr   rF   r^   r]   r   r   TzPlaylist for z deleted successfully.r   r6   zPlaylist not found.zError deleting playlist: N)r   r"   r,   r   r+   _can_delete_playlist_handle_delete_errorrh   r>   r   r^   r]   r   r  r  _log_deletion_activityr   r3   r   r   r   r   rJ   rK   rL   )r.   r/   r   rc   playlist_inforR   r1   r1   r2   r;     sB    



zPlaylistDeleteView.postc                 C   s>   t tjdd||jd}|| || |d}t|d|S )a  
        Handle GET request - show confirmation page.
        
        Args:
            request: Django HTTP request object
            playlist_id: ID of the playlist to delete
            
        Returns:
            Rendered confirmation template
        rF   ra   r  )rc   dependencies
can_deletezplaylists/confirm_delete.html)r   r"   r,   r   r+   _get_playlist_dependenciesr  r   )r.   r/   r   rc   r0   r1   r1   r2   r3   7  s    zPlaylistDeleteView.getc                 C   s&   t j }|j|kr"|jdkr"dS dS )z
        Check if playlist can be safely deleted.
        
        Args:
            playlist: Playlist model instance
            
        Returns:
            bool: True if playlist can be deleted
        r0  FT)rP   r   r   r^   rb   r  r1   r1   r2   r  P  s    
z'PlaylistDeleteView._can_delete_playlistc                 C   sf   |j  D ]"}|j D ]}|j   qq
|j  D ]}|j   q8|j    |  dS r  r  r  r1   r1   r2   r  d  s    
z+PlaylistDeleteView._delete_playlist_cascadec                 C   sf   |j  ddd}|j  D ]D}|j }|d  |7  < |j D ]}|d  |j 7  < qDq|S )z
        Get information about playlist dependencies.
        
        Args:
            playlist: Playlist model instance
            
        Returns:
            dict: Dependencies information
        r   )r   r   r   r   r   )r  r   r   r  r  )r.   rc   r  r   avails_countr   r1   r1   r2   r  |  s    
z-PlaylistDeleteView._get_playlist_dependenciesc                 C   sZ   zHt dtj d|j d|d  d|d  d|d  d		d
}|  W n   Y nX dS )z
        Log playlist deletion activity.
        
        Args:
            user: User who deleted the playlist
            playlist_info: Dictionary with playlist information
        zDelete Playlistr   z deleted playlist ID: rr   r  rF   z (Broadcast: r^   )r  N)r   rP   r%  r  r   )r.   r+   r  r  r1   r1   r2   r    s    *z)PlaylistDeleteView._log_deletion_activityc                 C   s6   |j ddkrtd|dS t|| tdS dS )a  
        Handle deletion errors for both AJAX and regular requests.
        
        Args:
            request: Django HTTP request object
            error_message: Error message string
            
        Returns:
            JsonResponse or HttpResponse redirect
        r   r   Fr   r6   Nr   r3   r   r   r:   r   r.   r/   r   r1   r1   r2   r    s    z'PlaylistDeleteView._handle_delete_errorN)r   r   r   r   r;   r3   r  r  r  r  r  r1   r1   r1   r2   r    s   :r  c                   @   sX   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dd Z
dd Zdd ZdS )PlaylistDuplicateViewzN
    Class-based view for duplicating playlists with all related objects.
    c           	   
   C   s  zJt tjdd||jd}|jd}|jdd dk}|rztj	|d
 }W q tk
r|   | |d	 Y W S X ntj
 tjd
d }| ||r| |d| dW S t  | |||}W 5 Q R X | |j|| |jddkr&tdd| d|jtd|jgddW S t|d| d td|jdW S W n` tjk
rp   | |d Y S  tk
r } z| |dt|  W Y S d}~X Y nX dS )z
        Handle playlist duplication.
        
        Args:
            request: Django HTTP request object
            playlist_id: ID of the playlist to duplicate
            
        Returns:
            JsonResponse or HttpResponse redirect
        rF   ra   r  r^   as_draftfalsetruer   zInvalid broadcast date format.rU   r  zA playlist already exists for z on this channel.r   r   Tz%Playlist duplicated successfully for .rf   )args)r   r   Znew_playlist_idredirect_urlrg   zOriginal playlist not found.zError duplicating playlist: N)r   r"   r,   r   r+   r7   r3   lowerrP   rQ   r   rO   _handle_duplicate_errorr   r&  _playlist_exists_for_dater   r  _duplicate_playlist_structure_log_duplication_activityr   r   rh   r	   r   r   r   rJ   rK   rL   )	r.   r/   r   original_playlistZnew_broadcast_dateZcopy_as_draftnew_datenew_playlistrR   r1   r1   r2   r;     sT    




zPlaylistDuplicateView.postc                 C   sN   t tjdd||jd}| |}||tj tj	dd d}t
|d|S )z
        Show duplication form.
        
        Args:
            request: Django HTTP request object
            playlist_id: ID of the playlist to duplicate
            
        Returns:
            Rendered duplication form template
        rF   ra   r  rU   r  )rc   suggested_datesZdefault_datezplaylists/duplicate_form.html)r   r"   r,   r   r+   _get_suggested_datesrP   r   r   r&  r   )r.   r/   r   rc   r  r0   r1   r1   r2   r3   
  s    
zPlaylistDuplicateView.getc                 C   s   t jj|j|j|d S )a  
        Check if a playlist already exists for the target date.
        
        Args:
            original_playlist: Original playlist instance
            target_date: Target broadcast date
            
        Returns:
            bool: True if playlist exists for the date
        )r>   rB   r^   )r"   r,   r-   r>   rB   exists)r.   r  Ztarget_dater1   r1   r2   r  &  s
    z/PlaylistDuplicateView._playlist_exists_for_datec              
   C   s  t |jd|| d| d|j|r&dnd|r0dndd}|  d}|j dD ]}| ||\}}t||||j	d}	|	  |j
 d	D ]j}
| |
|}t|	||
jtj d
}|  |
j dD ](}|d7 }t||j|j|d}|  qqqT|S )a0  
        Duplicate the entire playlist structure.
        
        Args:
            original_playlist: Original playlist instance
            new_date: New broadcast date
            as_draft: Whether to create as draft
            
        Returns:
            Playlists: New playlist instance
        r-  r  r  r0  )r>   r]   r^   r_   r`   rB   rb   rT   r   r  r  r  r  Zid_adspotsinAvailrU   )r  	id_adspotr   r   )r"   r>   rB   r   r  r   r   _calculate_new_window_timesr#   r~   r  _calculate_new_avail_startr$   r   rP   r%  r  r%   r  r   )r.   r  r  r  r  Ztraffic_counteroriginal_windowZnew_window_startZnew_window_end
new_windoworiginal_availZnew_avail_start	new_availZoriginal_adZnew_adr1   r1   r2   r  7  sV    


 z3PlaylistDuplicateView._duplicate_playlist_structurec                 C   sD   |j }|j}tj|| }tj|| }|d|dfS )a  
        Calculate new window start and end times for the new date.
        
        Args:
            original_window: Original window instance
            new_date: New broadcast date
            
        Returns:
            tuple: (new_start_time, new_end_time)
        r  )r|   r}   rP   r   ri   r   )r.   r  r  original_startZoriginal_end	new_startZnew_endr1   r1   r2   r  |  s    z1PlaylistDuplicateView._calculate_new_window_timesc                 C   s"   |j }tj|| }|dS )z
        Calculate new avail start time for the new date.
        
        Args:
            original_avail: Original avail instance
            new_date: New broadcast date
            
        Returns:
            str: New avail start time
        r  )r   rP   r   ri   r   )r.   r  r  r  r  r1   r1   r2   r    s    z0PlaylistDuplicateView._calculate_new_avail_startc                 C   sb   g }t j t jdd }tdD ]:}| ||s<|| |t jdd7 }t|dkr" q^q"|S )z
        Get suggested dates for duplication (next 7 days without existing playlists).
        
        Args:
            playlist: Original playlist instance
            
        Returns:
            list: List of suggested dates
        rU   r     r  )rP   r   r   r&  r   r  r   r~  )r.   rc   r  Zcurrent_dater   r1   r1   r2   r    s    

z*PlaylistDuplicateView._get_suggested_datesc                 C   sT   zBt dtj d|j d|j d|j d|j d	d}|  W n   Y nX dS )	z
        Log playlist duplication activity.
        
        Args:
            user: User who duplicated the playlist
            original_playlist: Original playlist instance
            new_playlist: New playlist instance
        zDuplicate Playlistr   z duplicated playlist ID: z to new playlist ID: z (New broadcast date: r  r  N)r   rP   r%  r  rh   r^   r   )r.   r+   r  r  r  r1   r1   r2   r    s    	$z/PlaylistDuplicateView._log_duplication_activityc                 C   s6   |j ddkrtd|dS t|| tdS dS )a  
        Handle duplication errors for both AJAX and regular requests.
        
        Args:
            request: Django HTTP request object
            error_message: Error message string
            
        Returns:
            JsonResponse or HttpResponse redirect
        r   r   Fr   r6   Nr  r  r1   r1   r2   r    s    z-PlaylistDuplicateView._handle_duplicate_errorN)r   r   r   r   r;   r3   r  r  r  r  r  r  r  r1   r1   r1   r2   r    s   FEr  )Eospytzjsonri   requestsrP   django.viewsr   	django.dbr   r   r   r   django.contribr   django.urlsr   r	   django.db.modelsr
   r   r   r   django.httpr   r   django.core.exceptionsr   django.utils.decoratorsr   django.views.decorators.csrfr   django.views.genericr   r   django.contrib.auth.mixinsr   django.contrib.auth.decoratorsr   django.views.decorators.httpr   django.shortcutsr   r   r   django.core.paginatorr   r   r   rest_framework.viewsr   apps.common.utilsr   apps.channels.modelsr    r!   apps.playlists.modelsr"   r#   r$   r%   apps.playlists.utilsr&   r'   r   r   r  r  r  r1   r1   r1   r2   <module>   st                  % 8        x I