Spaces:
Sleeping
Sleeping
| {% extends "base.html" %} | |
| {% block title %}NeetPrep Incorrect Questions{% endblock %} | |
| {% block head %} | |
| <style> | |
| .name-column { | |
| word-wrap: break-word; | |
| white-space: normal; | |
| max-width: 250px; | |
| } | |
| @media (max-width: 768px) { | |
| .name-column { | |
| max-width: 25%; | |
| } | |
| } | |
| .table-responsive { | |
| overflow-x: auto; | |
| } | |
| th:first-child, td:first-child { | |
| position: sticky; | |
| left: 0; | |
| z-index: 2; | |
| background-color: #212529; | |
| } | |
| th:nth-child(2), td:nth-child(2) { | |
| position: sticky; | |
| left: 40px; | |
| z-index: 2; | |
| background-color: #212529; | |
| } | |
| </style> | |
| {% endblock %} | |
| {% block content %} | |
| <div class="container-fluid mt-5" style="width: 90%; margin: auto;"> | |
| <div class="card bg-dark text-white"> | |
| <div class="card-header"> | |
| <h2>NeetPrep Incorrect Question Manager</h2> | |
| </div> | |
| <div class="card-body"> | |
| {% if neetprep_enabled %} | |
| <div class="d-flex flex-wrap gap-2 align-items-center mb-3"> | |
| <button id="sync-btn" class="btn btn-primary">Sync/Fetch Latest Incorrect Questions</button> | |
| <div class="form-check"> | |
| <input class="form-check-input" type="checkbox" id="force-sync-checkbox"> | |
| <label class="form-check-label" for="force-sync-checkbox"> | |
| Force full re-sync | |
| </label> | |
| </div> | |
| </div> | |
| <div class="d-flex flex-wrap gap-2 align-items-center"> | |
| <button id="classify-btn" class="btn btn-warning">Classify Unclassified Questions</button> | |
| <span class="ms-3">({{ unclassified_count }} unclassified)</span> | |
| </div> | |
| {% else %} | |
| <div class="alert alert-info" role="alert"> | |
| NeetPrep Sync is currently disabled. You can enable it in <a href="{{ url_for('settings.settings') }}" class="alert-link">Settings</a>. | |
| </div> | |
| {% endif %} | |
| <div id="sync-status" class="mt-3"></div> | |
| <div id="pdf-link-container" class="mt-3"></div> | |
| <hr class="my-4"> | |
| <h4>Filter by Subject</h4> | |
| <form id="subject-filter-form" method="get" action="{{ url_for('neetprep_bp.index') }}" class="mb-3"> | |
| <div class="row"> | |
| <div class="col-md-4"> | |
| <select name="subject" id="subject" class="form-select" onchange="this.form.submit()"> | |
| {% for subject in available_subjects %} | |
| <option value="{{ subject }}" {% if subject == selected_subject %}selected{% endif %}>{{ subject }}</option> | |
| {% endfor %} | |
| </select> | |
| </div> | |
| </div> | |
| </form> | |
| <h4>Generate PDF</h4> | |
| <form id="generate-pdf-form"> | |
| <div class="d-flex flex-wrap gap-2 mb-3"> | |
| {% if neetprep_enabled %} | |
| <button type="submit" name="pdf_type" value="all" class="btn btn-success">Generate PDF of All Incorrect Questions</button> | |
| <a href="/neetprep/edit" class="btn btn-secondary">Edit Synced Questions</a> | |
| {% endif %} | |
| <button type="submit" name="pdf_type" value="quiz" class="btn btn-primary">Start Quiz</button> | |
| <a href="/classified/edit" class="btn btn-secondary">Edit Classified Questions</a> | |
| </div> | |
| <h5>Or, Generate by Topic:</h5> | |
| <div class="table-responsive" style="max-height: 400px;"> | |
| <table class="table table-sm table-hover table-striped"> | |
| <thead> | |
| <tr> | |
| <th scope="col" style="width: 3%;">S.No.</th> | |
| <th scope="col" style="width: 3%;"></th> | |
| <th scope="col" class="name-column">Topic</th> | |
| {% if neetprep_enabled %}<th>NeetPrep Questions</th>{% endif %} | |
| <th>My Questions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="topic-list"> | |
| {% if topics %} | |
| {% for item in topics %} | |
| <tr> | |
| <td>{{ loop.index }}</td> | |
| <td><input class="form-check-input" type="checkbox" value="{{ item.topic }}"></td> | |
| <td class="name-column">{{ item.topic }}</td> | |
| {% if neetprep_enabled %}<td><span class="badge bg-secondary">{{ item.neetprep_count }}</span></td>{% endif %} | |
| <td><span class="badge bg-info">{{ item.my_questions_count }}</span></td> | |
| </tr> | |
| {% endfor %} | |
| {% else %} | |
| <tr> | |
| <td colspan="{% if neetprep_enabled %}5{% else %}4{% endif %}"><i>No topics found. {% if neetprep_enabled %}Sync with NeetPrep to fetch questions and topics.{% else %}No classified topics found.{% endif %}</i></td> | |
| </tr> | |
| {% endif %} | |
| </tbody> | |
| </table> | |
| </div> | |
| <button type="submit" name="pdf_type" value="selected" class="btn btn-info mt-2" {% if not topics %}disabled{% endif %}>Generate PDF for Selected Topics</button> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| {% endblock %} | |
| {% block scripts %} | |
| <script> | |
| function loadSettings() { | |
| const settings = JSON.parse(localStorage.getItem('pdfGeneratorSettings')); | |
| console.log("Loaded settings from localStorage:", settings); | |
| if (settings) { | |
| return settings; | |
| } else { | |
| console.log("No settings found in localStorage, using default."); | |
| return { | |
| images_per_page: 4, | |
| orientation: 'portrait', | |
| grid_rows: 4, | |
| grid_cols: 1, | |
| practice_mode: 'none' | |
| }; | |
| } | |
| } | |
| // Only attach event listeners if NeetPrep is enabled | |
| {% if neetprep_enabled %} | |
| document.getElementById('sync-btn').addEventListener('click', async () => { | |
| const syncStatus = document.getElementById('sync-status'); | |
| const forceSync = document.getElementById('force-sync-checkbox').checked; | |
| syncStatus.innerHTML = '<div class="alert alert-info">Syncing... This may take a while.</div>'; | |
| try { | |
| const response = await fetch('/neetprep/sync', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ force: forceSync }) | |
| }); | |
| const result = await response.json(); | |
| if (response.ok) { | |
| syncStatus.innerHTML = `<div class="alert alert-success">${result.status}</div>`; | |
| // Reload the page to show updated topics | |
| setTimeout(() => window.location.reload(), 2000); | |
| } else { | |
| syncStatus.innerHTML = `<div class="alert alert-danger">Error: ${result.error || 'Unknown error'}</div>`; | |
| } | |
| } catch (error) { | |
| syncStatus.innerHTML = `<div class="alert alert-danger">Request failed: ${error}</div>`; | |
| } | |
| }); | |
| document.getElementById('classify-btn').addEventListener('click', async () => { | |
| const syncStatus = document.getElementById('sync-status'); | |
| syncStatus.innerHTML = '<div class="alert alert-info">Classification started... This will take time due to rate limits.</div>'; | |
| try { | |
| const response = await fetch('/neetprep/classify', { method: 'POST' }); | |
| const result = await response.json(); | |
| if (response.ok) { | |
| syncStatus.innerHTML = `<div class="alert alert-success">${result.status}</div>`; | |
| setTimeout(() => window.location.reload(), 2000); | |
| } else { | |
| syncStatus.innerHTML = `<div class="alert alert-danger">Error: ${result.error || 'Unknown error'}</div>`; | |
| } | |
| } catch (error) { | |
| syncStatus.innerHTML = `<div class="alert alert-danger">Request failed: ${error}</div>`; | |
| } | |
| }); | |
| {% endif %} | |
| document.getElementById('generate-pdf-form').addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const pdfType = e.submitter.value; | |
| const statusDiv = document.getElementById('sync-status'); | |
| const settings = loadSettings(); | |
| if (pdfType === 'quiz') { | |
| const selectedTopics = Array.from(document.querySelectorAll('#topic-list input[type="checkbox"]:checked')).map(cb => cb.value); | |
| if (selectedTopics.length === 0) { | |
| statusDiv.innerHTML = '<div class="alert alert-warning">Please select at least one topic for the quiz.</div>'; | |
| return; | |
| } | |
| // For quiz, we do a full page navigation, so we create a form and submit it. | |
| const form = document.createElement('form'); | |
| form.method = 'POST'; | |
| form.action = '/neetprep/generate'; | |
| const typeInput = document.createElement('input'); | |
| typeInput.type = 'hidden'; | |
| typeInput.name = 'type'; | |
| typeInput.value = 'quiz'; | |
| form.appendChild(typeInput); | |
| const topicsInput = document.createElement('input'); | |
| topicsInput.type = 'hidden'; | |
| topicsInput.name = 'topics'; | |
| topicsInput.value = JSON.stringify(selectedTopics); | |
| form.appendChild(topicsInput); | |
| document.body.appendChild(form); | |
| form.submit(); | |
| return; // Stop the rest of the function | |
| } | |
| // Existing logic for PDF generation | |
| let payload = { | |
| type: pdfType, | |
| layout: { | |
| images_per_page: settings.images_per_page, | |
| orientation: settings.orientation, | |
| grid_rows: settings.grid_rows, | |
| grid_cols: settings.grid_cols, | |
| practice_mode: settings.practice_mode | |
| } | |
| }; | |
| if (pdfType === 'selected') { | |
| const selectedTopics = Array.from(document.querySelectorAll('#topic-list input[type="checkbox"]:checked')).map(cb => cb.value); | |
| if (selectedTopics.length === 0) { | |
| statusDiv.innerHTML = '<div class="alert alert-warning">Please select at least one topic.</div>'; | |
| return; | |
| } | |
| payload.topics = selectedTopics; | |
| } | |
| console.log("Payload sent to server:", payload); | |
| statusDiv.innerHTML = '<div class="alert alert-info">Generating PDF...</div>'; | |
| try { | |
| const response = await fetch('/neetprep/generate', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(payload) | |
| }); | |
| const result = await response.json(); | |
| if (response.ok && result.success) { | |
| statusDiv.innerHTML = '<div class="alert alert-success">PDF generated!</div>'; | |
| if(result.pdf_url) { | |
| const pdfLinkContainer = document.getElementById('pdf-link-container'); | |
| const link = document.createElement('a'); | |
| link.href = result.pdf_url; | |
| link.textContent = 'View Generated PDF'; | |
| link.className = 'btn btn-outline-light'; | |
| link.target = '_blank'; | |
| pdfLinkContainer.innerHTML = ''; // Clear previous links | |
| pdfLinkContainer.appendChild(link); | |
| window.open(result.pdf_url, '_blank'); | |
| } | |
| } else { | |
| statusDiv.innerHTML = `<div class="alert alert-danger">Error: ${result.error || 'PDF generation failed.'}</div>`; | |
| } | |
| } catch (error) { | |
| statusDiv.innerHTML = `<div class="alert alert-danger">Request failed: ${error}</div>`; | |
| } | |
| }); | |
| </script> | |
| {% endblock %} |