| <!DOCTYPE html>
|
| <html lang="en">
|
|
|
| <head>
|
| <meta charset="UTF-8">
|
| <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| <title>Diamond AI / Results</title>
|
| <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
| </head>
|
|
|
| <body>
|
| <canvas id="canvas3d" style="opacity: 0.8"></canvas>
|
| <div class="atmospheric-grain"></div>
|
|
|
| <div class="container animate-up">
|
| <nav style="padding: 0.5rem 0;">
|
| <div class="nav-logo">RESULTS / DASHBOARD</div>
|
| <div class="nav-meta">
|
| <span>STATUS: COMPLETE</span>
|
| </div>
|
| </nav>
|
|
|
| <header style="display: flex; justify-content: space-between; align-items: center; margin: 0.5rem 0;">
|
| <div>
|
| <p class="label-mini">Batch Processing Overview</p>
|
| <h1 style="font-size: 2.5rem; line-height: 1;">ANALYTICS <span class="italic">REPORT</span></h1>
|
| </div>
|
| <div style="display: flex; gap: 0.8rem;">
|
| <a href="/" class="btn-launch"
|
| style="text-decoration: none; padding: 0.6rem 1.2rem; font-size: 9px; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.5); color: white; opacity: 1;">←
|
| NEW BATCH</a>
|
| <a href="/download/{{ report_file }}" class="btn-launch"
|
| style="text-decoration: none; padding: 0.6rem 1.2rem; font-size: 9px;">EXPORT DATASET</a>
|
| </div>
|
| </header>
|
|
|
| {% if metrics %}
|
|
|
| <div class="dashboard-grid">
|
|
|
| <div class="dashboard-main">
|
|
|
| <div class="kpi-row">
|
| <div class="kpi-card">
|
| <span class="kpi-value">{{ (metrics.accuracy * 100)|round(1) }}%</span>
|
| <span class="kpi-label">ACCURACY</span>
|
| </div>
|
| <div class="kpi-card">
|
| <span class="kpi-value">{{ metrics.f1|round(3) }}</span>
|
| <span class="kpi-label">WEIGHTED F1</span>
|
| </div>
|
| <div class="kpi-card">
|
| <span class="kpi-value">{{ metrics.macro_f1|round(3) }}</span>
|
| <span class="kpi-label">MACRO F1</span>
|
| </div>
|
| </div>
|
|
|
|
|
| <div class="compact-table-section">
|
| <p class="section-label">CLASS-WISE METRICS</p>
|
| <div class="compact-table-wrapper">
|
| <table class="compact-table">
|
| <thead>
|
| <tr>
|
| <th>GRADE</th>
|
| <th>PREC</th>
|
| <th>RECALL</th>
|
| <th>F1</th>
|
| <th>SUP</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| {% for item in metrics.class_metrics %}
|
| <tr>
|
| <td class="grade-col">{{ item.label }}</td>
|
| <td>{{ item.precision }}</td>
|
| <td>{{ item.recall }}</td>
|
| <td>{{ item.f1 }}</td>
|
| <td class="sup-col">{{ item.support }}</td>
|
| </tr>
|
| {% endfor %}
|
| </tbody>
|
| </table>
|
| </div>
|
| </div>
|
| </div>
|
|
|
|
|
| <div class="dashboard-sidebar">
|
| <p class="section-label">CONFUSION MATRIX</p>
|
| <div class="cm-compact">
|
| <table class="cm-compact-table">
|
| <thead>
|
| <tr>
|
| <th class="cm-corner">A\P</th>
|
| {% for label in metrics.confusion_matrix.labels %}
|
| <th>{{ label }}</th>
|
| {% endfor %}
|
| </tr>
|
| </thead>
|
| <tbody>
|
| {% for row_idx in range(metrics.confusion_matrix.matrix|length) %}
|
| <tr>
|
| <td class="cm-row-label">{{ metrics.confusion_matrix.labels[row_idx] }}</td>
|
| {% for col_idx in range(metrics.confusion_matrix.matrix[row_idx]|length) %}
|
| {% set val = metrics.confusion_matrix.matrix[row_idx][col_idx] %}
|
| <td
|
| class="cm-cell {% if row_idx == col_idx %}cm-diag{% endif %} {% if val == 0 %}cm-zero{% endif %}">
|
| {{ val }}
|
| </td>
|
| {% endfor %}
|
| </tr>
|
| {% endfor %}
|
| </tbody>
|
| </table>
|
| </div>
|
|
|
|
|
| <div class="summary-mini">
|
| <div class="summary-mini-card">
|
| <span class="summary-mini-label">MACRO F1</span>
|
| <span class="summary-mini-value">{{ metrics.macro_f1|round(3) }}</span>
|
| </div>
|
| <div class="summary-mini-card">
|
| <span class="summary-mini-label">ACCURACY</span>
|
| <span class="summary-mini-value">{{ (metrics.accuracy * 100)|round(1) }}%</span>
|
| </div>
|
| </div>
|
| </div>
|
| </div>
|
| {% endif %}
|
|
|
|
|
| <section class="manifest-section">
|
| <div class="manifest-header" onclick="toggleManifest()">
|
| <p class="section-label" style="margin: 0;">INFERENCE MANIFEST</p>
|
| <span class="toggle-icon" id="manifest-toggle">▼</span>
|
| </div>
|
| <div class="manifest-content" id="manifest-content" style="display: block;">
|
| <div class="table-wrapper" style="max-height: 350px; margin-top: 0.5rem;">
|
| <table>
|
| <thead>
|
| <tr>
|
| <th>STONE ID</th>
|
| {% for feature in model_features %}
|
| <th>{{ feature }}</th>
|
| {% endfor %}
|
| <th>AI RESULT</th>
|
| {% for col in out_of_box_cols %}
|
| <th>{{ col }}</th>
|
| {% endfor %}
|
| <th>IMG</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| {% for row in report_data %}
|
| <tr>
|
| <td class="stone-id-col">{{ row.L_Code }}</td>
|
| {% for feature in model_features %}
|
| <td style="opacity: 0.7; font-size: 11px;">{{ row[feature] }}</td>
|
| {% endfor %}
|
| <td class="result-col">{{ row.Predicted_FGrdCol }}</td>
|
| {% for col in out_of_box_cols %}
|
| <td style="font-size: 10px;">
|
| {% if row[col] and row[col]|string != 'nan' %}
|
| <span class="badge-oob">{{ row[col] }}</span>
|
| {% else %}
|
| <span style="opacity: 0.2;">-</span>
|
| {% endif %}
|
| </td>
|
| {% endfor %}
|
| <td>
|
| {% if row.Image_Path != 'N/A' %}
|
| <img src="/image/{{ row.Image_Path }}" class="img-thumb"
|
| onclick="openModal(this.src)" alt="Diamond">
|
| {% else %}
|
| <span style="font-size: 8px; opacity: 0.3;">N/A</span>
|
| {% endif %}
|
| </td>
|
| </tr>
|
| {% endfor %}
|
| </tbody>
|
| </table>
|
| </div>
|
| </div>
|
| </section>
|
|
|
| <footer style="margin-top: 1rem; padding-bottom: 1rem; text-align: center;">
|
| <a href="/"
|
| style="font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; color: white; opacity: 0.5; text-decoration: none;">←
|
| NEW BATCH</a>
|
| </footer>
|
| </div>
|
|
|
|
|
| <div id="image-modal">
|
| <span class="modal-close" onclick="closeModal()">×</span>
|
| <img id="modal-img" src="" alt="Enlarged View">
|
| </div>
|
|
|
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
| <script>
|
| function toggleManifest() {
|
| const content = document.getElementById('manifest-content');
|
| const toggle = document.getElementById('manifest-toggle');
|
| if (content.style.display === 'none') {
|
| content.style.display = 'block';
|
| toggle.textContent = '▼';
|
| } else {
|
| content.style.display = 'none';
|
| toggle.textContent = '▶';
|
| }
|
| }
|
|
|
| function openModal(src) {
|
| const modal = document.getElementById('image-modal');
|
| const modalImg = document.getElementById('modal-img');
|
| modal.classList.add('active');
|
| modalImg.src = src;
|
| }
|
|
|
| function closeModal() {
|
| const modal = document.getElementById('image-modal');
|
| modal.classList.remove('active');
|
| }
|
|
|
| document.getElementById('image-modal').onclick = function (e) {
|
| if (e.target === this) closeModal();
|
| };
|
|
|
|
|
| const vertexShader = `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`;
|
| const fragmentShader = `
|
| uniform float uTime;
|
| varying vec2 vUv;
|
| vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }
|
| float snoise(vec2 v){
|
| const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
|
| vec2 i = floor(v + dot(v, C.yy) );
|
| vec2 x0 = v - i + dot(i, C.xx);
|
| vec2 i1; i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
| vec4 x12 = x0.xyxy + C.xxzz; x12.xy -= i1;
|
| i = mod(i, 289.0);
|
| vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + i.x + vec3(0.0, i1.x, 1.0 ));
|
| vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
|
| m = m*m ; m = m*m ;
|
| vec3 x = 2.0 * fract(p * C.www) - 1.0;
|
| vec3 h = abs(x) - 0.5;
|
| vec3 ox = floor(x + 0.5);
|
| vec3 a0 = x - ox;
|
| m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
|
| vec3 g; g.x = a0.x * x0.x + h.x * x0.y; g.yz = a0.yz * x12.xz + h.yz * x12.yw;
|
| return 130.0 * dot(m, g);
|
| }
|
| void main() {
|
| vec2 uv = vUv;
|
| float n = snoise(uv * 1.5 + uTime * 0.03);
|
| vec3 color = mix(vec3(0.0, 0.0, 0.0), vec3(0.05, 0.02, 0.1), n * 0.5 + 0.5);
|
| gl_FragColor = vec4(color, 1.0);
|
| }
|
| `;
|
| const scene = new THREE.Scene();
|
| const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
| const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('canvas3d'), antialias: true });
|
| renderer.setSize(window.innerWidth, window.innerHeight);
|
| const geometry = new THREE.PlaneGeometry(2, 2);
|
| const uniforms = { uTime: { value: 0 } };
|
| const material = new THREE.ShaderMaterial({ vertexShader, fragmentShader, uniforms });
|
| scene.add(new THREE.Mesh(geometry, material));
|
| const animate = (t) => {
|
| uniforms.uTime.value = t * 0.001;
|
| renderer.render(scene, camera);
|
| requestAnimationFrame(animate);
|
| };
|
| requestAnimationFrame(animate);
|
| window.onresize = () => renderer.setSize(window.innerWidth, window.innerHeight);
|
| </script>
|
| </body>
|
|
|
| </html> |