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

@ -27,14 +27,12 @@ public class ShoppingCart implements Analysis<Map<List<String>, List<String>>> {
@Override
public Map<List<String>, List<String>> getResult() {
if(result != null) {
if (result != null) {
return result;
}
result = new HashMap<>();
Apriori model = new Apriori();
for (int i = 0; i < 11; i++) {
instances.deleteAttributeAt(0);
}
@ -60,6 +58,8 @@ public class ShoppingCart implements Analysis<Map<List<String>, List<String>>> {
e.printStackTrace();
}
Apriori model = new Apriori();
model.setTreatZeroAsMissing(true);
try {
model.buildAssociations(instances);
@ -67,19 +67,19 @@ public class ShoppingCart implements Analysis<Map<List<String>, List<String>>> {
e.printStackTrace();
}
for(int i = 0; i < model.getAssociationRules().getRules().size();i++){
for (int i = 0; i < model.getAssociationRules().getRules().size(); i++) {
AssociationRule rule = model.getAssociationRules().getRules().get(i);
List<String> l1 = new ArrayList<>();
for(Item item : rule.getPremise()){
for (Item item : rule.getPremise()) {
l1.add(item.getAttribute().name());
}
List<String> l2 = new ArrayList<>();
for(Item item : rule.getConsequence()){
for (Item item : rule.getConsequence()) {
l2.add(item.getAttribute().name());
}
result.put(l1,l2);
result.put(l1, l2);
}

View File

@ -36,30 +36,20 @@ public class WeekOverview implements Analysis<Map<String, Map.Entry<Double, Inte
return result;
}
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++) {
Instance instance = instances.get(i);
double amount = instance.value(amountIndex);
String day = instance.stringValue(dayIndex);
double amount = instance.value(Attribute.PURCHASE_AMOUNT);
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)) {
result.put(day, new AbstractMap.SimpleEntry<>(0.0, 0));
}
result.put(day,
new AbstractMap.SimpleEntry<>(
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;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -14,22 +20,24 @@ import java.io.IOException;
*/
@WebFilter(urlPatterns = {"/*"})
public class AuthenticationFilter implements Filter{
public class AuthenticationFilter implements Filter {
private ServletContext context;
/**
* 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.
*/
public void init(FilterConfig fConfig) throws ServletException {
this.context = fConfig.getServletContext();
public void init(FilterConfig filterConfig) throws ServletException {
this.context = filterConfig.getServletContext();
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.
*
* @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 chain This parameter allows passing request along the chain of potential handlers until one of them handles the request.
@ -42,7 +50,7 @@ public class AuthenticationFilter implements Filter{
HttpServletResponse res = (HttpServletResponse) response;
HttpSession session = req.getSession(false);
String url = req.getRequestURI();
if(url.contains("login") || url.contains("logo") || url.contains("js/")){
if (url.contains("login") || url.contains("logo") || url.contains("js/")) {
chain.doFilter(request, response);
} else if (session == null || !((boolean) session.getAttribute("authentication"))) { //checking whether the session exists and if authentication succeed
this.context.log("Unauthorized access 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.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* manages data about .csv files.
@ -23,7 +27,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
public class Data {
@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
*/
@NotNull
static Path add(@NotNull String name) {
//cleanup old files
if (files.isEmpty()) {
static Path add(@NotNull String name, @NotNull String session) {
UUID uuid = UUID.randomUUID();
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 {
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 {
Files.delete(path);
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
} catch (IOException ex) {
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.
*

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());
PrintWriter out = resp.getWriter();
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.
*
* @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.
* @throws ServletException Defines a general exception a servlet can throw when it encounters difficulty.
@ -34,7 +35,7 @@ public class LoginServlet extends HttpServlet {
if (this.password.equals(password)) {
HttpSession newSession = request.getSession(true);
newSession.setAttribute("authentication", true);
newSession.setMaxInactiveInterval(5*60*60); //setting session to expiry in 5 hours
newSession.setMaxInactiveInterval(5 * 60 * 60); //setting session to expiry in 5 hours
response.sendRedirect("index.jsp");
} else {
RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html");

View File

@ -1,12 +1,11 @@
package de.hsel.spm.baudas.web;
import java.io.IOException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Servlet implementation class LogoutServlet.
@ -19,14 +18,16 @@ public class LogoutServlet extends HttpServlet {
/**
* 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 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.
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
HttpSession session = request.getSession(false);
if(session != null){
HttpSession session = request.getSession();
if (session != null) {
Data.delete(session.getId());
session.invalidate();
}
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.PrintWriter;
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
* @version 0.1
* @since 0.1
@ -45,7 +51,7 @@ public class TopFlopArticleDiagram extends HttpServlet {
List<String> labels = new ArrayList<>();
List<String> data = new ArrayList<>();
for(Map.Entry<String, Integer> entry : map.entrySet()) {
for (Map.Entry<String, Integer> entry : map.entrySet()) {
labels.add(entry.getKey());
data.add(entry.getValue().toString());
}

View File

@ -1,6 +1,7 @@
package de.hsel.spm.baudas.web;
import org.jetbrains.annotations.NotNull;
import weka.core.converters.CSVLoader;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
@ -20,7 +21,7 @@ import java.nio.file.StandardCopyOption;
/**
* saves uploaded files.
*
* @author Johannes Theiner
* @author Johannes Theiner, Julian Hinxlage
* @version 0.1
* @since 0.1
**/
@ -31,24 +32,71 @@ public class Upload extends HttpServlet {
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
protected void doPost(@NotNull HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
protected void doPost(@NotNull HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
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();
if (fileName == null) {
resp.sendRedirect("error.jsp?code=" + ErrorCode.FILE_NOT_FOUND);
return;
}
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)) {
Files.createFile(path);
}
Files.copy(inputStream, path, StandardCopyOption.REPLACE_EXISTING);
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-->
<!--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://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>
<%@include file="theming.jsp"%>
<script src="js/cache.js"></script>
<script src="js/week_overview.js"></script>
<script src="js/shopping_times.js"></script>
<script src="js/top_flop.js"></script>
<%--<script src="js/sold_articles.js"></script>--%>
<script src="js/clusters.js"></script>
<script src="js/shopping_cart.js"></script>
<!--Ende Skriptbereich-->
</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 contentType="text/html;charset=UTF-8" %>
<%@include file="header.jsp"%>
<%@include file="menu.jsp" %>
<% response.setCharacterEncoding(StandardCharsets.UTF_8.name()); %>
<% request.setCharacterEncoding(StandardCharsets.UTF_8.name()); %>
<!--Cards-->
<!--Analyse-Cards-->
<div id="analysis" class="row container">
<div id="analysis" class="row container black-text">
<!--Übersicht-->
<div class="col s12 m12">
<div id="overview" class="card white">
@ -16,7 +16,7 @@
<div class="card-content">
<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>
@ -63,70 +63,54 @@
</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-->
<div class="col s12 m6">
<div class="col s12 m12">
<div id="group_overview" class="card white">
<div class="card-content">
<div class="center">
<span class="card-title center">Gruppenübersicht</span>
</div>
<div>
<p>Coming Soon</p>
<table id="cluster_table">
</table>
</div>
</div>
</div>
</div>
</div>
<!--Marketing-Cards-->
<div id="marketing" class="row container">
<!--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 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>
<!--Marketing-Cards-->
<div id="marketing" class="row container">
<div class="row">
<div class="col s12 m6">
<div id="marketing_tip_3" class="card white">
<div class="card-content">
<div class="center">
<span class="card-title center">Marketingmaßnahme 3</span>
<span class="card-title center">Werbung</span>
</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>
@ -135,14 +119,78 @@
<div id="marketing_tip_4" class="card white">
<div class="card-content">
<div class="center">
<span class="card-title center">Marketingmaßnahme 4</span>
<span class="card-title center">Sortimentsveränderung</span>
</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>
<%@include file="footer.jsp"%>
<%@include file="footer.jsp" %>

View File

@ -1,3 +1,5 @@
Chart.defaults.global.defaultFontColor = "black";
$(document).ready(function () {
let dataset = $('#dataset');
request('files').then(results => {
@ -33,11 +35,12 @@ function updateCache() {
}
}
function updateAll(uuid) {
updateWeekOverviewChart(uuid);
updateShoppingTimesChart(uuid);
updateTopFlopChart(uuid);
updateClusters(uuid)
updateShoppingCartTable(uuid);
//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: {
labels: [0, 0, 0, 0, 0],
datasets: [{
label: "Warenanzahl",
label: "Kundenanzahl",
yAxisID: 'y-axis-1',
data: [0, 0, 0, 0, 0],
fill: true,
backgroundColor: 'rgba(104, 216, 154, 0.5)',
lineTension: 0
}, {
label: "Einnahmen in €",
yAxisID: 'y-axis-2',
data: [0, 0, 0, 0, 0],
fill: true,
backgroundColor: 'rgba(113, 114, 231, 1)',
@ -47,9 +49,25 @@ let week_overview = new Chart($("#overview_chart"), {
display: false
}
}],
yAxes: [{
display: true
}]
yAxes: [
{
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-->
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<%@include file="head.jsp"%>
</head>
<body class="blue-grey lighten-5">
<!--Navbar-->
<div class="navbar-fixed">
<nav class="nav-extended blue-grey darken-3 white-text row">
@ -38,12 +18,12 @@
<ul class="right hide-on-med-and-down">
<li><a data-target="slide-out" class="sidenav-trigger show-on-large"><i
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 class="right">
<li><a href="#" data-target="slide-out" class="sidenav-trigger"><i class="material-icons">menu</i></a>
</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-->
</ul>
</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<>();
assertTimeout(Duration.ofMillis(5), () -> result.set(overview.getResult()));
assertTimeout(Duration.ofMillis(15), () -> result.set(overview.getResult()));
assertEquals(2477, result.get().get("Montag").getKey());
assertEquals(3359, result.get().get("Dienstag").getKey());
@ -40,12 +39,12 @@ class WeekOverviewTest {
assertEquals(2399, result.get().get("Freitag").getKey());
assertEquals(18273, result.get().get("Samstag").getKey());
assertEquals(3216, result.get().get("Montag").getValue());
assertEquals(5503, result.get().get("Dienstag").getValue());
assertEquals(1888, result.get().get("Mittwoch").getValue());
assertEquals(3568, result.get().get("Donnerstag").getValue());
assertEquals(2953, result.get().get("Freitag").getValue());
assertEquals(26243, result.get().get("Samstag").getValue());
assertEquals(8, result.get().get("Montag").getValue());
assertEquals(13, result.get().get("Dienstag").getValue());
assertEquals(5, result.get().get("Mittwoch").getValue());
assertEquals(9, result.get().get("Donnerstag").getValue());
assertEquals(10, result.get().get("Freitag").getValue());
assertEquals(55, result.get().get("Samstag").getValue());
}
@Test
@ -57,8 +56,7 @@ class WeekOverviewTest {
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(23345, result.get().get("Dienstag").getKey());
@ -67,12 +65,12 @@ class WeekOverviewTest {
assertEquals(26413, result.get().get("Freitag").getKey());
assertEquals(146279, result.get().get("Samstag").getKey());
assertEquals(39839, result.get().get("Montag").getValue());
assertEquals(34992, result.get().get("Dienstag").getValue());
assertEquals(37299, result.get().get("Mittwoch").getValue());
assertEquals(35005, result.get().get("Donnerstag").getValue());
assertEquals(38911, result.get().get("Freitag").getValue());
assertEquals(214051, result.get().get("Samstag").getValue());
assertEquals(108, result.get().get("Montag").getValue());
assertEquals(93, result.get().get("Dienstag").getValue());
assertEquals(89, result.get().get("Mittwoch").getValue());
assertEquals(90, result.get().get("Donnerstag").getValue());
assertEquals(110, result.get().get("Freitag").getValue());
assertEquals(510, result.get().get("Samstag").getValue());
}
@Test
@ -84,8 +82,7 @@ class WeekOverviewTest {
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(267269, result.get().get("Dienstag").getKey());
@ -94,11 +91,11 @@ class WeekOverviewTest {
assertEquals(330907, result.get().get("Freitag").getKey());
assertEquals(1410789, result.get().get("Samstag").getKey());
assertEquals(426952, result.get().get("Montag").getValue());
assertEquals(379125, result.get().get("Dienstag").getValue());
assertEquals(373891, result.get().get("Mittwoch").getValue());
assertEquals(356996, result.get().get("Donnerstag").getValue());
assertEquals(480062, result.get().get("Freitag").getValue());
assertEquals(2015766, result.get().get("Samstag").getValue());
assertEquals(1058, result.get().get("Montag").getValue());
assertEquals(942, result.get().get("Dienstag").getValue());
assertEquals(915, result.get().get("Mittwoch").getValue());
assertEquals(919, result.get().get("Donnerstag").getValue());
assertEquals(1177, result.get().get("Freitag").getValue());
assertEquals(4989, result.get().get("Samstag").getValue());
}
}