diff --git a/LineGraph.java b/LineGraph.java new file mode 100644 index 0000000..7edbd99 --- /dev/null +++ b/LineGraph.java @@ -0,0 +1,708 @@ +import javafx.util.Pair; + +import java.sql.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; + + +/** + * Contains all data and methods needed to display the line chart. + */ +public class LineGraph implements Chart, Observable { + + private Campaign campaign; + + private Metric metric; + private TimeInterval timeInterval; + private ArrayList dataPoints; + + private List observers = new LinkedList(); // this will contain the window that displays the chart + + + public LineGraph(Metric metric, TimeInterval timeInterval, Observer observer, Campaign campaign, String predicate){ + this.metric = metric; + this.timeInterval = timeInterval; + observers.add(observer); + this.campaign = campaign; + + try { + this.campaign.connect().setAutoCommit(false); + } catch (SQLException e ){ + System.out.println(e); + } + calculateDataPoints(metric, predicate); + } + + public LineGraph(Campaign campaign){ + this.campaign = campaign; + } + + /** + * Use campaign's reference to get Campaign's data and calculation methods. + * Apply filters to campaign's data + * Calculates data points to be displayed on the chart. + * dataPoints' Pair contains value of the metric computed over some time and that time period + * (e.g. <30 bounces, 9am - 10am on 29/02/2020>) + * TriggerUpdate at the end so the View can get the dataPoints. + */ + /** + * Use campaign's reference to get Campaign's data and calculation methods. + * Apply filters to campaign's data + * Calculates data points to be displayed on the chart. + * dataPoints' Pair contains value of the metric computed over some time and that time period + * (e.g. <30 bounces, 9am - 10am on 29/02/2020>) + * TriggerUpdate at the end so the View can get the dataPoints. + */ + private void calculateDataPoints(Metric metric, String predicate){ + + switch (metric){ + case TOTAL_IMPRESSIONS: + dataPoints = calculateTotalImpressions(predicate); + break; + case TOTAL_IMPRESSION_COST: + dataPoints = calculateImpressionCosts(predicate); + break; + case TOTAL_CLICKS: + dataPoints = calculateTotalClicks(predicate); + break; + case TOTAL_CLICK_COST: + dataPoints = calculateClickCosts(predicate); + break; + case TOTAL_COST: + dataPoints = calculateTotalCosts(predicate); + break; + case TOTAL_CONVERSIONS: + dataPoints = calculateTotalConversions(predicate); + break; + case CONVERSION_RATE: + dataPoints = calculateConversionRates(predicate); + break; + case BOUNCES: + dataPoints = calculateBounces(predicate); + break; + case BOUNCE_RATE: + dataPoints = calculateBounceRates(predicate); + break; + case TOTAL_UNIQUES: + dataPoints = calculateTotalUniques(predicate); + break; + case CTR: + dataPoints = calculateCTRs(predicate); + break; + case CPA: + dataPoints = calculateCPAs(predicate); + break; + case CPC: + dataPoints = calculateCPCs(predicate); + break; + case CPM: + // impression + break; + + } + + triggerUpdate(); + } + + private LocalDateTime getEndDateTime(LocalDateTime startDateTime){ + LocalDateTime endDateTime; + switch(timeInterval){ + case HOUR: + endDateTime = startDateTime.plusHours(1); + break; + case DAY: + endDateTime = startDateTime.plusDays(1); + break; + case WEEK: + endDateTime= startDateTime.plusWeeks(1); + break; + case MONTH: + endDateTime = startDateTime.plusMonths(1); + break; + default: + endDateTime = startDateTime; //TODO handle this case differently + break; + } + return endDateTime; + } + + private ArrayList> calculateTotalImpressions(String predicate){ + ArrayList> dataPoints = new ArrayList>(); + ArrayList impressionLog = filterImpressionLog(predicate); + Collections.sort(impressionLog); + LocalDateTime startDateTime = impressionLog.get(0).getDateTime(); + + LocalDateTime endDateTime = getEndDateTime(startDateTime); + int i = 0; + ArrayList impressions = new ArrayList(); + while(impressionLog.size() > i){ + Impression impression = impressionLog.get(i); + if(impression.getDateTime().isBefore(endDateTime)){ + impressions.add(impression); + } + else{ + dataPoints.add(new DataPoint(campaign.calcImpressions(impressions), startDateTime)); + + startDateTime = endDateTime; + endDateTime = getEndDateTime(startDateTime); + impressions = new ArrayList(); + i--; + } + i++; + } + return dataPoints; + } + + private ArrayList> calculateImpressionCosts(String predicate) { + return null; + } + + private ArrayList> calculateTotalClicks(String predicate) { + ArrayList> dataPoints = new ArrayList>(); + ArrayList clickLog = filterClickLog(predicate); + clickLog.sort(getClickComparator()); + + LocalDateTime startDateTime = clickLog.get(0).getDateTime(); + LocalDateTime endDateTime = getEndDateTime(startDateTime); + int i = 0; + ArrayList clicks = new ArrayList(); + while(clickLog.size() > i){ + Click click = clickLog.get(i); + if(click.getDateTime().isBefore(endDateTime)){ + clicks.add(click); + } + else{ + dataPoints.add(new DataPoint(campaign.calcClicks(clicks), startDateTime)); + startDateTime = endDateTime; + endDateTime = getEndDateTime(startDateTime); + clicks = new ArrayList(); + i--; + } + i++; + } + return dataPoints; + } + + private ArrayList> calculateClickCosts(String predicate) { + ArrayList> dataPoints = new ArrayList>(); + ArrayList clickLog = filterClickLog(predicate); + clickLog.sort(getClickComparator()); + + LocalDateTime startDateTime = clickLog.get(0).getDateTime(); + LocalDateTime endDateTime = getEndDateTime(startDateTime); + int i = 0; + ArrayList clicks = new ArrayList(); + while(clickLog.size() > i){ + Click click = clickLog.get(i); + if(click.getDateTime().isBefore(endDateTime)){ + clicks.add(click); + } + else{ + dataPoints.add(new DataPoint(campaign.calcTotalClickCost(clicks), startDateTime)); + startDateTime = endDateTime; + endDateTime = getEndDateTime(startDateTime); + clicks = new ArrayList(); + i--; + } + i++; + } + return dataPoints; + } + + private ArrayList> calculateTotalCosts(String predicate) { + ArrayList> dataPoints = new ArrayList>(); + ArrayList> clickCosts = calculateClickCosts(predicate); + ArrayList> impressionCosts = calculateImpressionCosts(predicate); + + for(int i = 0; i < clickCosts.size(); i++){ + dataPoints.add(new DataPoint((clickCosts.get(i).getMetric() + impressionCosts.get(i).getMetric()), clickCosts.get(i).getStartTime())); + } + + return dataPoints; + } + + private ArrayList> calculateTotalConversions(String predicate) { + return null; + } + + private ArrayList> calculateConversionRates(String predicate) { + ArrayList> dataPoints = new ArrayList>(); + ArrayList> totalConversions = calculateTotalConversions(predicate); + ArrayList> totalClicks = calculateTotalClicks(predicate); + + for(int i = 0; i < totalConversions.size(); i++){ + dataPoints.add(new DataPoint((double)(totalConversions.get(i).getMetric() / totalClicks.get(i).getMetric()), totalConversions.get(i).getStartTime())); + } + + return dataPoints; + } + + private ArrayList> calculateBounces(String predicate) { + return null; + } + + private ArrayList> calculateBounceRates(String predicate) { + ArrayList> dataPoints = new ArrayList>(); + ArrayList> bounces = calculateBounces(predicate); + ArrayList> impressionCosts = calculateTotalImpressions(predicate); + + for(int i = 0; i < bounces.size(); i++){ + dataPoints.add(new DataPoint((double)(bounces.get(i).getMetric() / impressionCosts.get(i).getMetric()), bounces.get(i).getStartTime())); + } + + return dataPoints; + } + + private ArrayList> calculateTotalUniques(String predicate) { + ArrayList> dataPoints = new ArrayList>(); + ArrayList clickLog = filterClickLog(predicate); + clickLog.sort(getClickComparator()); + + LocalDateTime startDateTime = clickLog.get(0).getDateTime(); + LocalDateTime endDateTime = getEndDateTime(startDateTime); + int i = 0; + ArrayList clicks = new ArrayList(); + while(clickLog.size() > i){ + Click click = clickLog.get(i); + if(click.getDateTime().isBefore(endDateTime)){ + clicks.add(click); + } + else{ + dataPoints.add(new DataPoint(campaign.calcUniques(clicks), startDateTime)); + startDateTime = endDateTime; + endDateTime = getEndDateTime(startDateTime); + clicks = new ArrayList(); + i--; + } + i++; + } + return dataPoints; + } + + private ArrayList> calculateCTRs(String predicate) { + ArrayList> dataPoints = new ArrayList>(); + ArrayList> clicks = calculateTotalClicks(predicate); + ArrayList> impressions = calculateTotalImpressions(predicate); + + for(int i = 0; i < clicks.size(); i++){ //TODO add protection when arraylists have different sizes + dataPoints.add(new DataPoint((double)(clicks.get(i).getMetric() / impressions.get(i).getMetric()), clicks.get(i).getStartTime())); + } + return dataPoints; + } + + private ArrayList> calculateCPAs(String predicate) { + ArrayList> dataPoints = new ArrayList>(); + ArrayList> totalCosts = calculateTotalCosts(predicate); + ArrayList> totalConversions = calculateTotalConversions(predicate); + + for(int i = 0; i < totalCosts.size(); i++){ + dataPoints.add(new DataPoint((double)(totalCosts.get(i).getMetric() / totalConversions.get(i).getMetric()), totalCosts.get(i).getStartTime())); + } + + return dataPoints; + } + + private ArrayList> calculateCPCs(String predicate) { + ArrayList> dataPoints = new ArrayList>(); + ArrayList clickLog = filterClickLog(predicate); + clickLog.sort(getClickComparator()); + + LocalDateTime startDateTime = clickLog.get(0).getDateTime(); + LocalDateTime endDateTime = getEndDateTime(startDateTime); + int i = 0; + ArrayList clicks = new ArrayList(); + while(clickLog.size() > i){ + Click click = clickLog.get(i); + if(click.getDateTime().isBefore(endDateTime)){ + clicks.add(click); + } + else{ + dataPoints.add(new DataPoint(campaign.calcCPC(clicks), startDateTime)); + startDateTime = endDateTime; + endDateTime = getEndDateTime(startDateTime); + clicks = new ArrayList(); + i--; + } + i++; + } + return dataPoints; + } + + + + /** + * filters for impression log + * @param predicate + * @return + */ + public ArrayList filterImpressionLog(String predicate){ + + ArrayList impList = new ArrayList(); + String[] predicates = predicate.split(","); + + + try { + + Connection conn = campaign.connect(); + + + String sqlString = "SELECT * FROM impressionLog WHERE 1 = 1"; + + for(int i = 1; i < predicates.length + 1; i++){ + + if(predicates[i - 1].contains(":") && predicates[i - 1].contains("+")){ + String[] dates = predicates[i - 1].split(" \\+ "); + System.out.println(dates[0]); + sqlString += " and entry_date > "; + sqlString += "\'" + dates[0] + "\'"; + sqlString += " and entry_date < "; + sqlString += "\'" + dates[1] + "\'"; + } + switch (predicates[i - 1]){ + case "Male": + sqlString += " and gender='Male'"; + break; + + case "Female": + sqlString += " and gender='Female'"; + break; + + case "Social Media": + sqlString += " and context='Social Media'"; + break; + + case "News": + sqlString += " and context='News'"; + break; + + case "Shopping": + sqlString += " and context='Shopping'"; + break; + + case "Blog": + sqlString += " and context='Blog'"; + break; + + case "Low": + sqlString += " and income='Low'"; + break; + + case "Medium": + sqlString += " and income='Medium'"; + break; + + case "High": + sqlString += " and income='High'"; + break; + + case "<25": + sqlString += " and age='<25'"; + break; + + case "25-34": + sqlString += " and age='25-34'"; + break; + + case "35-44": + sqlString += " and age='35-44'"; + break; + + case "44-54": + sqlString += " and age='45-54'"; + break; + + case ">54": + sqlString += " and age='>54'"; + break; + + } + + + } + + System.out.println(sqlString); + PreparedStatement staten = conn.prepareStatement(sqlString); + ResultSet rs = staten.executeQuery(); + + while(rs.next()){ + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0"); + LocalDateTime dateTime = LocalDateTime.parse(rs.getString("entry_date"),formatter); + Impression imp = new Impression(dateTime, rs.getString("id"), rs.getString("gender"), rs.getString("age"), rs.getString("income"), rs.getString("context"), rs.getFloat("impression_cost")); + impList.add(imp); + } + + + + }catch(SQLException e){ + e.printStackTrace(); + } + + + return impList; + } + + + /** + * filter for the click log + * @param predicate + * @return + */ + + public ArrayList filterClickLog(String predicate){ + + ArrayList clickList = new ArrayList<>(); + String[] predicates = predicate.split(","); + + + try { + Connection conn = campaign.connect(); + String sqlString = "SELECT * FROM (SELECT clickLog.entry_date, clickLog.id, clickLog.click_cost, impressionLog.age, impressionLog.context, impressionLog.gender, impressionLog.income, impressionLog.impression_cost FROM clickLog INNER JOIN impressionLog ON impressionLog.id = clickLog.id GROUP BY clickLog.id, clickLog.entry_date) WHERE 1 = 1 "; + + for(int i = 1; i < predicates.length + 1; i++) { + + if(predicates[i - 1].contains(":") && predicates[i - 1].contains("+")){ + String[] dates = predicates[i - 1].split(" \\+ "); + System.out.println(dates[0]); + sqlString += " and entry_date > "; + sqlString += "\'" + dates[0] + "\'"; + sqlString += " and entry_date < "; + sqlString += "\'" + dates[1] + "\'"; + } + switch (predicates[i - 1]) { + case "Male": + sqlString += " and gender='Male'"; + break; + + case "Female": + sqlString += " and gender='Female'"; + break; + + case "Social Media": + sqlString += " and context='Social Media'"; + break; + + case "News": + sqlString += " and context='News'"; + break; + + case "Shopping": + sqlString += " and context='Shopping'"; + break; + + case "Blog": + sqlString += " and context='Blog'"; + break; + + case "Low": + sqlString += " and income='Low'"; + break; + + case "Medium": + sqlString += " and income='Medium'"; + break; + + case "High": + sqlString += " and income='High'"; + break; + + case "<25": + sqlString += " and age='<25'"; + break; + + case "25-34": + sqlString += " and age='25-34'"; + break; + + case "35-44": + sqlString += " and age='35-44'"; + break; + + case "44-54": + sqlString += " and age='45-54'"; + break; + + case ">54": + sqlString += " and age='>54'"; + break; + + + } + + + PreparedStatement staten = conn.prepareStatement(sqlString); + ResultSet rs = staten.executeQuery(); + + + while (rs.next()) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0"); + LocalDateTime dateTime = LocalDateTime.parse(rs.getString("entry_date"), formatter); + Click click = new Click(dateTime, rs.getString("id"), rs.getFloat("click_cost")); + clickList.add(click); + } + } + + }catch(SQLException e){ + System.out.println(e.getErrorCode()); + } + + return clickList; + } + + + /** + * filter for the Server database + * @param predicate + * @return + */ + public ArrayList filterServerLog(String predicate){ + + ArrayList entryList = new ArrayList<>(); + String[] predicates = predicate.split(","); + + try{ + Connection conn = campaign.connect(); + String sqlString = "SELECT * FROM (SELECT clickLog.id, clickLog.click_cost, clickLog.entry_date, serverLog.conversion, serverLog.pagesViewed, serverLog.exit_date FROM serverLog INNER JOIN clickLog ON clickLog.id = serverLog.id GROUP BY serverLog.id, serverLog.entry_date) WHERE 1 = 1 "; + + + + for(int i = 1; i < predicates.length + 1 ; i++) { + + if(predicates[i - 1].contains(":") && predicates[i - 1].contains("+")){ + String[] dates = predicates[i - 1].split(" \\+ "); + System.out.println(dates[0]); + sqlString += " and entry_date > "; + sqlString += "\'" + dates[0] + "\'"; + sqlString += " and entry_date < "; + sqlString += "\'" + dates[1] + "\'"; + } + switch (predicates[i - 1]) { + case "Male": + sqlString += " and gender='Male'"; + break; + + case "Female": + sqlString += " and gender='Female'"; + break; + + case "Social Media": + sqlString += " and context='Social Media'"; + break; + + case "News": + sqlString += " and context='News'"; + break; + + case "Shopping": + sqlString += " and context='Shopping'"; + break; + + case "Blog": + sqlString += " and context='Blog'"; + break; + + case "Low": + sqlString += " and income='Low'"; + break; + + case "Medium": + sqlString += " and income='Medium'"; + break; + + case "High": + sqlString += " and income='High'"; + break; + + case "<25": + sqlString += " and age='<25'"; + break; + + case "25-34": + sqlString += " and age='25-34'"; + break; + + case "35-44": + sqlString += " and age='35-44'"; + break; + + case "44-54": + sqlString += " and age='45-54'"; + break; + + case ">54": + sqlString += " and age='>54'"; + break; + + case "No": + sqlString += " and conversion='No'"; + break; + + case "Yes": + sqlString += " and conversion='Yes'"; + break; + + case "pagesViewed": + sqlString += " and pagesViewed <"; + sqlString += predicates[i]; + break; + + + } + } + + + System.out.println(sqlString); + PreparedStatement staten = conn.prepareStatement(sqlString); + ResultSet rs = staten.executeQuery(); + + while(rs.next()){ + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0"); + LocalDateTime entry_date = LocalDateTime.parse(rs.getString("entry_date"),formatter); + LocalDateTime exit_date = null; + if(rs.getString("exit_date") != null){ + exit_date = LocalDateTime.parse(rs.getString("exit_date"), formatter); + } + ServerEntry entry = new ServerEntry(entry_date, rs.getString("id"), exit_date, rs.getInt("pagesViewed"), rs.getString("conversion")); + entryList.add(entry); + } + + }catch(SQLException e){ + System.out.println(e); + } + + return entryList; + } + + + public ArrayList getDataPoints(){ return dataPoints; } + + @Override + public void addObserver(Observer observer) { + observers.add(observer); + } + + /** + * Observers should respond to this by calling getDataPoints() + */ + private void triggerUpdate() { + for (Observer observer : observers) { + if(observer==null){ + + } + else{ + observer.observableChanged(this); + } + } + } + + private Comparator getClickComparator(){ + Comparator dateComparator = new Comparator(){ + + @Override + public int compare(Click c1, Click c2) { + return c1.getDateTime().compareTo(c2.getDateTime()); + } + }; + return dateComparator; + } +} \ No newline at end of file