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 all 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
}

}
225 changes: 225 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,225 @@
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.numLoans > 0) {
this.propActiveLoans = (float) this.numActiveLoans / this.numLoans;
this.propOverdueLoans = (float) this.numOverdueLoans / this.numActiveLoans;

Check warning on line 73 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-L73

Added lines #L72 - L73 were not covered by tests
}
}

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

/**
* 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 82 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#L82

Added line #L82 was not covered by tests
if (loan.isOverdue()) {
this.totalValueOverdue += 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.isActive()) {
this.totalValueActive += loan.getValue();

Check warning on line 87 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#L87

Added line #L87 was not covered by tests
}
}

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

/**
* 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 97 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#L97

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

Check warning on line 100 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#L100

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

Check warning on line 103 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#L103

Added line #L103 was not covered by tests
}
}

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

/**
* 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 113 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#L113

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

Check warning on line 116 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#L116

Added line #L116 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 120 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#L120

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

Check warning on line 123 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#L123

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

Check warning on line 126 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#L126

Added line #L126 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 134 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#L134

Added line #L134 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 139 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-L139

Added lines #L136 - L139 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 143 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#L141-L143

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

public int getNumLoans() {
return numLoans;

Check warning on line 147 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#L147

Added line #L147 was not covered by tests
}

public int getNumOverdueLoans() {
return numOverdueLoans;

Check warning on line 151 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#L151

Added line #L151 was not covered by tests
}

public int getNumActiveLoans() {
return numActiveLoans;

Check warning on line 155 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#L155

Added line #L155 was not covered by tests
}

public float getPropOverdueLoans() {
return propOverdueLoans;

Check warning on line 159 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#L159

Added line #L159 was not covered by tests
}

public float getPropActiveLoans() {
return propActiveLoans;

Check warning on line 163 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#L163

Added line #L163 was not covered by tests
}

public float getTotalValueLoaned() {
return totalValueLoaned;

Check warning on line 167 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#L167

Added line #L167 was not covered by tests
}

public float getTotalValueOverdue() {
return totalValueOverdue;

Check warning on line 171 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#L171

Added line #L171 was not covered by tests
}

public float getTotalValueActive() {
return totalValueActive;

Check warning on line 175 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#L175

Added line #L175 was not covered by tests
}

public float getAverageLoanValue() {
return averageLoanValue;

Check warning on line 179 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#L179

Added line #L179 was not covered by tests
}

public float getAverageOverdueValue() {
return averageOverdueValue;

Check warning on line 183 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#L183

Added line #L183 was not covered by tests
}

public float getAverageActiveValue() {
return averageActiveValue;

Check warning on line 187 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#L187

Added line #L187 was not covered by tests
}

public Date getEarliestLoanDate() {
return earliestLoanDate;

Check warning on line 191 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#L191

Added line #L191 was not covered by tests
}

public Date getEarliestReturnDate() {
return earliestReturnDate;

Check warning on line 195 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#L195

Added line #L195 was not covered by tests
}

public Date getLatestLoanDate() {
return latestLoanDate;

Check warning on line 199 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#L199

Added line #L199 was not covered by tests
}

public Date getLatestReturnDate() {
return latestReturnDate;

Check warning on line 203 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#L203

Added line #L203 was not covered by tests
}

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

Check warning on line 208 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#L208

Added line #L208 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