diff --git a/src/main/java/de/hsel/spm/baudas/analysis/ShoppingCart.java b/src/main/java/de/hsel/spm/baudas/analysis/ShoppingCart.java index 3f01c76..6e5b443 100644 --- a/src/main/java/de/hsel/spm/baudas/analysis/ShoppingCart.java +++ b/src/main/java/de/hsel/spm/baudas/analysis/ShoppingCart.java @@ -27,14 +27,12 @@ public class ShoppingCart implements Analysis, List>> { @Override public Map, List> 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, List>> { e.printStackTrace(); } + Apriori model = new Apriori(); + model.setTreatZeroAsMissing(true); try { model.buildAssociations(instances); @@ -67,19 +67,19 @@ public class ShoppingCart implements Analysis, List>> { 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 l1 = new ArrayList<>(); - for(Item item : rule.getPremise()){ + for (Item item : rule.getPremise()) { l1.add(item.getAttribute().name()); } List 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); } diff --git a/src/main/java/de/hsel/spm/baudas/analysis/WeekOverview.java b/src/main/java/de/hsel/spm/baudas/analysis/WeekOverview.java index 7c54043..acb1f8d 100644 --- a/src/main/java/de/hsel/spm/baudas/analysis/WeekOverview.java +++ b/src/main/java/de/hsel/spm/baudas/analysis/WeekOverview.java @@ -36,30 +36,20 @@ public class WeekOverview implements Analysis(); - 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 ) ); } diff --git a/src/main/java/de/hsel/spm/baudas/web/AuthenticationFilter.java b/src/main/java/de/hsel/spm/baudas/web/AuthenticationFilter.java index d262553..df5f74f 100644 --- a/src/main/java/de/hsel/spm/baudas/web/AuthenticationFilter.java +++ b/src/main/java/de/hsel/spm/baudas/web/AuthenticationFilter.java @@ -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,27 +20,29 @@ 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 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. + * @param chain This parameter allows passing request along the chain of potential handlers until one of them handles the request. * @throws ServletException Defines a general exception a servlet can throw when it encounters difficulty. - * @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. */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { @@ -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"); diff --git a/src/main/java/de/hsel/spm/baudas/web/ClusterDiagram.java b/src/main/java/de/hsel/spm/baudas/web/ClusterDiagram.java new file mode 100644 index 0000000..4da9a7f --- /dev/null +++ b/src/main/java/de/hsel/spm/baudas/web/ClusterDiagram.java @@ -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> 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)); + } +} diff --git a/src/main/java/de/hsel/spm/baudas/web/Columns.java b/src/main/java/de/hsel/spm/baudas/web/Columns.java new file mode 100644 index 0000000..9b00650 --- /dev/null +++ b/src/main/java/de/hsel/spm/baudas/web/Columns.java @@ -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; + } +} diff --git a/src/main/java/de/hsel/spm/baudas/web/Data.java b/src/main/java/de/hsel/spm/baudas/web/Data.java index 6c8d159..88c3d32 100644 --- a/src/main/java/de/hsel/spm/baudas/web/Data.java +++ b/src/main/java/de/hsel/spm/baudas/web/Data.java @@ -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 files = new ConcurrentLinkedQueue<>(); + private static ConcurrentMap> files = new ConcurrentHashMap<>(); /** @@ -33,42 +37,55 @@ public class Data { * @return path to save file to */ @NotNull - static Path add(@NotNull String name) { - //cleanup old files - if (files.isEmpty()) { - try { - Files.list(Paths.get("")).forEach(path -> { - if (path.toFile().getName().endsWith(".csv")) { + 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")) { + 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(); - } + } + }); + } 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; } /** diff --git a/src/main/java/de/hsel/spm/baudas/web/ErrorCode.java b/src/main/java/de/hsel/spm/baudas/web/ErrorCode.java new file mode 100644 index 0000000..7fa1d53 --- /dev/null +++ b/src/main/java/de/hsel/spm/baudas/web/ErrorCode.java @@ -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; + } +} diff --git a/src/main/java/de/hsel/spm/baudas/web/Files.java b/src/main/java/de/hsel/spm/baudas/web/Files.java index 9688f07..362ef89 100644 --- a/src/main/java/de/hsel/spm/baudas/web/Files.java +++ b/src/main/java/de/hsel/spm/baudas/web/Files.java @@ -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()))); } } \ No newline at end of file diff --git a/src/main/java/de/hsel/spm/baudas/web/LoginServlet.java b/src/main/java/de/hsel/spm/baudas/web/LoginServlet.java index ce33667..e6571e0 100644 --- a/src/main/java/de/hsel/spm/baudas/web/LoginServlet.java +++ b/src/main/java/de/hsel/spm/baudas/web/LoginServlet.java @@ -23,10 +23,11 @@ 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 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. - * @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 doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { @@ -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"); diff --git a/src/main/java/de/hsel/spm/baudas/web/LogoutServlet.java b/src/main/java/de/hsel/spm/baudas/web/LogoutServlet.java index 165f851..cf04180 100644 --- a/src/main/java/de/hsel/spm/baudas/web/LogoutServlet.java +++ b/src/main/java/de/hsel/spm/baudas/web/LogoutServlet.java @@ -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 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() + "/"); diff --git a/src/main/java/de/hsel/spm/baudas/web/ShoppingCartDiagram.java b/src/main/java/de/hsel/spm/baudas/web/ShoppingCartDiagram.java new file mode 100644 index 0000000..9e62281 --- /dev/null +++ b/src/main/java/de/hsel/spm/baudas/web/ShoppingCartDiagram.java @@ -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 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> result = new HashMap<>(); + result = cart.getResult(); + + List col1 = new ArrayList<>(); + List col2 = new ArrayList<>(); + + for(Map.Entry, List> 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)); + } +} diff --git a/src/main/java/de/hsel/spm/baudas/web/TopFlopArticleDiagram.java b/src/main/java/de/hsel/spm/baudas/web/TopFlopArticleDiagram.java index 2d6692f..0e0abe3 100644 --- a/src/main/java/de/hsel/spm/baudas/web/TopFlopArticleDiagram.java +++ b/src/main/java/de/hsel/spm/baudas/web/TopFlopArticleDiagram.java @@ -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 labels = new ArrayList<>(); List data = new ArrayList<>(); - for(Map.Entry entry : map.entrySet()) { + for (Map.Entry entry : map.entrySet()) { labels.add(entry.getKey()); data.add(entry.getValue().toString()); } diff --git a/src/main/java/de/hsel/spm/baudas/web/Upload.java b/src/main/java/de/hsel/spm/baudas/web/Upload.java index 28f7c9d..18f61ec 100644 --- a/src/main/java/de/hsel/spm/baudas/web/Upload.java +++ b/src/main/java/de/hsel/spm/baudas/web/Upload.java @@ -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(""); } } \ No newline at end of file diff --git a/src/main/webapp/emptyHeader.jsp b/src/main/webapp/emptyHeader.jsp new file mode 100644 index 0000000..d779ff1 --- /dev/null +++ b/src/main/webapp/emptyHeader.jsp @@ -0,0 +1,13 @@ + +<%@include file="head.jsp"%> + + + +
diff --git a/src/main/webapp/error.jsp b/src/main/webapp/error.jsp new file mode 100644 index 0000000..a137dce --- /dev/null +++ b/src/main/webapp/error.jsp @@ -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); +%> + +
+
+
+
+ <%= errorCode.getMessage()%> +
+
+
+
+<% + } +%> + + +<%@include file="theming.jsp"%> \ No newline at end of file diff --git a/src/main/webapp/footer.jsp b/src/main/webapp/footer.jsp index 5e91340..c6fa151 100644 --- a/src/main/webapp/footer.jsp +++ b/src/main/webapp/footer.jsp @@ -1,24 +1,18 @@ - + - - - - - +<%@include file="theming.jsp"%> - -<%----%> - + + diff --git a/src/main/webapp/head.jsp b/src/main/webapp/head.jsp new file mode 100644 index 0000000..fab1aeb --- /dev/null +++ b/src/main/webapp/head.jsp @@ -0,0 +1,21 @@ +<% response.setCharacterEncoding(StandardCharsets.UTF_8.name()); %> +<% request.setCharacterEncoding(StandardCharsets.UTF_8.name()); %> + + + + BauDas + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp index 6879491..25064ff 100644 --- a/src/main/webapp/index.jsp +++ b/src/main/webapp/index.jsp @@ -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()); %> -
+
@@ -16,7 +16,7 @@
- +
@@ -63,86 +63,134 @@
- -
-
-
-
- Warenkorbanalyse -
-
-

Coming Soon

-
-
-
-
- -
+
Gruppenübersicht
-

Coming Soon

+ +
-
-
-
- -
+ +
+
+
+ Warenkorbanalyse +
+
+ +
+
+
+
-
-
-
-
- Marketingmaßnahme 1 + +
+
+
+
+
+
+ Werbung +
+
+

+ 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. +
+
+ Analysen, die für Werbung nützlich sein könnten: + Clusteranalyse, Wochenübersicht, Verkaufszahlen +

+
+
+
+
+
+
+
+
+ Sortimentsveränderung +
+
+

+ 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. +
+
+ Analysen, die bei der Sortimentswahl nützlich sein könnten: + Verkaufszahlen, Clusterübersicht +

+
+
+
+
-
-

Coming Soon

+
+
+
+
+
+ Rabatt +
+
+

+ 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. +
+
+ Analysen, die zur Platzierung von Rabatten nützlich sein können: + Wochenübersicht, Einkaufszeiten, Verkaufszahlen +

+
+
+
+
+
+
+
+
+ Produktplatzierung +
+
+

+ 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. +
+
+ Analysen, die bei der Produktplatzierung hilfreich sein könnten: + Warenkorbsanalyse, Verkaufszahlen +

+
+
+
+
-
-
-
-
-
-
- Marketingmaßnahme 2 -
-
-

Coming Soon

-
-
-
-
-
-
-
-
- Marketingmaßnahme 3 -
-
-

Coming Soon

-
-
-
-
-
-
-
-
- Marketingmaßnahme 4 -
-
-

Coming Soon

-
-
-
-
-
-<%@include file="footer.jsp"%> \ No newline at end of file + +<%@include file="footer.jsp" %> \ No newline at end of file diff --git a/src/main/webapp/js/cache.js b/src/main/webapp/js/cache.js index 8876e59..a7acfb1 100644 --- a/src/main/webapp/js/cache.js +++ b/src/main/webapp/js/cache.js @@ -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. } diff --git a/src/main/webapp/js/clusters.js b/src/main/webapp/js/clusters.js new file mode 100644 index 0000000..f0dfd22 --- /dev/null +++ b/src/main/webapp/js/clusters.js @@ -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 = $(""); + row.append(""); + row.append(""); + for (x in cluster_result){ + if(x == 0){ + row.append("" + "ø" + ""); + }else{ + row.append("" + x + ""); + } + } + row.append(""); + row.append(""); + + + appendClusterTable(row, "Alter"); + appendClusterTable(row, "Wohnort"); + appendClusterTable(row, "Einkaufstag"); + appendClusterTable(row, "Geschlecht"); + appendClusterTable(row, "Familienstand"); + appendClusterTable(row, "Einkaufsuhrzeit"); + + + row.append(""); + $("#cluster_table").append(row); +} + +function appendClusterTable(row, attrib){ + row.append(""); + row.append(""); + row.append("" + attrib + ""); + for (x in cluster_result){ + row.append($("" + cluster_result[x][attrib] +"")); + } + row.append(""); +} diff --git a/src/main/webapp/js/shopping_cart.js b/src/main/webapp/js/shopping_cart.js new file mode 100644 index 0000000..8edb9c5 --- /dev/null +++ b/src/main/webapp/js/shopping_cart.js @@ -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 = $(""); + row.append(""); + row.append("Artikel"); + row.append("Wird häufig zusammen gekauft mit") + row.append(""); + row.append(""); + row.append(""); + for (x in shopping_cart_result){ + row.append(""); + row.append("" + shopping_cart_result[x].col1 + ""); + row.append("" + shopping_cart_result[x].col2 + ""); + row.append(""); + } + row.append(""); + $("#shopping_cart_table").append(row); +} + diff --git a/src/main/webapp/js/week_overview.js b/src/main/webapp/js/week_overview.js index ee3d77c..522a3b1 100644 --- a/src/main/webapp/js/week_overview.js +++ b/src/main/webapp/js/week_overview.js @@ -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 €' + } + } + ] } } }); diff --git a/src/main/webapp/header.jsp b/src/main/webapp/menu.jsp similarity index 67% rename from src/main/webapp/header.jsp rename to src/main/webapp/menu.jsp index 5cd9784..c028fc5 100644 --- a/src/main/webapp/header.jsp +++ b/src/main/webapp/menu.jsp @@ -1,26 +1,6 @@ -<% response.setCharacterEncoding(StandardCharsets.UTF_8.name()); %> -<% request.setCharacterEncoding(StandardCharsets.UTF_8.name()); %> - - - - BauDas - - - - - - - - - +<%@include file="head.jsp"%> - - - @@ -96,4 +76,4 @@
- \ No newline at end of file + diff --git a/src/main/webapp/theming.jsp b/src/main/webapp/theming.jsp new file mode 100644 index 0000000..4df5f5e --- /dev/null +++ b/src/main/webapp/theming.jsp @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/test/java/de/hsel/spm/baudas/analysis/WeekOverviewTest.java b/src/test/java/de/hsel/spm/baudas/analysis/WeekOverviewTest.java index 2ed832e..5a18db5 100644 --- a/src/test/java/de/hsel/spm/baudas/analysis/WeekOverviewTest.java +++ b/src/test/java/de/hsel/spm/baudas/analysis/WeekOverviewTest.java @@ -30,8 +30,7 @@ class WeekOverviewTest { AtomicReference>> 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>> 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>> 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()); } }