diff --git a/data/demo.mv.db b/data/demo.mv.db index 53e4acaf43988a7bf54f4217885dc9ae2549665d..7454a2b717bca086ba694b4c3425baa4d43f456b 100644 Binary files a/data/demo.mv.db and b/data/demo.mv.db differ diff --git a/pom.xml b/pom.xml index ab27ed87124a7ee89738cfc700b7b6b851ac2594..d663acb49f56b24306e6bbe91ad5821bbd34cfa8 100644 --- a/pom.xml +++ b/pom.xml @@ -169,6 +169,13 @@ <version>3.6.0</version> <!-- Alebo najnovšiu verziu --> </dependency> + <dependency> + <groupId>org.webjars</groupId> + <artifactId>bootstrap</artifactId> + <version>5.1.3</version> + </dependency> + + </dependencies> diff --git a/src/main/java/com/dre0059/articleprocessor/controller/StatisticsController.java b/src/main/java/com/dre0059/articleprocessor/controller/StatisticsController.java new file mode 100644 index 0000000000000000000000000000000000000000..6bf4eae4831f42945f9ec28b00eed431117d703e --- /dev/null +++ b/src/main/java/com/dre0059/articleprocessor/controller/StatisticsController.java @@ -0,0 +1,42 @@ +package com.dre0059.articleprocessor.controller; + +import com.dre0059.articleprocessor.model.Dokument; +import com.dre0059.articleprocessor.repository.DocumentRepository; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +import java.util.*; +import java.util.stream.Collectors; + +@Controller +public class StatisticsController { + + private final DocumentRepository documentRepository; + + public StatisticsController(DocumentRepository documentRepository) { + this.documentRepository = documentRepository; + } + + @GetMapping("/statistics") + public String statistics(Model model) { + List<Dokument> documents = documentRepository.findAll(); + + // Status count + Map<String, Long> statusCount = documents.stream() + .collect(Collectors.groupingBy(Dokument::getStatus, Collectors.counting())); + + // odfiltruj PDF ktorĂ© nemajĂş kategoriu + Map<String, Long> categoryCount = documents.stream() + .filter(doc -> doc.getCategory() != null) // đź’ˇ Tu je fix + .collect(Collectors.groupingBy( + doc -> doc.getCategory().getName(), + Collectors.counting() + )); + + model.addAttribute("statusCount", statusCount); + model.addAttribute("categoryCount", categoryCount); + + return "statistics"; + } +} diff --git a/src/main/resources/templates/statistics.html b/src/main/resources/templates/statistics.html new file mode 100644 index 0000000000000000000000000000000000000000..18fa6da95e0cb7d99f40d7c7364d3fc6102e4f1b --- /dev/null +++ b/src/main/resources/templates/statistics.html @@ -0,0 +1,210 @@ +<!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <meta charset="UTF-8"> + <title>Document Statistics</title> + <script src="https://cdn.plot.ly/plotly-2.24.1.min.js"></script> + <link rel="stylesheet" th:href="@{/webjars/bootstrap/5.1.3/css/bootstrap.min.css}"> + <link rel="icon" type="image/x-icon" href="assets/favicon.ico" /> + + <link rel="stylesheet" th:href="@{/styles.css}" /> + <style> + .navbar-nav .nav-link { + font-size: 1.2rem; /* zväčšenie pĂsma */ + margin-right: 20px; /* medzera medzi poloĹľkami */ + color: rgb(51, 102, 255); /* farba textu v menu */ + } + + /* Zmena farby pri hover */ + .navbar-nav .nav-link:hover { + color: #00134d; + } + + .navbar-nav .nav-link.active { + color: rgb(51, 102, 255) !important; /* prepĂše bootstrap ÄŤiernu */ + } + + .navbar-nav .nav-link.active:hover { + color: #00134d !important; + } + + + h2 { + color: #ffffff; + text-align: center; + margin-top: 30px; + } + + .chart-container { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + gap: 20px; + margin-top: 30px; + } + + .chart-box { + flex: 1; + min-width: 400px; + height: 500px; + background-color: #6780a3; + border-radius: 12px; + padding: 15px; + overflow: hidden; + } + + .chart-box:hover { + transform: scale(1.01); + transition: transform 0.2s ease-in-out; + } + + .chart-box .js-plotly-plot { + position: absolute; + top: 0; + left: 0; + width: 100% !important; /* natiahne graf presne na šĂrku boxu */ + height: 100% !important; /* natiahne graf presne na výšku boxu */ + } + </style> +</head> +<body> +<!-- Navbar --> +<nav class="navbar navbar-expand-lg navbar-light bg-light static-top"> + <div class="container"> + <a class="navbar-brand" href="/upload">Article Processor</a> + <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" + aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> + <span class="navbar-toggler-icon"></span> + </button> + <div class="collapse navbar-collapse justify-content-end" id="navbarNav"> + <ul class="navbar-nav"> + <li class="nav-item"> + <a class="nav-link" href="/upload">Upload</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="/view">View all</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="/statistics">Statistics</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="/upload">About project</a> + </li> + </ul> + </div> + </div> +</nav> + +<!-- Page Content --> +<header class="masthead"> + <div class="container position-relative"> + <div class="row justify-content-center"> + <div class="col-xl-8"> + <div class="text-center text-white"> + <h1 class="mb-4">📊 Document Statistics</h1> + </div> + </div> + </div> + </div> +</header> + + +<div class="container mt-5"> + <div class="chart-container"> + <div id="statusChart" class="chart-box"></div> + <div id="categoryChart" class="chart-box"></div> + </div> +</div> + +<script th:inline="javascript"> + const statusData = [[${statusCount}]]; + const categoryData = [[${categoryCount}]]; + + const statusLabels = Object.keys(statusData); + const statusValues = Object.values(statusData); + + const categoryLabels = Object.keys(categoryData); + const categoryValues = Object.values(categoryData); + + // 🟡 PIE chart (animated) + const pieTrace = { + type: 'pie', + labels: statusLabels, + values: statusValues.map(() => 0), + hole: 0.3, + marker: { + colors: ['#1c375e', '#d4af37', '#66ccff', '#ffff66'] + } + }; + + const pieLayout = { + title: 'Documents by Status', + paper_bgcolor: '#6780a3', + font: { color: '#ffffff' } + }; + + Plotly.newPlot('statusChart', [pieTrace], pieLayout, { responsive: true }) + .then(() => { + let step = 0; + const totalSteps = 60; + + const animate = () => { + step++; + const interpolatedValues = statusValues.map(v => v * (step / totalSteps)); + Plotly.restyle('statusChart', { values: [interpolatedValues] }); + + if (step < totalSteps) { + setTimeout(animate, 15); + } + }; + + animate(); + }); + + // 🔵 BAR chart (animated) + const maxY = Math.max(...categoryValues); + const traceBar = { + type: 'bar', + x: categoryLabels, + y: categoryValues.map(() => 0), + marker: { + color: 'rgba(102, 255, 204, 0.7)', + line: { color: '#66ffff', width: 1.5 } + } + }; + + const layoutBar = { + title: 'Documents by Category', + paper_bgcolor: '#6780a3', + plot_bgcolor: '#6780a3', + font: { color: '#ffffff' }, + xaxis: { + title: '', + tickangle: -45, + automargin: true + }, + yaxis: { + title: 'Count', + range: [0, maxY * 1.1] + } + }; + + Plotly.newPlot('categoryChart', [traceBar], layoutBar, { responsive: true }) + .then(() => { + const update = { data: [{ y: categoryValues }] }; + setTimeout(() => { + Plotly.animate('categoryChart', update, { + frame: { duration: 800, redraw: true }, + transition: { duration: 500, easing: 'cubic-in-out' } + }); + }, 500); + }); +</script> + +<footer class="footer bg-light mt-5"> + <div class="container text-center"> + <p class="text-muted small">© Eliška Kozáčiková 2025. All Rights Reserved.</p> + </div> +</footer> +</body> +</html> diff --git a/src/main/resources/templates/upload.html b/src/main/resources/templates/upload.html index 180dcc82df3537db8d7db0f03d8a292fd3ccb6a7..4170ed7fb8a47ef34e990da932887278bcb58e0b 100644 --- a/src/main/resources/templates/upload.html +++ b/src/main/resources/templates/upload.html @@ -37,6 +37,7 @@ </style> </head> <body> + <nav class="navbar navbar-expand-lg navbar-light bg-light static-top"> <div class="container"> <a class="navbar-brand" href="/upload">Article Processor</a> @@ -53,7 +54,7 @@ <a class="nav-link" href="/view">View all</a> </li> <li class="nav-item"> - <a class="nav-link" href="/upload">Statistics</a> + <a class="nav-link" href="/statistics">Statistics</a> </li> <li class="nav-item"> <a class="nav-link" href="/upload">About project</a> @@ -63,6 +64,7 @@ </div> </nav> + <header class="masthead"> <div class="container position-relative"> <div class="row justify-content-center"> diff --git a/src/main/resources/templates/view-all.html b/src/main/resources/templates/view-all.html index d6a22d07ecb3fef093324a32391ad924eecd6724..f53c22bfd8c7deb62d55509b4276bfb89867f4a5 100644 --- a/src/main/resources/templates/view-all.html +++ b/src/main/resources/templates/view-all.html @@ -39,7 +39,7 @@ <body> -<!-- Navbar --> +<!-- navbar --> <nav class="navbar navbar-expand-lg navbar-light bg-light static-top"> <div class="container"> <a class="navbar-brand" href="/upload">Article Processor</a> @@ -53,10 +53,13 @@ <a class="nav-link" href="/upload">Upload</a> </li> <li class="nav-item"> - <a class="nav-link active" href="/view">View all</a> + <a class="nav-link" href="/view">View all</a> </li> <li class="nav-item"> - <a class="nav-link" href="/">About project</a> + <a class="nav-link" href="/statistics">Statistics</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="/upload">About project</a> </li> </ul> </div> @@ -141,5 +144,11 @@ </script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script> + +<footer class="footer bg-light mt-5"> + <div class="container text-center"> + <p class="text-muted small">© Eliška Kozáčiková 2025. All Rights Reserved.</p> + </div> +</footer> </body> </html> diff --git a/src/main/resources/templates/view-pdf.html b/src/main/resources/templates/view-pdf.html index 5ba8eab3f7943976b070525bc110453509cc599c..1723fdb8ad973d7474b2e7bba6828c15e8ab72da 100644 --- a/src/main/resources/templates/view-pdf.html +++ b/src/main/resources/templates/view-pdf.html @@ -39,7 +39,7 @@ <body> <!-- Navbar --> -<nav class="navbar navbar-expand-lg navbar-light bg-light"> +<nav class="navbar navbar-expand-lg navbar-light bg-light static-top"> <div class="container"> <a class="navbar-brand" href="/upload">Article Processor</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" @@ -55,7 +55,10 @@ <a class="nav-link" href="/view">View all</a> </li> <li class="nav-item"> - <a class="nav-link" href="/">About project</a> + <a class="nav-link" href="/upload">Statistics</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="/upload">About project</a> </li> </ul> </div>