Merge branch 'master' of gitea.joethei.xyz:/Studium/Softwareprojektmanagement into HEAD

This commit is contained in:
Johannes Theiner 2019-05-28 16:57:17 +02:00
commit e73edbbd21
25 changed files with 641 additions and 210 deletions

View File

@ -33,8 +33,6 @@ public class ShoppingCart implements Analysis<Map<List<String>, List<String>>> {
result = new HashMap<>(); result = new HashMap<>();
Apriori model = new Apriori();
for (int i = 0; i < 11; i++) { for (int i = 0; i < 11; i++) {
instances.deleteAttributeAt(0); instances.deleteAttributeAt(0);
} }
@ -60,6 +58,8 @@ public class ShoppingCart implements Analysis<Map<List<String>, List<String>>> {
e.printStackTrace(); e.printStackTrace();
} }
Apriori model = new Apriori();
model.setTreatZeroAsMissing(true); model.setTreatZeroAsMissing(true);
try { try {
model.buildAssociations(instances); model.buildAssociations(instances);

View File

@ -36,30 +36,20 @@ public class WeekOverview implements Analysis<Map<String, Map.Entry<Double, Inte
return result; return result;
} }
result = new HashMap<>(); result = new HashMap<>();
int dayIndex = Attribute.SHOPPING_DAY;
int amountIndex = Attribute.PURCHASE_AMOUNT;
int startArticles = Attribute.POWER_TOOLS;
int endArticles = Attribute.GARDENING_TOOLS + 1;
for (int i = 0; i < instances.numInstances(); i++) { for (int i = 0; i < instances.numInstances(); i++) {
Instance instance = instances.get(i); Instance instance = instances.get(i);
double amount = instance.value(amountIndex); double amount = instance.value(Attribute.PURCHASE_AMOUNT);
String day = instance.stringValue(dayIndex); String day = instance.stringValue(Attribute.SHOPPING_DAY);
int count = 0;
for (int j = startArticles; j < endArticles; j++) {
count += (int) instance.value(j);
}
if (!result.containsKey(day)) { if (!result.containsKey(day)) {
result.put(day, new AbstractMap.SimpleEntry<>(0.0, 0)); result.put(day, new AbstractMap.SimpleEntry<>(0.0, 0));
} }
result.put(day, result.put(day,
new AbstractMap.SimpleEntry<>( new AbstractMap.SimpleEntry<>(
result.get(day).getKey() + amount, result.get(day).getKey() + amount,
result.get(day).getValue() + count result.get(day).getValue() + 1
) )
); );
} }

View File

@ -1,6 +1,12 @@
package de.hsel.spm.baudas.web; package de.hsel.spm.baudas.web;
import javax.servlet.*; import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter; import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -19,17 +25,19 @@ public class AuthenticationFilter implements Filter{
/** /**
* Called by the web container to indicate to a filter that it is being placed into service. This filter manages the authentication. * Called by the web container to indicate to a filter that it is being placed into service. This filter manages the authentication.
* @param fConfig This parameter provides access to everything the code needs to work. *
* @param filterConfig This parameter provides access to everything the code needs to work.
* @throws ServletException Defines a general exception a servlet can throw when it encounters difficulty. * @throws ServletException Defines a general exception a servlet can throw when it encounters difficulty.
*/ */
public void init(FilterConfig fConfig) throws ServletException { public void init(FilterConfig filterConfig) throws ServletException {
this.context = fConfig.getServletContext(); this.context = filterConfig.getServletContext();
this.context.log("AuthenticationFilter initialized"); this.context.log("AuthenticationFilter initialized");
} }
/** /**
* The doFilter method of the Filter is called by the container each time a request/response pair is passed through the chain due to a client request for a resource at the end of the chain. * The doFilter method of the Filter is called by the container each time a request/response pair is passed through the chain due to a client request for a resource at the end of the chain.
*
* @param request This parameter provides access to everything the code needs to work. * @param request This parameter provides access to everything the code needs to work.
* @param response This parameter provides access to everything the code needs to issue a response. * @param response This parameter provides access to everything the code needs to issue a response.
* @param chain This parameter allows passing request along the chain of potential handlers until one of them handles the request. * @param chain This parameter allows passing request along the chain of potential handlers until one of them handles the request.

View File

@ -0,0 +1,47 @@
package de.hsel.spm.baudas.web;
import com.google.gson.Gson;
import de.hsel.spm.baudas.analysis.Cluster;
import org.jetbrains.annotations.NotNull;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* changes data from cluster analysis into a readable format
*
* @author Julian Hinxlage
* @version 0.1
* @since 0.1
**/
@WebServlet("/clusters")
public class ClusterDiagram extends HttpServlet {
@Override
protected void doGet(@NotNull HttpServletRequest req, @NotNull HttpServletResponse resp) throws IOException {
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
resp.setContentType("application/json");
PrintWriter out = resp.getWriter();
Map<Integer, Map<String, String>> result = new HashMap<>();
Gson gson = new Gson();
if (req.getParameter("id") == null) {
out.print(gson.toJson(result));
return;
}
UUID uuid = UUID.fromString(req.getParameter("id"));
File file = Data.get(uuid);
Cluster cluster = new Cluster(file);
result = cluster.getResult();
out.print(gson.toJson(result));
}
}

View File

@ -0,0 +1,11 @@
package de.hsel.spm.baudas.web;
public class Columns {
private String col1; //NOPMD
private String col2; //NOPMD
public Columns(String col1, String col2){
this.col1 = col1;
this.col2 = col2;
}
}

View File

@ -10,8 +10,12 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/** /**
* manages data about .csv files. * manages data about .csv files.
@ -23,7 +27,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
public class Data { public class Data {
@Getter @Getter
private static ConcurrentLinkedQueue<SavedFile> files = new ConcurrentLinkedQueue<>(); private static ConcurrentMap<String, List<SavedFile>> files = new ConcurrentHashMap<>();
/** /**
@ -33,44 +37,57 @@ public class Data {
* @return path to save file to * @return path to save file to
*/ */
@NotNull @NotNull
static Path add(@NotNull String name) { static Path add(@NotNull String name, @NotNull String session) {
//cleanup old files UUID uuid = UUID.randomUUID();
if (files.isEmpty()) {
if (!files.containsKey(session))
files.put(session, new ArrayList<>());
if (files.get(session).size() >= 5) {
SavedFile file = files.get(session).iterator().next();
if (!get(file.getUuid()).delete()) {
System.err.println("failed to delete file..." + file);
}
files.get(session).remove(file);
}
files.get(session).add(new SavedFile(uuid, name, LocalDateTime.now()));
return Paths.get(getFileName(uuid));
}
/**
* delete all files for session.
*
* @param session session id
*/
static void delete(String session) {
for (SavedFile file : files.get(session)) {
if (!get(file.getUuid()).delete())
System.err.println("failed to delete file..." + file);
}
files.remove(session);
//deleting all files older than x days.
int days = 30;
try { try {
Files.list(Paths.get("")).forEach(path -> { Files.list(Paths.get("")).forEach(path -> {
if (path.toFile().getName().endsWith(".csv")) { if (path.toFile().getName().endsWith("*.csv")) {
long diff = new Date().getTime() - path.toFile().lastModified();
if (diff > (long) days * 24 * 60 * 60 * 1000) {
try { try {
Files.delete(path); Files.delete(path);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
}
}); });
} catch (IOException ex) { } catch (IOException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
} }
Path path;
UUID uuid = UUID.randomUUID();
if (files.size() >= 5) {
//remove last and add new one
SavedFile file = files.poll();
if (!get(file.getUuid()).delete()) {
System.out.println("failed to delete file...");
}
path = Paths.get(getFileName(uuid));
files.offer(new SavedFile(uuid, name, LocalDateTime.now()));
} else {
files.add(new SavedFile(uuid, name, LocalDateTime.now()));
path = Paths.get(getFileName(uuid));
}
return path;
}
/** /**
* generates File from uuid. * generates File from uuid.
* *

View File

@ -0,0 +1,35 @@
package de.hsel.spm.baudas.web;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* all possible error codes.
*
* @author Julian Hinxlage
* @version 0.1
*/
@Getter
@AllArgsConstructor
public enum ErrorCode {
FILE_NOT_FOUND("Die hochgeladene Datei konnte nicht gefunden werden"),
EMPTY_FILE("Die hochgeladene Datei ist leer"),
INVALID_FORMAT("Die hochgeladene Datei hat ein falsches Format");
private String message;
/**
* is there a error code with this name ?.
*
* @param message string to search for
* @return error code exits ?
*/
public static boolean exists(String message) {
for (ErrorCode code : values()) {
System.out.println(message + " == " + code.name());
if (code.name().equals(message))
return true;
}
return false;
}
}

View File

@ -31,6 +31,6 @@ public class Files extends HttpServlet {
resp.setCharacterEncoding(StandardCharsets.UTF_8.name()); resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
PrintWriter out = resp.getWriter(); PrintWriter out = resp.getWriter();
Gson gson = new Gson(); Gson gson = new Gson();
out.print(gson.toJson(Data.getFiles())); out.print(gson.toJson(Data.getFiles().get(req.getSession().getId())));
} }
} }

View File

@ -23,6 +23,7 @@ public class LoginServlet extends HttpServlet {
/** /**
* This is a Servlet that manages the Login and creates Sessions. * This is a Servlet that manages the Login and creates Sessions.
*
* @param request This parameter provides access to everything the code needs to work. * @param request This parameter provides access to everything the code needs to work.
* @param response This parameter provides access to everything the code needs to issue a response. * @param response This parameter provides access to everything the code needs to issue a response.
* @throws ServletException Defines a general exception a servlet can throw when it encounters difficulty. * @throws ServletException Defines a general exception a servlet can throw when it encounters difficulty.

View File

@ -1,12 +1,11 @@
package de.hsel.spm.baudas.web; package de.hsel.spm.baudas.web;
import java.io.IOException;
import javax.servlet.annotation.WebServlet; import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import java.io.IOException;
/** /**
* Servlet implementation class LogoutServlet. * Servlet implementation class LogoutServlet.
@ -19,14 +18,16 @@ public class LogoutServlet extends HttpServlet {
/** /**
* This is a Servlet that manages the Logout and deletes Sessions. * This is a Servlet that manages the Logout and deletes Sessions.
*
* @param request This parameter provides access to everything the code needs to work. * @param request This parameter provides access to everything the code needs to work.
* @param response This parameter provides access to everything the code needs to issue a response. * @param response This parameter provides access to everything the code needs to issue a response.
* @throws IOException Signals that an I/O exception of some sort has occurred. This class is the general class of exceptions produced by failed or interrupted I/O operations. * @throws IOException Signals that an I/O exception of some sort has occurred. This class is the general class of exceptions produced by failed or interrupted I/O operations.
*/ */
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
HttpSession session = request.getSession(false); HttpSession session = request.getSession();
if (session != null) { if (session != null) {
Data.delete(session.getId());
session.invalidate(); session.invalidate();
} }
response.sendRedirect(request.getContextPath() + "/"); response.sendRedirect(request.getContextPath() + "/");

View File

@ -0,0 +1,64 @@
package de.hsel.spm.baudas.web;
import com.google.gson.Gson;
import de.hsel.spm.baudas.analysis.ShoppingCart;
import org.jetbrains.annotations.NotNull;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* Changes data to readable format for table
*
* @author Karsten Eden
* @version 0.1
* @since 0.1
*/
@WebServlet("/shopping_cart")
public class ShoppingCartDiagram extends HttpServlet {
private static final long serialVersionUID = 5026732432605473505L;
protected void doGet(@NotNull HttpServletRequest req, @NotNull HttpServletResponse resp) throws IOException {
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
resp.setContentType("application/json");
PrintWriter out = resp.getWriter();
Gson gson = new Gson();
List<Columns> colList = new ArrayList<>();
if (req.getParameter("id") == null) {
out.print(gson.toJson(colList));
return;
}
UUID uuid = UUID.fromString(req.getParameter("id"));
File file = Data.get(uuid);
ShoppingCart cart = new ShoppingCart(file);
Map<List<String>, List<String>> result = new HashMap<>();
result = cart.getResult();
List<String> col1 = new ArrayList<>();
List<String> col2 = new ArrayList<>();
for(Map.Entry<List<String>, List<String>> entry : result.entrySet()){
col1.add(entry.getKey().toString());
col2.add(entry.getValue().toString());
}
for(int i = 0; i < col1.size(); i++ ){
colList.add(new Columns(col1.get(i).replaceAll("\\[", "").replaceAll("\\]", ""), col2.get(i).replaceAll("\\[", "").replaceAll("\\]", "")));
}
out.print(gson.toJson(colList));
}
}

View File

@ -12,9 +12,15 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/** /**
* top/flop articles list diagram.
*
* @author Johannes Theiner * @author Johannes Theiner
* @version 0.1 * @version 0.1
* @since 0.1 * @since 0.1

View File

@ -1,6 +1,7 @@
package de.hsel.spm.baudas.web; package de.hsel.spm.baudas.web;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import weka.core.converters.CSVLoader;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.MultipartConfig;
@ -20,7 +21,7 @@ import java.nio.file.StandardCopyOption;
/** /**
* saves uploaded files. * saves uploaded files.
* *
* @author Johannes Theiner * @author Johannes Theiner, Julian Hinxlage
* @version 0.1 * @version 0.1
* @since 0.1 * @since 0.1
**/ **/
@ -31,24 +32,71 @@ public class Upload extends HttpServlet {
private static final long serialVersionUID = 14144111845151L; private static final long serialVersionUID = 14144111845151L;
/**
* let weka check if the format is valid.
*
* @param stream input stream
* @return format valid
*/
private boolean checkFormat(InputStream stream) {
CSVLoader loader = new CSVLoader();
try {
loader.setSource(stream);
return loader.getDataSet() != null;
} catch (Exception e) {
return false;
}
}
@Override @Override
protected void doPost(@NotNull HttpServletRequest req, HttpServletResponse resp) protected void doPost(@NotNull HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
throws ServletException, IOException {
req.setCharacterEncoding(StandardCharsets.UTF_8.name()); req.setCharacterEncoding(StandardCharsets.UTF_8.name());
Part filePart = req.getPart("file"); Part filePart = req.getPart("file");
if (filePart == null) {
resp.sendRedirect("error.jsp?code=" + ErrorCode.FILE_NOT_FOUND);
return;
}
if (filePart.getSubmittedFileName() == null) {
resp.sendRedirect("error.jsp?code=" + ErrorCode.FILE_NOT_FOUND);
return;
}
String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString();
if (fileName == null) {
resp.sendRedirect("error.jsp?code=" + ErrorCode.FILE_NOT_FOUND);
return;
}
InputStream inputStream = filePart.getInputStream(); InputStream inputStream = filePart.getInputStream();
if (inputStream == null) {
resp.sendRedirect("error.jsp?code=" + ErrorCode.FILE_NOT_FOUND);
return;
}
if (inputStream.available() == 0) {
resp.sendRedirect("error.jsp?code=" + ErrorCode.EMPTY_FILE);
return;
}
Path path = Data.add(fileName); if (!checkFormat(inputStream)) {
resp.sendRedirect("error.jsp?code=" + ErrorCode.INVALID_FORMAT);
return;
} else {
inputStream = filePart.getInputStream();
}
Path path = Data.add(fileName, req.getSession().getId());
if (!Files.exists(path)) { if (!Files.exists(path)) {
Files.createFile(path); Files.createFile(path);
} }
Files.copy(inputStream, path, StandardCopyOption.REPLACE_EXISTING); Files.copy(inputStream, path, StandardCopyOption.REPLACE_EXISTING);
resp.sendRedirect(""); resp.sendRedirect("");
} }
} }

View File

@ -0,0 +1,13 @@
<%@include file="head.jsp"%>
<!--Navbar-->
<div class="navbar-fixed">
<nav class="nav-extended blue-grey darken-3 white-text row">
<a href="#" class="brand-logo hide-on-small-and-down center">BauDas</a>
<!--Navbar Items-->
<div class="nav-wrapper col 2 right">
</div>
</nav>
</div>
<br>

32
src/main/webapp/error.jsp Normal file
View File

@ -0,0 +1,32 @@
<%@ page import="de.hsel.spm.baudas.web.ErrorCode" %>
<%@ page import="java.nio.charset.StandardCharsets" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.Arrays" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" %>
<%@include file="emptyHeader.jsp"%>
<% response.setCharacterEncoding(StandardCharsets.UTF_8.name()); %>
<% request.setCharacterEncoding(StandardCharsets.UTF_8.name()); %>
<%
String code = request.getParameter("code");
if(code != null && ErrorCode.exists(code)){
ErrorCode errorCode = ErrorCode.valueOf(code);
%>
<div class="row">
<div class="col s4 push-s4">
<div class="card-panel red darken-1">
<h5 class="white-text center-align">
<%= errorCode.getMessage()%>
</h5>
</div>
</div>
</div>
<%
}
%>
<%@include file="theming.jsp"%>

View File

@ -1,24 +1,18 @@
<!--Anfang Skriptbereich--> <!--Anfang Skriptbereich-->
<!--Script für Diagramme--> <!--Script für Diagramme-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.min.js" integrity="sha256-xKeoJ50pzbUGkpQxDYHD7o7hxe0LaOGeguUidbq6vis=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.min.js"
integrity="sha256-xKeoJ50pzbUGkpQxDYHD7o7hxe0LaOGeguUidbq6vis=" crossorigin="anonymous"></script>
<!--Script für Materlialize-->
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script> <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script> <%@include file="theming.jsp"%>
<script src="https://cdn.jsdelivr.net/npm/google-palette@1.1.0/palette.min.js"></script>
<script>
M.AutoInit();
</script>
<script src="js/cache.js"></script> <script src="js/cache.js"></script>
<script src="js/week_overview.js"></script> <script src="js/week_overview.js"></script>
<script src="js/shopping_times.js"></script> <script src="js/shopping_times.js"></script>
<script src="js/top_flop.js"></script> <script src="js/top_flop.js"></script>
<script src="js/clusters.js"></script>
<%--<script src="js/sold_articles.js"></script>--%> <script src="js/shopping_cart.js"></script>
<!--Ende Skriptbereich--> <!--Ende Skriptbereich-->
</body> </body>

21
src/main/webapp/head.jsp Normal file
View File

@ -0,0 +1,21 @@
<% response.setCharacterEncoding(StandardCharsets.UTF_8.name()); %>
<% request.setCharacterEncoding(StandardCharsets.UTF_8.name()); %>
<!DOCTYPE html>
<html lang="de">
<head>
<title>BauDas</title>
<!--Import Google Icon Font-->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!--Import materialize.css-->
<link type="text/css" rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css"
media="screen,projection"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.css"
integrity="sha256-aa0xaJgmK/X74WM224KMQeNQC2xYKwlAt08oZqjeF0E=" crossorigin="anonymous"/>
<meta charset="UTF-8">
<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=utf-8">
<!--Let browser know website is optimized for mobile-->
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>

View File

@ -1,12 +1,12 @@
<%@ page import="java.nio.charset.StandardCharsets" %> <%@ page import="java.nio.charset.StandardCharsets" %>
<%@ page contentType="text/html;charset=UTF-8" %> <%@ page contentType="text/html;charset=UTF-8" %>
<%@include file="header.jsp"%> <%@include file="menu.jsp" %>
<% response.setCharacterEncoding(StandardCharsets.UTF_8.name()); %> <% response.setCharacterEncoding(StandardCharsets.UTF_8.name()); %>
<% request.setCharacterEncoding(StandardCharsets.UTF_8.name()); %> <% request.setCharacterEncoding(StandardCharsets.UTF_8.name()); %>
<!--Cards--> <!--Cards-->
<!--Analyse-Cards--> <!--Analyse-Cards-->
<div id="analysis" class="row container"> <div id="analysis" class="row container black-text">
<!--Übersicht--> <!--Übersicht-->
<div class="col s12 m12"> <div class="col s12 m12">
<div id="overview" class="card white"> <div id="overview" class="card white">
@ -16,7 +16,7 @@
<div class="card-content"> <div class="card-content">
<div> <div>
<canvas id="overview_chart" width="1000" height="400"></canvas> <canvas class="black-text" id="overview_chart" width="1000" height="400"></canvas>
</div> </div>
</div> </div>
</div> </div>
@ -63,70 +63,54 @@
</div> </div>
</div> </div>
<!--Warenkorbanlyse-->
<div id="shopping_card_analysis" class="col s12 m6">
<div class="card white">
<div class="card-content">
<div class="center">
<span class="card-title center">Warenkorbanalyse</span>
</div>
<div>
<p>Coming Soon</p>
</div>
</div>
</div>
</div>
<!--Gruppenübersicht--> <!--Gruppenübersicht-->
<div class="col s12 m6"> <div class="col s12 m12">
<div id="group_overview" class="card white"> <div id="group_overview" class="card white">
<div class="card-content"> <div class="card-content">
<div class="center"> <div class="center">
<span class="card-title center">Gruppenübersicht</span> <span class="card-title center">Gruppenübersicht</span>
</div> </div>
<div> <div>
<p>Coming Soon</p> <table id="cluster_table">
</table>
</div> </div>
</div> </div>
<!--Warenkorbanlyse-->
<div id="shopping_card_analysis" class="col s12 m12">
<div class="card white">
<div class="center">
<span class="card-title center">Warenkorbanalyse</span>
</div>
<div class="card-content">
<table id="shopping_cart_table">
</table>
</div> </div>
</div> </div>
</div> </div>
<!--Marketing-Cards--> <!--Marketing-Cards-->
<div id="marketing" class="row container"> <div id="marketing" class="row container">
<div class="row">
<div class="col s12 m6">
<div id="marketing_tip_1" class="card white">
<div class="card-content">
<div class="center">
<span class="card-title center">Marketingmaßnahme 1</span>
</div>
<div>
<p>Coming Soon</p>
</div>
</div>
</div>
</div>
<div class="col s12 m6">
<div id="marketing_tip_2" class="card white">
<div class="card-content">
<div class="center">
<span class="card-title center">Marketingmaßnahme 2</span>
</div>
<div>
<p>Coming Soon</p>
</div>
</div>
</div>
</div>
<div class="col s12 m6"> <div class="col s12 m6">
<div id="marketing_tip_3" class="card white"> <div id="marketing_tip_3" class="card white">
<div class="card-content"> <div class="card-content">
<div class="center"> <div class="center">
<span class="card-title center">Marketingmaßnahme 3</span> <span class="card-title center">Werbung</span>
</div> </div>
<div> <div>
<p>Coming Soon</p> <p>
Werbung dient dazu, auf bestimmte Produkte, Angebote oder den Markt im
Allgemeinen aufmerksam zu machen. Sie kann über verschiedene Wege verbreitet
werden, etwa das Internet, Zeitungsannoncen oder auch per TV/Radio.
Im Gegensatz zu Rabatten (die zeitabhängig am besten funktionieren) ist Werbung
stark von der Zielgruppe abhängig. Insbesondere in der Kombination mit anderen
Marketingmaßnahmen kann sie so ihre volle Wirkung entfalten.
<br>
<br>
Analysen, die für Werbung nützlich sein könnten:
Clusteranalyse, Wochenübersicht, Verkaufszahlen
</p>
</div> </div>
</div> </div>
</div> </div>
@ -135,14 +119,78 @@
<div id="marketing_tip_4" class="card white"> <div id="marketing_tip_4" class="card white">
<div class="card-content"> <div class="card-content">
<div class="center"> <div class="center">
<span class="card-title center">Marketingmaßnahme 4</span> <span class="card-title center">Sortimentsveränderung</span>
</div> </div>
<div> <div>
<p>Coming Soon</p> <p>
Gibt es eine Produktgruppe, die sich besonders gut verkauft, so sollte unter
Umständen über die Erweiterung des Sortiments innerhalb dieser Gruppe
nachgedacht werden.
Im Umkehrschluss dazu bietet sich an, schlecht verkaufte Produkte, welche keinen
hohen Gewinn abwerfen aus dem täglichen Verkaufsgeschäft wieder zu entfernen.
Dies verringert die Bildung von „Ladenhütern“ und schafft Platz für neue, sich
besser verkaufende Produkte.
<br>
<br>
Analysen, die bei der Sortimentswahl nützlich sein könnten:
Verkaufszahlen, Clusterübersicht
</p>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col s12 m6">
<div id="marketing_tip_1" class="card white">
<div class="card-content">
<div class="center">
<span class="card-title center">Rabatt</span>
</div>
<div>
<p>
Rabatte können z.B. an Tagen angeboten werden, an denen nur wenige Kunden
erscheinen. Dadurch wird einerseits an dieses Tagen oder Uhrzeiten das
Kaufverhalten angeregt, als auch der Andrang an anderen reduziert. Dies dient
insbesondere der Entzerrung des zeitabhängigen Kaufverhaltens.
Des Weiteren können durch Rabatte weniger gut laufende Produkte in den
Vordergrund gerückt du vergünstigt angeboten werden.
Zusätzlich dazu können sich gut verkaufende Produkte mit einem (vermeintlich)
verringerten Preis versehen werden. Dies kann beim Kunden den „inneren
Schnäppchenjäger“ wecken und dafür sorgen, dass er sich dieses gute Angebot
nicht entgehen lassen kann.
<br>
<br>
Analysen, die zur Platzierung von Rabatten nützlich sein können:
Wochenübersicht, Einkaufszeiten, Verkaufszahlen
</p>
</div>
</div>
</div>
</div>
<div class="col s12 m6">
<div id="marketing_tip_2" class="card white">
<div class="card-content">
<div class="center">
<span class="card-title center">Produktplatzierung </span>
</div>
<div>
<p>
Es gibt bestimmte Produkte, die werden häufig zusammen erworben. Sind diese
Produkte quer durch den Laden verstreut, so müssen die Kunden zusätzlich zu
ihren Wunschprodukten auch an anderen Auslagen vorbei und die Anzahl an
Spontankäufen eben jener weiteren Produkte steigt.
<br>
<br>
Analysen, die bei der Produktplatzierung hilfreich sein könnten:
Warenkorbsanalyse, Verkaufszahlen
</p>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<%@include file="footer.jsp" %> <%@include file="footer.jsp" %>

View File

@ -1,3 +1,5 @@
Chart.defaults.global.defaultFontColor = "black";
$(document).ready(function () { $(document).ready(function () {
let dataset = $('#dataset'); let dataset = $('#dataset');
request('files').then(results => { request('files').then(results => {
@ -33,11 +35,12 @@ function updateCache() {
} }
} }
function updateAll(uuid) { function updateAll(uuid) {
updateWeekOverviewChart(uuid); updateWeekOverviewChart(uuid);
updateShoppingTimesChart(uuid); updateShoppingTimesChart(uuid);
updateTopFlopChart(uuid); updateTopFlopChart(uuid);
updateClusters(uuid)
updateShoppingCartTable(uuid);
//add new charts here. //add new charts here.
} }

View File

@ -0,0 +1,55 @@
let cluster_result;
function updateClusters(id) {
if(typeof id !== 'undefined') {
request('clusters', id).then(function (data) {
cluster_result = data;
drawClusterTable();
});
}else request('clusters').then(function(data) {
cluster_result = data;
drawClusterTable();
});
}
function drawClusterTable(){
let table = document.getElementById("cluster_table");
while(table.hasChildNodes()){
table.removeChild(table.firstChild);
}
let row = $("<thead>");
row.append("<tr>");
row.append("<td></td>");
for (x in cluster_result){
if(x == 0){
row.append("<td>" + "&oslash;" + "</td>");
}else{
row.append("<td>" + x + "</td>");
}
}
row.append("</tr>");
row.append("</thead>");
appendClusterTable(row, "Alter");
appendClusterTable(row, "Wohnort");
appendClusterTable(row, "Einkaufstag");
appendClusterTable(row, "Geschlecht");
appendClusterTable(row, "Familienstand");
appendClusterTable(row, "Einkaufsuhrzeit");
row.append("</tbody>");
$("#cluster_table").append(row);
}
function appendClusterTable(row, attrib){
row.append("</tbody>");
row.append("<tr>");
row.append("<td>" + attrib + "</td>");
for (x in cluster_result){
row.append($("<td>" + cluster_result[x][attrib] +"</td>"));
}
row.append("</tr>");
}

View File

@ -0,0 +1,37 @@
let shopping_cart_result;
function updateShoppingCartTable(id) {
if(typeof id !== 'undefined') {
request('shopping_cart', id).then(function (data) {
shopping_cart_result = data;
drawShoppingCartTable();
});
}else request('shopping_cart_result').then(function(data) {
shopping_cart_result = data;
drawShoppingCartTable();
});
}
function drawShoppingCartTable(){
let table = document.getElementById("shopping_cart_table");
while(table.hasChildNodes()){
table.removeChild(table.firstChild);
}
let row = $("<thead>");
row.append("<tr>");
row.append("<th>Artikel</th>");
row.append("<th>Wird häufig zusammen gekauft mit</th>")
row.append("</tr>");
row.append("</thead>");
row.append("<tbody>");
for (x in shopping_cart_result){
row.append("<tr>");
row.append("<td>" + shopping_cart_result[x].col1 + "</td>");
row.append("<td>" + shopping_cart_result[x].col2 + "</td>");
row.append("</tr>");
}
row.append("</tbody>");
$("#shopping_cart_table").append(row);
}

View File

@ -5,13 +5,15 @@ let week_overview = new Chart($("#overview_chart"), {
data: { data: {
labels: [0, 0, 0, 0, 0], labels: [0, 0, 0, 0, 0],
datasets: [{ datasets: [{
label: "Warenanzahl", label: "Kundenanzahl",
yAxisID: 'y-axis-1',
data: [0, 0, 0, 0, 0], data: [0, 0, 0, 0, 0],
fill: true, fill: true,
backgroundColor: 'rgba(104, 216, 154, 0.5)', backgroundColor: 'rgba(104, 216, 154, 0.5)',
lineTension: 0 lineTension: 0
}, { }, {
label: "Einnahmen in €", label: "Einnahmen in €",
yAxisID: 'y-axis-2',
data: [0, 0, 0, 0, 0], data: [0, 0, 0, 0, 0],
fill: true, fill: true,
backgroundColor: 'rgba(113, 114, 231, 1)', backgroundColor: 'rgba(113, 114, 231, 1)',
@ -47,9 +49,25 @@ let week_overview = new Chart($("#overview_chart"), {
display: false display: false
} }
}], }],
yAxes: [{ yAxes: [
display: true {
}] display: true,
position: 'right',
id: 'y-axis-1',
scaleLabel: {
display: true,
labelString: 'Kundenanzahl'
}
},
{
display: true,
id: 'y-axis-2',
scaleLabel: {
display: true,
labelString: 'Einnahmen in €'
}
}
]
} }
} }
}); });

View File

@ -1,26 +1,6 @@
<% response.setCharacterEncoding(StandardCharsets.UTF_8.name()); %>
<% request.setCharacterEncoding(StandardCharsets.UTF_8.name()); %>
<!DOCTYPE html>
<html lang="de">
<head>
<title>BauDas</title>
<!--Import Google Icon Font-->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!--Import materialize.css-->
<link type="text/css" rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css"
media="screen,projection"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.css"
integrity="sha256-aa0xaJgmK/X74WM224KMQeNQC2xYKwlAt08oZqjeF0E=" crossorigin="anonymous"/>
<meta charset="UTF-8">
<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=utf-8">
<!--Let browser know website is optimized for mobile--> <%@include file="head.jsp"%>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body class="blue-grey lighten-5">
<!--Navbar--> <!--Navbar-->
<div class="navbar-fixed"> <div class="navbar-fixed">
<nav class="nav-extended blue-grey darken-3 white-text row"> <nav class="nav-extended blue-grey darken-3 white-text row">
@ -38,12 +18,12 @@
<ul class="right hide-on-med-and-down"> <ul class="right hide-on-med-and-down">
<li><a data-target="slide-out" class="sidenav-trigger show-on-large"><i <li><a data-target="slide-out" class="sidenav-trigger show-on-large"><i
class="material-icons left">menu</i>Men&uuml;</a></li><!--Sidenav--> class="material-icons left">menu</i>Men&uuml;</a></li><!--Sidenav-->
<li><a href="login.html"><i class="material-icons left">person_outline</i>Logout</a></li><!--Logout--> <li><a href="logout"><i class="material-icons left">person_outline</i>Logout</a></li><!--Logout-->
</ul> </ul>
<ul class="right"> <ul class="right">
<li><a href="#" data-target="slide-out" class="sidenav-trigger"><i class="material-icons">menu</i></a> <li><a href="#" data-target="slide-out" class="sidenav-trigger"><i class="material-icons">menu</i></a>
</li><!--Sidenav--> </li><!--Sidenav-->
<li><a href="login.html" class="sidenav-trigger"><i class="material-icons">person_outline</i></a></li> <li><a href="logout" class="sidenav-trigger"><i class="material-icons">person_outline</i></a></li>
<!--Logout--> <!--Logout-->
</ul> </ul>
</div> </div>

View File

@ -0,0 +1,5 @@
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/google-palette@1.1.0/palette.min.js"></script>
<script>
M.AutoInit();
</script>

View File

@ -30,8 +30,7 @@ class WeekOverviewTest {
AtomicReference<Map<String, Map.Entry<Double, Integer>>> result = new AtomicReference<>(); AtomicReference<Map<String, Map.Entry<Double, Integer>>> result = new AtomicReference<>();
assertTimeout(Duration.ofMillis(5), () -> result.set(overview.getResult())); assertTimeout(Duration.ofMillis(15), () -> result.set(overview.getResult()));
assertEquals(2477, result.get().get("Montag").getKey()); assertEquals(2477, result.get().get("Montag").getKey());
assertEquals(3359, result.get().get("Dienstag").getKey()); assertEquals(3359, result.get().get("Dienstag").getKey());
@ -40,12 +39,12 @@ class WeekOverviewTest {
assertEquals(2399, result.get().get("Freitag").getKey()); assertEquals(2399, result.get().get("Freitag").getKey());
assertEquals(18273, result.get().get("Samstag").getKey()); assertEquals(18273, result.get().get("Samstag").getKey());
assertEquals(3216, result.get().get("Montag").getValue()); assertEquals(8, result.get().get("Montag").getValue());
assertEquals(5503, result.get().get("Dienstag").getValue()); assertEquals(13, result.get().get("Dienstag").getValue());
assertEquals(1888, result.get().get("Mittwoch").getValue()); assertEquals(5, result.get().get("Mittwoch").getValue());
assertEquals(3568, result.get().get("Donnerstag").getValue()); assertEquals(9, result.get().get("Donnerstag").getValue());
assertEquals(2953, result.get().get("Freitag").getValue()); assertEquals(10, result.get().get("Freitag").getValue());
assertEquals(26243, result.get().get("Samstag").getValue()); assertEquals(55, result.get().get("Samstag").getValue());
} }
@Test @Test
@ -57,8 +56,7 @@ class WeekOverviewTest {
AtomicReference<Map<String, Map.Entry<Double, Integer>>> result = new AtomicReference<>(); AtomicReference<Map<String, Map.Entry<Double, Integer>>> result = new AtomicReference<>();
assertTimeout(Duration.ofMillis(7), () -> result.set(overview.getResult())); assertTimeout(Duration.ofMillis(15), () -> result.set(overview.getResult()));
assertEquals(26273, result.get().get("Montag").getKey()); assertEquals(26273, result.get().get("Montag").getKey());
assertEquals(23345, result.get().get("Dienstag").getKey()); assertEquals(23345, result.get().get("Dienstag").getKey());
@ -67,12 +65,12 @@ class WeekOverviewTest {
assertEquals(26413, result.get().get("Freitag").getKey()); assertEquals(26413, result.get().get("Freitag").getKey());
assertEquals(146279, result.get().get("Samstag").getKey()); assertEquals(146279, result.get().get("Samstag").getKey());
assertEquals(39839, result.get().get("Montag").getValue()); assertEquals(108, result.get().get("Montag").getValue());
assertEquals(34992, result.get().get("Dienstag").getValue()); assertEquals(93, result.get().get("Dienstag").getValue());
assertEquals(37299, result.get().get("Mittwoch").getValue()); assertEquals(89, result.get().get("Mittwoch").getValue());
assertEquals(35005, result.get().get("Donnerstag").getValue()); assertEquals(90, result.get().get("Donnerstag").getValue());
assertEquals(38911, result.get().get("Freitag").getValue()); assertEquals(110, result.get().get("Freitag").getValue());
assertEquals(214051, result.get().get("Samstag").getValue()); assertEquals(510, result.get().get("Samstag").getValue());
} }
@Test @Test
@ -84,8 +82,7 @@ class WeekOverviewTest {
AtomicReference<Map<String, Map.Entry<Double, Integer>>> result = new AtomicReference<>(); AtomicReference<Map<String, Map.Entry<Double, Integer>>> result = new AtomicReference<>();
assertTimeout(Duration.ofMillis(60), () -> result.set(overview.getResult())); assertTimeout(Duration.ofMillis(50), () -> result.set(overview.getResult()));
assertEquals(295688, result.get().get("Montag").getKey()); assertEquals(295688, result.get().get("Montag").getKey());
assertEquals(267269, result.get().get("Dienstag").getKey()); assertEquals(267269, result.get().get("Dienstag").getKey());
@ -94,11 +91,11 @@ class WeekOverviewTest {
assertEquals(330907, result.get().get("Freitag").getKey()); assertEquals(330907, result.get().get("Freitag").getKey());
assertEquals(1410789, result.get().get("Samstag").getKey()); assertEquals(1410789, result.get().get("Samstag").getKey());
assertEquals(426952, result.get().get("Montag").getValue()); assertEquals(1058, result.get().get("Montag").getValue());
assertEquals(379125, result.get().get("Dienstag").getValue()); assertEquals(942, result.get().get("Dienstag").getValue());
assertEquals(373891, result.get().get("Mittwoch").getValue()); assertEquals(915, result.get().get("Mittwoch").getValue());
assertEquals(356996, result.get().get("Donnerstag").getValue()); assertEquals(919, result.get().get("Donnerstag").getValue());
assertEquals(480062, result.get().get("Freitag").getValue()); assertEquals(1177, result.get().get("Freitag").getValue());
assertEquals(2015766, result.get().get("Samstag").getValue()); assertEquals(4989, result.get().get("Samstag").getValue());
} }
} }