~ refactor analysis

Signed-off-by: Johannes Theiner <j.theiner@live.de>

#SPM-32: add work 2h development
This commit is contained in:
Johannes Theiner 2019-05-29 09:05:34 +02:00
parent a823234c88
commit 5d898338ee
21 changed files with 226 additions and 586 deletions

View File

@ -1,15 +1,15 @@
![BauDas Logo](https://services.joethei.xyz/images/bauDas.png) ![BauDas Logo](https://services.joethei.xyz/images/bauDas.png)
#Kundenkarten Verwaltung #Kundenkarten Analyse
[![Build Status](https://teamcity.joethei.xyz/app/rest/builds/buildType:(id:Studium_Programmierung_Softwareprojektmanagement_SpmProd)/statusIcon)](https://tcstatus.joethei.space/dashboard/spm) [![Build Status](https://teamcity.joethei.xyz/app/rest/builds/buildType:(id:Studium_Programmierung_Softwareprojektmanagement_SpmProd)/statusIcon)](https://tcstatus.joethei.space/dashboard/spm)
[![Quality Gate](https://sonarqube.joethei.xyz/api/project_badges/measure?project=Studium_Programmierung_Softwareprojektmanagement&metric=alert_status) [![Quality Gate](https://sonarqube.joethei.xyz/api/project_badges/measure?project=Studium_Programmierung_Softwareprojektmanagement&metric=alert_status)
![Code Coverage](https://sonarqube.joethei.xyz/api/project_badges/measure?project=Studium_Programmierung_Softwareprojektmanagement&metric=coverage)](https://sonarqube.joethei.xyz/dashboard?id=Studium_Programmierung_Softwareprojektmanagement) ![Code Coverage](https://sonarqube.joethei.xyz/api/project_badges/measure?project=Studium_Programmierung_Softwareprojektmanagement&metric=coverage)](https://sonarqube.joethei.xyz/dashboard?id=Studium_Programmierung_Softwareprojektmanagement)
## Requirements ## minimal Requirements
- Java 8 - Java 8
- Maven 3 - Maven 3
- Tomcat 9 - Tomcat 7
## Development Setup ## Development Setup
@ -20,7 +20,7 @@
- restart the server if needed - restart the server if needed
## TODO: better Name for this ## get the war
- clone this repository - clone this repository
- run maven goal ````package```` - run maven goal ````package````
- war can be found in target folder - war can be found in target folder

28
pom.xml
View File

@ -16,10 +16,13 @@
<java.version>1.8</java.version> <java.version>1.8</java.version>
<checkstyle_file>checkstyle.xml</checkstyle_file> <checkstyle_file>checkstyle.xml</checkstyle_file>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jacoco.dataFile>${project.build.directory}/jacoco.exec</jacoco.dataFile> <sonar.sources>src/main</sonar.sources>
<sonar.tests>src/test</sonar.tests>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
<sonar.jacoco.xmlReportPaths>${project.basedir}/target/jacoco.exec</sonar.jacoco.xmlReportPaths>
</properties> </properties>
<developers> <developers>
<developer> <developer>
<name>Johannes Theiner</name> <name>Johannes Theiner</name>
@ -33,7 +36,7 @@
<name>Charlotte Friedemann</name> <name>Charlotte Friedemann</name>
<email>charlotte.friedemann@informatik-emden.de</email> <email>charlotte.friedemann@informatik-emden.de</email>
<roles> <roles>
<role>Developer</role> <role>Project Management</role>
</roles> </roles>
</developer> </developer>
<developer> <developer>
@ -71,6 +74,11 @@
<url>https://youtrack.joethei.xyz/issues/SPM</url> <url>https://youtrack.joethei.xyz/issues/SPM</url>
</issueManagement> </issueManagement>
<ciManagement>
<system>TeamCity</system>
<url>https://teamcity.joethei.xyz</url>
</ciManagement>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
@ -220,6 +228,10 @@
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId> <artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.4</version> <version>0.8.4</version>
<configuration>
<rules>
</rules>
</configuration>
<executions> <executions>
<execution> <execution>
<id>default-prepare-agent</id> <id>default-prepare-agent</id>
@ -244,6 +256,16 @@
</plugin> </plugin>
</plugins> </plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.6.0.1398</version>
</plugin>
</plugins>
</pluginManagement>
</build> </build>
<reporting> <reporting>

View File

@ -15,6 +15,7 @@ import java.util.Map;
* *
* @author Johannes Theiner * @author Johannes Theiner
* @version 0.1 * @version 0.1
* @since 0.4
*/ */
public class Cluster implements Analysis<Map<Integer, Map<String, String>>> { public class Cluster implements Analysis<Map<Integer, Map<String, String>>> {
@ -26,7 +27,7 @@ public class Cluster implements Analysis<Map<Integer, Map<String, String>>> {
} }
/** /**
* get result of cluster analysis. * get result of cluster analysis, cluster 0 is the average.
* *
* @return Result * @return Result
*/ */

View File

@ -1,22 +0,0 @@
package de.hsel.spm.baudas.analysis;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* object to better handle day, hour combinations.
*
* @author Johannes Theiner
* @version 0.1
* @since 0.1
**/
@Data
@EqualsAndHashCode
@AllArgsConstructor
public class DayHour {
private String day;
private String hour;
}

View File

@ -15,10 +15,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* The shopping-cart Analysis. * The shopping-cart analysis.
* *
* @author Julian Hinxlage * @author Julian Hinxlage
* @version 0.1 * @version 0.4
*/ */
public class ShoppingCart implements Analysis<Map<List<String>, List<String>>> { public class ShoppingCart implements Analysis<Map<List<String>, List<String>>> {
@ -37,7 +37,7 @@ public class ShoppingCart implements Analysis<Map<List<String>, List<String>>> {
instances.deleteAttributeAt(0); instances.deleteAttributeAt(0);
} }
//alle artikel anzahlen auf 0 oder 1 setzen //convert data to only contain bought/not bought
NumericCleaner nc = new NumericCleaner(); NumericCleaner nc = new NumericCleaner();
nc.setMaxThreshold(1.0); nc.setMaxThreshold(1.0);
nc.setMaxDefault(1.0); nc.setMaxDefault(1.0);
@ -48,7 +48,7 @@ public class ShoppingCart implements Analysis<Map<List<String>, List<String>>> {
e.printStackTrace(); e.printStackTrace();
} }
//zahl werte zu nomial werten umwandeln
NumericToNominal num2nom = new NumericToNominal(); NumericToNominal num2nom = new NumericToNominal();
num2nom.setAttributeIndices("first-last"); num2nom.setAttributeIndices("first-last");
try { try {
@ -70,19 +70,18 @@ public class ShoppingCart implements Analysis<Map<List<String>, List<String>>> {
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); AssociationRule rule = model.getAssociationRules().getRules().get(i);
List<String> l1 = new ArrayList<>(); List<String> premises = new ArrayList<>();
for (Item item : rule.getPremise()) { for (Item item : rule.getPremise()) {
l1.add(item.getAttribute().name()); premises.add(item.getAttribute().name());
} }
List<String> l2 = new ArrayList<>(); List<String> consequences = new ArrayList<>();
for (Item item : rule.getConsequence()) { for (Item item : rule.getConsequence()) {
l2.add(item.getAttribute().name()); consequences.add(item.getAttribute().name());
} }
result.put(l1, l2); result.put(premises, consequences);
} }
return result; return result;
} }

View File

@ -4,20 +4,21 @@ import weka.core.Instance;
import weka.core.Instances; import weka.core.Instances;
import java.io.File; import java.io.File;
import java.util.AbstractMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* Shopping Times Analysis. * count customers at all times.
* *
* @author Johannes Theiner * @author Johannes Theiner
* @version 0.1 * @version 0.1
* @since 0.1 * @since 0.1
**/ **/
public class ShoppingTimes implements Analysis<Map<DayHour, Integer>> { public class ShoppingTimes implements Analysis<Map<Map.Entry<String, String>, Integer>> {
private Instances instances; private Instances instances;
private Map<DayHour, Integer> result; private Map<Map.Entry<String, String>, Integer> result;
public ShoppingTimes(File file) { public ShoppingTimes(File file) {
instances = load(file); instances = load(file);
@ -30,7 +31,7 @@ public class ShoppingTimes implements Analysis<Map<DayHour, Integer>> {
* @return Map of Day-Hour Combinations and the corresponding user count * @return Map of Day-Hour Combinations and the corresponding user count
*/ */
@Override @Override
public Map<DayHour, Integer> getResult() { public Map<Map.Entry<String, String>, Integer> getResult() {
if (result != null) { if (result != null) {
return result; return result;
} }
@ -38,7 +39,7 @@ public class ShoppingTimes implements Analysis<Map<DayHour, Integer>> {
result = new HashMap<>(); result = new HashMap<>();
for (Instance instance : instances) { for (Instance instance : instances) {
DayHour dayHour = new DayHour(instance.stringValue(Attribute.SHOPPING_DAY), instance.stringValue(Attribute.SHOPPING_HOUR)); Map.Entry<String, String> dayHour = new AbstractMap.SimpleImmutableEntry<>(instance.stringValue(Attribute.SHOPPING_DAY), instance.stringValue(Attribute.SHOPPING_HOUR));
int tmp = 1; int tmp = 1;
if (result.containsKey(dayHour)) if (result.containsKey(dayHour))
tmp = result.get(dayHour) + 1; tmp = result.get(dayHour) + 1;

View File

@ -11,14 +11,12 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
* get sum for every article and sort them ascending. * get sum for every article bought and sort them in ascending order.
* *
* @author Karsten Eden * @author Karsten Eden
* @version 0.1 * @version 0.1
* @since 0.1 * @since 0.2
*/ */
public class TopFlopArticle implements Analysis<Map<String, Integer>> { public class TopFlopArticle implements Analysis<Map<String, Integer>> {
private Instances instances; private Instances instances;

View File

@ -9,7 +9,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* Week WeekOverviewDiagram Analysis. * generates a brief week overview.
* *
* @author Julian Hinxlage * @author Julian Hinxlage
* @version 0.1 * @version 0.1
@ -28,7 +28,7 @@ public class WeekOverview implements Analysis<Map<String, Map.Entry<Double, Inte
/** /**
* get results for analysis. * get results for analysis.
* *
* @return day, amount, count * @return day, purchase amount, customer count
*/ */
@Override @Override
public Map<String, Map.Entry<Double, Integer>> getResult() { public Map<String, Map.Entry<Double, Integer>> getResult() {
@ -45,13 +45,14 @@ public class WeekOverview implements Analysis<Map<String, Map.Entry<Double, Inte
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));
} else {
result.put(day,
new AbstractMap.SimpleEntry<>(
result.get(day).getKey() + amount,
result.get(day).getValue() + 1
)
);
} }
result.put(day,
new AbstractMap.SimpleEntry<>(
result.get(day).getKey() + amount,
result.get(day).getValue() + 1
)
);
} }

View File

@ -23,15 +23,23 @@ import java.util.*;
**/ **/
@WebServlet("/clusters") @WebServlet("/clusters")
public class ClusterDiagram extends HttpServlet { public class ClusterDiagram extends HttpServlet {
private static Map<Integer, Map<String, String>> cache;
@Override @Override
protected void doGet(@NotNull HttpServletRequest req, @NotNull HttpServletResponse resp) throws IOException { protected void doGet(@NotNull HttpServletRequest req, @NotNull HttpServletResponse resp) throws IOException {
req.setCharacterEncoding(StandardCharsets.UTF_8.name()); req.setCharacterEncoding(StandardCharsets.UTF_8.name());
resp.setCharacterEncoding(StandardCharsets.UTF_8.name()); resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
resp.setContentType("application/json"); resp.setContentType("application/json");
PrintWriter out = resp.getWriter(); PrintWriter out = resp.getWriter();
Gson gson = new Gson();
if(cache != null) {
out.println(gson.toJson(cache));
return;
}
Map<Integer, Map<String, String>> result = new HashMap<>(); Map<Integer, Map<String, String>> result = new HashMap<>();
Gson gson = new Gson();
if (req.getParameter("id") == null) { if (req.getParameter("id") == null) {
out.print(gson.toJson(result)); out.print(gson.toJson(result));

View File

@ -1,11 +0,0 @@
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

@ -26,17 +26,24 @@ public class ShoppingCartDiagram extends HttpServlet {
private static final long serialVersionUID = 5026732432605473505L; private static final long serialVersionUID = 5026732432605473505L;
private static List<Map.Entry<String, String>> cache;
protected void doGet(@NotNull HttpServletRequest req, @NotNull HttpServletResponse resp) throws IOException { protected void doGet(@NotNull HttpServletRequest req, @NotNull HttpServletResponse resp) throws IOException {
req.setCharacterEncoding(StandardCharsets.UTF_8.name()); req.setCharacterEncoding(StandardCharsets.UTF_8.name());
resp.setCharacterEncoding(StandardCharsets.UTF_8.name()); resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
resp.setContentType("application/json"); resp.setContentType("application/json");
PrintWriter out = resp.getWriter(); PrintWriter out = resp.getWriter();
Gson gson = new Gson(); Gson gson = new Gson();
List<Columns> colList = new ArrayList<>();
if(cache != null) {
out.println(gson.toJson(cache));
return;
}
List<Map.Entry<String, String>> result = new ArrayList<>();
if (req.getParameter("id") == null) { if (req.getParameter("id") == null) {
out.print(gson.toJson(colList)); out.print(gson.toJson(result));
return; return;
} }
@ -44,21 +51,20 @@ public class ShoppingCartDiagram extends HttpServlet {
File file = Data.get(uuid); File file = Data.get(uuid);
ShoppingCart cart = new ShoppingCart(file); ShoppingCart cart = new ShoppingCart(file);
Map<List<String>, List<String>> result = new HashMap<>(); Map<List<String>, List<String>> map = cart.getResult();
result = cart.getResult();
List<String> col1 = new ArrayList<>(); List<String> col1 = new ArrayList<>();
List<String> col2 = new ArrayList<>(); List<String> col2 = new ArrayList<>();
for(Map.Entry<List<String>, List<String>> entry : result.entrySet()){ for(Map.Entry<List<String>, List<String>> entry : map.entrySet()){
col1.add(entry.getKey().toString()); col1.add(String.join(", ", entry.getKey()));
col2.add(entry.getValue().toString()); col2.add(String.join(", ", entry.getValue()));
} }
for(int i = 0; i < col1.size(); i++ ){ for(int i = 0; i < col1.size(); i++ ){
colList.add(new Columns(col1.get(i).replaceAll("\\[", "").replaceAll("\\]", ""), col2.get(i).replaceAll("\\[", "").replaceAll("\\]", ""))); result.add(new AbstractMap.SimpleEntry<>(col1.get(i), col2.get(i)));
} }
out.print(gson.toJson(colList)); out.print(gson.toJson(result));
} }
} }

View File

@ -1,7 +1,6 @@
package de.hsel.spm.baudas.web; package de.hsel.spm.baudas.web;
import com.google.gson.Gson; import com.google.gson.Gson;
import de.hsel.spm.baudas.analysis.DayHour;
import de.hsel.spm.baudas.analysis.ShoppingTimes; import de.hsel.spm.baudas.analysis.ShoppingTimes;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -13,16 +12,7 @@ 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.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
@ -62,7 +52,7 @@ public class ShoppingTimesDiagram extends HttpServlet {
UUID uuid = UUID.fromString(id); UUID uuid = UUID.fromString(id);
if(cache.containsKey(uuid)) { if (cache.containsKey(uuid)) {
out.print(gson.toJson(cache.get(uuid))); out.print(gson.toJson(cache.get(uuid)));
return; return;
} }
@ -71,13 +61,13 @@ public class ShoppingTimesDiagram extends HttpServlet {
File file = Data.get(uuid); File file = Data.get(uuid);
ShoppingTimes shoppingTimes = new ShoppingTimes(file); ShoppingTimes shoppingTimes = new ShoppingTimes(file);
Map<DayHour, Integer> map = shoppingTimes.getResult(); Map<Map.Entry<String, String>, Integer> map = shoppingTimes.getResult();
Set<String> days = new TreeSet<>(); Set<String> days = new TreeSet<>();
Set<String> hours = new TreeSet<>(comparator); Set<String> hours = new TreeSet<>(comparator);
for (DayHour dayHour : map.keySet()) { for (Map.Entry<String, String> dayHour : map.keySet()) {
days.add(dayHour.getDay()); days.add(dayHour.getKey());
hours.add(dayHour.getHour()); hours.add(dayHour.getValue());
} }
result.put("labels", hours); result.put("labels", hours);
@ -86,8 +76,8 @@ public class ShoppingTimesDiagram extends HttpServlet {
if (!result.containsKey(day)) if (!result.containsKey(day))
result.put(day, new ArrayList<>()); result.put(day, new ArrayList<>());
for (String hour : hours) { for (String hour : hours) {
if (map.containsKey(new DayHour(day, hour))) { if (map.containsKey(new AbstractMap.SimpleEntry<>(day, hour))) {
result.get(day).add(map.get(new DayHour(day, hour)).toString()); result.get(day).add(map.get(new AbstractMap.SimpleEntry<>(day, hour)).toString());
} else result.get(day).add("0"); } else result.get(day).add("0");
} }
} }

View File

@ -17,7 +17,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.*;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
@ -46,13 +45,13 @@ public class TopFlopArticleDiagram extends HttpServlet {
String id = req.getParameter("id"); String id = req.getParameter("id");
boolean match = Pattern.matches("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", id); boolean match = Pattern.matches("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", id);
if(id == null || !match) { if (id == null || !match) {
return; return;
} }
UUID uuid = UUID.fromString(id); UUID uuid = UUID.fromString(id);
if(cache.containsKey(uuid)) { if (cache.containsKey(uuid)) {
out.println(gson.toJson(cache.get(uuid))); out.println(gson.toJson(cache.get(uuid)));
return; return;
} }

View File

@ -19,3 +19,4 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head> </head>
<body>

View File

@ -23,7 +23,7 @@
</div> </div>
<!--Einkaufszeiten--> <!--Einkaufszeiten-->
<div class="col s12 m6"> <div class="col s12 m12">
<div id="shopping_times" class="card white"> <div id="shopping_times" class="card white">
<div class="card-content"> <div class="card-content">
<div class="center"> <div class="center">
@ -75,122 +75,124 @@
</table> </table>
</div> </div>
</div> </div>
</div>
</div>
<!--Warenkorbanlyse--> <!--Warenkorbanlyse-->
<div id="shopping_card_analysis" class="col s12 m12"> <div id="shopping_card_analysis" class="col s12 m12">
<div class="card white"> <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>
<!--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"> <div class="center">
<span class="card-title center">Warenkorbanalyse</span> <span class="card-title center">Werbung</span>
</div> </div>
<div class="card-content"> <div>
<table id="shopping_cart_table"> <p>
</table> 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>
</div>
<!--Marketing-Cards--> <div class="col s12 m6">
<div id="marketing" class="row container"> <div id="marketing_tip_4" class="card white">
<div class="row"> <div class="card-content">
<div class="col s12 m6"> <div class="center">
<div id="marketing_tip_3" class="card white"> <span class="card-title center">Sortimentsveränderung</span>
<div class="card-content">
<div class="center">
<span class="card-title center">Werbung</span>
</div>
<div>
<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 class="col s12 m6"> <div>
<div id="marketing_tip_4" class="card white"> <p>
<div class="card-content"> Gibt es eine Produktgruppe, die sich besonders gut verkauft, so sollte unter
<div class="center"> Umständen über die Erweiterung des Sortiments innerhalb dieser Gruppe
<span class="card-title center">Sortimentsveränderung</span> nachgedacht werden.
</div> Im Umkehrschluss dazu bietet sich an, schlecht verkaufte Produkte, welche keinen
<div> hohen Gewinn abwerfen aus dem täglichen Verkaufsgeschäft wieder zu entfernen.
<p> Dies verringert die Bildung von „Ladenhütern“ und schafft Platz für neue, sich
Gibt es eine Produktgruppe, die sich besonders gut verkauft, so sollte unter besser verkaufende Produkte.
Umständen über die Erweiterung des Sortiments innerhalb dieser Gruppe <br>
nachgedacht werden. <br>
Im Umkehrschluss dazu bietet sich an, schlecht verkaufte Produkte, welche keinen Analysen, die bei der Sortimentswahl nützlich sein könnten:
hohen Gewinn abwerfen aus dem täglichen Verkaufsgeschäft wieder zu entfernen. Verkaufszahlen, Clusterübersicht
Dies verringert die Bildung von „Ladenhütern“ und schafft Platz für neue, sich </p>
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>
<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>
<%@include file="footer.jsp" %> <%@include file="footer.jsp" %>

View File

@ -23,7 +23,7 @@ function drawClusterTable(){
row.append("<td></td>"); row.append("<td></td>");
for (x in cluster_result){ for (x in cluster_result){
if(x == 0){ if(x == 0){
row.append("<td>" + "&oslash;" + "</td>"); row.append("<td>" + "Durchschnitt" + "</td>");
}else{ }else{
row.append("<td>" + x + "</td>"); row.append("<td>" + x + "</td>");
} }

View File

@ -27,8 +27,8 @@ function drawShoppingCartTable(){
row.append("<tbody>"); row.append("<tbody>");
for (x in shopping_cart_result){ for (x in shopping_cart_result){
row.append("<tr>"); row.append("<tr>");
row.append("<td>" + shopping_cart_result[x].col1 + "</td>"); row.append("<td>" + shopping_cart_result[x].key + "</td>");
row.append("<td>" + shopping_cart_result[x].col2 + "</td>"); row.append("<td>" + shopping_cart_result[x].value + "</td>");
row.append("</tr>"); row.append("</tr>");
} }
row.append("</tbody>"); row.append("</tbody>");

View File

@ -17,7 +17,7 @@
<div class="nav-wrapper col 2 right"> <div class="nav-wrapper col 2 right">
<ul cslass="right hide-on-med-and-down"> <ul cslass="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>Datens&auml;tze</a></li><!--Sidenav-->
<li><a href="logout"><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">

View File

@ -1,301 +0,0 @@
<!DOCTYPE html>
<html>
<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" />
<!--Let browser know website is optimized for mobile-->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body class="blue-grey lighten-5">
<!--Navbar-->
<div class="navbar-fixed">
<nav class="nav-extended blue-grey lighten-3 white-text row">
<a href="#" class="brand-logo hide-on-small-and-down center">BauDas</a>
<!--Navbar Tabs-->
<div class="nav-content col 12 ">
<ul class="tabs tabs-transparent blue-grey lighten-3 white-text">
<li class="tab"><a class="active" href="#analysis"><i class="material-icons left">poll</i>Analyse</a></li>
<li class="tab"><a href="#marketing"><i class="material-icons left">public</i>Marketing</a></li>
</ul>
</div>
<!--Navbar Items-->
<div class="nav-wrapper col 2 right">
<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ü</a></li><!--Sidenav-->
<li><a href="../login.html"><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><!--Logout-->
</ul>
</div>
</nav>
</div>
<br>
<!--Sidenav Content-->
<ul id="slide-out" class="sidenav">
<li><div class="user-view">
<div class="background">
<img style="width: 100%" src="../logoOrginal.gif">
</div>
<br>
<br>
</div></li>
<li><div class="divider"></div></li>
<li><div class="input-field container">
<label>Gruppenauswahl</label>
<br>
<select>
<option value="0">alle</option>
<option value="1">Gruppe 1</option>
<option value="2">Gruppe 2</option>
<option value="3">Gruppe 3</option>
</select>
</div></li>
<li><div class="input-field container">
<label>Datensatzauswahl</label>
<br>
<select>
<option value="0">aktuell</option>
<option value="1">Datensatz 1</option>
<option value="2">Datensatz 2</option>
<option value="3">Datensatz 3</option>
<option value="4">Datensatz 4</option>
<option value="5">Datensatz 5</option>
</select>
<br>
</div></li>
<li><div class="divider"></div></li>
<li><form action="upload">
<br>
<div class="file-field input-field container">
<div class="btn blue-grey lighten-2">
<span>File</span>
<input type="file">
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text" placeholder="Datei hochladen">
</div>
</div>
</form></li>
<li><div class="container">
<a class="waves-effect waves-light btn blue-grey lighten-2 right"><i class="material-icons left">file_upload</i>Hochladen</a>
</div></li>
</ul>
<!--Cards-->
<!--Analyse-Cards-->
<div id="analysis" class="row container">
<!--Übersicht-->
<div class="col s12 m12">
<div id="overview" class="card white">
<div class="center">
<span class="card-title center">Übersicht</span>
</div>
<div class="card-action center">
<a href="#">Tagesübersicht</a>
<a href="#">Wochenübersicht</a>
</div>
<div class="card-content">
<div>
<canvas id="overview_chart" width="1000" height="400"></canvas>
</div>
</div>
</div>
</div>
<!--Einkaufszeiten-->
<div class="col s12 m6">
<div id="shoping_times" class="card white">
<div class="card-content">
<div class="center">
<span class="card-title">Einkaufszeiten</span>
</div>
<div class="center">
<div class="input-field col s12">
<select>
<option value="0">Montag</option>
<option value="1">Dienstag</option>
<option value="2">Mittwoch</option>
<option value="3">Donnerstag</option>
<option value="4">Freitag</option>
<option value="5">Samstag</option>
<option value="6">Sonntag</option>
</select>
<label>Wochentag</label>
</div>
</div>
<div>
<canvas id="shoping_times_chart" width="1000" height="400"></canvas>
</div>
</div>
</div>
</div>
<!--Verkaufte Artikel-->
<div class="col s12 m6">
<div id="sold_articles" class="card white">
<div class="card-content">
<div class="center">
<span class="card-title center">Verkaufte Artikel</span>
</div>
<div class="center">
<div class="input-field col s12">
<select>
<option value="0">Artikel 1</option>
<option value="1">Artikel 2</option>
<option value="2">Artikel 3</option>
</select>
<label>Artikel</label>
</div>
</div>
<div>
<canvas id="sold_articles_cake" width="1000" height="400"></canvas>
</div>
</div>
</div>
</div>
<!--Top Artikel-->
<div class="col s12 m6">
<div id="top_articles" class="card white">
<div class="center">
<span class="card-title center">Top Artikel</span>
</div>
<div class="card-action center">
<a href="#">Umsatz</a>
<a href="#">Stückzahl</a>
</div>
<div class="card-content">
<div>
<canvas id="top_articles_chart" width="1000" height="400"></canvas>
</div>
</div>
</div>
</div>
<!--Flop Artikel-->
<div class="col s12 m6">
<div id="flop_articles" class="card white">
<div class="center">
<span class="card-title center">Flop Artikel</span>
</div>
<div class="card-action center">
<a href="#">Umsatz</a>
<a href="#">Stückzahl</a>
</div>
<div class="card-content">
<div>
<canvas id="flop_articles_chart" width="1000" height="400"></canvas>
</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-->
<div class="col s12 m6">
<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>
</div>
</div>
</div>
</div>
</div>
<!--Marketing-Cards-->
<div id="marketing" class="row container">
<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 id="marketing_tip_3" class="card white">
<div class="card-content">
<div class="center">
<span class="card-title center">Marketingmaßnahme 3</span>
</div>
<div>
<p>Coming Soon</p>
</div>
</div>
</div>
</div>
<div class="col s12 m6">
<div id="marketing_tip_4" class="card white">
<div class="card-content">
<div class="center">
<span class="card-title center">Marketingmaßnahme 4</span>
</div>
<div>
<p>Coming Soon</p>
</div>
</div>
</div>
</div>
</div>
<!--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="js/overview.js"></script>
<script src="js/shoping_times.js"></script>
<script src="js/sold_articles.js"></script>
<script src="js/top_articles.js"></script>
<script src="js/flop_articles.js"></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 type="text/javascript" src="js/materialize_components.js"></script>
<!--Ende Skriptbereich-->
</body>
</html>

View File

@ -1,55 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Registrierung</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" />
<!--Let browser know website is optimized for mobile-->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body class="blue-grey lighten-5">
<div class="container">
<!--Login-->
<div class="center">
<div class="card white">
<div class="center">
<span class="card-title center">Regstrierung</span>
</div>
<div class="card-content">
<div>
<img style="width: 100%" src="../logoOrginal.gif">
</div>
<div class="divider"></div>
<div class="row">
<div class="col s12">
<input id="login" type="text">
<label for="login">Login-Name</label>
</div>
<div class="col s12">
<input id="password" type="text">
<label for="password">Passwort</label>
</div>
</div>
</div>
<div class="card-action right">
<a href="#">Bestätigen</a>
<a href="../login.html">Zurück</a>
</div>
</div>
</div>
</div>
<!--Anfang Skriptbereich-->
<!--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 type="text/javascript" src="js/materialize_components.js"></script>
<!--Ende Skriptbereich-->
</body>
</html>

View File

@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test;
import java.io.File; import java.io.File;
import java.net.URL; import java.net.URL;
import java.time.Duration; import java.time.Duration;
import java.util.AbstractMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -24,15 +25,15 @@ class ShoppingTimesTest {
assert url != null; assert url != null;
ShoppingTimes overview = new ShoppingTimes(new File(url.getFile())); ShoppingTimes overview = new ShoppingTimes(new File(url.getFile()));
AtomicReference<Map<DayHour, Integer>> results = new AtomicReference<>(); AtomicReference<Map<Map.Entry<String, String>, Integer>> results = new AtomicReference<>();
assertTimeout(Duration.ofMillis(1700), () -> results.set(overview.getResult())); assertTimeout(Duration.ofMillis(1700), () -> results.set(overview.getResult()));
assertEquals(4, results.get().get(new DayHour("Montag", "10-12 Uhr"))); assertEquals(4, results.get().get(new AbstractMap.SimpleEntry<>("Montag", "10-12 Uhr")));
assertEquals(9, results.get().get(new DayHour("Samstag", "12-14 Uhr"))); assertEquals(9, results.get().get(new AbstractMap.SimpleEntry<>("Samstag", "12-14 Uhr")));
assertEquals(1, results.get().get(new DayHour("Donnerstag", "14-17 Uhr"))); assertEquals(1, results.get().get(new AbstractMap.SimpleEntry<>("Donnerstag", "14-17 Uhr")));
assertEquals(3, results.get().get(new DayHour("Mittwoch", ">17 Uhr"))); assertEquals(3, results.get().get(new AbstractMap.SimpleEntry<>("Mittwoch", ">17 Uhr")));
assertEquals(6, results.get().get(new DayHour("Dienstag", "<10 Uhr"))); assertEquals(6, results.get().get(new AbstractMap.SimpleEntry<>("Dienstag", "<10 Uhr")));
} }
@Test @Test
@ -41,15 +42,15 @@ class ShoppingTimesTest {
assert url != null; assert url != null;
ShoppingTimes overview = new ShoppingTimes(new File(url.getFile())); ShoppingTimes overview = new ShoppingTimes(new File(url.getFile()));
AtomicReference<Map<DayHour, Integer>> results = new AtomicReference<>(); AtomicReference<Map<Map.Entry<String, String>, Integer>> results = new AtomicReference<>();
assertTimeout(Duration.ofMillis(3000), () -> results.set(overview.getResult())); assertTimeout(Duration.ofMillis(3000), () -> results.set(overview.getResult()));
assertEquals(37, results.get().get(new DayHour("Montag", "10-12 Uhr"))); assertEquals(37, results.get().get(new AbstractMap.SimpleEntry<>("Montag", "10-12 Uhr")));
assertEquals(87, results.get().get(new DayHour("Samstag", "12-14 Uhr"))); assertEquals(87, results.get().get(new AbstractMap.SimpleEntry<>("Samstag", "12-14 Uhr")));
assertEquals(23, results.get().get(new DayHour("Donnerstag", "14-17 Uhr"))); assertEquals(23, results.get().get(new AbstractMap.SimpleEntry<>("Donnerstag", "14-17 Uhr")));
assertEquals(18, results.get().get(new DayHour("Mittwoch", ">17 Uhr"))); assertEquals(18, results.get().get(new AbstractMap.SimpleEntry<>("Mittwoch", ">17 Uhr")));
assertEquals(18, results.get().get(new DayHour("Dienstag", "<10 Uhr"))); assertEquals(18, results.get().get(new AbstractMap.SimpleEntry<>("Dienstag", "<10 Uhr")));
} }
@Test @Test
@ -58,14 +59,14 @@ class ShoppingTimesTest {
assert url != null; assert url != null;
ShoppingTimes overview = new ShoppingTimes(new File(url.getFile())); ShoppingTimes overview = new ShoppingTimes(new File(url.getFile()));
AtomicReference<Map<DayHour, Integer>> results = new AtomicReference<>(); AtomicReference<Map<Map.Entry<String, String>, Integer>> results = new AtomicReference<>();
assertTimeout(Duration.ofMillis(3000), () -> results.set(overview.getResult())); assertTimeout(Duration.ofMillis(3000), () -> results.set(overview.getResult()));
assertEquals(303, results.get().get(new DayHour("Montag", "10-12 Uhr"))); assertEquals(303, results.get().get(new AbstractMap.SimpleEntry<>("Montag", "10-12 Uhr")));
assertEquals(737, results.get().get(new DayHour("Samstag", "12-14 Uhr"))); assertEquals(737, results.get().get(new AbstractMap.SimpleEntry<>("Samstag", "12-14 Uhr")));
assertEquals(192, results.get().get(new DayHour("Donnerstag", "14-17 Uhr"))); assertEquals(192, results.get().get(new AbstractMap.SimpleEntry<>("Donnerstag", "14-17 Uhr")));
assertEquals(199, results.get().get(new DayHour("Mittwoch", ">17 Uhr"))); assertEquals(199, results.get().get(new AbstractMap.SimpleEntry<>("Mittwoch", ">17 Uhr")));
assertEquals(149, results.get().get(new DayHour("Dienstag", "<10 Uhr"))); assertEquals(149, results.get().get(new AbstractMap.SimpleEntry<>("Dienstag", "<10 Uhr")));
} }
} }