Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #71 Implement Analytics class #72

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/main/java/seedu/address/commons/util/DateUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,17 @@
return DATE_FORMATTER.format(date);
}

/**
* Adds a number of days to a Date object.
*
* @param date The Date object to add days to.
* @param days The number of days to add.
* @return The new Date object.
*/
public static Date addDay(Date date, int days) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not urgent, but I think we can refactor date representation in our project to use LocalDate or LocalDateTime. It is a more recent API that has more inbuilt methods such as plusDays() which can be used to avoid weird methods such as this,

long time = date.getTime();
time += days * 24 * 60 * 60 * 1000;
return new Date(time);

Check warning on line 53 in src/main/java/seedu/address/commons/util/DateUtil.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/commons/util/DateUtil.java#L51-L53

Added lines #L51 - L53 were not covered by tests
}

}
227 changes: 227 additions & 0 deletions src/main/java/seedu/address/model/person/Analytics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
package seedu.address.model.person;

import java.util.Date;

/**
* Represents the analytics of a LoanRecords object.
*/
public class Analytics {

private int numLoans; // total number of loans
private int numOverdueLoans; // total number of overdue loans
private int numActiveLoans; // total number of active loans

private float propOverdueLoans; // proportion of loans that are overdue over active loans

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment does not match implementation, which suggests it is proportion of overdue loans over all loans.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, it's fixed.

private float propActiveLoans; // proportion of loans that are active over total loans

private float totalValueLoaned; // total value of all loans
private float totalValueOverdue; // total value of all overdue loans
private float totalValueActive; // total value of all active loans

private float averageLoanValue; // average loan value of all loans
private float averageOverdueValue; // average loan value of all overdue loans
private float averageActiveValue; // average loan value of all active loans

private Date earliestLoanDate; // earliest loan date of all loans
private Date earliestReturnDate; // earliest return date of active loans
private Date latestLoanDate; // latest loan date of all loans
private Date latestReturnDate; // latest return date of active loans

private Analytics() {
this.numLoans = 0;
this.numOverdueLoans = 0;
this.numActiveLoans = 0;

Check warning on line 33 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L30-L33

Added lines #L30 - L33 were not covered by tests

this.propOverdueLoans = 0;
this.propActiveLoans = 0;

Check warning on line 36 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L35-L36

Added lines #L35 - L36 were not covered by tests

this.totalValueLoaned = 0;
this.totalValueOverdue = 0;
this.totalValueActive = 0;

Check warning on line 40 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L38-L40

Added lines #L38 - L40 were not covered by tests

this.averageLoanValue = 0;
this.averageOverdueValue = 0;
this.averageActiveValue = 0;

Check warning on line 44 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L42-L44

Added lines #L42 - L44 were not covered by tests

this.earliestLoanDate = null;
this.earliestReturnDate = null;
this.latestLoanDate = null;
this.latestReturnDate = null;
}

Check warning on line 50 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L46-L50

Added lines #L46 - L50 were not covered by tests

/**
* Updates the fields that count the number of various loans.
* @param loan The loan to update the fields with.
*/
private void updateNumFields(Loan loan) {
this.numLoans++;

Check warning on line 57 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L57

Added line #L57 was not covered by tests
if (loan.isOverdue()) {
this.numOverdueLoans++;

Check warning on line 59 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L59

Added line #L59 was not covered by tests
}
if (loan.isActive()) {
this.numActiveLoans++;

Check warning on line 62 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L62

Added line #L62 was not covered by tests
}
}

Check warning on line 64 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L64

Added line #L64 was not covered by tests

/**
* Updates the fields that calculate the proportion of various loans.
* This method should be called after the fields that count the number of various loans have been updated.
*/
private void updatePropFields() {
if (this.numActiveLoans > 0) {
this.propActiveLoans = (float) this.numActiveLoans / this.numLoans;

Check warning on line 72 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L72

Added line #L72 was not covered by tests
}
if (this.numLoans > 0) {
this.propOverdueLoans = (float) this.numOverdueLoans / this.numLoans;

Check warning on line 75 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L75

Added line #L75 was not covered by tests
}
}

Check warning on line 77 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L77

Added line #L77 was not covered by tests

/**
* Updates the fields that calculate the total value of various loans.
* @param loan The loan to update the fields with.
*/
private void updateValueFields(Loan loan) {
this.totalValueLoaned += loan.getValue();

Check warning on line 84 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L84

Added line #L84 was not covered by tests
if (loan.isOverdue()) {
this.totalValueOverdue += loan.getValue();

Check warning on line 86 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L86

Added line #L86 was not covered by tests
}
if (loan.isActive()) {
this.totalValueActive += loan.getValue();

Check warning on line 89 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L89

Added line #L89 was not covered by tests
}
}

Check warning on line 91 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L91

Added line #L91 was not covered by tests

/**
* Updates the fields that calculate the average value of various loans.
* This method should be called after the fields that calculate the total value of various loans have been updated.
*/
private void updateAverageFields() {
if (this.numActiveLoans > 0) {
this.averageActiveValue = this.totalValueActive / this.numActiveLoans;

Check warning on line 99 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L99

Added line #L99 was not covered by tests
}
if (this.numOverdueLoans > 0) {
this.averageOverdueValue = this.totalValueOverdue / this.numOverdueLoans;

Check warning on line 102 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L102

Added line #L102 was not covered by tests
}
if (this.numLoans > 0) {
this.averageLoanValue = this.totalValueLoaned / this.numLoans;

Check warning on line 105 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L105

Added line #L105 was not covered by tests
}
}

Check warning on line 107 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L107

Added line #L107 was not covered by tests

/**
* Updates the fields that calculate the earliest and latest dates of various loans.
* @param loan The loan to update the fields with.
*/
private void updateDateFields(Loan loan) {
if (this.earliestLoanDate == null || loan.getStartDate().before(this.earliestLoanDate)) {
this.earliestLoanDate = loan.getStartDate();

Check warning on line 115 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L115

Added line #L115 was not covered by tests
}
if (this.latestLoanDate == null || loan.getStartDate().after(this.latestLoanDate)) {
this.latestLoanDate = loan.getStartDate();

Check warning on line 118 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L118

Added line #L118 was not covered by tests
}
if (!loan.isReturned()) {
if (this.earliestReturnDate == null || loan.getReturnDate().before(this.earliestReturnDate)) {
this.earliestReturnDate = loan.getReturnDate();

Check warning on line 122 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L122

Added line #L122 was not covered by tests
}
if (this.latestReturnDate == null || loan.getReturnDate().after(this.latestReturnDate)) {
this.latestReturnDate = loan.getReturnDate();

Check warning on line 125 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L125

Added line #L125 was not covered by tests
}
}
}

Check warning on line 128 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L128

Added line #L128 was not covered by tests

/**
* Returns an Analytics object that represents the analytics of a LoanRecords object.
* @param loanRecords The LoanRecords object to get the analytics from.
* @return The Analytics object that represents the analytics of the LoanRecords object.
*/
public static Analytics getAnalytics(LoanRecords loanRecords) {
Analytics analytics = new Analytics();

Check warning on line 136 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L136

Added line #L136 was not covered by tests
for (int i = 0; i < loanRecords.size(); i++) {
Loan loan = loanRecords.getLoan(i);
analytics.updateNumFields(loan);
analytics.updateValueFields(loan);
analytics.updateDateFields(loan);

Check warning on line 141 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L138-L141

Added lines #L138 - L141 were not covered by tests
}
analytics.updatePropFields();
analytics.updateAverageFields();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that the update methods must be called in a specific order to prevent errors. While it is not a concern now because each Analytics object is effectively immutable and getAnalytics() is the only way of obtaining it, it would be good if this can be addressed so that future iterations do not run into related bugs.

return analytics;

Check warning on line 145 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L143-L145

Added lines #L143 - L145 were not covered by tests
}

public int getNumLoans() {
return numLoans;

Check warning on line 149 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L149

Added line #L149 was not covered by tests
}

public int getNumOverdueLoans() {
return numOverdueLoans;

Check warning on line 153 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L153

Added line #L153 was not covered by tests
}

public int getNumActiveLoans() {
return numActiveLoans;

Check warning on line 157 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L157

Added line #L157 was not covered by tests
}

public float getPropOverdueLoans() {
return propOverdueLoans;

Check warning on line 161 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L161

Added line #L161 was not covered by tests
}

public float getPropActiveLoans() {
return propActiveLoans;

Check warning on line 165 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L165

Added line #L165 was not covered by tests
}

public float getTotalValueLoaned() {
return totalValueLoaned;

Check warning on line 169 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L169

Added line #L169 was not covered by tests
}

public float getTotalValueOverdue() {
return totalValueOverdue;

Check warning on line 173 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L173

Added line #L173 was not covered by tests
}

public float getTotalValueActive() {
return totalValueActive;

Check warning on line 177 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L177

Added line #L177 was not covered by tests
}

public float getAverageLoanValue() {
return averageLoanValue;

Check warning on line 181 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L181

Added line #L181 was not covered by tests
}

public float getAverageOverdueValue() {
return averageOverdueValue;

Check warning on line 185 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L185

Added line #L185 was not covered by tests
}

public float getAverageActiveValue() {
return averageActiveValue;

Check warning on line 189 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L189

Added line #L189 was not covered by tests
}

public Date getEarliestLoanDate() {
return earliestLoanDate;

Check warning on line 193 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L193

Added line #L193 was not covered by tests
}

public Date getEarliestReturnDate() {
return earliestReturnDate;

Check warning on line 197 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L197

Added line #L197 was not covered by tests
}

public Date getLatestLoanDate() {
return latestLoanDate;

Check warning on line 201 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L201

Added line #L201 was not covered by tests
}

public Date getLatestReturnDate() {
return latestReturnDate;

Check warning on line 205 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L205

Added line #L205 was not covered by tests
}

@Override
public String toString() {
return "Number of loans: " + numLoans + "\n"

Check warning on line 210 in src/main/java/seedu/address/model/person/Analytics.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Analytics.java#L210

Added line #L210 was not covered by tests

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe StringBuilder will be more appropriate since this is a significant number of strings?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, it's standard to just add strings like this since this is a constant operation.

+ "Number of overdue loans: " + numOverdueLoans + "\n"
+ "Number of active loans: " + numActiveLoans + "\n"
+ "Proportion of overdue loans: " + propOverdueLoans + "\n"
+ "Proportion of active loans: " + propActiveLoans + "\n"
+ "Total value loaned: " + totalValueLoaned + "\n"
+ "Total value of overdue loans: " + totalValueOverdue + "\n"
+ "Total value of active loans: " + totalValueActive + "\n"
+ "Average loan value: " + averageLoanValue + "\n"
+ "Average value of overdue loans: " + averageOverdueValue + "\n"
+ "Average value of active loans: " + averageActiveValue + "\n"
+ "Earliest loan date: " + earliestLoanDate + "\n"
+ "Earliest return date: " + earliestReturnDate + "\n"
+ "Latest loan date: " + latestLoanDate + "\n"
+ "Latest return date: " + latestReturnDate + "\n";
}

}
9 changes: 9 additions & 0 deletions src/main/java/seedu/address/model/person/Loan.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@
return !isReturned;
}

/**
* Returns true if the loan is overdue.
*/
public boolean isOverdue() {
// shift return date to the next day
Date returnDateNextDay = DateUtil.addDay(returnDate, 1);

Check warning on line 104 in src/main/java/seedu/address/model/person/Loan.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Loan.java#L104

Added line #L104 was not covered by tests
return !isReturned && new Date().after(returnDateNextDay);
}

/**
* Marks the loan as returned.
*/
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/seedu/address/model/person/Person.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
return address;
}

public Analytics getAnalytics() {
return Analytics.getAnalytics(loanRecords);

Check warning on line 61 in src/main/java/seedu/address/model/person/Person.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Person.java#L61

Added line #L61 was not covered by tests

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that each time the analytics are viewed, it is reconstructed. Is it possible to refactor this in the future such that it is maintained and modified?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible, but it's a trade-off between memory and runtime.

}

/**
* Returns an immutable tag set, which throws {@code UnsupportedOperationException}
* if modification is attempted.
Expand Down
Loading