diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c19f15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/target/ +/.idea +.classpath +.project +/.settings \ No newline at end of file diff --git a/codingcompetition2019.iml b/codingcompetition2019.iml new file mode 100644 index 0000000..54ddd53 --- /dev/null +++ b/codingcompetition2019.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/feedback.txt b/feedback.txt index 8234592..ec30eba 100644 --- a/feedback.txt +++ b/feedback.txt @@ -1,9 +1,12 @@ -Your team (name of each individual participating): -How many JUnits were you able to get to pass? +Your team (name of each individual participating): Ishan Baniya +How many JUnits were you able to get to pass? 10/10 Document and describe any enhancements included to help the judges properly grade your submission. - Step 1: - Step 2: + Step 1: Added meaningful description for each methods in CodingCompCSVUtil.java file for other developers to understand the code easily. + Step 2: Added a GUI that allows users to view the most impactful disaster by year and category. -Feedback for the coding competition? Things you would like to see in future events? \ No newline at end of file +Feedback for the coding competition? Things you would like to see in future events? +# It would have been better to have comments/example on the methods so it would be easier to know what to do for the implementation. + + diff --git a/src/main/java/codingcompetition2019/CodingCompCSVUtil.java b/src/main/java/codingcompetition2019/CodingCompCSVUtil.java index 74f3074..27ef7c6 100644 --- a/src/main/java/codingcompetition2019/CodingCompCSVUtil.java +++ b/src/main/java/codingcompetition2019/CodingCompCSVUtil.java @@ -1,58 +1,263 @@ package codingcompetition2019; +import java.io.File; import java.io.IOException; -import java.util.List; +import java.util.*; public class CodingCompCSVUtil { - public List> readCSVFileByCountry(String fileName, String countryName) throws IOException { - // TODO implement this method - return null; - } - - public List> readCSVFileWithHeaders(String fileName) throws IOException { - // TODO implement this method - return null; - } - - public List> readCSVFileWithoutHeaders(String fileName) throws IOException { - // TODO implement this method - return null; - } - - public DisasterDescription getMostImpactfulYear(List> records) { - // TODO implement this method - return null; - } - - public DisasterDescription getMostImpactfulYearByCategory(String category, List> records) { - // TODO implement this method - return null; - } - - public DisasterDescription getMostImpactfulDisasterByYear(String year, List> records) { - // TODO implement this method - return null; - } - - public DisasterDescription getTotalReportedIncidentsByCategory(String category, List> records) { - // TODO implement this method - return null; - } - - /** - * This method will return the count if the number of incident falls within the provided range. - * To simplify the problem, we assume: - * + A value of -1 is provided if the max range is NOT applicable. - * + A min value can be provided, without providing a max value (which then has to be -1 like indicated above). - * + If a max value is provided, then a max value is also needed. - */ - public int countImpactfulYearsWithReportedIncidentsWithinRange(List> records, int min, int max) { - // TODO implement this method - return -1; - } - - public boolean firstRecordsHaveMoreReportedIndicents(List> records1, List> records2) { - // TODO implement this method - return false; - } + + /** + * This method reads the CSV file and returns the data about the specified country + * Each row has a data about the disaster for the a year separated by comma. + * Store the data separated by comma in a list if the name of the country matches. + * Store all the list with the matching country name in a list and return it. + * + * @param fileName filePath of the CSV file with data + * @param countryName name of the country to filter the records by + * @return Data about the disaster for that country + * @throws IOException error on reading the file + */ + public List> readCSVFileByCountry(String fileName, String countryName) throws IOException { + List> countries = readCSVFileWithoutHeaders(fileName); + + // Filter the csv values by country + List> countryEntries = new ArrayList>(); + for (List entry : countries) { + // Check if the name of the country matches + if (entry.get(0).trim().toLowerCase().equals(countryName.trim().toLowerCase())) { + countryEntries.add(entry); + } + } + return countryEntries; + } + + /** + * This method reads the CSV file including the header. + * Each line of the CSV has data about the disaster separated by comma. Store it in a list. + * Add that list to a master list and return the master list. + * + * @param fileName filePath of the CSV file with data + * @return list with a list of data related to the disaster + * @throws IOException error on reading the file + */ + public List> readCSVFileWithHeaders(String fileName) throws IOException { + // Get all entries without the header + File inputFile = new File(fileName); + + List> csvEntriesWithHeader = new ArrayList>(); + + if (inputFile.exists()) { + try { + Scanner input = new Scanner(inputFile); + // Read each line an add it to the list + while (input.hasNext()) { + String currentLine = input.nextLine(); + // Split the line by comma and store it as array + List values = Arrays.asList(currentLine.split(",")); + csvEntriesWithHeader.add(values); + } + + input.close(); + } catch (IOException ex) { + throw ex; + } catch (Exception ex) { + // Other unexpected error occurred + ex.printStackTrace(); + return null; + } + } + + return csvEntriesWithHeader; + } + + /** + * This method reads the CSV file without the header. + * Each line of the CSV has data about the disaster separated by comma. Store it in a list + * Add that list to a master list and return the master list. + * + * @param fileName filePath of the CSV file with data + * @return list with a list of data related to the disaster + * @throws IOException error on reading the file + */ + public List> readCSVFileWithoutHeaders(String fileName) throws IOException { + // Get all records with the header + List> allCSVEntries = readCSVFileWithHeaders(fileName); + + //Remove the first entry of header + allCSVEntries.remove(0); + + return allCSVEntries; + } + + /** + * This method iterates through the records list and checks the number of reported incidents for each record + * It finds the year with the most number of reported incidents + * + * @param records list of data related to a disaster event + * @return info about the most impactful Disaster based on year + */ + public DisasterDescription getMostImpactfulYear(List> records) { + + // See if there is at least one record + if (records.size() > 0) { + + //Assume the first entry is the most impactful + int year = Integer.parseInt(records.get(0).get(2)); + int maxImpact = Integer.parseInt(records.get(0).get(3)); + String entity = records.get(0).get(0); + + //Iterate through each entry and get the find the most impact + for (List entry : records) { + int currentImpact = Integer.parseInt(entry.get(3)); + if (currentImpact > maxImpact) { + maxImpact = currentImpact; + year = Integer.parseInt(entry.get(2)); + entity = entry.get(0); + } + } + + DisasterDescription disaster = new DisasterDescription(); + disaster.setYear(year); + disaster.setReportedIncidentsNum(maxImpact); + disaster.setCategory(entity); + + return disaster; + } + + return new DisasterDescription(); + } + + /** + * This method filters the records by the given category. + * It calls getMostImpactfulYear() method to find the most impactful year after filtering the list. + * Then returns the info about the most impactful disaster for that given category. + * + * @param category Category of disaster + * @param records List of data about the disaster + * @return Info about the most impactful disaster for the given category + */ + public DisasterDescription getMostImpactfulYearByCategory(String category, List> records) { + + //Filter the list by category + List> recordByCategory = new LinkedList>(); + for (List record : records) { + // Check if the category matches + if (record.get(0).toLowerCase().trim().equals(category.toLowerCase().trim())) { + recordByCategory.add(record); + } + } + + return getMostImpactfulYear(recordByCategory); + } + + /** + * This method filters the list by the given year. + * It calls getMostImpactfulYear() method to find the most impactful year after filtering the list. + * Then it returns info about the most impactful disaster for that given year. + * + * @param year Filter the record based on this year + * @param records List of data about the disaster + * @return Info about the most impactful disaster for the given year + */ + public DisasterDescription getMostImpactfulDisasterByYear(String year, List> records) { + + // Filter the record by year + List> recordsByYear = new LinkedList>(); + for (List record : records) { + if (record.get(2).trim().toLowerCase().equals(year.trim().toLowerCase())) { + // Ignore for all natural disasters + if (!record.get(0).trim().toLowerCase().equals("all natural disasters")) { + recordsByYear.add(record); + } + } + } + + return getMostImpactfulYear(recordsByYear); + } + + /** + * This method iterates through the list of records + * For each record, if the category matches, it retrieves the number of reported incidents + * Then it calculates the total number of reported incidents for that category. + * + * @param category Disaster category to filter the records + * @param records List of data about disaster + * @return Total number of reported incidents for the given category + */ + public DisasterDescription getTotalReportedIncidentsByCategory(String category, List> records) { + + int totalIncidents = 0; + for (List record : records) { + // Check if the category matches + if (record.get(0).toLowerCase().trim().equals(category.toLowerCase().trim())) { + totalIncidents += Integer.parseInt(record.get(3)); + } + } + + DisasterDescription disaster = new DisasterDescription(); + disaster.setCategory(category); + disaster.setReportedIncidentsNum(totalIncidents); + + return disaster; + } + + + /** + * This method will return the count if the number of incident falls within the provided range. + * To simplify the problem, we assume: + * + A value of -1 is provided if the max range is NOT applicable. + * + A min value can be provided, without providing a max value (which then has to be -1 like indicated above). + * + If a max value is provided, then a max value is also needed. + */ + public int countImpactfulYearsWithReportedIncidentsWithinRange(List> records, int min, int max) { + boolean hasMaximumRange = max != -1; + int count = 0; + + for (List record : records) { + int reportedIncident = Integer.parseInt(record.get(3)); + + if (hasMaximumRange) { + if (reportedIncident >= min && reportedIncident <= max) { + count++; + } + } else { // No maximum range + if (reportedIncident >= min) { + count++; + } + } + } + + return count; + } + + /** + * This method checks if the first list has more reported incidents than the second list. + * It calls getTotalReportedIncidents() to get the total number of reported incidents for both records + * It compares the total number of reported incidents for both records. + * + * @param records1 List of data about disaster + * @param records2 List of data about disaster + * @return Boolean value indicating record1 has more reported incidents than record2 + */ + public boolean firstRecordsHaveMoreReportedIndicents(List> records1, List> records2) { + return getTotalReportedIncidents(records1) > getTotalReportedIncidents(records2); + } + + /** + * This method iterates through the records + * For each record, it retrieves the number of reported incident. + * It calculates the total reported incidents for that given data. + * + * @param records List of data about the disaster + * @return int total reported incidents + */ + private int getTotalReportedIncidents(List> records) { + int total = 0; + for (List entry : records) { + total += Integer.parseInt(entry.get(3)); + } + return total; + } + + } diff --git a/src/main/java/codingcompetition2019/DisasterDescription.java b/src/main/java/codingcompetition2019/DisasterDescription.java index 662626b..226d12e 100644 --- a/src/main/java/codingcompetition2019/DisasterDescription.java +++ b/src/main/java/codingcompetition2019/DisasterDescription.java @@ -1,5 +1,58 @@ package codingcompetition2019; +/** + * This class holds basic information about a given disaster + * It has the year the disaster occurred + * It has the total number of reported incidents for that given disaster + * It has the category of the disaster + * It has the country the description occurred + */ public class DisasterDescription { - // TODO finish this class + + private String category; + private String country; + private int reportedIncidentsNum; + private int year; + + /** + * No-arg constructor + */ + public DisasterDescription() { + + } + + + // Accessors for data field + public String getCategory() { + return category; + } + + public int getReportedIncidentsNum() { + return reportedIncidentsNum; + } + + public String getYear() { + return year + ""; + } + + public String getCountry() { + return country; + } + + // Mutator for data field + public void setCategory(String category) { + this.category = category; + } + + public void setReportedIncidentsNum(int reportedIncidentsNum) { + this.reportedIncidentsNum = reportedIncidentsNum; + } + + public void setYear(int year) { + this.year = year; + } + + public void setCountry(String country) { + this.country = country; + } } diff --git a/src/main/java/codingcompetition2019/NatualDisasterViewer.java b/src/main/java/codingcompetition2019/NatualDisasterViewer.java new file mode 100644 index 0000000..99b3342 --- /dev/null +++ b/src/main/java/codingcompetition2019/NatualDisasterViewer.java @@ -0,0 +1,9 @@ +package codingcompetition2019; + +import codingcompetition2019.gui.WelcomeScreen; + +public class NatualDisasterViewer { + public static void main(String[] args){ + (new WelcomeScreen()).setVisible(true); + } +} diff --git a/src/main/java/codingcompetition2019/gui/AllNaturalDisasters.java b/src/main/java/codingcompetition2019/gui/AllNaturalDisasters.java new file mode 100644 index 0000000..0f4bf54 --- /dev/null +++ b/src/main/java/codingcompetition2019/gui/AllNaturalDisasters.java @@ -0,0 +1,156 @@ +package codingcompetition2019.gui; + +import codingcompetition2019.CodingCompCSVUtil; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.*; +import java.util.List; + +public class AllNaturalDisasters extends JFrame { + + private String naturalDisasterByTypeFile = "src/main/resources/natural-disasters-by-type.csv"; + private CodingCompCSVUtil util; + private List> records; + private ArrayList disasters; + private ArrayList years; + + private JLabel lblImpactedYear; + private JLabel lblTotalIncidents; + private JLabel lblDisasterName; + private JLabel lblReportedIncidentsByYear; + + public AllNaturalDisasters(){ + super("All Natural Disasters"); + util = new CodingCompCSVUtil(); + try{ + records = util.readCSVFileWithoutHeaders(naturalDisasterByTypeFile); + getDisasterCategoriesAndYear(); + } + catch (Exception ex) { + + } + + createGUIComponents(); + + setSize(400, 400); + setLocationRelativeTo(null); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + } + + private void getDisasterCategoriesAndYear(){ + Set categories = new HashSet(); + Set yearList = new TreeSet(); + + for(List record : records){ + String category = record.get(0).trim(); + String year = record.get(2).trim(); + if (!categories.contains(category)) + categories.add(category); + + if (!yearList.contains(year)) + yearList.add(year); + } + disasters = new ArrayList(); + years = new ArrayList(); + + disasters.addAll(categories); + years.addAll(yearList); + } + + private void createGUIComponents(){ + Container pane = this.getContentPane(); + pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS)); + + pane.add(Box.createVerticalGlue()); + pane.add(getSelectionMenuByCategory()); + pane.add(Box.createVerticalStrut(20)); + pane.add(getDisasterInfoPanelByCategory()); + pane.add(Box.createVerticalStrut(40)); + pane.add(getSelectionMenuByYear()); + pane.add(Box.createVerticalStrut(20)); + pane.add(getDisasterInfoPanelByYear()); + } + + private JPanel getDisasterInfoPanelByCategory(){ + JPanel disasterInfo = new JPanel(); + disasterInfo.setBorder(BorderFactory.createTitledBorder("Disaster Info By Category")); + disasterInfo.setLayout(new GridLayout(2, 2)); + + disasterInfo.add(new JLabel("Most impacted year: ")); + lblImpactedYear = new JLabel(util.getMostImpactfulYearByCategory(disasters.get(0), records).getYear() + ""); + disasterInfo.add(lblImpactedYear); + disasterInfo.add(new JLabel("Total reported incidents: ")); + lblTotalIncidents = new JLabel(util.getTotalReportedIncidentsByCategory(disasters.get(0), records).getReportedIncidentsNum() + ""); + disasterInfo.add(lblTotalIncidents); + + + return disasterInfo; + } + + private JPanel getSelectionMenuByCategory(){ + JPanel selection = new JPanel(); + selection.setLayout(new GridLayout(1, 2)); + selection.setBorder(new EmptyBorder(10,10,10,10)); + selection.add(new JLabel("Select Disaster Type")); + + final JComboBox cbDisaster = new JComboBox(disasters.toArray()); + selection.add(cbDisaster); + cbDisaster.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateCategoryInfo((String)cbDisaster.getSelectedItem()); + } + }); + + return selection; + } + + private JPanel getSelectionMenuByYear(){ + JPanel selection = new JPanel(); + selection.setLayout(new GridLayout(1, 2)); + selection.setBorder(new EmptyBorder(10,10,10,10)); + selection.add(new JLabel("Select Year")); + + final JComboBox cbYear = new JComboBox(years.toArray()); + selection.add(cbYear); + cbYear.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateYearInfo((String)cbYear.getSelectedItem()); + } + }); + + return selection; + } + + private JPanel getDisasterInfoPanelByYear(){ + JPanel disasterInfo = new JPanel(); + disasterInfo.setBorder(BorderFactory.createTitledBorder("Disaster Info By Year")); + disasterInfo.setLayout(new GridLayout(2, 2)); + + disasterInfo.add(new JLabel("Most impactful disaster: ")); + lblDisasterName = new JLabel(util.getMostImpactfulDisasterByYear(years.get(0), records).getYear() + ""); + disasterInfo.add(lblDisasterName); + + disasterInfo.add(new JLabel("Total reported incidents: ")); + lblReportedIncidentsByYear = new JLabel(util.getTotalReportedIncidentsByCategory(disasters.get(0), records).getReportedIncidentsNum() + ""); + disasterInfo.add(lblReportedIncidentsByYear); + + + return disasterInfo; + } + + private void updateCategoryInfo(String category){ + lblTotalIncidents.setText(util.getTotalReportedIncidentsByCategory(category, records).getReportedIncidentsNum() + ""); + lblImpactedYear.setText(util.getMostImpactfulYearByCategory(category, records).getYear() + ""); + } + + private void updateYearInfo(String year){ + lblDisasterName.setText(util.getMostImpactfulDisasterByYear(year, records).getCategory() + ""); + lblReportedIncidentsByYear.setText(util.getMostImpactfulDisasterByYear(year, records).getReportedIncidentsNum() + ""); + } + + +} diff --git a/src/main/java/codingcompetition2019/gui/WelcomeScreen.java b/src/main/java/codingcompetition2019/gui/WelcomeScreen.java new file mode 100644 index 0000000..64459d3 --- /dev/null +++ b/src/main/java/codingcompetition2019/gui/WelcomeScreen.java @@ -0,0 +1,58 @@ +package codingcompetition2019.gui; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class WelcomeScreen extends JFrame { + + private JComboBox cbData; + + public WelcomeScreen() { + super("Natural Disaster Info"); + createGUIComponents(); + setSize(400, 300); + setLocationRelativeTo(null); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + } + + private void createGUIComponents() { + JPanel dataTypePanel = new JPanel(); + dataTypePanel.setLayout(new GridLayout(1, 2)); + dataTypePanel.setBorder(new EmptyBorder(20, 20, 20, 20)); + dataTypePanel.add(new JLabel("Select Data Type")); + + String[] data = {"Natural Disasters by Type"}; + cbData = new JComboBox(data); + dataTypePanel.add(cbData); + + JButton btnGetData = new JButton("View Data"); + btnGetData.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + switch(cbData.getSelectedIndex()){ + case 0: + (new AllNaturalDisasters()).setVisible(true); break; +// case 1: +// (new Earthquake()).setVisible(true); break; +// case 3: +// (new Volcano()).setVisible(true); break; + } + } + }); + + Container pane = this.getContentPane(); + pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS)); + + pane.add(Box.createVerticalStrut(25)); + pane.add(new JLabel("Natural Disaster Info")); + pane.add(Box.createVerticalStrut(50)); + pane.add(dataTypePanel); + pane.add(Box.createVerticalStrut(50)); + pane.add(btnGetData); + pane.add(Box.createVerticalStrut(25)); + } + + +}