U
     hp                     @   s  d Z ddlmZmZmZmZ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ZddlZddlZeeZde_ed	d
ejd< G dd deZeejdde_dZdhZdZeejd< eejd< dZdZ dd Z!dd Z"dZ#dZ$e%ddd Z&ej%d d!d"gd#d$d% Z'ej%d&d!d"gd#e"d'd( Z(ej%d)d"gd#e"d*d+ Z)e%d,d-d. Z*ed/krej+d0d1d2d3 dS )4z_
Flask Web Interface for Image Restyling
Single-file application with login and restyling form
    )Flaskrender_template_stringrequestredirecturl_forsessionflashjsonify)	timedeltadatetimewraps)ProxyFix)secure_filenameNz'darveys-image-restyling-secret-key-2024   )ZdaysZPERMANENT_SESSION_LIFETIMEc                   @   s   e Zd ZdddZdd ZdS )PrefixMiddleware c                 C   s   || _ || _d S )N)appprefix)selfr   r    r   app.py__init__   s    zPrefixMiddleware.__init__c                 C   sV   |d  | jr@|d t| jd  |d< | j|d< | ||S |ddg dgS d S )NZ	PATH_INFOZSCRIPT_NAMEZ404)zContent-Typez
text/plains	   Not Found)
startswithr   lenr   )r   environZstart_responser   r   r   __call__   s    
zPrefixMiddleware.__call__N)r   )__name__
__module____qualname__r   r   r   r   r   r   r      s   
r   z/image-restyling)r   z/var/www/html/Open-AI/images_inzipi  @UPLOAD_FOLDERZMAX_CONTENT_LENGTHZdarveyszdarveys@openai9876c                 C   s    d| ko|  ddd  tkS )N.   )rsplitlowerALLOWED_EXTENSIONS)filenamer   r   r   allowed_file0   s    r(   c                    s   t   fdd}|S )Nc                     s(   dt krtdd ttdS  | |S )N	logged_inz!Please login to access this page.Zwarninglogin)r   r   r   r   )argskwargsfr   r   decorated_function5   s    
z*login_required.<locals>.decorated_functionr   )r.   r/   r   r-   r   login_required4   s    r0   a  
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login - Image Restyling</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .login-container {
            background: white;
            padding: 40px;
            border-radius: 10px;
            box-shadow: 0 10px 40px rgba(0,0,0,0.2);
            width: 100%;
            max-width: 400px;
        }
        h1 {
            color: #333;
            margin-bottom: 10px;
            text-align: center;
        }
        .subtitle {
            color: #666;
            text-align: center;
            margin-bottom: 30px;
            font-size: 14px;
        }
        .form-group {
            margin-bottom: 20px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            color: #333;
            font-weight: 500;
        }
        input[type="text"],
        input[type="password"] {
            width: 100%;
            padding: 12px;
            border: 2px solid #ddd;
            border-radius: 5px;
            font-size: 14px;
            transition: border-color 0.3s;
        }
        input[type="text"]:focus,
        input[type="password"]:focus {
            outline: none;
            border-color: #667eea;
        }
        .btn {
            width: 100%;
            padding: 12px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            transition: transform 0.2s;
        }
        .btn:hover {
            transform: translateY(-2px);
        }
        .btn:active {
            transform: translateY(0);
        }
        .alert {
            padding: 12px;
            border-radius: 5px;
            margin-bottom: 20px;
            font-size: 14px;
        }
        .alert-danger {
            background: #fee;
            color: #c33;
            border: 1px solid #fcc;
        }
        .alert-success {
            background: #efe;
            color: #3c3;
            border: 1px solid #cfc;
        }
        .error {
            color: #c33;
            font-size: 12px;
            margin-top: 5px;
            display: none;
        }
    </style>
</head>
<body>
    <div class="login-container">
        <h1>Image Restyling</h1>
        <p class="subtitle">Login to access the restyling interface</p>

        {% with messages = get_flashed_messages(with_categories=true) %}
            {% if messages %}
                {% for category, message in messages %}
                    <div class="alert alert-{{ category }}">{{ message }}</div>
                {% endfor %}
            {% endif %}
        {% endwith %}

        <form method="POST" onsubmit="return validateLogin()">
            <div class="form-group">
                <label for="username">Username</label>
                <input type="text" id="username" name="username" required>
                <div class="error" id="username-error">Username is required</div>
            </div>
            <div class="form-group">
                <label for="password">Password</label>
                <input type="password" id="password" name="password" required>
                <div class="error" id="password-error">Password is required</div>
            </div>
            <button type="submit" class="btn">Login</button>
        </form>
    </div>

    <script>
        function validateLogin() {
            let isValid = true;

            const username = document.getElementById('username').value.trim();
            const password = document.getElementById('password').value;

            // Reset errors
            document.getElementById('username-error').style.display = 'none';
            document.getElementById('password-error').style.display = 'none';

            // Validate username
            if (username === '') {
                document.getElementById('username-error').style.display = 'block';
                isValid = false;
            }

            // Validate password
            if (password === '') {
                document.getElementById('password-error').style.display = 'block';
                isValid = false;
            }

            return isValid;
        }
    </script>
</body>
</html>
u
<  
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image Restyling Dashboard</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: #f5f5f5;
            min-height: 100vh;
        }
        .navbar {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 15px 30px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .navbar h1 {
            font-size: 24px;
        }
        .logout-btn {
            padding: 8px 20px;
            background: rgba(255,255,255,0.2);
            color: white;
            border: 2px solid white;
            border-radius: 5px;
            cursor: pointer;
            text-decoration: none;
            font-weight: 600;
            transition: background 0.3s;
        }
        .logout-btn:hover {
            background: rgba(255,255,255,0.3);
        }
        .container {
            max-width: 800px;
            margin: 40px auto;
            padding: 0 20px;
        }
        .card {
            background: white;
            padding: 40px;
            border-radius: 10px;
            box-shadow: 0 5px 20px rgba(0,0,0,0.1);
        }
        h2 {
            color: #333;
            margin-bottom: 10px;
        }
        .subtitle {
            color: #666;
            margin-bottom: 30px;
            font-size: 14px;
        }
        .form-group {
            margin-bottom: 25px;
        }
        label {
            display: block;
            margin-bottom: 8px;
            color: #333;
            font-weight: 600;
            font-size: 14px;
        }
        textarea {
            width: 100%;
            padding: 12px;
            border: 2px solid #ddd;
            border-radius: 5px;
            font-size: 14px;
            font-family: inherit;
            resize: vertical;
            min-height: 120px;
            transition: border-color 0.3s;
        }
        textarea:focus {
            outline: none;
            border-color: #667eea;
        }
        select {
            width: 100%;
            padding: 12px;
            border: 2px solid #ddd;
            border-radius: 5px;
            font-size: 14px;
            cursor: pointer;
            background: white;
            transition: border-color 0.3s;
        }
        select:focus {
            outline: none;
            border-color: #667eea;
        }
        .button-group {
            display: flex;
            gap: 15px;
            margin-top: 30px;
        }
        .btn {
            flex: 1;
            padding: 12px;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            transition: transform 0.2s;
        }
        .btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        .btn-secondary {
            background: #6c757d;
            color: white;
        }
        .btn:hover {
            transform: translateY(-2px);
        }
        .btn:active {
            transform: translateY(0);
        }
        .error {
            color: #c33;
            font-size: 12px;
            margin-top: 5px;
            display: none;
        }
        .alert {
            padding: 12px;
            border-radius: 5px;
            margin-bottom: 20px;
            font-size: 14px;
        }
        .alert-success {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        .alert-danger {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        .alert-warning {
            background: #fff3cd;
            color: #856404;
            border: 1px solid #ffeaa7;
        }
        .char-count {
            font-size: 12px;
            color: #666;
            text-align: right;
            margin-top: 5px;
        }
        .file-upload-wrapper {
            position: relative;
            overflow: hidden;
            display: inline-block;
            width: 100%;
        }
        .file-upload-input {
            display: none;
        }
        .file-upload-label {
            display: block;
            padding: 12px;
            background: #f8f9fa;
            border: 2px dashed #ddd;
            border-radius: 5px;
            text-align: center;
            cursor: pointer;
            transition: all 0.3s;
        }
        .file-upload-label:hover {
            border-color: #667eea;
            background: #f0f4ff;
        }
        .file-name-display {
            margin-top: 10px;
            font-size: 14px;
            color: #666;
        }
        .upload-progress {
            margin-top: 10px;
            display: none;
        }
        .progress-bar {
            width: 100%;
            height: 25px;
            background: #f0f0f0;
            border-radius: 5px;
            overflow: hidden;
        }
        .progress-bar-fill {
            height: 100%;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            transition: width 0.3s;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 12px;
            font-weight: 600;
        }
        .upload-info {
            margin-top: 10px;
            padding: 10px;
            background: #e8f5e9;
            border-radius: 5px;
            font-size: 13px;
            color: #2e7d32;
            display: none;
        }
        .btn-primary:disabled {
            background: #ccc;
            cursor: not-allowed;
            transform: none;
        }
    </style>
</head>
<body>
    <div class="navbar">
        <h1>Image Restyling Dashboard</h1>
        <a href="{{ url_for('logout') }}" class="logout-btn">Logout</a>
    </div>

    <div class="container">
        <div class="card">
            <h2>Restyle Images</h2>
            <p class="subtitle">Configure your image restyling parameters</p>

            {% with messages = get_flashed_messages(with_categories=true) %}
                {% if messages %}
                    {% for category, message in messages %}
                        <div class="alert alert-{{ category }}">{{ message }}</div>
                    {% endfor %}
                {% endif %}
            {% endwith %}

            <form method="POST" onsubmit="return validateForm()">
                <div class="form-group">
                    <label for="zipfile">Upload Images (ZIP file) *</label>
                    <div class="file-upload-wrapper">
                        <input type="file" id="zipfile" name="zipfile" class="file-upload-input" accept=".zip" onchange="handleFileSelect(event)">
                        <label for="zipfile" class="file-upload-label">
                            <span>📁 Click to select ZIP file (Max 20 MB)</span>
                        </label>
                    </div>
                    <div class="file-name-display" id="file-name-display"></div>
                    <div class="upload-progress" id="upload-progress">
                        <div class="progress-bar">
                            <div class="progress-bar-fill" id="progress-bar-fill" style="width: 0%">0%</div>
                        </div>
                    </div>
                    <div class="upload-info" id="upload-info"></div>
                    <div class="error" id="file-error"></div>
                </div>

                <div class="form-group">
                    <label for="prompt">Restyling Prompt *</label>
                    <textarea id="prompt" name="prompt" placeholder="Enter your restyling instructions here..." maxlength="1000" oninput="updateCharCount()"></textarea>
                    <div class="char-count"><span id="char-count">0</span>/1000 characters</div>
                    <div class="error" id="prompt-error">Prompt is required (minimum 10 characters)</div>
                </div>

                <div class="form-group">
                    <label for="quality">Quality *</label>
                    <select id="quality" name="quality">
                        <option value="">-- Select Quality --</option>
                        <option value="high">High</option>
                        <option value="medium">Medium</option>
                    </select>
                    <div class="error" id="quality-error">Please select a quality option</div>
                </div>

                <div class="button-group">
                    <button type="submit" id="submit-btn" class="btn btn-primary" disabled>Submit</button>
                    <button type="button" class="btn btn-secondary" onclick="resetForm()">Cancel</button>
                </div>
            </form>
        </div>
    </div>

    <script>
        let uploadedTimestamp = null;

        function handleFileSelect(event) {
            const file = event.target.files[0];
            const fileError = document.getElementById('file-error');
            const fileNameDisplay = document.getElementById('file-name-display');

            // Reset error
            fileError.style.display = 'none';
            fileError.textContent = '';

            if (!file) {
                return;
            }

            // Validate file type
            if (!file.name.toLowerCase().endsWith('.zip')) {
                fileError.textContent = 'Only ZIP files are allowed';
                fileError.style.display = 'block';
                event.target.value = '';
                return;
            }

            // Validate file size (20 MB)
            const maxSize = 20 * 1024 * 1024;
            if (file.size > maxSize) {
                fileError.textContent = 'File size must not exceed 20 MB';
                fileError.style.display = 'block';
                event.target.value = '';
                return;
            }

            // Display file name
            fileNameDisplay.textContent = `Selected: ${file.name} (${(file.size / 1024 / 1024).toFixed(2)} MB)`;

            // Upload file via AJAX
            uploadFile(file);
        }

        function uploadFile(file) {
            const formData = new FormData();
            formData.append('zipfile', file);

            const uploadProgress = document.getElementById('upload-progress');
            const progressBarFill = document.getElementById('progress-bar-fill');
            const uploadInfo = document.getElementById('upload-info');
            const fileError = document.getElementById('file-error');

            // Show progress bar
            uploadProgress.style.display = 'block';
            uploadInfo.style.display = 'none';

            const xhr = new XMLHttpRequest();

            // Upload progress
            xhr.upload.addEventListener('progress', function(e) {
                if (e.lengthComputable) {
                    const percentComplete = Math.round((e.loaded / e.total) * 100);
                    progressBarFill.style.width = percentComplete + '%';
                    progressBarFill.textContent = percentComplete + '%';
                }
            });

            // Upload complete
            xhr.addEventListener('load', function() {
                if (xhr.status === 200) {
                    const response = JSON.parse(xhr.responseText);
                    if (response.success) {
                        uploadedTimestamp = response.timestamp;
                        uploadInfo.innerHTML = `✓ ${response.message}<br>Extracted ${response.file_count} images`;
                        uploadInfo.style.display = 'block';

                        // Enable submit button
                        document.getElementById('submit-btn').disabled = false;
                    } else {
                        fileError.textContent = response.message;
                        fileError.style.display = 'block';
                        uploadProgress.style.display = 'none';
                    }
                } else {
                    const response = JSON.parse(xhr.responseText);
                    fileError.textContent = response.message || 'Upload failed';
                    fileError.style.display = 'block';
                    uploadProgress.style.display = 'none';
                }
            });

            // Upload error
            xhr.addEventListener('error', function() {
                fileError.textContent = 'Upload failed. Please try again.';
                fileError.style.display = 'block';
                uploadProgress.style.display = 'none';
            });

            // Send request
            xhr.open('POST', '{{ url_for("upload_images") }}', true);
            xhr.send(formData);
        }

        function updateCharCount() {
            const prompt = document.getElementById('prompt').value;
            document.getElementById('char-count').textContent = prompt.length;
        }

        function validateForm() {
            let isValid = true;

            const prompt = document.getElementById('prompt').value.trim();
            const quality = document.getElementById('quality').value;

            // Reset errors
            document.getElementById('prompt-error').style.display = 'none';
            document.getElementById('quality-error').style.display = 'none';

            // Validate prompt
            if (prompt === '' || prompt.length < 10) {
                document.getElementById('prompt-error').style.display = 'block';
                isValid = false;
            }

            // Validate quality
            if (quality === '') {
                document.getElementById('quality-error').style.display = 'block';
                isValid = false;
            }

            return isValid;
        }

        function resetForm() {
            if (confirm('Are you sure you want to clear the form?')) {
                // Reset file upload
                document.getElementById('zipfile').value = '';
                document.getElementById('file-name-display').textContent = '';
                document.getElementById('upload-progress').style.display = 'none';
                document.getElementById('upload-info').style.display = 'none';
                document.getElementById('file-error').style.display = 'none';
                uploadedTimestamp = null;

                // Reset form fields
                document.getElementById('prompt').value = '';
                document.getElementById('quality').value = '';
                updateCharCount();

                // Disable submit button
                document.getElementById('submit-btn').disabled = true;

                // Hide any error messages
                document.getElementById('prompt-error').style.display = 'none';
                document.getElementById('quality-error').style.display = 'none';
            }
        }
    </script>
</body>
</html>
/c                   C   s    dt krttdS ttdS )Nr)   	dashboardr*   )r   r   r   r   r   r   r   index  s    r3   z/loginZGETPOST)methodsc                  C   s   dt krttdS tjdkrtjdd } tjdd}| rF|sXtdd t	t
S | tkr|tkrd	t _d	t d< | t d< td
d ttdS tdd t	t
S t	t
S )Nr)   r2   r4   usernamer   passwordz#Username and password are required.dangerTzLogin successful! Welcome back.successz/Invalid username or password. Please try again.)r   r   r   r   methodformgetstripr   r   LOGIN_TEMPLATEUSERNAMEPASSWORDZ	permanent)r6   r7   r   r   r   r*     s"    



r*   z
/dashboardc                  C   s   t jdkrt jdd } t jdd }g }| rBt| dk rL|d t| dkrb|d |d	krt|d
 |r|D ]}t|d q|tt	S td| dd tt	S tt	S )Nr4   promptr   quality
   z+Prompt must be at least 10 characters long.i  z'Prompt must not exceed 1000 characters.)ZhighZmediumz%Please select a valid quality option.r8   z*Restyling job submitted successfully with z	 quality!r9   )
r   r:   r;   r<   r=   r   appendr   r   DASHBOARD_TEMPLATE)rA   rB   errorserrorr   r   r   r2     s"    



r2   z/upload-imagesc                  C   s  zdt jkr"tddddfW S t jd } | jdkrJtddddfW S t| jshtddddfW S t d	}tj	
tjd
 |}tj|dd t| j}tj	
||}| | dddddddh}g }t|d}|jD ]}| rqtj	|jd  }	|	|krtj	|j}
||8}tj	
||
}t|d}||  W 5 Q R X W 5 Q R X ||
 qW 5 Q R X t| d| d}tj	
||}t|ddd8}t|}|dg t|D ]}||g qW 5 Q R X |t d< tdd|t!|ddfW S  tj"k
r8   tdd ddf Y S  t#k
rz } z$tdd!t$| dd"f W Y S d#}~X Y nX d#S )$z%Handle ZIP file upload and processingzipfileFzNo file uploaded)r9   messagei  r   zNo file selectedzOnly ZIP files are allowedz%Y%m%d_%H%M%Sr!   T)exist_okz.jpgz.jpegz.pngz.gifz.bmpz.tiffz.webprr#   wbzcatalog-z.csvw)newliner'   Zupload_timestampzImages uploaded successfully)r9   rI   	timestampZ
file_count   zInvalid ZIP filezError processing file: i  N)%r   filesr	   r'   r(   r   Znowstrftimeospathjoinr   configmakedirsr   ZsaverH   ZZipFileZfilelistis_dirsplitextr%   basenameopenwritereadrD   removecsvwriterZwriterowsortedr   r   Z
BadZipFile	Exceptionstr)filerO   Ztimestamp_folderr'   Ztemp_zip_pathZimage_extensionsZextracted_filesZzip_ref	file_infoZfile_extbase_filenamesourceZtarget_pathtargetZcsv_filenameZcsv_pathZcsvfiler`   er   r   r   upload_images  sb    






"

rj   z/logoutc                   C   s   t   tdd ttdS )Nz&You have been logged out successfully.r9   r*   )r   clearr   r   r   r   r   r   r   logout<  s    
rl   __main__Tz0.0.0.0i  )debugZhostZport),__doc__Zflaskr   r   r   r   r   r   r   r	   r   r
   	functoolsr   Zwerkzeug.middleware.proxy_fixr   Zwerkzeug.utilsr   rS   rH   r_   r   r   Z
secret_keyrV   objectr   Zwsgi_appr!   r&   ZMAX_FILE_SIZEr?   r@   r(   r0   r>   rE   Zrouter3   r*   r2   rj   rl   runr   r   r   r   <module>   sT   (


 $   G

S

