diff --git a/.idea/misc.xml b/.idea/misc.xml index c3f3b0ab1ba765e267b1979ac687f9786d56644c..e42c9ea07099a590458543d35140e277f349c1a2 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -7,7 +7,7 @@ </list> </option> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/out" /> </component> </project> \ No newline at end of file diff --git a/.idea/runConfigurations/App.xml b/.idea/runConfigurations/App.xml new file mode 100644 index 0000000000000000000000000000000000000000..1616e183f65e8e8d5a0773860b33ed96643df3b7 --- /dev/null +++ b/.idea/runConfigurations/App.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="App" type="Application" factoryName="Application"> + <option name="MAIN_CLASS_NAME" value="com.example.App" /> + <module name="ad-auction-dashboard" /> + <option name="VM_PARAMETERS" value="--module-path "$USER_HOME$/.m2/repository/org/openjfx/javafx-controls/17.0.2/javafx-controls-17.0.2-win.jar;$USER_HOME$/.m2/repository/org/openjfx/javafx-graphics/17.0.2/javafx-graphics-17.0.2-win.jar;$USER_HOME$/.m2/repository/org/openjfx/javafx-base/17.0.2/javafx-base-17.0.2-win.jar;$USER_HOME$/.m2/repository/org/openjfx/javafx-fxml/17.0.2/javafx-fxml-17.0.2-win.jar" --add-modules javafx.controls,javafx.fxml,javafx.graphics" /> + <extension name="coverage"> + <pattern> + <option name="PATTERN" value="com.example.*" /> + <option name="ENABLED" value="true" /> + </pattern> + </extension> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2d59d634e439e2fecef77356edb1d63e371fe32e..65973ee15b99d0cddbf1991674b00f284f0f3d7f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> @@ -12,9 +12,10 @@ <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <javafx.version>21.0.2</javafx.version> + <javafx.version>17.0.2</javafx.version> </properties> + <dependencies> <dependency> <groupId>org.openjfx</groupId> @@ -46,8 +47,22 @@ <artifactId>log4j-core</artifactId> <version>2.20.0</version> </dependency> + <dependency> + <groupId>org.jfree</groupId> + <artifactId>org.jfree.chart.fx</artifactId> + <version>2.0.1</version> + </dependency> + <dependency> + <groupId>org.jfree</groupId> + <artifactId>jfreechart</artifactId> + <version>1.5.3</version> + </dependency> + <dependency> + <groupId>org.xerial</groupId> + <artifactId>sqlite-jdbc</artifactId> + <version>3.36.0.3</version> + </dependency> </dependencies> - <build> <plugins> <plugin> @@ -73,6 +88,10 @@ </executions> <configuration> <mainClass>com.example.App</mainClass> + <options> + <option>--add-modules</option> + <option>javafx.controls,javafx.fxml,javafx.graphics,javafx.web</option> + </options> </configuration> </plugin> <plugin> @@ -82,4 +101,4 @@ </plugin> </plugins> </build> -</project> \ No newline at end of file +</project> diff --git a/src/main/java/com/example/App.java b/src/main/java/com/example/App.java index 62d96f9c98ecaafdb4d3a8e8bb5eaec3f5446446..e41db8ecd0be6bf48ccad7b938da882fc16644e2 100644 --- a/src/main/java/com/example/App.java +++ b/src/main/java/com/example/App.java @@ -14,12 +14,20 @@ public class App extends Application { private static final Logger logger = LogManager.getLogger(App.class); private Stage stage; - private ServerLog serverLog; + /** + * Main method to enter the app. + * @param args + */ + public static void main(String[] args){ logger.info("Launching app"); launch(); } + /** + * Start method called when JavaFX is launched. + */ + @Override public void start(Stage stage) throws Exception { instance = this; @@ -27,10 +35,9 @@ public class App extends Application { StackPane root = new StackPane(); Label welcomeMessage = new Label("Ad Auction"); root.getChildren().add(welcomeMessage); - Label serverStats = new Label("Number of Bounces: " + this.serverLog.getNumberOfBounces() + "\nNumber of Conversions: " + this.serverLog.getNumberOfConversions()); - root.getChildren().add(serverStats); Scene scene = new Scene(root, 800, 600); stage.setScene(scene); + stage.setTitle("Login"); stage.show(); logger.info("App started"); Login login = new Login(stage); @@ -38,15 +45,19 @@ public class App extends Application { logger.info("Login completed"); } + /** + * Display input files page after login. + */ + public void showInputFilesPage(){ InputFilesPage inputFilesPage = new InputFilesPage(stage); inputFilesPage.show(); logger.info("Input files page"); } - public void init() { - this.serverLog = new ServerLog("../../server_log.csv"); - } + /** + * A getter method to globally access the app instance. + */ public static App getInstance() { return instance; } diff --git a/src/main/java/com/example/ChartCreator.java b/src/main/java/com/example/ChartCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..13680581a67757fbf1ad670712aeabd6efbbb733 --- /dev/null +++ b/src/main/java/com/example/ChartCreator.java @@ -0,0 +1,831 @@ +package com.example; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.CategoryAxis; +import org.jfree.chart.axis.CategoryLabelPositions; +import org.jfree.chart.plot.CategoryPlot; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.renderer.category.LineAndShapeRenderer; +import org.jfree.data.category.DefaultCategoryDataset; + +import java.awt.*; +import java.awt.geom.Ellipse2D; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.*; + +public class ChartCreator { + + private ArrayList<String[]> clicks; + private ArrayList<String[]> impressions; + private ArrayList<String[]> interactions; + private Filter filter; + + public ChartCreator(LogManager logManager) { + this.clicks = logManager.getClickData(); + this.impressions = logManager.getImpressionData(); + this.interactions = logManager.getServerData(); + this.filter = new Filter(); + } + + public void getClicks() { + System.out.println(this.clicks); + } + + /** + * Generates chart for total clicks + */ + public JFreeChart genClickChart(String time,String gender,String income,ArrayList<String> context,ArrayList<String> age) { + Map<String,Integer> clickMap = new TreeMap<>(); + ArrayList<String[]> filteredClicks= filter.filterPipeline(clicks,impressions,interactions,gender,income,context,age)[0]; + if (time.equals("Daily")) { + clickMap = getDailyClicks(filteredClicks); + } else if (time.equals("Weekly")) { + clickMap = getWeeklyClicks(filteredClicks); + } else if (time.equals("Monthly")) { + clickMap = getMonthlyClicks(filteredClicks); + } + + DefaultCategoryDataset dataset = createIntegerDataset(clickMap); + System.out.println("Row count: " + dataset.getRowCount()); + System.out.println("Column count: " + dataset.getColumnCount()); + return(createChart("Total Clicks","Clicks",dataset)); + } + + /** + * Generates chart for total impressions + * @return + */ + public JFreeChart genImpressionChart(String timeFlag,String gender,String income,ArrayList<String> context,ArrayList<String> age) { + Map<String,Integer> impressionMap = new TreeMap<>(); + ArrayList<String[]> filteredImpressions= filter.filterPipeline(clicks,impressions,interactions,gender,income,context,age)[1]; + + if (timeFlag.equals("Daily")) { + impressionMap = getDailyImpressions(filteredImpressions); + } else if (timeFlag.equals("Weekly")) { + impressionMap = getWeeklyImpressions(filteredImpressions); + } else if (timeFlag.equals("Monthly")) { + impressionMap = getMonthlyImpressions(filteredImpressions); + } + + var dataset = createIntegerDataset(impressionMap); + return(createChart("Total impressions","Impressions",dataset)); + } + + /** + * Create chart for total uniques + * @return + */ + public JFreeChart genUniquesChart(String timeFlag,String gender,String income,ArrayList<String> context,ArrayList<String> age) { + Map<String,Integer> clickMap = new TreeMap<>(); + ArrayList<String[]> filteredClicks= filter.filterPipeline(clicks,impressions,interactions,gender,income,context,age)[0]; + + if (timeFlag.equals("Daily")) { + clickMap = getDailyUniques(filteredClicks); + } else if (timeFlag.equals("Weekly")) { + clickMap = getWeeklyUniques(filteredClicks); + } else if (timeFlag.equals("Monthly")) { + clickMap = getMonthlyUniques(filteredClicks); + } + + var dataset = createIntegerDataset(clickMap); + return(createChart("Total Uniques","Uniques",dataset)); + } + + /** + * Create chart for total bounces + * @return + */ + public JFreeChart genBounceChart(String timeFlag,String gender,String income,ArrayList<String> context,ArrayList<String> age) { + Map<String,Integer> interactionMap = new TreeMap<>(); + ArrayList<String[]> filteredInteractions= filter.filterPipeline(clicks,impressions,interactions,gender,income,context,age)[2]; + + if (timeFlag.equals("Daily")) { + interactionMap = getDailyBounces(filteredInteractions); + } else if (timeFlag.equals("Weekly")) { + interactionMap = getWeeklyBounces(filteredInteractions); + } else if (timeFlag.equals("Monthly")) { + interactionMap = getMonthlyBounces(filteredInteractions); + } + + var dataset = createIntegerDataset(interactionMap); + return(createChart("Total Bounces","Bounces",dataset)); + } + /** + * Create chart for total conversions + * @return + */ + public JFreeChart genConversionChart(String timeFlag,String gender,String income,ArrayList<String> context,ArrayList<String> age) { + Map<String,Integer> conversionMap = new TreeMap<>(); + ArrayList<String[]> filteredInteractions= filter.filterPipeline(clicks,impressions,interactions,gender,income,context,age)[2]; + + if (timeFlag.equals("Daily")) { + conversionMap = getDailyConversions(filteredInteractions); + } else if (timeFlag.equals("Weekly")) { + conversionMap = getWeeklyConversions(filteredInteractions); + }else if (timeFlag.equals("Monthly")) { + conversionMap = getMonthlyConversions(filteredInteractions); + } + + var dataset = createIntegerDataset(conversionMap); + return(createChart("Total conversions","Conversions",dataset)); + } + + + /** + * Create chart for total cost + * @return + */ + public JFreeChart genCostChart(String timeFlag,String gender,String income,ArrayList<String> context,ArrayList<String> age) { + Map<String,Float> costMap = new TreeMap<>(); + ArrayList<String[]> [] filteredLogs = filter.filterPipeline(clicks,impressions,interactions,gender,income,context,age); + ArrayList<String[]> filteredClicks = filteredLogs[0]; + ArrayList<String[]> filteredImpressions = filteredLogs[1]; + + if (timeFlag.equals("Daily")) { + costMap = getDailyCost(filteredClicks,filteredImpressions); + } else if (timeFlag.equals("Weekly")) { + costMap = getWeeklyCost(filteredClicks,filteredImpressions); + } else if (timeFlag.equals("Monthly")) { + costMap = getMonthlyCost(filteredClicks,filteredImpressions); + } + + var dataset = createFloatDataset(costMap); + return(createChart("Total cost","Cost",dataset)); + + } + + /** + * Create chart for CTR + * @return + */ + public JFreeChart genCTRChart(String timeFlag,String gender, String income,ArrayList<String> context,ArrayList<String> age) { + Map<String,Integer> clickMap = new TreeMap<>(); + Map<String,Integer> impressionMap = new TreeMap<>(); + Map<String,Float> ctrMap = new TreeMap<>(); + ArrayList<String[]> [] filteredLogs = filter.filterPipeline(clicks,impressions,interactions,gender,income,context,age); + ArrayList<String[]> filteredClicks = filteredLogs[0]; + ArrayList<String[]> filteredImpressions = filteredLogs[1]; + + if(timeFlag.equals("Daily")) { + clickMap = getDailyClicks(filteredClicks); + impressionMap = getDailyImpressions(filteredImpressions); + } else if (timeFlag.equals("Weekly")){ + clickMap = getWeeklyClicks(filteredClicks); + impressionMap = getWeeklyImpressions(filteredImpressions); + } else if (timeFlag.equals("Monthly")){ + clickMap = getMonthlyClicks(filteredClicks); + impressionMap = getMonthlyImpressions(filteredImpressions); + } + + for(String date : impressionMap.keySet()) { + int clicks = clickMap.getOrDefault(date,0); + int impressions = impressionMap.get(date); + float ctr = (float) clicks/impressions; + ctrMap.put(date,ctr); + } + + var dataset = createFloatDataset(ctrMap); + return(createChart("CTR", "Clicks per impression", dataset)); + } + + /** + * Create chart for CPA + * @return + */ + public JFreeChart genCPAChart(String timeFlag,String gender,String income,ArrayList<String> context,ArrayList<String> age) { + Map<String,Integer> conversionMap = new TreeMap<>(); + Map<String,Float> costMap = new TreeMap<>(); + Map<String,Float> cpaMap = new TreeMap<>(); + ArrayList<String[]> [] filteredLogs = filter.filterPipeline(clicks,impressions,interactions,gender,income,context,age); + ArrayList<String[]> filteredClicks = filteredLogs[0]; + ArrayList<String[]> filteredImpressions = filteredLogs[1]; + ArrayList<String[]> filteredInteractions = filteredLogs[2]; + + + if (timeFlag.equals("Daily")) { + conversionMap = getDailyConversions(filteredInteractions); + costMap = getDailyCost(filteredClicks, filteredImpressions); + } else if (timeFlag.equals("Weekly")) { + conversionMap = getWeeklyConversions(filteredInteractions); + costMap = getWeeklyCost(filteredClicks,filteredImpressions); + } else if (timeFlag.equals("Monthly")) { + conversionMap = getMonthlyConversions(filteredInteractions); + costMap = getMonthlyCost(filteredClicks,filteredImpressions); + } + + for(String date : conversionMap.keySet()) { + float cost = costMap.getOrDefault(date,(float) 0); + int conversions = conversionMap.get(date); + float cpa = cost/conversions; + cpaMap.put(date,cpa); + } + + var dataset = createFloatDataset(cpaMap); + return(createChart("CPA","Cost per conversion",dataset)); + } + + /** + * Create chart for CPC + * @return + */ + public JFreeChart genCPCChart(String timeFlag, String gender,String income,ArrayList<String> context,ArrayList<String> age) { + Map<String,Integer> clickMap = new TreeMap<>(); + Map<String,Float> costMap = new TreeMap<>(); + Map<String,Float> cpcMap = new TreeMap<>(); + ArrayList<String[]> [] filteredLogs = filter.filterPipeline(clicks,impressions,interactions,gender,income,context,age); + ArrayList<String[]> filteredClicks = filteredLogs[0]; + ArrayList<String[]> filteredImpressions = filteredLogs[1]; + + + if (timeFlag.equals("Daily")) { + clickMap = getDailyClicks(filteredClicks); + costMap = getDailyCost(filteredClicks, filteredImpressions); + } else if (timeFlag.equals("Weekly")) { + clickMap = getWeeklyClicks(filteredClicks); + costMap = getWeeklyCost(filteredClicks,filteredImpressions); + } else if (timeFlag.equals("Monthly")) { + clickMap = getMonthlyClicks(filteredClicks); + costMap = getMonthlyCost(filteredClicks,filteredImpressions); + } + + for(String date : clickMap.keySet()) { + float cost = costMap.getOrDefault(date,(float) 0); + int clicks = clickMap.get(date); + float cpc = cost/clicks; + cpcMap.put(date,cpc); + } + + var dataset = createFloatDataset(cpcMap); + return(createChart("CPC","Cost per click",dataset)); + } + + /** + * Generate chart for CPM + * @return + */ + public JFreeChart genCPMChart(String timeFlag,String gender,String income,ArrayList<String> context,ArrayList<String> age) { + Map<String,Integer> impressionMap = new TreeMap<>(); + Map<String,Float> costMap = new TreeMap<>(); + Map<String,Float> cpmMap = new TreeMap<>(); + ArrayList<String[]> [] filteredLogs = filter.filterPipeline(clicks,impressions,interactions,gender,income,context,age); + ArrayList<String[]> filteredClicks = filteredLogs[0]; + ArrayList<String[]> filteredImpressions = filteredLogs[1]; + + + if (timeFlag.equals("Daily")) { + impressionMap = getDailyImpressions(filteredImpressions); + costMap = getDailyCost(filteredClicks, filteredImpressions); + } else if (timeFlag.equals("Weekly")) { + impressionMap = getWeeklyImpressions(filteredImpressions); + costMap = getWeeklyCost(filteredClicks,filteredImpressions); + } else if (timeFlag.equals("Monthly")) { + impressionMap = getMonthlyImpressions(filteredImpressions); + costMap = getMonthlyCost(filteredClicks,filteredImpressions); + } + + //Convert impressions into thousands + Map<String,Float> impressionThousandMap = new TreeMap<>(); + for (Map.Entry<String,Integer> entry : impressionMap.entrySet()) { + impressionThousandMap.put(entry.getKey(),(float) entry.getValue()/1000); + } + + for(String date : impressionThousandMap.keySet()) { + float cost = costMap.get(date); + float thousandImpressions = impressionThousandMap.get(date); + float cpm = cost/thousandImpressions; + cpmMap.put(date,cpm); + } + + var dataset = createFloatDataset(cpmMap); + return(createChart("CPM","Cost per thousand impressions",dataset)); + } + + /** + * Create chart for bounce rate + * @return + */ + public JFreeChart genBounceRateChart(String timeFlag, String gender,String income,ArrayList<String> context,ArrayList<String> age) { + Map<String,Integer> clickMap = new TreeMap<>(); + Map<String,Integer> bounceMap = new TreeMap<>(); + Map<String,Float> bounceRateMap = new TreeMap<>(); + ArrayList<String[]> [] filteredLogs = filter.filterPipeline(clicks,impressions,interactions,gender,income,context,age); + ArrayList<String[]> filteredClicks = filteredLogs[0]; + ArrayList<String[]> filteredInteractions = filteredLogs[2]; + + if (timeFlag.equals("Daily")) { + clickMap = getDailyClicks(filteredClicks); + bounceMap = getDailyBounces(filteredInteractions); + } else if (timeFlag.equals("Weekly")) { + clickMap = getWeeklyClicks(filteredClicks); + bounceMap = getWeeklyBounces(filteredInteractions); + } else if (timeFlag.equals("Monthly")) { + clickMap = getMonthlyClicks(filteredClicks); + bounceMap = getMonthlyBounces(filteredInteractions); + } + + for (String date : clickMap.keySet()) { + int clicks = clickMap.get(date); + int bounces = bounceMap.get(date); + float bounceRate = (float) bounces/clicks; + bounceRateMap.put(date,bounceRate); + } + + var dataset = createFloatDataset(bounceRateMap); + return(createChart("Bounce Rate","Bounces per click",dataset)); + + } + + + /** + * Reusable method for creating chart + * @param name + * @param metric + * @param dataset + * @return + */ + public JFreeChart createChart(String name, String metric, DefaultCategoryDataset dataset) { + JFreeChart chart = ChartFactory.createLineChart( + name, + "Date", + metric, + dataset, + PlotOrientation.VERTICAL, + false, + true, + false + ); + // Adjusting font + CategoryPlot plot = chart.getCategoryPlot(); + CategoryAxis xAxis = plot.getDomainAxis(); + xAxis.setTickLabelFont(new Font("Arial", Font.PLAIN, 9)); // Change font size here + xAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90); + + //Creating marker dot at plot points + LineAndShapeRenderer renderer = new LineAndShapeRenderer(); + renderer.setSeriesShapesVisible(0, true); + renderer.setSeriesLinesVisible(0, true); + Shape dot = new Ellipse2D.Double(-5, -5, 10, 10); + renderer.setSeriesShape(0, dot); + plot.setRenderer(renderer); + + return chart; + } + + /** + * Creates dataset with float values + * @param map + * @return + */ + public DefaultCategoryDataset createFloatDataset(Map<String,Float> map) { + DefaultCategoryDataset dataset = new DefaultCategoryDataset(); + for (Map.Entry<String, Float> entry: map.entrySet()) { + dataset.addValue(entry.getValue(), "Entry", entry.getKey()); + } + return dataset; + } + + /** + * Creates dataset with integer values + * @param map + * @return + */ + public DefaultCategoryDataset createIntegerDataset(Map<String,Integer> map) { + DefaultCategoryDataset dataset = new DefaultCategoryDataset(); + for (Map.Entry<String, Integer> entry: map.entrySet()) { + dataset.addValue(entry.getValue(), "Entry", entry.getKey()); + } + return dataset; + } + + /** + * Accumulate daily clicks + * @param clicks + * @return + */ + public Map<String,Integer> getDailyClicks (ArrayList<String[]> clicks) { + Map<String,Integer> clickMap = new TreeMap<>(); + for (String[] click : clicks){ + String date = click[0].split(" ")[0]; + clickMap.put(date, clickMap.getOrDefault(date, 1) + 1); + } + return clickMap; + } + + /** + * Accumulate weekly clicks + * @param clicks + * @return + */ + public Map<String,Integer> getWeeklyClicks (ArrayList<String[]> clicks) { + Map<String,Integer> clickMap = new TreeMap<>(); + + LocalDate earliestDate = getEarliestDate(clicks); + for (String[] click : clicks) { + LocalDate date = LocalDate.parse(click[0].split(" ")[0]); + int weekNumber = (int) ChronoUnit.WEEKS.between(earliestDate,date) + 1; + String week = "Week " + Integer.toString(weekNumber); + clickMap.put(week,clickMap.getOrDefault(week,1) + 1); + } + + return clickMap; + } + + /** + * Accumulate monthly clicks + * @param clicks + * @return + */ + public Map<String,Integer> getMonthlyClicks (ArrayList<String[]> clicks) { + Map<String,Integer> clickMap = new TreeMap<>(); + + LocalDate earliestDate = getEarliestDate(clicks); + for (String[] click : clicks) { + LocalDate date = LocalDate.parse(click[0].split(" ")[0]); + int monthNumber = (int) ChronoUnit.MONTHS.between(earliestDate,date) + 1; + String month = "Month " + Integer.toString(monthNumber); + clickMap.put(month,clickMap.getOrDefault(month,1) + 1); + } + + return clickMap; + } + + + /** + * Accumulate daily impressions + * @param impressions + * @return + */ + public Map<String,Integer> getDailyImpressions (ArrayList<String[]> impressions) { + Map<String,Integer> impressionMap = new TreeMap<>(); + for (String[] impression : impressions){ + String date = impression[0].split(" ")[0]; + impressionMap.put(date, impressionMap.getOrDefault(date, 1) + 1); + } + return impressionMap; + } + + /** + * Accumulate weekly impressions + * @param impressions + * @return + */ + public Map<String,Integer> getWeeklyImpressions (ArrayList<String[]> impressions) { + Map<String,Integer> impressionMap = new TreeMap<>(); + + LocalDate earliestDate = getEarliestDate(impressions); + for (String[] impression : impressions) { + LocalDate date = LocalDate.parse(impression[0].split(" ")[0]); + int weekNumber = (int) ChronoUnit.WEEKS.between(earliestDate,date) + 1; + String week = "Week " + Integer.toString(weekNumber); + impressionMap.put(week,impressionMap.getOrDefault(week,1) + 1); + } + return impressionMap; + } + + /** + * Accumulate monthly impressions + * @param impressions + * @return + */ + public Map<String,Integer> getMonthlyImpressions (ArrayList<String[]> impressions) { + Map<String,Integer> impressionMap = new TreeMap<>(); + + LocalDate earliestDate = getEarliestDate(impressions); + for (String[] impression : impressions) { + LocalDate date = LocalDate.parse(impression[0].split(" ")[0]); + int monthNumber = (int) ChronoUnit.MONTHS.between(earliestDate,date) + 1; + String month = "Month " + Integer.toString(monthNumber); + impressionMap.put(month,impressionMap.getOrDefault(month,1) + 1); + } + return impressionMap; + } + + + /** + * Accumulate daily uniques + * @param clicks + * @return + */ + public Map<String,Integer> getDailyUniques (ArrayList<String[]> clicks) { + Map<String,Integer> clickMap = new TreeMap<>(); + Set<String> uniqueIDs = new HashSet<>(); + + for(String[] click : clicks) { + String dateTime = click[0]; + String id = click[1]; + + if (!uniqueIDs.contains(id)) { + uniqueIDs.add(id); + String date = dateTime.split(" ")[0]; + + clickMap.put(date,clickMap.getOrDefault(date,1)+1); + } + } + return clickMap; + } + + /** + * Accumulate weekly uniques + * @param clicks + * @return + */ + public Map<String,Integer> getWeeklyUniques (ArrayList<String[]> clicks) { + Map<String,Integer> clickMap = new TreeMap<>(); + Set<String> uniqueIDs = new HashSet<>(); + + LocalDate earliestDate = getEarliestDate(clicks); + + for(String[] click : clicks) { + LocalDate date = LocalDate.parse(click[0].split(" ")[0]); + int weekNumber = (int) ChronoUnit.WEEKS.between(earliestDate,date) + 1; + String week = "Week " + Integer.toString(weekNumber); + String id = click[1]; + + if (!uniqueIDs.contains(id)) { + uniqueIDs.add(id); + clickMap.put(week,clickMap.getOrDefault(week,1)+1); + } + } + return clickMap; + + } + + /** + * Accumulate monthly uniques + * @param clicks + * @return + */ + public Map<String,Integer> getMonthlyUniques (ArrayList<String[]> clicks) { + Map<String,Integer> clickMap = new TreeMap<>(); + Set<String> uniqueIDs = new HashSet<>(); + + LocalDate earliestDate = getEarliestDate(clicks); + + for(String[] click : clicks) { + LocalDate date = LocalDate.parse(click[0].split(" ")[0]); + int monthNumber = (int) ChronoUnit.MONTHS.between(earliestDate,date) + 1; + String month = "Month " + Integer.toString(monthNumber); + String id = click[1]; + + if (!uniqueIDs.contains(id)) { + uniqueIDs.add(id); + clickMap.put(month,clickMap.getOrDefault(month,1)+1); + } + } + return clickMap; + + } + + /** + * Accumulate daily conversions + * @param interactions + * @return + */ + public Map<String,Integer> getDailyConversions (ArrayList<String[]> interactions) { + Map<String,Integer> conversionMap = new TreeMap<>(); + for (String[] interaction : interactions){ + if (interaction[4].equals("Yes")) { + String date = interaction[0].split(" ")[0]; + conversionMap.put(date, conversionMap.getOrDefault(date, 1) + 1); + } + } + return conversionMap; + } + + /** + * Accumulate weekly conversions + * @param interactions + * @return + */ + public Map<String,Integer> getWeeklyConversions(ArrayList<String[]> interactions) { + Map<String,Integer> conversionMap = new TreeMap<>(); + + LocalDate earliestDate = getEarliestDate(interactions); + for (String[] interaction : interactions) { + if (interaction[4].equals("Yes")) { + LocalDate date = LocalDate.parse(interaction[0].split(" ")[0]); + int weekNumber = (int) ChronoUnit.WEEKS.between(earliestDate,date) + 1; + String week = "Week " + Integer.toString(weekNumber); + conversionMap.put(week,conversionMap.getOrDefault(week,1) + 1); + } + } + return conversionMap; + } + + /** + * Accumulate monthly conversions + * @param interactions + * @return + */ + public Map<String,Integer> getMonthlyConversions(ArrayList<String[]> interactions) { + Map<String,Integer> conversionMap = new TreeMap<>(); + + LocalDate earliestDate = getEarliestDate(interactions); + for (String[] interaction : interactions) { + if (interaction[4].equals("Yes")) { + LocalDate date = LocalDate.parse(interaction[0].split(" ")[0]); + int monthNumber = (int) ChronoUnit.MONTHS.between(earliestDate,date) + 1; + String month = "Month " + Integer.toString(monthNumber); + conversionMap.put(month,conversionMap.getOrDefault(month,1) + 1); + } + } + return conversionMap; + } + + + /** + * Accumulate daily cost + * @param clicks + * @param impressions + * @return + */ + public Map<String,Float> getDailyCost (ArrayList<String[]> clicks, ArrayList<String[]> impressions) { + Map<String,Float> costMap = new TreeMap<>(); + + for (String[] click : clicks) { + String dateTime = click[0]; + String date = dateTime.split(" ")[0]; + float cost = Float.parseFloat(click[2]); + costMap.put(date,costMap.getOrDefault(date,cost) + cost); + } + + for (String[] impression: impressions) { + String dateTime = impression[0]; + String date = dateTime.split(" ")[0]; + float cost = Float.parseFloat(impression[6]); + costMap.put(date,costMap.getOrDefault(date,cost) + cost); + } + + return costMap; + } + + /** + * Accumulate weekly cost + * @param clicks + * @param impressions + * @return + */ + public Map<String,Float> getWeeklyCost (ArrayList<String[]> clicks, ArrayList<String[]> impressions) { + Map<String,Float> costMap = new TreeMap<>(); + + LocalDate earliestDate = getEarliestDate(impressions); + for (String[] click : clicks) { + LocalDate date = LocalDate.parse(click[0].split(" ")[0]); + int weekNumber = (int) ChronoUnit.WEEKS.between(earliestDate,date) + 1; + String week = "Week " + Integer.toString(weekNumber); + float cost = Float.parseFloat(click[2]); + costMap.put(week,costMap.getOrDefault(week,cost) + cost); + } + + for (String[] impression : impressions) { + LocalDate date = LocalDate.parse(impression[0].split(" ")[0]); + int weekNumber = (int) ChronoUnit.WEEKS.between(earliestDate,date) + 1; + String week = "Week " + Integer.toString(weekNumber); + float cost = Float.parseFloat(impression[6]); + costMap.put(week,costMap.getOrDefault(week,cost) + cost); + } + return costMap; + } + + /** + * Accumulate monthly cost + * @param clicks + * @param impressions + * @return + */ + public Map<String,Float> getMonthlyCost (ArrayList<String[]> clicks, ArrayList<String[]> impressions) { + Map<String,Float> costMap = new TreeMap<>(); + + LocalDate earliestDate = getEarliestDate(impressions); + for (String[] click : clicks) { + LocalDate date = LocalDate.parse(click[0].split(" ")[0]); + int monthNumber = (int) ChronoUnit.MONTHS.between(earliestDate,date) + 1; + String month = "Month " + Integer.toString(monthNumber); + float cost = Float.parseFloat(click[2]); + costMap.put(month,costMap.getOrDefault(month,cost) + cost); + } + + for (String[] impression : impressions) { + LocalDate date = LocalDate.parse(impression[0].split(" ")[0]); + int monthNumber = (int) ChronoUnit.MONTHS.between(earliestDate,date) + 1; + String month = "Month " + Integer.toString(monthNumber); + float cost = Float.parseFloat(impression[6]); + costMap.put(month,costMap.getOrDefault(month,cost) + cost); + } + return costMap; + } + + /** + * Accumulate daily bounces + * @param interactions + * @return + */ + public Map<String,Integer> getDailyBounces(ArrayList<String[]> interactions) { + Map<String,Integer> interactionMap = new TreeMap<>(); + + for (String[] interaction : interactions){ + if (interaction[3].equals("1")) { + String dateTime = interaction[0]; + String date = dateTime.split(" ")[0]; + + interactionMap.put(date, interactionMap.getOrDefault(date, 1) + 1); + } + } + return interactionMap; + } + + /** + * Accumulate weekly bounces + * @param interactions + * @return + */ + public Map<String, Integer> getWeeklyBounces(ArrayList<String[]> interactions) { + Map<String,Integer> bounceMap = new TreeMap<>(); + + LocalDate earliestDate = getEarliestDate(interactions); + for (String[] interaction : interactions) { + if (interaction[3].equals("1")) { + LocalDate date = LocalDate.parse(interaction[0].split(" ")[0]); + int weekNumber = (int) ChronoUnit.WEEKS.between(earliestDate,date) + 1; + String week = "Week " + Integer.toString(weekNumber); + bounceMap.put(week,bounceMap.getOrDefault(week,1) + 1); + } + } + return bounceMap; + } + + /** + * Accumulate monthly bounces + * @param interactions + * @return + */ + public Map<String, Integer> getMonthlyBounces(ArrayList<String[]> interactions) { + Map<String,Integer> bounceMap = new TreeMap<>(); + + LocalDate earliestDate = getEarliestDate(interactions); + for (String[] interaction : interactions) { + if (interaction[3].equals("1")) { + LocalDate date = LocalDate.parse(interaction[0].split(" ")[0]); + int monthNumber = (int) ChronoUnit.MONTHS.between(earliestDate,date) + 1; + String month = "Month " + Integer.toString(monthNumber); + bounceMap.put(month,bounceMap.getOrDefault(month,1) + 1); + } + } + return bounceMap; + } + + + + /** + * Get earliest date from set of data entries + * @param data + * @return + */ + public LocalDate getEarliestDate(ArrayList<String[]> data) { + LocalDate firstDate = null; + for(String[] entry : data) { + LocalDate date = LocalDate.parse(entry[0].split(" ")[0]); + if(firstDate == null || date.isBefore(firstDate)) { + firstDate = date; + } + } + return firstDate; + } + + + /** + * Updates chart with time filter applied + * @param currentChart + * @param timeFlag + * @return + */ + public JFreeChart updateChart(String currentChart, String timeFlag, String gender,String income, ArrayList<String> context, ArrayList<String> age) { + if (currentChart.equals("Clicks")) { + return (genClickChart(timeFlag,gender,income,context,age)); + } else if (currentChart.equals("Impressions")) { + return (genImpressionChart(timeFlag,gender,income,context,age)); + } else if (currentChart.equals("Conversions")) { + return (genConversionChart(timeFlag,gender,income,context,age)); + } else if (currentChart.equals("Bounces")) { + return (genBounceChart(timeFlag,gender,income,context,age)); + } else if (currentChart.equals("Cost")) { + return (genCostChart(timeFlag,gender,income,context,age)); + } else if (currentChart.equals("Uniques")) { + return (genUniquesChart(timeFlag,gender,income,context,age)); + } else if (currentChart.equals("BounceRate")){ + return (genBounceRateChart(timeFlag,gender,income,context,age)); + } else if (currentChart.equals("CTR")) { + return (genCTRChart(timeFlag,gender,income,context,age)); + } else if (currentChart.equals("CPA")) { + return (genCPAChart(timeFlag,gender,income,context,age)); + } else if (currentChart.equals("CPC")) { + return (genCPCChart(timeFlag,gender,income,context,age)); + } else { + return (genCPMChart(timeFlag,gender,income,context,age)); + } + + } + +} diff --git a/src/main/java/com/example/ChartPage.java b/src/main/java/com/example/ChartPage.java new file mode 100644 index 0000000000000000000000000000000000000000..0ae07bb5cc15e4a8f425c2312da7c351d65c4b11 --- /dev/null +++ b/src/main/java/com/example/ChartPage.java @@ -0,0 +1,715 @@ +package com.example; + +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.layout.Region; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import javafx.stage.Stage; +import org.jfree.chart.fx.ChartViewer; + +import java.util.ArrayList; +import java.util.List; + +public class ChartPage { + private Stage stage; + private LogManager logManager; + private ChartCreator chartCreator; + private String timeFlag; + private String currentChart; + + private String gender; + private ArrayList<String> age; + private String income; + private ArrayList<String> context; + + // Define style constants + private final String BACKGROUND_COLOR = "#f5f5f7"; + private final String PRIMARY_COLOR = "#4285F4"; + private final String PRIMARY_DARK_COLOR = "#3367d6"; + private final String SECTION_BACKGROUND = "white"; + private final String HEADER_COLOR = "#333333"; + private final String TEXT_COLOR = "#555555"; + + public ChartPage(Stage stage, LogManager logManager) { + this.stage = stage; + this.logManager = logManager; + this.chartCreator = new ChartCreator(logManager); + this.timeFlag = "Daily"; + this.currentChart = "Clicks"; + + this.gender = ""; + this.age = new ArrayList<>(); + this.income = ""; + this.context = new ArrayList<>(); + } + + public void show() { + // Create main layout + BorderPane root = new BorderPane(); + root.setStyle("-fx-background-color: " + BACKGROUND_COLOR + ";"); + + // Chart display area + ChartViewer chartViewer = new ChartViewer(chartCreator.updateChart(currentChart, timeFlag, gender, income, context, age)); + chartViewer.setMaxSize(800, 600); + + // Create a container for the chart, add styling + BorderPane chartContainer = new BorderPane(); + chartContainer.setCenter(chartViewer); + chartContainer.setStyle("-fx-background-color: " + SECTION_BACKGROUND + "; " + + "-fx-background-radius: 10; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 10, 0, 0, 2);"); + chartContainer.setPadding(new Insets(15)); + + // ===== Top navigation bar ===== + HBox navBar = createNavigationBar(); + + // ===== Right side metrics and filter panel ===== + ScrollPane filterPanel = createFilterPanel(chartViewer); + + // ===== Bottom time granularity selector ===== + HBox timeGranularityBar = createTimeGranularityBar(chartViewer); + + // Set layout positions + root.setTop(navBar); + root.setCenter(chartContainer); + root.setRight(filterPanel); + root.setBottom(timeGranularityBar); + + // Set margins + BorderPane.setMargin(chartContainer, new Insets(10, 10, 10, 10)); + BorderPane.setMargin(filterPanel, new Insets(10, 10, 10, 5)); + BorderPane.setMargin(timeGranularityBar, new Insets(5, 10, 15, 10)); + + // Create scene + Scene scene = new Scene(root, 1300, 800); + stage.setScene(scene); + stage.setTitle("Ad Auction Dashboard - Data Charts"); + stage.show(); + } + + // Create top navigation bar + private HBox createNavigationBar() { + HBox navBar = new HBox(15); + navBar.setAlignment(Pos.CENTER_LEFT); + navBar.setPadding(new Insets(15, 20, 15, 20)); + navBar.setStyle("-fx-background-color: " + SECTION_BACKGROUND + "; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 5, 0, 0, 2);"); + + Label title = new Label("Data Visualization"); + title.setFont(Font.font("Arial", FontWeight.BOLD, 20)); + title.setTextFill(Color.web(HEADER_COLOR)); + + Region spacer = new Region(); + HBox.setHgrow(spacer, javafx.scene.layout.Priority.ALWAYS); + + // Button to access overall metrics + Button overallMetricsButton = createStyledButton("Overall Metrics", PRIMARY_COLOR, PRIMARY_DARK_COLOR); + + // Button to compare multiple charts + Button compareChartsButton = createStyledButton("Compare Charts", PRIMARY_COLOR, PRIMARY_DARK_COLOR); + + // Logout button + Button logOutButton = createStyledButton("Logout", "#757575", "#616161"); + + // File selection button + Button fileSelectionButton = createStyledButton("Select Files", "#757575", "#616161"); + + // Set button actions + overallMetricsButton.setOnAction(e -> { + OverallMetricsPage metricsPage = new OverallMetricsPage(stage, logManager); + metricsPage.show(); + }); + + compareChartsButton.setOnAction(e -> { + ArrayList<String> currentCharts = new ArrayList<>(); + currentCharts.add(this.currentChart); + currentCharts.add("Impressions"); // Default second chart + + ArrayList<String> timeFlags = new ArrayList<>(); + timeFlags.add(this.timeFlag); + timeFlags.add("Daily"); // Default time flag for second chart + + ArrayList<String> genders = new ArrayList<>(); + genders.add(this.gender); + genders.add(""); // Default gender for second chart + + ArrayList<String> incomes = new ArrayList<>(); + incomes.add(this.income); + incomes.add(""); // Default income for second chart + + ArrayList<ArrayList<String>> contexts = new ArrayList<>(); + contexts.add(this.context); + contexts.add(new ArrayList<>()); // Default context for second chart + + ArrayList<ArrayList<String>> ages = new ArrayList<>(); + ages.add(this.age); + ages.add(new ArrayList<>()); // Default age for second chart + + MultiChartPage multiChartPage = new MultiChartPage(stage, logManager, currentCharts, timeFlags, genders, incomes, contexts, ages); + multiChartPage.show(); + }); + + logOutButton.setOnAction(e -> { + Login login = new Login(stage); + login.show(); + }); + + fileSelectionButton.setOnAction(e -> { + InputFilesPage inputFilesPage = new InputFilesPage(stage); + inputFilesPage.show(); + }); + + navBar.getChildren().addAll(title, spacer, fileSelectionButton, overallMetricsButton, compareChartsButton, logOutButton); + return navBar; + } + + // Create filter panel + private ScrollPane createFilterPanel(ChartViewer chartViewer) { + VBox filterPanel = new VBox(20); + filterPanel.setPadding(new Insets(20)); + filterPanel.setStyle("-fx-background-color: " + SECTION_BACKGROUND + "; " + + "-fx-background-radius: 10; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 5, 0, 0, 2);"); + + // Title for filter panel + Label filterTitle = new Label("Chart Settings"); + filterTitle.setFont(Font.font("Arial", FontWeight.BOLD, 18)); + filterTitle.setTextFill(Color.web(HEADER_COLOR)); + + // Add different filter sections + VBox metricsSection = createMetricsOptions(chartViewer); + VBox genderSection = createGenderOptions(chartViewer); + VBox incomeSection = createIncomeOptions(chartViewer); + VBox contextSection = createContextOptions(chartViewer); + VBox ageSection = createAgeOptions(chartViewer); + + // Add sections to filter panel + filterPanel.getChildren().addAll( + filterTitle, + new Separator(), + metricsSection, + new Separator(), + genderSection, + new Separator(), + incomeSection, + new Separator(), + contextSection, + new Separator(), + ageSection + ); + + // Create a scroll pane to hold the filter panel + ScrollPane scrollPane = new ScrollPane(filterPanel); + scrollPane.setFitToWidth(true); + scrollPane.setPrefWidth(300); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED); + scrollPane.getStyleClass().add("edge-to-edge"); + + return scrollPane; + } + + // Create time granularity bar + private HBox createTimeGranularityBar(ChartViewer chartViewer) { + HBox timeBar = new HBox(20); + timeBar.setAlignment(Pos.CENTER); + timeBar.setPadding(new Insets(15, 20, 15, 20)); + timeBar.setStyle("-fx-background-color: " + SECTION_BACKGROUND + "; " + + "-fx-background-radius: 10; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 5, 0, 0, 2);"); + + Label timeLabel = new Label("Time Granularity:"); + timeLabel.setFont(Font.font("Arial", FontWeight.BOLD, 14)); + timeLabel.setTextFill(Color.web(HEADER_COLOR)); + + ToggleGroup timeToggleGroup = new ToggleGroup(); + + RadioButton dailyButton = createStyledRadioButton("Daily", timeToggleGroup); + dailyButton.setSelected(true); + + RadioButton weeklyButton = createStyledRadioButton("Weekly", timeToggleGroup); + RadioButton monthlyButton = createStyledRadioButton("Monthly", timeToggleGroup); + + // Set action handlers for time granularity buttons + dailyButton.setOnAction(e -> { + timeFlag = "Daily"; + updateChart(chartViewer); + }); + + weeklyButton.setOnAction(e -> { + timeFlag = "Weekly"; + updateChart(chartViewer); + }); + + monthlyButton.setOnAction(e -> { + timeFlag = "Monthly"; + updateChart(chartViewer); + }); + + timeBar.getChildren().addAll(timeLabel, dailyButton, weeklyButton, monthlyButton); + + return timeBar; + } + + // Create metrics options + private VBox createMetricsOptions(ChartViewer chartViewer) { + VBox metricsBox = new VBox(10); + + Label metricsLabel = new Label("Metrics"); + metricsLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + metricsLabel.setTextFill(Color.web(HEADER_COLOR)); + + ToggleGroup metricsToggleGroup = new ToggleGroup(); + + RadioButton clicksButton = createStyledRadioButton("Total Clicks", metricsToggleGroup); + clicksButton.setSelected(true); + + RadioButton impressionsButton = createStyledRadioButton("Total Impressions", metricsToggleGroup); + RadioButton uniquesButton = createStyledRadioButton("Unique Visitors", metricsToggleGroup); + RadioButton bouncesButton = createStyledRadioButton("Bounces", metricsToggleGroup); + RadioButton conversionsButton = createStyledRadioButton("Conversions", metricsToggleGroup); + RadioButton costButton = createStyledRadioButton("Total Cost", metricsToggleGroup); + + // Create a separator for ratio metrics + Separator separator = new Separator(); + separator.setPadding(new Insets(5, 0, 5, 0)); + + Label ratioLabel = new Label("Ratio Metrics"); + ratioLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + ratioLabel.setTextFill(Color.web(HEADER_COLOR)); + ratioLabel.setPadding(new Insets(5, 0, 5, 0)); + + RadioButton ctrButton = createStyledRadioButton("Click-Through Rate (CTR)", metricsToggleGroup); + RadioButton cpaButton = createStyledRadioButton("Cost Per Acquisition (CPA)", metricsToggleGroup); + RadioButton cpcButton = createStyledRadioButton("Cost Per Click (CPC)", metricsToggleGroup); + RadioButton cpmButton = createStyledRadioButton("Cost Per Mille (CPM)", metricsToggleGroup); + RadioButton bounceRateButton = createStyledRadioButton("Bounce Rate", metricsToggleGroup); + + // Set action handlers for metrics buttons + clicksButton.setOnAction(e -> { + currentChart = "Clicks"; + updateChart(chartViewer); + }); + + impressionsButton.setOnAction(e -> { + currentChart = "Impressions"; + updateChart(chartViewer); + }); + + uniquesButton.setOnAction(e -> { + currentChart = "Uniques"; + updateChart(chartViewer); + }); + + bouncesButton.setOnAction(e -> { + currentChart = "Bounces"; + updateChart(chartViewer); + }); + + conversionsButton.setOnAction(e -> { + currentChart = "Conversions"; + updateChart(chartViewer); + }); + + costButton.setOnAction(e -> { + currentChart = "Cost"; + updateChart(chartViewer); + }); + + ctrButton.setOnAction(e -> { + currentChart = "CTR"; + updateChart(chartViewer); + }); + + cpaButton.setOnAction(e -> { + currentChart = "CPA"; + updateChart(chartViewer); + }); + + cpcButton.setOnAction(e -> { + currentChart = "CPC"; + updateChart(chartViewer); + }); + + cpmButton.setOnAction(e -> { + currentChart = "CPM"; + updateChart(chartViewer); + }); + + bounceRateButton.setOnAction(e -> { + currentChart = "BounceRate"; + updateChart(chartViewer); + }); + + metricsBox.getChildren().addAll( + metricsLabel, + clicksButton, + impressionsButton, + uniquesButton, + bouncesButton, + conversionsButton, + costButton, + separator, + ratioLabel, + ctrButton, + cpaButton, + cpcButton, + cpmButton, + bounceRateButton + ); + + return metricsBox; + } + + // Create gender options + private VBox createGenderOptions(ChartViewer chartViewer) { + VBox genderBox = new VBox(10); + + Label genderLabel = new Label("Gender"); + genderLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + genderLabel.setTextFill(Color.web(HEADER_COLOR)); + + ToggleGroup genderToggleGroup = new ToggleGroup(); + + RadioButton allGenderButton = createStyledRadioButton("All", genderToggleGroup); + allGenderButton.setSelected(true); + + RadioButton maleButton = createStyledRadioButton("Male", genderToggleGroup); + RadioButton femaleButton = createStyledRadioButton("Female", genderToggleGroup); + + // Set action handlers for gender buttons + allGenderButton.setOnAction(e -> { + gender = ""; + updateChart(chartViewer); + }); + + maleButton.setOnAction(e -> { + gender = "Male"; + updateChart(chartViewer); + }); + + femaleButton.setOnAction(e -> { + gender = "Female"; + updateChart(chartViewer); + }); + + genderBox.getChildren().addAll(genderLabel, allGenderButton, maleButton, femaleButton); + + return genderBox; + } + + // Create income options + private VBox createIncomeOptions(ChartViewer chartViewer) { + VBox incomeBox = new VBox(10); + + Label incomeLabel = new Label("Income"); + incomeLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + incomeLabel.setTextFill(Color.web(HEADER_COLOR)); + + ToggleGroup incomeToggleGroup = new ToggleGroup(); + + RadioButton allIncomeButton = createStyledRadioButton("All", incomeToggleGroup); + allIncomeButton.setSelected(true); + + RadioButton lowButton = createStyledRadioButton("Low", incomeToggleGroup); + RadioButton mediumButton = createStyledRadioButton("Medium", incomeToggleGroup); + RadioButton highButton = createStyledRadioButton("High", incomeToggleGroup); + + // Set action handlers for income buttons + allIncomeButton.setOnAction(e -> { + income = ""; + updateChart(chartViewer); + }); + + lowButton.setOnAction(e -> { + income = "Low"; + updateChart(chartViewer); + }); + + mediumButton.setOnAction(e -> { + income = "Medium"; + updateChart(chartViewer); + }); + + highButton.setOnAction(e -> { + income = "High"; + updateChart(chartViewer); + }); + + incomeBox.getChildren().addAll(incomeLabel, allIncomeButton, lowButton, mediumButton, highButton); + + return incomeBox; + } + + // Create context options + private VBox createContextOptions(ChartViewer chartViewer) { + VBox contextBox = new VBox(10); + + Label contextLabel = new Label("Context"); + contextLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + contextLabel.setTextFill(Color.web(HEADER_COLOR)); + + CheckBox newsCheck = createStyledCheckBox("News"); + CheckBox shoppingCheck = createStyledCheckBox("Shopping"); + CheckBox socialMediaCheck = createStyledCheckBox("Social Media"); + CheckBox blogCheck = createStyledCheckBox("Blog"); + CheckBox hobbyCheck = createStyledCheckBox("Hobby"); + CheckBox travelCheck = createStyledCheckBox("Travel"); + + // Button to clear all context filters + Button clearContextButton = new Button("Clear All"); + clearContextButton.setFont(Font.font("Arial", 12)); + clearContextButton.setStyle("-fx-background-color: #f0f0f0; -fx-text-fill: #333333;"); + clearContextButton.setOnAction(e -> { + newsCheck.setSelected(false); + shoppingCheck.setSelected(false); + socialMediaCheck.setSelected(false); + blogCheck.setSelected(false); + hobbyCheck.setSelected(false); + travelCheck.setSelected(false); + context.clear(); + updateChart(chartViewer); + }); + + // Set action handlers for context checkboxes + newsCheck.setOnAction(e -> { + if (newsCheck.isSelected()) { + context.add("News"); + } else { + context.remove("News"); + } + updateChart(chartViewer); + }); + + shoppingCheck.setOnAction(e -> { + if (shoppingCheck.isSelected()) { + context.add("Shopping"); + } else { + context.remove("Shopping"); + } + updateChart(chartViewer); + }); + + socialMediaCheck.setOnAction(e -> { + if (socialMediaCheck.isSelected()) { + context.add("Social Media"); + } else { + context.remove("Social Media"); + } + updateChart(chartViewer); + }); + + blogCheck.setOnAction(e -> { + if (blogCheck.isSelected()) { + context.add("Blog"); + } else { + context.remove("Blog"); + } + updateChart(chartViewer); + }); + + hobbyCheck.setOnAction(e -> { + if (hobbyCheck.isSelected()) { + context.add("Hobby"); + } else { + context.remove("Hobby"); + } + updateChart(chartViewer); + }); + + travelCheck.setOnAction(e -> { + if (travelCheck.isSelected()) { + context.add("Travel"); + } else { + context.remove("Travel"); + } + updateChart(chartViewer); + }); + + HBox clearButtonContainer = new HBox(); + clearButtonContainer.setAlignment(Pos.CENTER_RIGHT); + clearButtonContainer.getChildren().add(clearContextButton); + clearButtonContainer.setPadding(new Insets(5, 0, 0, 0)); + + contextBox.getChildren().addAll( + contextLabel, + newsCheck, + shoppingCheck, + socialMediaCheck, + blogCheck, + hobbyCheck, + travelCheck, + clearButtonContainer + ); + + return contextBox; + } + + // Create age options + private VBox createAgeOptions(ChartViewer chartViewer) { + VBox ageBox = new VBox(10); + + Label ageLabel = new Label("Age"); + ageLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + ageLabel.setTextFill(Color.web(HEADER_COLOR)); + + CheckBox under25Check = createStyledCheckBox("Under 25"); + CheckBox age25to34Check = createStyledCheckBox("25-34"); + CheckBox age35to44Check = createStyledCheckBox("35-44"); + CheckBox age45to54Check = createStyledCheckBox("45-54"); + CheckBox over54Check = createStyledCheckBox("Over 54"); + + // Button to clear all age filters + Button clearAgeButton = new Button("Clear All"); + clearAgeButton.setFont(Font.font("Arial", 12)); + clearAgeButton.setStyle("-fx-background-color: #f0f0f0; -fx-text-fill: #333333;"); + clearAgeButton.setOnAction(e -> { + under25Check.setSelected(false); + age25to34Check.setSelected(false); + age35to44Check.setSelected(false); + age45to54Check.setSelected(false); + over54Check.setSelected(false); + age.clear(); + updateChart(chartViewer); + }); + + // Set action handlers for age checkboxes + under25Check.setOnAction(e -> { + if (under25Check.isSelected()) { + age.add("<25"); + } else { + age.remove("<25"); + } + updateChart(chartViewer); + }); + + age25to34Check.setOnAction(e -> { + if (age25to34Check.isSelected()) { + age.add("25-34"); + } else { + age.remove("25-34"); + } + updateChart(chartViewer); + }); + + age35to44Check.setOnAction(e -> { + if (age35to44Check.isSelected()) { + age.add("35-44"); + } else { + age.remove("35-44"); + } + updateChart(chartViewer); + }); + + age45to54Check.setOnAction(e -> { + if (age45to54Check.isSelected()) { + age.add("45-54"); + } else { + age.remove("45-54"); + } + updateChart(chartViewer); + }); + + over54Check.setOnAction(e -> { + if (over54Check.isSelected()) { + age.add(">54"); + } else { + age.remove(">54"); + } + updateChart(chartViewer); + }); + + HBox clearButtonContainer = new HBox(); + clearButtonContainer.setAlignment(Pos.CENTER_RIGHT); + clearButtonContainer.getChildren().add(clearAgeButton); + clearButtonContainer.setPadding(new Insets(5, 0, 0, 0)); + + ageBox.getChildren().addAll( + ageLabel, + under25Check, + age25to34Check, + age35to44Check, + age45to54Check, + over54Check, + clearButtonContainer + ); + + return ageBox; + } + + // Create a section with title and content + private VBox createSection(String title, VBox content) { + VBox section = new VBox(10); + + Label sectionTitle = new Label(title); + sectionTitle.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + sectionTitle.setTextFill(Color.web(HEADER_COLOR)); + + Separator separator = new Separator(); + separator.setStyle("-fx-opacity: 0.3;"); + + section.getChildren().addAll(sectionTitle, separator, content); + + return section; + } + + // Create styled button + private Button createStyledButton(String text, String bgColor, String hoverColor) { + Button button = new Button(text); + button.setPrefSize(140, 35); + button.setFont(Font.font("Arial", FontWeight.NORMAL, 14)); + + String style = String.format( + "-fx-background-color: %s; " + + "-fx-text-fill: white; " + + "-fx-font-weight: normal; " + + "-fx-background-radius: 5; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 2, 0, 0, 1); " + + "-fx-cursor: hand;", bgColor); + + String hoverStyle = String.format( + "-fx-background-color: %s; " + + "-fx-text-fill: white; " + + "-fx-font-weight: normal; " + + "-fx-background-radius: 5; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 3, 0, 0, 2); " + + "-fx-cursor: hand;", hoverColor); + + button.setStyle(style); + + button.setOnMouseEntered(e -> button.setStyle(hoverStyle)); + button.setOnMouseExited(e -> button.setStyle(style)); + + return button; + } + + // Create styled radio button + private RadioButton createStyledRadioButton(String text, ToggleGroup group) { + RadioButton radioButton = new RadioButton(text); + radioButton.setToggleGroup(group); + radioButton.setFont(Font.font("Arial", 13)); + radioButton.setTextFill(Color.web(TEXT_COLOR)); + return radioButton; + } + + // Create styled checkbox + private CheckBox createStyledCheckBox(String text) { + CheckBox checkBox = new CheckBox(text); + checkBox.setFont(Font.font("Arial", 13)); + checkBox.setTextFill(Color.web(TEXT_COLOR)); + return checkBox; + } + + // Update chart + private void updateChart(ChartViewer chartViewer) { + chartViewer.setChart(chartCreator.updateChart(currentChart, timeFlag, gender, income, context, age)); + } +} + diff --git a/src/main/java/com/example/Click.java b/src/main/java/com/example/Click.java deleted file mode 100644 index 32ac27def8f318685b4a69e2c5b858b09af56f57..0000000000000000000000000000000000000000 --- a/src/main/java/com/example/Click.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.example; - -public class Click { - private String date; - private String id; - private double clickCost; - - public Click(String date, String iD, double clickCost) { - this.date = date; - this.id = iD; - this.clickCost = clickCost; - } - - @Override - public String toString() { - return "Click{" + - "date='" + date + '\'' + - ", id='" + id + '\'' + - ", ClickCost='" + clickCost + '\''+"}"; - } -} diff --git a/src/main/java/com/example/ClickLogReader.java b/src/main/java/com/example/ClickLogReader.java deleted file mode 100644 index 830560f0b72827f7b3c7f4715bc7ce3422a58bb2..0000000000000000000000000000000000000000 --- a/src/main/java/com/example/ClickLogReader.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.example; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.List; - -public class ClickLogReader { - public List<Click> readCSV(String fileName) { - List<Click> clickReport = new ArrayList<>(); - try (BufferedReader clickLogReader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/" + fileName)))) { - String line; - boolean firstLine = true; - while ((line = clickLogReader.readLine()) != null) { - if (firstLine) { - firstLine = false; // Skip header - continue; - } - - String[] values = line.split(","); - if (values.length == 3) { - clickReport.add(new Click(values[0], values[1], Double.parseDouble(values[2]))); - } - } - } catch (IOException | NumberFormatException e) { - e.printStackTrace(); - - } - - return clickReport; - } -} diff --git a/src/main/java/com/example/EditPage.java b/src/main/java/com/example/EditPage.java new file mode 100644 index 0000000000000000000000000000000000000000..259decb6d62f7d345198a65d25f60484134c33b0 --- /dev/null +++ b/src/main/java/com/example/EditPage.java @@ -0,0 +1,26 @@ +package com.example; + +import javafx.stage.Stage; + +import java.util.ArrayList; + +public abstract class EditPage { + private String timeFlag; + private String currentChart; + private String gender; + private ArrayList<String> age; + private String income; + private ArrayList<String> context; + + private LogManager logManager; + private Stage stage; + + + public EditPage(){ + } + + /** + * Display method + */ + public abstract void show(); +} diff --git a/src/main/java/com/example/EditPage1.java b/src/main/java/com/example/EditPage1.java new file mode 100644 index 0000000000000000000000000000000000000000..7e8d5c51c96317d35fbf242a36a76b419262e095 --- /dev/null +++ b/src/main/java/com/example/EditPage1.java @@ -0,0 +1,516 @@ +package com.example; + +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.layout.Region; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import javafx.stage.Stage; + +import java.util.ArrayList; + +public class EditPage1 extends EditPage { + + private ArrayList<String> timeFlag; + private ArrayList<String> currentChart; + private ArrayList<String> gender; + private ArrayList<ArrayList<String>> age; + private ArrayList<String> income; + private ArrayList<ArrayList<String>> context; + + private LogManager logManager; + private Stage stage; + + // Define style constants + private final String BACKGROUND_COLOR = "#f5f5f7"; + private final String PRIMARY_COLOR = "#4285F4"; + private final String PRIMARY_DARK_COLOR = "#3367d6"; + private final String SECTION_BACKGROUND = "white"; + private final String HEADER_COLOR = "#333333"; + private final String TEXT_COLOR = "#555555"; + + public EditPage1(Stage stage, LogManager logManager, ArrayList<String> currentChart, + ArrayList<String> timeFlag, ArrayList<String> gender, ArrayList<String> income, + ArrayList<ArrayList<String>> context, ArrayList<ArrayList<String>> age) { + this.currentChart = currentChart; + this.timeFlag = timeFlag; + this.gender = gender; + this.income = income; + this.context = context; + this.age = age; + this.logManager = logManager; + this.stage = stage; + } + + @Override + public void show() { + // Create main layout + BorderPane root = new BorderPane(); + root.setStyle("-fx-background-color: " + BACKGROUND_COLOR + ";"); + + // Create top navigation bar + HBox navBar = createNavigationBar(); + + // Create central content area + GridPane contentPane = createContentPane(); + + // Set layout + root.setTop(navBar); + root.setCenter(contentPane); + + // Set margins + BorderPane.setMargin(contentPane, new Insets(20, 20, 20, 20)); + + Scene scene = new Scene(root, 1300, 800); + stage.setScene(scene); + stage.setTitle("Ad Auction Dashboard - Edit Chart 1"); + stage.show(); + } + + // Create top navigation bar + private HBox createNavigationBar() { + HBox navBar = new HBox(15); + navBar.setAlignment(Pos.CENTER_LEFT); + navBar.setPadding(new Insets(15, 20, 15, 20)); + navBar.setStyle("-fx-background-color: " + SECTION_BACKGROUND + "; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 5, 0, 0, 2);"); + + Label title = new Label("Edit Chart 1"); + title.setFont(Font.font("Arial", FontWeight.BOLD, 18)); + title.setTextFill(Color.web(HEADER_COLOR)); + + Region spacer = new Region(); + HBox.setHgrow(spacer, javafx.scene.layout.Priority.ALWAYS); + + Button backButton = createStyledButton("Back to Charts", "#757575", "#616161"); + Button overallMetricsButton = createStyledButton("Overall Metrics", PRIMARY_COLOR, PRIMARY_DARK_COLOR); + Button logOutButton = createStyledButton("Logout", "#757575", "#616161"); + + // Set button events + backButton.setOnAction(e -> { + MultiChartPage multiChartPage = new MultiChartPage(stage, logManager, currentChart, timeFlag, gender, income, context, age); + multiChartPage.show(); + }); + + overallMetricsButton.setOnAction(e -> { + OverallMetricsPage metricsPage = new OverallMetricsPage(stage, logManager); + metricsPage.show(); + }); + + logOutButton.setOnAction(e -> { + Login login = new Login(stage); + login.show(); + }); + + // Add to navigation bar + navBar.getChildren().addAll(title, spacer, backButton, overallMetricsButton, logOutButton); + + return navBar; + } + + // Create central content area + private GridPane createContentPane() { + GridPane contentPane = new GridPane(); + contentPane.setHgap(20); + contentPane.setVgap(20); + contentPane.setAlignment(Pos.CENTER); + + // Create filter groups + VBox metricsSection = createMetricsSection(); + VBox timeSection = createTimeSection(); + VBox genderSection = createGenderSection(); + VBox incomeSection = createIncomeSection(); + VBox contextSection = createContextSection(); + VBox ageSection = createAgeSection(); + + // Add groups to grid + contentPane.add(metricsSection, 0, 0); + contentPane.add(timeSection, 1, 0); + contentPane.add(genderSection, 2, 0); + contentPane.add(incomeSection, 3, 0); + contentPane.add(contextSection, 4, 0); + contentPane.add(ageSection, 5, 0); + + return contentPane; + } + + // Create metrics group + private VBox createMetricsSection() { + return createSectionBox("Metrics", createMetricsContent()); + } + + // Create time group + private VBox createTimeSection() { + return createSectionBox("Time Granularity", createTimeContent()); + } + + // Create gender group + private VBox createGenderSection() { + return createSectionBox("Gender", createGenderContent()); + } + + // Create income group + private VBox createIncomeSection() { + return createSectionBox("Income", createIncomeContent()); + } + + // Create context group + private VBox createContextSection() { + return createSectionBox("Context", createContextContent()); + } + + // Create age group + private VBox createAgeSection() { + return createSectionBox("Age", createAgeContent()); + } + + // Create a generic group box + private VBox createSectionBox(String title, VBox content) { + VBox sectionBox = new VBox(15); + sectionBox.setPadding(new Insets(20)); + sectionBox.setMinWidth(200); + sectionBox.setMaxWidth(200); + sectionBox.setMinHeight(400); + sectionBox.setStyle("-fx-background-color: " + SECTION_BACKGROUND + "; " + + "-fx-background-radius: 10; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 5, 0, 0, 2);"); + + Label titleLabel = new Label(title); + titleLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + titleLabel.setTextFill(Color.web(HEADER_COLOR)); + + Separator separator = new Separator(); + separator.setStyle("-fx-opacity: 0.3;"); + + sectionBox.getChildren().addAll(titleLabel, separator, content); + + return sectionBox; + } + + // Create metrics content + private VBox createMetricsContent() { + VBox content = new VBox(10); + ToggleGroup metricOptions = new ToggleGroup(); + + // Create radio buttons for different metrics + RadioButton clickMetric = createStyledRadioButton("Total Clicks", metricOptions); + clickMetric.setSelected(true); + clickMetric.setOnAction(e -> { + this.currentChart.set(0, "Clicks"); + }); + + RadioButton impressionMetric = createStyledRadioButton("Total Impressions", metricOptions); + impressionMetric.setOnAction(e -> { + this.currentChart.set(0, "Impressions"); + }); + + RadioButton uniqueMetric = createStyledRadioButton("Unique Visitors", metricOptions); + uniqueMetric.setOnAction(e -> { + this.currentChart.set(0, "Uniques"); + }); + + RadioButton bouncesMetric = createStyledRadioButton("Bounces", metricOptions); + bouncesMetric.setOnAction(e -> { + this.currentChart.set(0, "Bounces"); + }); + + RadioButton conversionMetric = createStyledRadioButton("Conversions", metricOptions); + conversionMetric.setOnAction(e -> { + this.currentChart.set(0, "Conversions"); + }); + + RadioButton costMetric = createStyledRadioButton("Total Cost", metricOptions); + costMetric.setOnAction(e -> { + this.currentChart.set(0, "Cost"); + }); + + RadioButton ctrMetric = createStyledRadioButton("Click-Through Rate (CTR)", metricOptions); + ctrMetric.setOnAction(e -> { + this.currentChart.set(0, "CTR"); + }); + + RadioButton cpaMetric = createStyledRadioButton("Cost Per Acquisition (CPA)", metricOptions); + cpaMetric.setOnAction(e -> { + this.currentChart.set(0, "CPA"); + }); + + RadioButton cpcMetric = createStyledRadioButton("Cost Per Click (CPC)", metricOptions); + cpcMetric.setOnAction(e -> { + this.currentChart.set(0, "CPC"); + }); + + RadioButton cpmMetric = createStyledRadioButton("Cost Per Mille (CPM)", metricOptions); + cpmMetric.setOnAction(e -> { + this.currentChart.set(0, "CPM"); + }); + + RadioButton bounceRateMetric = createStyledRadioButton("Bounce Rate", metricOptions); + bounceRateMetric.setOnAction(e -> { + this.currentChart.set(0, "BounceRate"); + }); + + content.getChildren().addAll( + clickMetric, impressionMetric, uniqueMetric, bouncesMetric, + conversionMetric, costMetric, ctrMetric, cpaMetric, + cpcMetric, cpmMetric, bounceRateMetric + ); + + return content; + } + + // Create time content + private VBox createTimeContent() { + VBox content = new VBox(10); + ToggleGroup timeOptions = new ToggleGroup(); + + RadioButton dailyButton = createStyledRadioButton("Daily", timeOptions); + dailyButton.setSelected(true); + dailyButton.setOnAction(e -> { + this.timeFlag.set(0, "Daily"); + }); + + RadioButton weeklyButton = createStyledRadioButton("Weekly", timeOptions); + weeklyButton.setOnAction(e -> { + this.timeFlag.set(0, "Weekly"); + }); + + RadioButton monthlyButton = createStyledRadioButton("Monthly", timeOptions); + monthlyButton.setOnAction(e -> { + this.timeFlag.set(0, "Monthly"); + }); + + content.getChildren().addAll(dailyButton, weeklyButton, monthlyButton); + + return content; + } + + // Create gender content + private VBox createGenderContent() { + VBox content = new VBox(10); + ToggleGroup genderOptions = new ToggleGroup(); + + RadioButton bothGenderButton = createStyledRadioButton("All", genderOptions); + bothGenderButton.setSelected(true); + bothGenderButton.setOnAction(e -> { + this.gender.set(0, ""); + }); + + RadioButton maleButton = createStyledRadioButton("Male", genderOptions); + maleButton.setOnAction(e -> { + this.gender.set(0, "Male"); + }); + + RadioButton femaleButton = createStyledRadioButton("Female", genderOptions); + femaleButton.setOnAction(e -> { + this.gender.set(0, "Female"); + }); + + content.getChildren().addAll(bothGenderButton, maleButton, femaleButton); + + return content; + } + + // Create income content + private VBox createIncomeContent() { + VBox content = new VBox(10); + ToggleGroup incomeOptions = new ToggleGroup(); + + RadioButton anyIncomeButton = createStyledRadioButton("All", incomeOptions); + anyIncomeButton.setSelected(true); + anyIncomeButton.setOnAction(e -> { + this.income.set(0, ""); + }); + + RadioButton lowButton = createStyledRadioButton("Low", incomeOptions); + lowButton.setOnAction(e -> { + this.income.set(0, "Low"); + }); + + RadioButton mediumButton = createStyledRadioButton("Medium", incomeOptions); + mediumButton.setOnAction(e -> { + this.income.set(0, "Medium"); + }); + + RadioButton highButton = createStyledRadioButton("High", incomeOptions); + highButton.setOnAction(e -> { + this.income.set(0, "High"); + }); + + content.getChildren().addAll(anyIncomeButton, lowButton, mediumButton, highButton); + + return content; + } + + // Create context content + private VBox createContextContent() { + VBox content = new VBox(10); + + CheckBox newsButton = createStyledCheckBox("News"); + newsButton.setOnAction(e -> { + if (newsButton.isSelected()) { + this.context.get(0).add("News"); + } else { + this.context.get(0).remove("News"); + } + }); + + CheckBox shoppingButton = createStyledCheckBox("Shopping"); + shoppingButton.setOnAction(e -> { + if (shoppingButton.isSelected()) { + this.context.get(0).add("Shopping"); + } else { + this.context.get(0).remove("Shopping"); + } + }); + + CheckBox socialButton = createStyledCheckBox("Social Media"); + socialButton.setOnAction(e -> { + if (socialButton.isSelected()) { + this.context.get(0).add("Social Media"); + } else { + this.context.get(0).remove("Social Media"); + } + }); + + CheckBox blogButton = createStyledCheckBox("Blog"); + blogButton.setOnAction(e -> { + if (blogButton.isSelected()) { + this.context.get(0).add("Blog"); + } else { + this.context.get(0).remove("Blog"); + } + }); + + CheckBox hobbyButton = createStyledCheckBox("Hobby"); + hobbyButton.setOnAction(e -> { + if (hobbyButton.isSelected()) { + this.context.get(0).add("Hobby"); + } else { + this.context.get(0).remove("Hobby"); + } + }); + + CheckBox travelButton = createStyledCheckBox("Travel"); + travelButton.setOnAction(e -> { + if (travelButton.isSelected()) { + this.context.get(0).add("Travel"); + } else { + this.context.get(0).remove("Travel"); + } + }); + + content.getChildren().addAll(newsButton, shoppingButton, socialButton, blogButton, hobbyButton, travelButton); + + return content; + } + + // Create age content + private VBox createAgeContent() { + VBox content = new VBox(10); + + CheckBox age1Button = createStyledCheckBox("Under 25"); + age1Button.setOnAction(e -> { + if (age1Button.isSelected()) { + this.age.get(0).add("<25"); + } else { + this.age.get(0).remove("<25"); + } + }); + + CheckBox age2Button = createStyledCheckBox("25-34"); + age2Button.setOnAction(e -> { + if (age2Button.isSelected()) { + this.age.get(0).add("25-34"); + } else { + this.age.get(0).remove("25-34"); + } + }); + + CheckBox age3Button = createStyledCheckBox("35-44"); + age3Button.setOnAction(e -> { + if (age3Button.isSelected()) { + this.age.get(0).add("35-44"); + } else { + this.age.get(0).remove("35-44"); + } + }); + + CheckBox age4Button = createStyledCheckBox("45-54"); + age4Button.setOnAction(e -> { + if (age4Button.isSelected()) { + this.age.get(0).add("45-54"); + } else { + this.age.get(0).remove("45-54"); + } + }); + + CheckBox age5Button = createStyledCheckBox("Over 54"); + age5Button.setOnAction(e -> { + if (age5Button.isSelected()) { + this.age.get(0).add(">54"); + } else { + this.age.get(0).remove(">54"); + } + }); + + content.getChildren().addAll(age1Button, age2Button, age3Button, age4Button, age5Button); + + return content; + } + + // Create styled radio button + private RadioButton createStyledRadioButton(String text, ToggleGroup group) { + RadioButton radioButton = new RadioButton(text); + radioButton.setToggleGroup(group); + radioButton.setFont(Font.font("Arial", 13)); + radioButton.setTextFill(Color.web(TEXT_COLOR)); + radioButton.setPadding(new Insets(3, 0, 3, 0)); + return radioButton; + } + + // Create styled checkbox + private CheckBox createStyledCheckBox(String text) { + CheckBox checkBox = new CheckBox(text); + checkBox.setFont(Font.font("Arial", 13)); + checkBox.setTextFill(Color.web(TEXT_COLOR)); + checkBox.setPadding(new Insets(3, 0, 3, 0)); + return checkBox; + } + + // Create styled button + private Button createStyledButton(String text, String bgColor, String hoverColor) { + Button button = new Button(text); + button.setPrefSize(120, 35); + button.setFont(Font.font("Arial", FontWeight.NORMAL, 13)); + + String style = String.format( + "-fx-background-color: %s; " + + "-fx-text-fill: white; " + + "-fx-font-weight: normal; " + + "-fx-background-radius: 5; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 2, 0, 0, 1); " + + "-fx-cursor: hand;", bgColor); + + String hoverStyle = String.format( + "-fx-background-color: %s; " + + "-fx-text-fill: white; " + + "-fx-font-weight: normal; " + + "-fx-background-radius: 5; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 3, 0, 0, 2); " + + "-fx-cursor: hand;", hoverColor); + + button.setStyle(style); + + button.setOnMouseEntered(e -> button.setStyle(hoverStyle)); + button.setOnMouseExited(e -> button.setStyle(style)); + + return button; + } +} diff --git a/src/main/java/com/example/EditPage2.java b/src/main/java/com/example/EditPage2.java new file mode 100644 index 0000000000000000000000000000000000000000..eaf58c0e64d6f5cb84e3ff9fdb401f3b8e2804ce --- /dev/null +++ b/src/main/java/com/example/EditPage2.java @@ -0,0 +1,311 @@ +package com.example; + +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; + +import java.util.ArrayList; + +public class EditPage2 extends EditPage{ + private ArrayList<String> timeFlag; + private ArrayList<String> currentChart; + private ArrayList<String> gender; + private ArrayList<ArrayList<String>> age; + private ArrayList<String> income; + private ArrayList<ArrayList<String>> context; + + private LogManager logManager; + private Stage stage; + public EditPage2(Stage stage, LogManager logManager, ArrayList<String> currentChart, + ArrayList<String> timeFlag, ArrayList<String> gender, ArrayList<String> income, + ArrayList<ArrayList<String>> context, ArrayList<ArrayList<String>> age) { + this.currentChart = currentChart; + this.timeFlag = timeFlag; + this.gender = gender; + this.income = income; + this.context = context; + this.age = age; + this.logManager = logManager; + this.stage = stage; + } + + @Override + public void show() { + + //Metric options + ToggleGroup metricOptions = new ToggleGroup(); + + RadioButton clickMetric = new RadioButton("Total Clicks"); + clickMetric.setToggleGroup(metricOptions); + clickMetric.setSelected(true); + clickMetric.setOnAction(e -> { + this.currentChart.set(1,"Clicks"); + }); + RadioButton impressionMetric = new RadioButton("Total Impressions"); + impressionMetric.setToggleGroup(metricOptions); + impressionMetric.setOnAction(e -> { + this.currentChart.set(1,"Impressions"); + }); + RadioButton uniqueMetric = new RadioButton("Total uniques"); + uniqueMetric.setToggleGroup(metricOptions); + uniqueMetric.setOnAction(e -> { + this.currentChart.set(1,"Uniques"); + }); + RadioButton bouncesMetric = new RadioButton("Total bounces"); + bouncesMetric.setToggleGroup(metricOptions); + bouncesMetric.setOnAction(e -> { + this.currentChart.set(1,"Bounces"); + }); + RadioButton conversionMetric = new RadioButton("Total conversions"); + conversionMetric.setToggleGroup(metricOptions); + conversionMetric.setOnAction(e -> { + this.currentChart.set(1,"Conversions"); + }); + RadioButton costMetric = new RadioButton("Total cost"); + costMetric.setToggleGroup(metricOptions); + costMetric.setOnAction(e -> { + this.currentChart.set(1,"Cost"); + }); + RadioButton ctrMetric = new RadioButton("CTR"); + ctrMetric.setToggleGroup(metricOptions); + ctrMetric.setOnAction(e -> { + this.currentChart.set(1,"CTR"); + }); + RadioButton cpaMetric = new RadioButton("CPA"); + cpaMetric.setToggleGroup(metricOptions); + cpaMetric.setOnAction(e -> { + this.currentChart.set(1,"CPA"); + }); + RadioButton cpcMetric = new RadioButton("CPC"); + cpcMetric.setToggleGroup(metricOptions); + cpcMetric.setOnAction(e -> { + this.currentChart.set(1,"CPC"); + }); + RadioButton cpmMetric = new RadioButton("CPM"); + cpmMetric.setToggleGroup(metricOptions); + cpmMetric.setOnAction(e -> { + this.currentChart.set(1,"CPM"); + }); + RadioButton bounceRateMetric = new RadioButton("Bounce Rate"); + bounceRateMetric.setToggleGroup(metricOptions); + bounceRateMetric.setOnAction(e -> { + this.currentChart.set(1,"BounceRate"); + }); + var metricHolder = new VBox(); + var metricLabel = new Label("Metrics"); + metricHolder.getChildren().addAll(metricLabel,clickMetric,impressionMetric,uniqueMetric,bouncesMetric,conversionMetric,costMetric,ctrMetric,cpaMetric,cpcMetric,cpmMetric,bounceRateMetric); + + //Time granularity buttons + ToggleGroup timeOptions = new ToggleGroup(); + + RadioButton dailyButton = new RadioButton("Daily"); + dailyButton.setSelected(true); + dailyButton.setToggleGroup(timeOptions); + dailyButton.setOnAction(e -> { + this.timeFlag.set(1,"Daily"); + }); + RadioButton weeklyButton = new RadioButton("Weekly"); + weeklyButton.setToggleGroup(timeOptions); + weeklyButton.setOnAction(e -> { + this.timeFlag.set(1,"Weekly"); + }); + RadioButton monthlyButton = new RadioButton("Monthly"); + monthlyButton.setToggleGroup(timeOptions); + monthlyButton.setOnAction(e -> { + this.timeFlag.set(1,"Monthly"); + }); + var timeHolder = new VBox(); + var timeTabel = new Label("Time"); + timeHolder.getChildren().addAll(timeTabel,dailyButton,weeklyButton,monthlyButton); + + //Gender Buttons + ToggleGroup genderOptions = new ToggleGroup(); + + RadioButton bothGenderButton = new RadioButton("Both"); + bothGenderButton.setSelected(true); + bothGenderButton.setToggleGroup(genderOptions); + bothGenderButton.setOnAction(e -> { + this.gender.set(1,""); + }); + RadioButton maleButton = new RadioButton("Male"); + maleButton.setToggleGroup(genderOptions); + maleButton.setOnAction(e -> { + this.gender.set(1,"Male"); + }); + RadioButton femaleButton = new RadioButton("Female"); + femaleButton.setToggleGroup(genderOptions); + femaleButton.setOnAction(e -> { + this.gender.set(1,"Female"); + }); + var genderHolder = new VBox(); + var genderLabel = new Label("Gender"); + genderHolder.getChildren().addAll(genderLabel,bothGenderButton,maleButton,femaleButton); + + // Income buttons + ToggleGroup incomeOptions = new ToggleGroup(); + + RadioButton anyIncomeButton = new RadioButton("Any"); + anyIncomeButton.setSelected(true); + anyIncomeButton.setToggleGroup(incomeOptions); + anyIncomeButton.setOnAction(e -> { + this.income.set(1,""); + }); + RadioButton lowButton = new RadioButton("Low"); + lowButton.setToggleGroup(incomeOptions); + lowButton.setOnAction(e -> { + this.income.set(1,"Low"); + }); + RadioButton mediumButton = new RadioButton("Medium"); + mediumButton.setToggleGroup(incomeOptions); + mediumButton.setOnAction(e -> { + this.income.set(1,"Medium"); + }); + RadioButton highButton = new RadioButton("High"); + highButton.setToggleGroup(incomeOptions); + highButton.setOnAction(e -> { + this.income.set(1,"High"); + }); + var incomeHolder = new VBox(); + var incomeLabel = new Label("Income"); + incomeHolder.getChildren().addAll(incomeLabel,anyIncomeButton,lowButton,mediumButton,highButton); + + // Context checkboxes + CheckBox newsButton = new CheckBox("News"); + CheckBox shoppingButton = new CheckBox("Shopping"); + CheckBox socialButton = new CheckBox("Social Media"); + CheckBox blogButton = new CheckBox("Blog"); + CheckBox hobbyButton = new CheckBox("Hobbies"); + CheckBox travelButton = new CheckBox("Travel"); + + newsButton.setOnAction(e -> { + if (newsButton.isSelected()) { + this.context.get(1).add("News"); + } else { + this.context.get(1).remove("News"); + } + }); + shoppingButton.setOnAction(e -> { + if (shoppingButton.isSelected()) { + this.context.get(1).add("Shopping"); + } else { + this.context.get(1).remove("Shopping"); + } + }); + socialButton.setOnAction(e -> { + if (socialButton.isSelected()) { + this.context.get(1).add("Social Media"); + } else { + this.context.get(1).remove("Social Media"); + } + }); + blogButton.setOnAction(e -> { + if (blogButton.isSelected()) { + this.context.get(1).add("Blog"); + } else { + this.context.get(1).remove("Blog"); + } + }); + hobbyButton.setOnAction(e -> { + if (hobbyButton.isSelected()) { + this.context.get(1).add("Hobby"); + } else { + this.context.get(1).remove("Hobby"); + } + }); + travelButton.setOnAction(e -> { + if (travelButton.isSelected()) { + this.context.get(1).add("Travel"); + } else { + this.context.get(1).remove("Travel"); + } + }); + var contextHolder = new VBox(); + var contextLabel = new Label("Context"); + contextHolder.getChildren().addAll(contextLabel,newsButton,socialButton,shoppingButton,blogButton,hobbyButton,travelButton); + + + // Age checkboxes + CheckBox age1Button = new CheckBox("<25"); + CheckBox age2Button = new CheckBox("25-34"); + CheckBox age3Button = new CheckBox("35-44"); + CheckBox age4Button = new CheckBox("45-54"); + CheckBox age5Button = new CheckBox(">54"); + + age1Button.setOnAction(e -> { + if (age1Button.isSelected()) { + this.age.get(1).add("<25"); + } else { + this.age.get(1).remove("<25"); + } + }); + age2Button.setOnAction(e -> { + if (age2Button.isSelected()) { + this.age.get(1).add("25-34"); + } else { + this.age.get(1).remove("25-34"); + } + }); + age3Button.setOnAction(e -> { + if (age3Button.isSelected()) { + this.age.get(1).add("35-44"); + } else { + this.age.get(1).remove("35-44"); + } + }); + age4Button.setOnAction(e -> { + if (age4Button.isSelected()) { + this.age.get(1).add("45-54"); + } else { + this.age.get(1).remove("45-54"); + } + }); + age5Button.setOnAction(e -> { + if (age5Button.isSelected()) { + this.age.get(1).add(">54"); + } else { + this.age.get(1).remove(">54"); + } + }); + var ageHolder = new VBox(); + var ageLabel = new Label("Age"); + ageHolder.getChildren().addAll(ageLabel,age1Button,age2Button,age3Button,age4Button,age5Button); + + //Navigation Buttons + var buttonHolder = new HBox(); + var backButton = new Button("Back"); + var logOutButton = new Button("Logout"); + var overallMetricsButton = new Button("Overall Metrics"); + buttonHolder.getChildren().addAll(backButton, overallMetricsButton, logOutButton); + + backButton.setOnAction(e -> { + MultiChartPage multiChartPage = new MultiChartPage(stage,logManager,currentChart,timeFlag,gender,income,context,age); + multiChartPage.show(); + }); + + overallMetricsButton.setOnAction(e -> { + OverallMetricsPage metricsPage = new OverallMetricsPage(stage, logManager); + metricsPage.show(); + }); + + logOutButton.setOnAction(e -> { + Login login = new Login(stage); + login.show(); + }); + + + var allOptionHolder = new HBox(20); + allOptionHolder.getChildren().addAll(metricHolder,timeHolder,genderHolder,incomeHolder,contextHolder,ageHolder); + BorderPane root = new BorderPane(); + root.setCenter(allOptionHolder); + root.setTop(buttonHolder); + Scene scene = new Scene(root,1300,800); + stage.setScene(scene); + stage.setTitle("Edit Chart 2"); + stage.show(); + + + } +} diff --git a/src/main/java/com/example/Filter.java b/src/main/java/com/example/Filter.java new file mode 100644 index 0000000000000000000000000000000000000000..b18c9c850fde5eec5c2a95bb47976286346fb10b --- /dev/null +++ b/src/main/java/com/example/Filter.java @@ -0,0 +1,137 @@ +package com.example; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; + +public class Filter { + + /** + * Filters impression data by gender + * @param impressions + * @param gender + * @return + */ + public ArrayList<String[]> genderFilter(ArrayList<String[]> impressions, String gender) { + //No gender filters + if (gender.equals("")) { + return impressions; + } else { + //Filter impressions + ArrayList<String[]> filteredImpressions = new ArrayList<>(); + for (String[] row : impressions) { + if (row[2].equals(gender)) { + filteredImpressions.add(row); + } + } return filteredImpressions; + } + } + + /** + * Filter impressions by income + * @param impressions + * @param income + * @return + */ + public ArrayList<String[]> incomeFilter(ArrayList<String[]>impressions, String income) { + if (income.equals("")) { + return impressions; + } else { + //Filter impressions + ArrayList<String[]> filteredImpressions = new ArrayList<>(); + for (String[] row : impressions) { + if (row[4].equals(income)) { + filteredImpressions.add(row); + } + } return filteredImpressions; + } + } + + /** + * Filter impressions by context + * @param impressions + * @param context + * @return + */ + public ArrayList<String[]> contextFilter(ArrayList<String[]> impressions, ArrayList<String> context) { + if (context.size() == 0) { + return impressions; + } else { + ArrayList<String[]> filteredImpressions = new ArrayList<>(); + for (String[] row : impressions) { + if (context.contains(row[5])) { + filteredImpressions.add(row); + } + } return filteredImpressions; + } + } + + /** + * Filter impressions by age + * @param impressions + * @param age + * @return + */ + public ArrayList<String[]> ageFilter(ArrayList<String[]> impressions, ArrayList<String> age) { + if (age.size() == 0) { + return impressions; + } else { + ArrayList<String[]> filteredImpressions = new ArrayList<>(); + for (String[] row : impressions) { + if (age.contains(row[3])) { + filteredImpressions.add(row); + } + } return filteredImpressions; + } + } + + + /** + * Applies all filters to data + * @param clicks + * @param impressions + * @param interactions + * @param gender + * @param income + * @return + */ + public ArrayList<String[]> [] filterPipeline(ArrayList<String[]> clicks, ArrayList<String[]> impressions, ArrayList<String[]> interactions, String gender, String income, ArrayList<String> context,ArrayList<String> age){ + ArrayList<String[]> [] filteredLogs = new ArrayList[3]; + HashSet<String> users = new HashSet<>(); + + // Get filtered impressions and unique IDs + ArrayList<String[]> filteredImpressions = ageFilter(contextFilter(incomeFilter(genderFilter(impressions,gender),income),context),age); + for (String [] entry : filteredImpressions) { + users.add(entry[1]); + } + + //Get associated clicks and interactions + ArrayList<String[]> filteredClicks = idMatch(clicks,users); + ArrayList<String[]> filteredInteractions = idMatch(interactions,users); + + //Add filtered logs to output list + filteredLogs[0] = filteredClicks; + filteredLogs[1] = filteredImpressions; + filteredLogs[2] = filteredInteractions; + + return filteredLogs; + } + + /** + * Matches click log or server log to impression log + * @param log + * @param users + * @return + */ + public ArrayList<String[]> idMatch(ArrayList<String[]> log, HashSet<String> users) { + ArrayList<String[]> filteredLog = new ArrayList<>(); + for (String[] entry : log) { + if (users.contains(entry[1])) { + filteredLog.add(entry); + } + } + return filteredLog; + } + + +} diff --git a/src/main/java/com/example/Impression.java b/src/main/java/com/example/Impression.java deleted file mode 100644 index 15ecf3eec052dad22495fe9967c540174869bffc..0000000000000000000000000000000000000000 --- a/src/main/java/com/example/Impression.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.example; - -/** - * Represents an individual impression (advertisement view) from the CSV file. - * Each instance of this class corresponds to one row in the dataset. - */ -public class Impression { - private String date; // Date when the impression occurred - private String id; // Unique identifier for the impression - private String gender; // Gender of the user who viewed the ad - private String age; // Age group of the user - private String income; // Income level of the user - private String context; // Context in which the ad was displayed (e.g., News, Blog) - private double impressionCost;// Cost of this impression in monetary units - - /** - * Constructor for creating an Impression object. - * - * @param date Date of the impression - * @param id Unique ID of the impression - * @param gender Gender of the viewer - * @param age Age group of the viewer - * @param income Income level of the viewer - * @param context Context where the ad was displayed - * @param impressionCost Cost of the impression - */ - public Impression(String date, String id, String gender, String age, String income, String context, double impressionCost) { - this.date = date; - this.id = id; - this.gender = gender; - this.age = age; - this.income = income; - this.context = context; - this.impressionCost = impressionCost; - } - - /** - * Converts the Impression object into a readable string format. - * - * @return A string representation of the impression details. - */ - @Override - public String toString() { - return "Impression{" + - "date='" + date + '\'' + - ", id='" + id + '\'' + - ", gender='" + gender + '\'' + - ", age='" + age + '\'' + - ", income='" + income + '\'' + - ", context='" + context + '\'' + - ", impressionCost=" + impressionCost + - '}'; - } -} diff --git a/src/main/java/com/example/ImpressionLogReader.java b/src/main/java/com/example/ImpressionLogReader.java deleted file mode 100644 index d2c471071aa21aa9b0f18e70138e77724781313e..0000000000000000000000000000000000000000 --- a/src/main/java/com/example/ImpressionLogReader.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.example; - -import java.io.*; -import java.util.ArrayList; -import java.util.List; - -/** - * Reads the impression_log.csv file and converts each row into an Impression object. - * This class is responsible for loading the dataset into a structured format. - */ -public class ImpressionLogReader { - /** - * Reads a CSV file and parses its contents into a list of Impression objects. - * - * @param fileName The name of the CSV file to read. - * @return A list of Impression objects containing the parsed data. - */ - public List<Impression> readCSV(String fileName) { - List<Impression> impressions = new ArrayList<>(); - - // Try to open the file and read its contents - try (BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/" + fileName)))) { - String line; - boolean firstLine = true; // Flag to skip the header row - - while ((line = br.readLine()) != null) { - if (firstLine) { - firstLine = false; // Skip the first line (column names) - continue; - } - - // Split the line by commas to extract individual values - String[] values = line.split(","); - - // Ensure the row has the expected number of columns before processing - if (values.length == 7) { - impressions.add(new Impression( - values[0], // Date - values[1], // ID - values[2], // Gender - values[3], // Age - values[4], // Income - values[5], // Context - Double.parseDouble(values[6]) // Impression Cost (converted from String to double) - )); - } - } - } catch (IOException | NumberFormatException e) { - e.printStackTrace(); // Print error details if file reading fails - } - - return impressions; // Return the list of impressions - } -} diff --git a/src/main/java/com/example/InputFilesPage.java b/src/main/java/com/example/InputFilesPage.java index bb293b73114f3a1c96575ffbaef11d4237527b14..a06f40a6fbc3751a032b959fa59ddf0466921cac 100644 --- a/src/main/java/com/example/InputFilesPage.java +++ b/src/main/java/com/example/InputFilesPage.java @@ -1,10 +1,15 @@ package com.example; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.*; -import javafx.scene.layout.GridPane; +import javafx.scene.layout.*; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; import javafx.stage.FileChooser; import javafx.stage.Stage; @@ -14,72 +19,124 @@ public class InputFilesPage { private Stage stage; private Scene scene; - private File impressionLogFile; private File clickLogFile; private File serverLogFile; + // Log manager to be passed around the system (Use it in constructors for new scenes), essentially containing all the files and data + public LogManager logManager = new LogManager(); + InputFilesPage(Stage stage) { this.stage = stage; initialize(); } private void initialize() { - GridPane grid = new GridPane(); - grid.setAlignment(Pos.CENTER); - grid.setHgap(10); - grid.setVgap(10); - grid.setPadding(new Insets(25)); - + // Use BorderPane as main layout + BorderPane mainLayout = new BorderPane(); + mainLayout.setPadding(new Insets(20)); + mainLayout.setStyle("-fx-background-color: #f5f5f7;"); + + // Create header section + VBox headerBox = new VBox(15); + headerBox.setAlignment(Pos.CENTER); + headerBox.setPadding(new Insets(20, 0, 40, 0)); + Label titleLabel = new Label("Input Files"); - titleLabel.setStyle("-fx-font-size: 20px; -fx-font-weight: bold;"); - grid.add(titleLabel, 0, 0, 2, 1); - - Label impressionLogLabel = new Label("Impression Log:"); - grid.add(impressionLogLabel, 0, 1); - Button impressionLogButton = new Button("Select File"); - grid.add(impressionLogButton, 1, 1); - - Label clickLogLabel = new Label("Click Log:"); - grid.add(clickLogLabel, 0, 2); - Button clickLogButton = new Button("Select File"); - grid.add(clickLogButton, 1, 2); - - Label serverLogLabel = new Label("Server Log:"); - grid.add(serverLogLabel, 0, 3); - Button serverLogButton = new Button("Select File"); - grid.add(serverLogButton, 1, 3); - - Button proceedButton = new Button("Proceed"); - grid.add(proceedButton, 0, 4); - Button logoutButton = new Button("Logout"); - grid.add(logoutButton, 1, 4); - + titleLabel.setFont(Font.font("Arial", FontWeight.BOLD, 28)); + titleLabel.setTextFill(Color.web("#333333")); + + Label subtitleLabel = new Label("Please select data files for analysis"); + subtitleLabel.setFont(Font.font("Arial", 14)); + subtitleLabel.setTextFill(Color.web("#666666")); + + headerBox.getChildren().addAll(titleLabel, subtitleLabel); + mainLayout.setTop(headerBox); + + // File selection section using VBox + VBox fileSelectionBox = new VBox(20); + fileSelectionBox.setAlignment(Pos.CENTER); + fileSelectionBox.setPadding(new Insets(10, 30, 30, 30)); + fileSelectionBox.setStyle("-fx-background-color: white; -fx-background-radius: 10; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 10, 0, 0, 2);"); + + // Initialize properties + StringProperty impressionProperty = new SimpleStringProperty(""); + StringProperty clickProperty = new SimpleStringProperty(""); + StringProperty serverProperty = new SimpleStringProperty(""); + + // Create file selection rows + HBox impressionRow = createFileSelectionRow("Impression Log:", impressionProperty); + HBox clickRow = createFileSelectionRow("Click Log:", clickProperty); + HBox serverRow = createFileSelectionRow("Server Log:", serverProperty); + + // Create button area + HBox buttonBox = new HBox(20); + buttonBox.setAlignment(Pos.CENTER); + buttonBox.setPadding(new Insets(20, 0, 10, 0)); + + Button proceedButton = createStyledButton("Proceed", "#4285F4", "#3367d6"); + Button logoutButton = createStyledButton("Logout", "#757575", "#616161"); + + buttonBox.getChildren().addAll(proceedButton, logoutButton); + + // Add all components to file selection box + fileSelectionBox.getChildren().addAll( + impressionRow, + createSeparator(), + clickRow, + createSeparator(), + serverRow, + createSeparator(), + buttonBox); + + mainLayout.setCenter(fileSelectionBox); + + // File chooser FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Select Log File"); - + + // Get buttons and set click handlers + Button impressionLogButton = (Button) impressionRow.getChildren().get(1); + Button clickLogButton = (Button) clickRow.getChildren().get(1); + Button serverLogButton = (Button) serverRow.getChildren().get(1); + + // Store the impression log in logManager impressionLogButton.setOnAction(e -> { - clickLogFile = fileChooser.showOpenDialog(stage); - if (clickLogFile != null) { + impressionLogFile = fileChooser.showOpenDialog(stage); + if (impressionLogFile != null) { + impressionProperty.set(impressionLogFile.getName()); + logManager.assignImpressionLog(impressionLogFile); + logManager.convertImpressionLog(); System.out.println("Impression Log Selected: " + impressionLogFile.getAbsolutePath()); } }); + // Store the click log in LogManager clickLogButton.setOnAction(e -> { clickLogFile = fileChooser.showOpenDialog(stage); if (clickLogFile != null) { + clickProperty.set(clickLogFile.getName()); + logManager.assignClickLog(clickLogFile); + logManager.convertClickLog(); System.out.println("Click Log Selected: " + clickLogFile.getAbsolutePath()); } }); + + // Store the server log in LogManager serverLogButton.setOnAction(e -> { serverLogFile = fileChooser.showOpenDialog(stage); if (serverLogFile != null) { + serverProperty.set(serverLogFile.getName()); + logManager.assignServerLog(serverLogFile); + logManager.convertServerLog(); System.out.println("Server Log Selected: " + serverLogFile.getAbsolutePath()); } }); proceedButton.setOnAction(e -> { System.out.println("Proceed Button clicked"); + ChartPage chartPage = new ChartPage(stage, logManager); + chartPage.show(); }); logoutButton.setOnAction(e -> { @@ -88,10 +145,121 @@ public class InputFilesPage { login.show(); }); - scene = new Scene(grid, 600, 400); - + scene = new Scene(mainLayout, 800, 600); + } + + // Create a file selection row + private HBox createFileSelectionRow(String labelText, StringProperty fileNameProperty) { + HBox row = new HBox(15); + row.setAlignment(Pos.CENTER_LEFT); + row.setPadding(new Insets(10, 0, 10, 0)); + + Label label = new Label(labelText); + label.setMinWidth(120); + label.setFont(Font.font("Arial", FontWeight.NORMAL, 14)); + label.setTextFill(Color.web("#333333")); + + Button selectButton = new Button("Select File"); + selectButton.setPrefSize(120, 35); + selectButton.setStyle( + "-fx-background-color: #f0f0f0; " + + "-fx-text-fill: #333333; " + + "-fx-font-weight: normal; " + + "-fx-font-size: 13px; " + + "-fx-background-radius: 4; " + + "-fx-border-radius: 4; " + + "-fx-border-color: #d0d0d0; " + + "-fx-border-width: 1px; " + + "-fx-cursor: hand;"); + + selectButton.setOnMouseEntered(e -> + selectButton.setStyle( + "-fx-background-color: #e8e8e8; " + + "-fx-text-fill: #333333; " + + "-fx-font-weight: normal; " + + "-fx-font-size: 13px; " + + "-fx-background-radius: 4; " + + "-fx-border-radius: 4; " + + "-fx-border-color: #c0c0c0; " + + "-fx-border-width: 1px; " + + "-fx-cursor: hand;")); + + selectButton.setOnMouseExited(e -> + selectButton.setStyle( + "-fx-background-color: #f0f0f0; " + + "-fx-text-fill: #333333; " + + "-fx-font-weight: normal; " + + "-fx-font-size: 13px; " + + "-fx-background-radius: 4; " + + "-fx-border-radius: 4; " + + "-fx-border-color: #d0d0d0; " + + "-fx-border-width: 1px; " + + "-fx-cursor: hand;")); + + Label fileNameLabel = new Label(); + fileNameLabel.textProperty().bind(fileNameProperty); + fileNameLabel.setFont(Font.font("Arial", 13)); + fileNameLabel.setTextFill(Color.web("#666666")); + fileNameLabel.setMinWidth(200); + fileNameLabel.setMaxWidth(300); + + // File status indicator + Region statusIndicator = new Region(); + statusIndicator.setPrefSize(8, 8); + statusIndicator.setStyle("-fx-background-color: #cccccc; -fx-background-radius: 4;"); + + // Update status indicator when file name changes + fileNameProperty.addListener((obs, oldVal, newVal) -> { + if (newVal != null && !newVal.isEmpty()) { + statusIndicator.setStyle("-fx-background-color: #4CAF50; -fx-background-radius: 4;"); + } else { + statusIndicator.setStyle("-fx-background-color: #cccccc; -fx-background-radius: 4;"); + } + }); + + row.getChildren().addAll(label, selectButton, fileNameLabel, statusIndicator); + return row; + } + + // Create separator + private Separator createSeparator() { + Separator separator = new Separator(); + separator.setStyle("-fx-opacity: 0.3;"); + return separator; + } + + // Create styled button + private Button createStyledButton(String text, String bgColor, String hoverColor) { + Button button = new Button(text); + button.setPrefSize(120, 40); + button.setFont(Font.font("Arial", FontWeight.BOLD, 14)); + + String style = String.format( + "-fx-background-color: %s; " + + "-fx-text-fill: white; " + + "-fx-font-weight: bold; " + + "-fx-background-radius: 5; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 3, 0, 0, 1); " + + "-fx-cursor: hand;", bgColor); + + button.setStyle(style); + + button.setOnMouseEntered(e -> + button.setStyle(String.format( + "-fx-background-color: %s; " + + "-fx-text-fill: white; " + + "-fx-font-weight: bold; " + + "-fx-background-radius: 5; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2); " + + "-fx-cursor: hand;", hoverColor))); + + button.setOnMouseExited(e -> button.setStyle(style)); + + return button; } + public void show() { + stage.setTitle("File Selection"); stage.setScene(scene); stage.show(); } diff --git a/src/main/java/com/example/LogManager.java b/src/main/java/com/example/LogManager.java new file mode 100644 index 0000000000000000000000000000000000000000..5abbef965d984826a962fd2929318a7be176fda0 --- /dev/null +++ b/src/main/java/com/example/LogManager.java @@ -0,0 +1,156 @@ +package com.example; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Responsible for handling csv and data conversions + */ +public class LogManager { + private File impressionLog; + private File clickLog; + private File serverLog; + + private ArrayList<String[]> impressionData = new ArrayList<>(); + private ArrayList<String[]> clickData = new ArrayList<>(); + private ArrayList<String[]> serverData = new ArrayList<>(); + + /** + * Stores impression csv + * @param impressionLog + */ + public void assignImpressionLog(File impressionLog) { + this.impressionLog = impressionLog; + } + + /** + * Stores click csv + * @param clickLog + */ + public void assignClickLog(File clickLog) { + this.clickLog = clickLog; + } + + /** + * Stores server csv + * @param serverLog + */ + public void assignServerLog(File serverLog){ + this.serverLog = serverLog; + } + + /** + * Convert click csv into list + */ + public void convertClickLog() { + String line; + String comma = ","; + boolean headerLine = true; + + try (BufferedReader br = new BufferedReader(new FileReader(this.clickLog))) { + while ((line = br.readLine()) != null) { + if (headerLine) { + headerLine = false; + continue; + } + String[] row = line.split(comma); + this.clickData.add(row); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Convert server csv into list + */ + public void convertServerLog() { + String line; + String comma = ","; + boolean headerLine = true; + + try (BufferedReader br = new BufferedReader(new FileReader(this.serverLog))) { + while ((line = br.readLine()) != null) { + if (headerLine) { + headerLine = false; + continue; + } + String[] row = line.split(comma); + this.serverData.add(row); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Convert impression csv into list + */ + public void convertImpressionLog() { + String line; + String comma = ","; + boolean headerLine = true; + + try (BufferedReader br = new BufferedReader(new FileReader(this.impressionLog))) { + while ((line = br.readLine()) != null) { + if (headerLine) { + headerLine = false; + continue; + } + String[] row = line.split(comma); + this.impressionData.add(row); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + + //BELOW HERE ARE ALL GETTER AND PRINTING METHODS + public ArrayList<String[]> getImpressionData(){ + return this.impressionData; + } + + public ArrayList<String[]> getClickData(){ + return this.clickData; + } + + public ArrayList<String[]> getServerData(){ + return this.serverData; + } + + public void printImpressionData() { + for (String[] row : this.impressionData) { + System.out.println(Arrays.toString(row)); + } + } + + public void printClickData() { + for (String[] row : this.clickData) { + System.out.println(Arrays.toString(row)); + } + } + + public void printServerData() { + for (String[] row : this.serverData) { + System.out.println(Arrays.toString(row)); + } + } + + + public File getClickLog() { + return clickLog; + } + + public File getServerLog() { + return serverLog; + } + + public File getImpressionLog() { + return impressionLog; + } +} diff --git a/src/main/java/com/example/Login.java b/src/main/java/com/example/Login.java index 241ae229779c249148880bc8768e511e1f3a609d..a212b438a8e751835b602f6f2349f519516a33c4 100644 --- a/src/main/java/com/example/Login.java +++ b/src/main/java/com/example/Login.java @@ -7,65 +7,212 @@ import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.PasswordField; import javafx.scene.control.TextField; -import javafx.scene.layout.GridPane; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import javafx.scene.text.TextAlignment; import javafx.stage.Stage; -import java.awt.*; - public class Login { + /** + * Reference to primary stage. + * UI for login scene. + */ private Stage stage; private Scene loginScene; + /** + * Constructors for primary stage + initializes the login class. + * @param stage + */ Login(Stage stage) { this.stage = stage; initialize(); } + /** + * Initializing the UI component + Layout. + */ private void initialize() { - GridPane grid = new GridPane(); - grid.setAlignment(Pos.CENTER); - grid.setHgap(10); - grid.setVgap(10); - grid.setPadding(new Insets(25, 25, 25, 25)); - - Label userNameLabel = new Label("Username:"); - grid.add(userNameLabel, 0, 0); - + // Create main layout + BorderPane mainLayout = new BorderPane(); + mainLayout.setStyle("-fx-background-color: #f5f5f7;"); + + // Create title section + VBox headerBox = new VBox(15); + headerBox.setAlignment(Pos.CENTER); + headerBox.setPadding(new Insets(40, 0, 30, 0)); + + Label titleLabel = new Label("Ad Auction Dashboard"); + titleLabel.setFont(Font.font("Arial", FontWeight.BOLD, 32)); + titleLabel.setTextFill(Color.web("#333333")); + + Label subtitleLabel = new Label("Please login to access the system"); + subtitleLabel.setFont(Font.font("Arial", 16)); + subtitleLabel.setTextFill(Color.web("#666666")); + + headerBox.getChildren().addAll(titleLabel, subtitleLabel); + mainLayout.setTop(headerBox); + + // Create login form + VBox loginBox = new VBox(20); + loginBox.setAlignment(Pos.CENTER); + loginBox.setPadding(new Insets(30, 40, 40, 40)); + loginBox.setMaxWidth(400); + loginBox.setStyle("-fx-background-color: white; " + + "-fx-background-radius: 10; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 10, 0, 0, 2);"); + + // Username input area + VBox usernameBox = new VBox(8); + Label userNameLabel = new Label("Username"); + userNameLabel.setFont(Font.font("Arial", FontWeight.NORMAL, 14)); + userNameLabel.setTextFill(Color.web("#333333")); + TextField userName = new TextField(); - grid.add(userName, 1, 0); - - Label passwordLabel = new Label("Password:"); - grid.add(passwordLabel, 0, 1); - + userName.setPromptText("Enter username"); + userName.setPrefHeight(40); + userName.setStyle("-fx-background-radius: 5; " + + "-fx-border-radius: 5; " + + "-fx-border-color: #e0e0e0; " + + "-fx-border-width: 1px; " + + "-fx-font-size: 14px; " + + "-fx-padding: 8px;"); + + usernameBox.getChildren().addAll(userNameLabel, userName); + + // Password input area + VBox passwordBox = new VBox(8); + Label passwordLabel = new Label("Password"); + passwordLabel.setFont(Font.font("Arial", FontWeight.NORMAL, 14)); + passwordLabel.setTextFill(Color.web("#333333")); + PasswordField passwordField = new PasswordField(); - grid.add(passwordField, 1, 1); - - - Button loginButton = new Button("Login"); - grid.add(loginButton, 1, 2); - + passwordField.setPromptText("Enter password"); + passwordField.setPrefHeight(40); + passwordField.setStyle("-fx-background-radius: 5; " + + "-fx-border-radius: 5; " + + "-fx-border-color: #e0e0e0; " + + "-fx-border-width: 1px; " + + "-fx-font-size: 14px; " + + "-fx-padding: 8px;"); + + passwordBox.getChildren().addAll(passwordLabel, passwordField); + + // Message display area Label message = new Label(); - grid.add(message, 1, 3); - + message.setFont(Font.font("Arial", 14)); + message.setTextAlignment(TextAlignment.CENTER); + message.setAlignment(Pos.CENTER); + message.setMinHeight(30); + + // Login button + Button loginButton = new Button("Login"); + loginButton.setPrefSize(320, 45); + loginButton.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + + String buttonStyle = "-fx-background-color: #4285F4; " + + "-fx-text-fill: white; " + + "-fx-font-weight: bold; " + + "-fx-background-radius: 5; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 3, 0, 0, 1); " + + "-fx-cursor: hand;"; + + String buttonHoverStyle = "-fx-background-color: #3367d6; " + + "-fx-text-fill: white; " + + "-fx-font-weight: bold; " + + "-fx-background-radius: 5; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2); " + + "-fx-cursor: hand;"; + + loginButton.setStyle(buttonStyle); + loginButton.setOnMouseEntered(e -> loginButton.setStyle(buttonHoverStyle)); + loginButton.setOnMouseExited(e -> loginButton.setStyle(buttonStyle)); + + // Tip information + Label tipLabel = new Label("Tip: Leave username and password empty to login directly"); + tipLabel.setFont(Font.font("Arial", 12)); + tipLabel.setTextFill(Color.web("#888888")); + tipLabel.setAlignment(Pos.CENTER); + tipLabel.setPadding(new Insets(15, 0, 0, 0)); + + // Add all components to login box + loginBox.getChildren().addAll(usernameBox, passwordBox, message, loginButton, tipLabel); + + // Footer copyright information + HBox footerBox = new HBox(); + footerBox.setAlignment(Pos.CENTER); + footerBox.setPadding(new Insets(20, 0, 20, 0)); + + Label footerLabel = new Label("© 2023 Ad Auction Dashboard System"); + footerLabel.setTextFill(Color.web("#999999")); + footerLabel.setFont(Font.font("Arial", 12)); + + footerBox.getChildren().add(footerLabel); + + // Add components to main layout + mainLayout.setCenter(loginBox); + mainLayout.setBottom(footerBox); + + // Login button event handler loginButton.setOnAction(event -> { String username = userName.getText(); String password = passwordField.getText(); if (authenticate(username, password)) { - message.setText("Login Successful"); + message.setText("Login successful!"); + message.setTextFill(Color.web("#4CAF50")); App.getInstance().showInputFilesPage(); } else { - message.setText("Login Failed"); + message.setText("Login failed!"); + message.setTextFill(Color.web("#F44336")); + // Add slight shake effect + loginBox.setStyle(loginBox.getStyle() + "; -fx-effect: dropshadow(gaussian, rgba(244,67,54,0.1), 10, 0, 0, 2);"); + // Restore after 1 second + new Thread(() -> { + try { + Thread.sleep(1000); + javafx.application.Platform.runLater(() -> { + loginBox.setStyle("-fx-background-color: white; " + + "-fx-background-radius: 10; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 10, 0, 0, 2);"); + }); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }).start(); } }); - loginScene = new Scene(grid, 600, 400); + // Set Enter key to trigger login button + passwordField.setOnAction(loginButton.getOnAction()); + userName.setOnAction(loginButton.getOnAction()); + /** + * Creating a scene containing a grid layout. + */ + loginScene = new Scene(mainLayout, 800, 600); } + + /** + * Method that checks the correct login info. + * @param username set to empty. + * @param password set to empty. + * @return User & Password if true. Otherwise, false. + * Kept the login info as empty for the time being. + */ private boolean authenticate(String username, String password) { - return "admin".equals(username) && "password".equals(password); + return "".equals(username) && "".equals(password); } + + /** + * Display login scene. + */ public void show(){ stage.setScene(loginScene); + stage.setTitle("Ad Auction Dashboard - Login"); stage.show(); } } diff --git a/src/main/java/com/example/MultiChartPage.java b/src/main/java/com/example/MultiChartPage.java new file mode 100644 index 0000000000000000000000000000000000000000..d2a01f3d6f6104c375f31356cc45c364bd569e75 --- /dev/null +++ b/src/main/java/com/example/MultiChartPage.java @@ -0,0 +1,240 @@ +package com.example; + +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.layout.Region; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import javafx.stage.Stage; +import org.jfree.chart.fx.ChartViewer; + +import java.util.ArrayList; + +public class MultiChartPage { + private Stage stage; + private ArrayList<String> currentCharts; + private ArrayList<String> timeFlags; + private ArrayList<String> genders; + private ArrayList<String> incomes; + private ArrayList<ArrayList<String>> contexts; + private ArrayList<ArrayList<String>> ages; + + private ChartCreator chartCreator; + private LogManager logManager; + + // Define style constants + private final String BACKGROUND_COLOR = "#f5f5f7"; + private final String PRIMARY_COLOR = "#4285F4"; + private final String PRIMARY_DARK_COLOR = "#3367d6"; + private final String SECTION_BACKGROUND = "white"; + private final String HEADER_COLOR = "#333333"; + private final String TEXT_COLOR = "#555555"; + + public MultiChartPage(Stage stage, LogManager logManager, ArrayList<String> currentCharts, ArrayList<String> timeFlags, + ArrayList<String> genders, ArrayList<String> incomes, ArrayList<ArrayList<String>> contexts, + ArrayList<ArrayList<String>> ages) { + this.stage = stage; + this.logManager = logManager; + this.chartCreator = new ChartCreator(logManager); + + this.currentCharts = currentCharts; + this.timeFlags = timeFlags; + this.genders = genders; + this.incomes = incomes; + this.contexts = contexts; + this.ages = ages; + } + + public void show() { + // Create main layout + BorderPane root = new BorderPane(); + root.setStyle("-fx-background-color: " + BACKGROUND_COLOR + ";"); + + // Create top navigation bar + HBox navBar = createNavigationBar(); + + // Create chart area + VBox chartSection = createChartSection(); + + // Create bottom edit button area + HBox editBar = createEditButtonBar(); + + // Set layout + root.setTop(navBar); + root.setCenter(chartSection); + root.setBottom(editBar); + + // Set margins + BorderPane.setMargin(chartSection, new Insets(20, 20, 20, 20)); + BorderPane.setMargin(editBar, new Insets(0, 0, 30, 0)); + + Scene scene = new Scene(root, 1300, 800); + stage.setScene(scene); + stage.setTitle("Ad Auction Dashboard - Multi-Chart View"); + stage.show(); + } + + // Create top navigation bar + private HBox createNavigationBar() { + HBox navBar = new HBox(15); + navBar.setAlignment(Pos.CENTER_LEFT); + navBar.setPadding(new Insets(15, 20, 15, 20)); + navBar.setStyle("-fx-background-color: " + SECTION_BACKGROUND + "; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 5, 0, 0, 2);"); + + Label title = new Label("Multi-Chart Comparison View"); + title.setFont(Font.font("Arial", FontWeight.BOLD, 18)); + title.setTextFill(Color.web(HEADER_COLOR)); + + Region spacer = new Region(); + HBox.setHgrow(spacer, javafx.scene.layout.Priority.ALWAYS); + + Button backButton = createStyledButton("Back to Charts", "#757575", "#616161"); + Button overallMetricsButton = createStyledButton("Overall Metrics", PRIMARY_COLOR, PRIMARY_DARK_COLOR); + Button logOutButton = createStyledButton("Logout", "#757575", "#616161"); + + // Set button events + backButton.setOnAction(e -> { + ChartPage chartPage = new ChartPage(stage, logManager); + chartPage.show(); + }); + + overallMetricsButton.setOnAction(e -> { + OverallMetricsPage metricsPage = new OverallMetricsPage(stage, logManager); + metricsPage.show(); + }); + + logOutButton.setOnAction(e -> { + Login login = new Login(stage); + login.show(); + }); + + // Add to navigation bar + navBar.getChildren().addAll(title, spacer, backButton, overallMetricsButton, logOutButton); + + return navBar; + } + + // Create chart area + private VBox createChartSection() { + VBox chartSection = new VBox(30); + chartSection.setAlignment(Pos.CENTER); + + // Add description text + Label descriptionLabel = new Label("Dual Chart View - Compare different metrics or filter conditions simultaneously"); + descriptionLabel.setFont(Font.font("Arial", 14)); + descriptionLabel.setTextFill(Color.web("#666666")); + + // Create chart container + HBox chartContainer = new HBox(30); + chartContainer.setAlignment(Pos.CENTER); + + // Create ChartViewer instance for chart 1 + ChartViewer chartViewer1 = new ChartViewer(chartCreator.updateChart(currentCharts.get(0), timeFlags.get(0), + genders.get(0), incomes.get(0), + contexts.get(0), ages.get(0))); + + // Create ChartViewer instance for chart 2 + ChartViewer chartViewer2 = new ChartViewer(chartCreator.updateChart(currentCharts.get(1), timeFlags.get(1), + genders.get(1), incomes.get(1), + contexts.get(1), ages.get(1))); + + // Create first chart area + VBox chart1Box = createChartBox("Chart 1", chartViewer1); + + // Create second chart area + VBox chart2Box = createChartBox("Chart 2", chartViewer2); + + // Add charts to container + chartContainer.getChildren().addAll(chart1Box, chart2Box); + + // Add all elements to chart section + chartSection.getChildren().addAll(descriptionLabel, chartContainer); + + return chartSection; + } + + // Create single chart box + private VBox createChartBox(String title, ChartViewer chartViewer) { + VBox chartBox = new VBox(10); + chartBox.setAlignment(Pos.CENTER); + chartBox.setPadding(new Insets(15)); + chartBox.setStyle("-fx-background-color: " + SECTION_BACKGROUND + "; " + + "-fx-background-radius: 10; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 10, 0, 0, 2);"); + + // Chart title + Label titleLabel = new Label(title); + titleLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + titleLabel.setTextFill(Color.web(HEADER_COLOR)); + + // Set chart size + chartViewer.setMinSize(550, 400); + chartViewer.setMaxSize(550, 400); + + chartBox.getChildren().addAll(titleLabel, chartViewer); + + return chartBox; + } + + // Create bottom edit button area + private HBox createEditButtonBar() { + HBox editBar = new HBox(20); + editBar.setAlignment(Pos.CENTER); + + Button editChart1Button = createStyledButton("Edit Chart 1", PRIMARY_COLOR, PRIMARY_DARK_COLOR); + Button editChart2Button = createStyledButton("Edit Chart 2", PRIMARY_COLOR, PRIMARY_DARK_COLOR); + + // Set button events + editChart1Button.setOnAction(e -> { + EditPage1 editPage = new EditPage1(stage, logManager, currentCharts, timeFlags, genders, incomes, contexts, ages); + editPage.show(); + }); + + editChart2Button.setOnAction(e -> { + EditPage2 editPage2 = new EditPage2(stage, logManager, currentCharts, timeFlags, genders, incomes, contexts, ages); + editPage2.show(); + }); + + editBar.getChildren().addAll(editChart1Button, editChart2Button); + + return editBar; + } + + // Create styled button + private Button createStyledButton(String text, String bgColor, String hoverColor) { + Button button = new Button(text); + button.setPrefSize(120, 40); + button.setFont(Font.font("Arial", FontWeight.NORMAL, 14)); + + String style = String.format( + "-fx-background-color: %s; " + + "-fx-text-fill: white; " + + "-fx-font-weight: normal; " + + "-fx-background-radius: 5; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 2, 0, 0, 1); " + + "-fx-cursor: hand;", bgColor); + + String hoverStyle = String.format( + "-fx-background-color: %s; " + + "-fx-text-fill: white; " + + "-fx-font-weight: normal; " + + "-fx-background-radius: 5; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 3, 0, 0, 2); " + + "-fx-cursor: hand;", hoverColor); + + button.setStyle(style); + + button.setOnMouseEntered(e -> button.setStyle(hoverStyle)); + button.setOnMouseExited(e -> button.setStyle(style)); + + return button; + } +} diff --git a/src/main/java/com/example/OverallMetricsCalculator.java b/src/main/java/com/example/OverallMetricsCalculator.java new file mode 100644 index 0000000000000000000000000000000000000000..b00a08046b2cf52240edc7c8f5f35b545c8478c2 --- /dev/null +++ b/src/main/java/com/example/OverallMetricsCalculator.java @@ -0,0 +1,162 @@ +package com.example; + +import java.util.ArrayList; +import java.util.HashSet; + + +/** + * Does all calculations for overall metric values + */ +public class OverallMetricsCalculator { + + /** + * Calculate total impressions for campaign + * @param impressionData + * @return + */ + public int calcImpressions (ArrayList<String[]> impressionData) { + int impressions = 0; + for (String[] impression : impressionData){ + impressions += 1; + } + return impressions; + } + + /** + * Calculate total clicks for campaign + * @param clickData + * @return + */ + public int calcClicks (ArrayList<String[]> clickData) { + int clicks = 0; + for (String[] click : clickData){ + clicks += 1; + } + return clicks; + } + + /** + * Calculate total unique clicks for campaign + * @param clickData + * @return + */ + public int calcUniques (ArrayList<String[]> clickData) { + int uniques; + HashSet<String> uniqueID = new HashSet<String>(); + for (String[] click : clickData) { + uniqueID.add(click[1]); + } + uniques = uniqueID.size(); + return uniques; + } + + /** + * Calculate total bounces for campaign + * @param serverData + * @return + */ + public int calcBounces (ArrayList<String[]> serverData) { + int bounces = 0; + for (String[] interaction : serverData) { + if (interaction[3].equals("1")) { + bounces += 1; + } + } + return bounces; + } + + /** + * Calculate total conversions for campaign + * @param serverData + * @return + */ + public int calcConversions (ArrayList<String[]> serverData) { + int conversions = 0; + for (String[] interaction : serverData) { + if (interaction[4].equals("Yes")) { + conversions += 1; + } + } + return conversions; + } + + /** + * Calculates total cost for campaign + * @param clickData + * @param impressionData + * @return + */ + public float calcCost (ArrayList<String[]> clickData, ArrayList<String[]> impressionData) { + float totalCost = 0; + for (String[] click : clickData) { + totalCost += Float.parseFloat(click[2]); + } + for (String[] impression : impressionData) { + totalCost += Float.parseFloat(impression[6]); + } + return totalCost; + } + + /** + * Returns number of clicks per impression for campaign + * @param clickData + * @param impressionData + * @return + */ + public float calcCTR (ArrayList<String[]> clickData, ArrayList<String[]> impressionData) { + int clicks = this.calcClicks(clickData); + int impressions = this.calcImpressions(impressionData); + return ( (float) clicks/impressions); + } + + /** + * Calculate cost per conversion for campaign + * @param clickData + * @param impressionData + * @param serverData + * @return + */ + public float calcCPA(ArrayList<String[]> clickData, ArrayList<String[]> impressionData, ArrayList<String[]> serverData){ + int conversions = calcConversions(serverData); + float cost = calcCost(clickData,impressionData); + return(cost/conversions); + } + + /** + * Calculate cost per click for campaign + * @param clickData + * @param impressionData + * @return + */ + public float calcCPC(ArrayList<String[]> clickData, ArrayList<String[]> impressionData, ArrayList<String[]> serverData) { + int clicks = calcClicks(clickData); + float cost = calcCost(clickData,impressionData); + return (cost/clicks); + } + + /** + * Calculate cost per thousand impressions for campaign + * @param clickData + * @param impressionData + * @return + */ + public float calcCPM(ArrayList<String[]> clickData, ArrayList<String[]> impressionData) { + float cost = calcCost(clickData,impressionData); + float thousandImpressions = (float) this.calcImpressions(impressionData)/1000; + return(cost/thousandImpressions); + } + + + /** + * Calculate bounce rate for campaign + * @param clickData + * @param serverData + * @return + */ + public float calcBounceRate(ArrayList<String[]> clickData, ArrayList<String[]> serverData){ + int clicks = this.calcClicks(clickData); + int bounces = this.calcBounces(serverData); + return ((float) clicks/bounces); + } + +} diff --git a/src/main/java/com/example/OverallMetricsPage.java b/src/main/java/com/example/OverallMetricsPage.java new file mode 100644 index 0000000000000000000000000000000000000000..afa99ec56d1c65008a24908c1f6e94636614ba50 --- /dev/null +++ b/src/main/java/com/example/OverallMetricsPage.java @@ -0,0 +1,274 @@ +package com.example; + +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.Separator; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.layout.Region; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import javafx.scene.text.TextAlignment; +import javafx.stage.Stage; + +import java.util.ArrayList; + + +public class OverallMetricsPage { + private Stage stage; + private Scene scene; + + public LogManager logManager; + private ArrayList<String[]> clickData; + private ArrayList<String[]> impressionData; + private ArrayList<String[]> serverData; + private OverallMetricsCalculator metricsCalculator = new OverallMetricsCalculator(); + + // Define style constants + private final String BACKGROUND_COLOR = "#f5f5f7"; + private final String PRIMARY_COLOR = "#4285F4"; + private final String PRIMARY_DARK_COLOR = "#3367d6"; + private final String SECTION_BACKGROUND = "white"; + private final String HEADER_COLOR = "#333333"; + private final String TEXT_COLOR = "#555555"; + private final String ERROR_COLOR = "#F44336"; + private final String SUCCESS_COLOR = "#4CAF50"; + + OverallMetricsPage(Stage stage, LogManager logManager) { + this.stage = stage; + this.logManager = logManager; + this.clickData = logManager.getClickData(); + this.impressionData = logManager.getImpressionData(); + this.serverData = logManager.getServerData(); + initialize(); + } + + private void initialize() { + // Create main layout + BorderPane root = new BorderPane(); + root.setStyle("-fx-background-color: " + BACKGROUND_COLOR + ";"); + + // Create top title bar + HBox topBar = createTopBar(); + + // Create central content area + VBox contentBox = createContentBox(); + + // Create bottom button area + HBox bottomBar = createBottomBar(); + + // Set layout + root.setTop(topBar); + root.setCenter(contentBox); + root.setBottom(bottomBar); + + // Set margins + BorderPane.setMargin(contentBox, new Insets(20, 30, 20, 30)); + BorderPane.setMargin(bottomBar, new Insets(0, 0, 20, 0)); + + // Create scene + scene = new Scene(root, 600, 650); + } + + private HBox createTopBar() { + HBox topBar = new HBox(); + topBar.setAlignment(Pos.CENTER); + topBar.setPadding(new Insets(20, 0, 10, 0)); + + Label titleLabel = new Label("Overall Ad Campaign Metrics"); + titleLabel.setFont(Font.font("Arial", FontWeight.BOLD, 24)); + titleLabel.setTextFill(Color.web(HEADER_COLOR)); + + topBar.getChildren().add(titleLabel); + + return topBar; + } + + private VBox createContentBox() { + VBox contentBox = new VBox(15); + contentBox.setAlignment(Pos.TOP_CENTER); + contentBox.setPadding(new Insets(30, 40, 30, 40)); + contentBox.setMaxWidth(540); + contentBox.setStyle("-fx-background-color: " + SECTION_BACKGROUND + "; " + + "-fx-background-radius: 10; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 10, 0, 0, 2);"); + + // Add description text + Label descriptionLabel = new Label("Below are the overall ad campaign metrics calculated from uploaded data files"); + descriptionLabel.setFont(Font.font("Arial", 14)); + descriptionLabel.setTextFill(Color.web("#666666")); + descriptionLabel.setWrapText(true); + descriptionLabel.setTextAlignment(TextAlignment.CENTER); + + Separator separator = new Separator(); + separator.setOpacity(0.3); + separator.setPadding(new Insets(10, 0, 10, 0)); + + // Create metrics grid + GridPane metricsGrid = new GridPane(); + metricsGrid.setHgap(20); + metricsGrid.setVgap(15); + metricsGrid.setAlignment(Pos.CENTER); + + // Calculate all metric values + int impressionsValue = metricsCalculator.calcImpressions(impressionData); + int clicksValue = metricsCalculator.calcClicks(clickData); + int uniquesValue = metricsCalculator.calcUniques(clickData); + int bouncesValue = metricsCalculator.calcBounces(serverData); + int conversionsValue = metricsCalculator.calcConversions(serverData); + float costValue = metricsCalculator.calcCost(clickData, impressionData); + float ctrValue = metricsCalculator.calcCTR(clickData, impressionData); + float cpaValue = metricsCalculator.calcCPA(clickData, impressionData, serverData); + float cpcValue = metricsCalculator.calcCPC(clickData, impressionData, serverData); + float cpmValue = metricsCalculator.calcCPM(clickData, impressionData); + float bounceRateValue = metricsCalculator.calcBounceRate(clickData, serverData); + + // First column: Basic quantitative metrics + Label basicMetricsTitle = new Label("Basic Quantitative Metrics"); + basicMetricsTitle.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + basicMetricsTitle.setTextFill(Color.web(HEADER_COLOR)); + + VBox basicMetricsBox = new VBox(12); + basicMetricsBox.getChildren().addAll( + basicMetricsTitle, + createMetricItem("Total Impressions", impressionsValue + "", false), + createMetricItem("Total Clicks", clicksValue + "", false), + createMetricItem("Unique Visitors", uniquesValue + "", false), + createMetricItem("Bounces", bouncesValue + "", false), + createMetricItem("Conversions", conversionsValue + "", false), + createMetricItem("Total Cost", String.format("%.2f", costValue) + " ¥", false) + ); + + // Second column: Ratio metrics + Label ratioMetricsTitle = new Label("Ratio and Performance Metrics"); + ratioMetricsTitle.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + ratioMetricsTitle.setTextFill(Color.web(HEADER_COLOR)); + + VBox ratioMetricsBox = new VBox(12); + ratioMetricsBox.getChildren().addAll( + ratioMetricsTitle, + createMetricItem("Click-Through Rate (CTR)", formatPercentage(ctrValue), true), + createMetricItem("Cost Per Acquisition (CPA)", String.format("%.2f", cpaValue) + " ¥", true), + createMetricItem("Cost Per Click (CPC)", String.format("%.2f", cpcValue) + " ¥", true), + createMetricItem("Cost Per Mille (CPM)", String.format("%.2f", cpmValue) + " ¥", true), + createMetricItem("Bounce Rate", formatPercentage(bounceRateValue), true) + ); + + // Add to metrics grid + metricsGrid.add(basicMetricsBox, 0, 0); + metricsGrid.add(ratioMetricsBox, 1, 0); + + // Add all components to content box + contentBox.getChildren().addAll( + descriptionLabel, + separator, + metricsGrid + ); + + return contentBox; + } + + private HBox createBottomBar() { + HBox bottomBar = new HBox(15); + bottomBar.setAlignment(Pos.CENTER); + + Button backButton = createStyledButton("Back to Charts", PRIMARY_COLOR, PRIMARY_DARK_COLOR); + Button logoutButton = createStyledButton("Logout", "#757575", "#616161"); + + // Button events + backButton.setOnAction(e -> { + ChartPage chartPage = new ChartPage(stage, logManager); + chartPage.show(); + }); + + logoutButton.setOnAction(e -> { + System.out.println("Logout Button clicked"); + Login login = new Login(stage); + login.show(); + }); + + bottomBar.getChildren().addAll(backButton, logoutButton); + + return bottomBar; + } + + // Create metric item + private HBox createMetricItem(String label, String value, boolean isRatio) { + HBox item = new HBox(10); + item.setAlignment(Pos.CENTER_LEFT); + + Label metricLabel = new Label(label + ":"); + metricLabel.setFont(Font.font("Arial", FontWeight.NORMAL, 14)); + metricLabel.setTextFill(Color.web(TEXT_COLOR)); + metricLabel.setMinWidth(150); + + Label valueLabel = new Label(value); + valueLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + + // Set text color based on whether it's a ratio metric + if (isRatio) { + // If value contains NaN, display in red + if (value.contains("NaN")) { + valueLabel.setTextFill(Color.web(ERROR_COLOR)); + } else { + valueLabel.setTextFill(Color.web(PRIMARY_COLOR)); + } + } else { + valueLabel.setTextFill(Color.web(HEADER_COLOR)); + } + + item.getChildren().addAll(metricLabel, valueLabel); + + return item; + } + + // Create styled button + private Button createStyledButton(String text, String bgColor, String hoverColor) { + Button button = new Button(text); + button.setPrefSize(120, 40); + button.setFont(Font.font("Arial", FontWeight.NORMAL, 14)); + + String style = String.format( + "-fx-background-color: %s; " + + "-fx-text-fill: white; " + + "-fx-font-weight: normal; " + + "-fx-background-radius: 5; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 2, 0, 0, 1); " + + "-fx-cursor: hand;", bgColor); + + String hoverStyle = String.format( + "-fx-background-color: %s; " + + "-fx-text-fill: white; " + + "-fx-font-weight: normal; " + + "-fx-background-radius: 5; " + + "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 3, 0, 0, 2); " + + "-fx-cursor: hand;", hoverColor); + + button.setStyle(style); + + button.setOnMouseEntered(e -> button.setStyle(hoverStyle)); + button.setOnMouseExited(e -> button.setStyle(style)); + + return button; + } + + // Format percentage display + private String formatPercentage(float value) { + if (Float.isNaN(value)) { + return "NaN"; + } + return String.format("%.2f%%", value * 100); + } + + public void show() { + stage.setScene(scene); + stage.setTitle("Ad Auction Dashboard - Overall Metrics"); + stage.show(); + } +} diff --git a/src/main/java/com/example/ServerLog.java b/src/main/java/com/example/ServerLog.java deleted file mode 100644 index 44b2f76e5e5faf5a9928582bfe637d11ee91adb5..0000000000000000000000000000000000000000 --- a/src/main/java/com/example/ServerLog.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.example; - -import java.util.ArrayList; - -public class ServerLog { - - private String filepath; - - private ArrayList<ServerLogRecord> records; - - public ServerLog(String filepath) { - this.filepath = filepath; - this.records = new ArrayList<>(); - this.loadLogFile(filepath); - } - - private void loadLogFile(String filename) { - ArrayList<String[]> data = Utils.readCSV(filename); - System.out.println("CSV Loaded, size: " + data.size()); - for (int i = 1; i < data.size(); i++) { - String[] elements = data.get(i); - System.out.println("Parsing row: " + String.join("|", elements)); - try { - this.records.add(new ServerLogRecord( - elements[0], elements[1], elements[2], - Integer.parseInt(elements[3]), - elements[4].equalsIgnoreCase("yes") - )); - } catch (Exception e) { - System.err.println("Error parsing row: " + String.join("|", elements)); - e.printStackTrace(); - } - } - } - - - public int size() { - return this.records.size(); - } - - public ServerLogRecord getRecordAt(int index) { - return this.records.get(index); - } - - public int getNumberOfBounces() { - int total = 0; - for (ServerLogRecord record: this.records) { - total += record.browseTime() < 120 || record.getPagesViewed() == 1 ? 1 : 0; - } - return total; - } - - public int getNumberOfConversions() { - int total = 0; - for (ServerLogRecord record: this.records) { - total += record.isConversion() ? 1 : 0; - } - return total; - } - - public static void main(String[] args) { - ServerLog serverLog = new ServerLog("server_log.csv"); - System.out.println("Bounces: " + serverLog.getNumberOfBounces()); - System.out.println("Conversions: " + serverLog.getNumberOfConversions()); - System.out.println("Total Records: " + serverLog.size()); - } - - -} diff --git a/src/main/java/com/example/ServerLogRecord.java b/src/main/java/com/example/ServerLogRecord.java deleted file mode 100644 index b49af3eb6d46a045f0f4f92e10303f9745a238bf..0000000000000000000000000000000000000000 --- a/src/main/java/com/example/ServerLogRecord.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.example; - -import java.time.Duration; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - -public class ServerLogRecord { - - final private static String DATE_PATTERN = "yyyy-MM-dd HH:mm:ss"; - - private LocalDateTime entryDate; - private String Id; - private LocalDateTime exitDate; - private int pagesViewed; - private boolean conversion; - - public ServerLogRecord(String entryDate, - String Id, - String exitDate, - int pagesViewed, - boolean conversion) { - if (!entryDate.equalsIgnoreCase("n/a")) { - this.entryDate = LocalDateTime.parse(entryDate, DateTimeFormatter.ofPattern(DATE_PATTERN)); - } - this.Id = Id; - if (!exitDate.equalsIgnoreCase("n/a")) { - this.exitDate = LocalDateTime.parse(exitDate, DateTimeFormatter.ofPattern(DATE_PATTERN)); - } - this.pagesViewed = pagesViewed; - this.conversion = conversion; - } - - public LocalDateTime getEntryDate() { - return entryDate; - } - - public String getId() { - return Id; - } - - public LocalDateTime getExitDate() { - return exitDate; - } - - public int getPagesViewed() { - return pagesViewed; - } - - public boolean isConversion() { - return conversion; - } - - public long browseTime() { - if (this.entryDate == null || this.exitDate == null) { - return 0; - } else { - Duration interval = Duration.between(this.entryDate, this.exitDate); - return interval.toSeconds(); - } - } - - @Override - public String toString() { - return "ServerLogRecord{" + - "entryDate=" + entryDate + - ", Id='" + Id + '\'' + - ", exitDate=" + exitDate + - ", pagesViewed=" + pagesViewed + - ", conversion=" + conversion + - '}'; - } -} diff --git a/src/main/java/com/example/TestClickLog.java b/src/main/java/com/example/TestClickLog.java deleted file mode 100644 index db0474e24164087c420103c4f38948922a27cfbe..0000000000000000000000000000000000000000 --- a/src/main/java/com/example/TestClickLog.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.example; - -import java.io.FileWriter; -import java.io.IOException; -import java.util.List; - -public class TestClickLog { - public static void main(String[] args) { - ClickLogReader reader = new ClickLogReader(); - List<Click> clicks = reader.readCSV("clicks_log.csv"); - - if (!clicks.isEmpty()) { - System.out.println("CSV Loaded! Total Rows: " + clicks.size()); - - // Save all impressions to a file - try (FileWriter writer = new FileWriter("clicks_output.txt")) { - for (Click click : clicks) { - writer.write(click.toString() + "\n"); - } - System.out.println("All clicks saved to `clicks_output.txt`."); - } catch (IOException e) { - e.printStackTrace(); - System.out.println("Failed to save data to file."); - } - - } else { - System.out.println("No data found!"); - } - } -} diff --git a/src/main/java/com/example/TestImpressionLog.java b/src/main/java/com/example/TestImpressionLog.java deleted file mode 100644 index 4e2b71caee19309e82fa28cc47c9a5146d2c2ec3..0000000000000000000000000000000000000000 --- a/src/main/java/com/example/TestImpressionLog.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.example; - -import java.io.FileWriter; -import java.io.IOException; -import java.util.List; - -public class TestImpressionLog { - public static void main(String[] args) { - ImpressionLogReader reader = new ImpressionLogReader(); - List<Impression> impressions = reader.readCSV("impression_log.csv"); - - if (!impressions.isEmpty()) { - System.out.println("CSV Loaded! Total Rows: " + impressions.size()); - - // Save all impressions to a file - try (FileWriter writer = new FileWriter("impressions_output.txt")) { - for (Impression impression : impressions) { - writer.write(impression.toString() + "\n"); - } - System.out.println("All impressions saved to `impressions_output.txt`."); - } catch (IOException e) { - e.printStackTrace(); - System.out.println("Failed to save data to file."); - } - - } else { - System.out.println("No data found!"); - } - } -} diff --git a/src/main/java/com/example/Utils.java b/src/main/java/com/example/Utils.java deleted file mode 100644 index 084c7a957b3963bb2f2126c290ffcb5311990347..0000000000000000000000000000000000000000 --- a/src/main/java/com/example/Utils.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.example; - -import java.util.ArrayList; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; - -public class Utils { - - - public static ArrayList<String[]> readCSV(String filename) { - ArrayList<String[]> result = new ArrayList<>(); - try { - InputStream inputStream = Utils.class.getClassLoader().getResourceAsStream(filename); - if (inputStream == null) { - throw new IllegalArgumentException("File not found: " + filename); - } - - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); - String line; - while ((line = reader.readLine()) != null) { - line = line.strip(); - if (!line.isEmpty()) { - result.add(line.split(",")); - } - } - reader.close(); - } catch (Exception e) { - e.printStackTrace(); - } - return result; - } -} \ No newline at end of file