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

[#634] Support defining sinceDate, untilDate and period in report config #1501

Closed
wants to merge 8 commits into from
12 changes: 11 additions & 1 deletion docs/ug/configFiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,20 @@ e.g.: `example.java` in `example-repo` can either be in the `test` group or the

## `report-config.json`

You can optionally use `report-config.json` to customize report generation by providing the following information. ([example](report-config.csv))
You can optionally use `report-config.json` to customize report generation by providing the following information. ([example](report-config.json))

**Fields to provide**:
* `title`: Title of the generated report, which is also the title of the deployed dashboard. Default: "RepoSense Report"
* `sinceDate`: The first day of the period to be analyzed, in the format `DD/MM/YYYY`. Default: One month before the current date.
* `untilDate`: The last day of the period to be analyzed, in the format `DD/MM/YYYY`. Default: Current date.
* `period`: The period of the analysis window, in the format `nd` (for `n` days) or `nw` (for `n` weeks). It is used to calculate end date if only start date is specified, or calculate end date if only start date is specified.

<box type="info" seamless>

* `sinceDate`, `untilDate` and `period` cannot be declared together and will be ignored if all 3 configuration settings are present in the report configuration.
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the reason for ignoring all 3? Why not just take since and until in this case, assuming nothing provided in CLI arguments. Don't do changes as of now, just explain the intuition behind this logic for now.

Copy link
Member Author

Choose a reason for hiding this comment

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

I followed the logic originally present for CLI arguments, whereby if all 3 arguments are provided we will throw an exception. In this case, because it is in report config, I chose to ignore instead of throwing an exception.

* CLI arguments takes precedence over the values provided in this report configuration. If the same configuration is set as a CLI argument as well as in the report configuration, the value in the report configuration is ignored.
* Likewise, if there is a conflict, such as `sinceDate`, `untilDate` and `period` being declared together even though some of them were declared in the report configuration, only the CLI arguments will be taken and the report configuration will be completely ignored.
Copy link
Contributor

Choose a reason for hiding this comment

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

What if all 3 are provided in the report config itself and nothing is provided as CLI arguments?

Copy link
Member Author

Choose a reason for hiding this comment

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

Similar to the first part, all 3 will be ignored, as if the user tried to specify all 3 in the CLI arguments instead of the report config.

</box>

<!-- ==================================================================================================== -->

Expand Down
5 changes: 4 additions & 1 deletion docs/ug/report-config.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"title": "CS2103 RepoSense Report"
"title": "CS2103 RepoSense Report",
"sinceDate": "23/01/2021",
"untilDate": "23/03/2021",
"period": "30d"
}
72 changes: 70 additions & 2 deletions src/main/java/reposense/model/ReportConfiguration.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,85 @@
package reposense.model;

import java.util.Date;
import java.util.Optional;
import java.util.logging.Logger;

import reposense.parser.ParseException;
import reposense.parser.PeriodArgumentType;
import reposense.parser.SinceDateArgumentType;
import reposense.system.LogsManager;
import reposense.util.TimeUtil;

/**
* Represents configuration information from JSON config file for generated report.
*/
public class ReportConfiguration {
private static final Logger logger = LogsManager.getLogger(ReportConfiguration.class);
private static final String DEFAULT_TITLE = "RepoSense Report";
private static final String MESSAGE_INVALID_DATE_PROVIDED = "Invalid date provided in report config: %s.";
private static final String MESSAGE_IGNORING_REPORT_CONFIG = "Ignoring the report config provided.";
private String title;
private String sinceDate;
private String untilDate;
private String period;

public String getTitle() {
if (title == null) {
if (this.title == null) {
return DEFAULT_TITLE;
}

return title;
return this.title;
}

public Optional<Date> getSinceDate() {
if (SinceDateArgumentType.FIRST_COMMIT_DATE_SHORTHAND.equals(this.sinceDate)) {
return Optional.of(SinceDateArgumentType.ARBITRARY_FIRST_COMMIT_DATE);
}

return this.getDate(this.sinceDate, "00:00:00");
}

public Optional<Date> getUntilDate() {
return this.getDate(this.untilDate, "23:59:59");
}

public Optional<Integer> getPeriod() {
if (this.period == null) {
return Optional.empty();
}

try {
return PeriodArgumentType.parse(this.period);
} catch (ParseException pe) {
logger.warning(pe.getMessage() + " " + MESSAGE_IGNORING_REPORT_CONFIG);
return Optional.empty();
}
}

/**
* This function resets the values of the {@code sinceDate}, {@code untilDate} and {@code period} to null, such that
* RepoSense ignores the configuration provided in the report config provided.
*/
public void ignoreSinceUntilDateAndPeriod() {
this.sinceDate = null;
this.untilDate = null;
this.period = null;
}

/**
* This function parses the given {@code date} String into a Date object.
*/
private Optional<Date> getDate(String date, String time) {
if (date == null) {
return Optional.empty();
}

try {
String value = TimeUtil.extractDate(date);
return Optional.of(TimeUtil.parseDate(value + " " + time));
} catch (java.text.ParseException pe) {
logger.warning(String.format(MESSAGE_INVALID_DATE_PROVIDED, date) + " " + MESSAGE_IGNORING_REPORT_CONFIG);
return Optional.empty();
}
}
}
62 changes: 49 additions & 13 deletions src/main/java/reposense/parser/ArgsParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ public class ArgsParser {
private static final String PROGRAM_DESCRIPTION =
"RepoSense is a contribution analysis tool for Git repositories.";
private static final String MESSAGE_HEADER_MUTEX = "mutual exclusive arguments";
private static final String MESSAGE_SINCE_DATE_LATER_THAN_UNTIL_DATE =
"\"Since Date\" cannot be later than \"Until Date\".";
private static final String MESSAGE_SINCE_DATE_LATER_THAN_TODAY_DATE =
"\"Since Date\" must not be later than today's date.";
private static final String MESSAGE_HAVE_SINCE_DATE_UNTIL_DATE_AND_PERIOD =
"\"Since Date\", \"Until Date\", and \"Period\" cannot be applied together.";
private static final String MESSAGE_USING_DEFAULT_CONFIG_PATH =
Expand Down Expand Up @@ -243,6 +239,9 @@ public static CliArguments parse(String[] args) throws HelpScreenException, Pars
Namespace results = parser.parseArgs(args);
Date sinceDate;
Date untilDate;
Optional<Date> tempSinceDate = Optional.empty();
Optional<Date> tempUntilDate = Optional.empty();
Optional<Integer> tempPeriod = Optional.empty();

Path configFolderPath = results.get(CONFIG_FLAGS[0]);
Path reportFolderPath = results.get(VIEW_FLAGS[0]);
Expand Down Expand Up @@ -274,30 +273,67 @@ public static CliArguments parse(String[] args) throws HelpScreenException, Pars
}
}

boolean isSinceDateProvided = cliSinceDate.isPresent();
boolean isUntilDateProvided = cliUntilDate.isPresent();
boolean isPeriodProvided = cliPeriod.isPresent();
if (isSinceDateProvided && isUntilDateProvided && isPeriodProvided) {
// CLI arguments are treated with the highest priority and will throw an Exception if a conflict occurs
if (cliSinceDate.isPresent() && cliUntilDate.isPresent() && cliPeriod.isPresent()) {
throw new ParseException(MESSAGE_HAVE_SINCE_DATE_UNTIL_DATE_AND_PERIOD);
}
int numCloningThreads = results.get(CLONING_THREADS_FLAG[0]);
int numAnalysisThreads = results.get(ANALYSIS_THREADS_FLAG[0]);

boolean isSinceDateProvided = cliSinceDate.isPresent() || reportConfig.getSinceDate().isPresent();
boolean isUntilDateProvided = cliUntilDate.isPresent() || reportConfig.getUntilDate().isPresent();
boolean isPeriodProvided = cliPeriod.isPresent() || reportConfig.getPeriod().isPresent();

// Report config is treated with less priority and will be ignored if there is a conflict
if (isSinceDateProvided && isUntilDateProvided && isPeriodProvided) {
logger.warning(MESSAGE_IGNORING_REPORT_SINCE_DATE_UNTIL_DATE_AND_PERIOD);
reportConfig.ignoreSinceUntilDateAndPeriod();
isSinceDateProvided = cliSinceDate.isPresent();
isUntilDateProvided = cliUntilDate.isPresent();
isPeriodProvided = cliPeriod.isPresent();
}
if (cliSinceDate.isPresent() && reportConfig.getSinceDate().isPresent()) {
logger.warning(MESSAGE_IGNORING_REPORT_CONFIG_SINCE_DATE);
}
if (cliUntilDate.isPresent() && reportConfig.getUntilDate().isPresent()) {
logger.warning(MESSAGE_IGNORING_REPORT_CONFIG_UNTIL_DATE);
}
if (cliPeriod.isPresent() && reportConfig.getPeriod().isPresent()) {
logger.warning(MESSAGE_IGNORING_REPORT_CONFIG_PERIOD);
}

// Set the values to those provided by the CLI arguments first, otherwise take from report config
if (cliPeriod.isPresent()) {
tempPeriod = cliPeriod;
} else if (reportConfig.getPeriod().isPresent()) {
tempPeriod = reportConfig.getPeriod();
}
if (cliSinceDate.isPresent()) {
tempSinceDate = cliSinceDate;
} else if (reportConfig.getSinceDate().isPresent()) {
tempSinceDate = reportConfig.getSinceDate();
}
if (cliUntilDate.isPresent()) {
tempUntilDate = cliUntilDate;
} else if (reportConfig.getUntilDate().isPresent()) {
tempUntilDate = reportConfig.getUntilDate();
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Find the whole logic above a bit cumbersome. There is a better way to avoid a few of the checks by writing the if conditions in a clever manner. I will leave that to you to figure out.

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought of the following, not sure if that is what you are looking for?

tempPeriod = Optional.ofNullable(cliPeriod.orElse(reportConfig.getPeriod().orElse(null)));
tempSinceDate = Optional.ofNullable(cliSinceDate.orElse(reportConfig.getSinceDate().orElse(null)));
tempUntilDate = Optional.ofNullable(cliUntilDate.orElse(reportConfig.getUntilDate().orElse(null)));

Copy link
Contributor

Choose a reason for hiding this comment

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

I meant for the whole page in general. Not just the last three lines. Cause I can see you see checking if arguments are present or not 2/3 times. So I didn't like that part of the logic. Can try to make sure that you check these only once and make it work.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry @Tejas2805 I still can't figure out how to better write this part of the code. My train of thought for this is:

  1. Check if all 3 arguments are provided (either from report config, from CLI, or from both). If yes, we ignore all of them and take only the CLI arguments.
  2. For each of the 3 arguments, if it is given in both the CLI and in the report config, we give the user a warning.
  3. Afterwards, we take only the CLI arguments.
  4. We then process the actual since and until dates, based on the processed information. If both since and until dates are provided (which only happens in CLI), the period is ignored without warning.

I feel that they are all checking different types of conditions, so I can't wrap my head around how to better craft this section.

Date currentDate = TimeUtil.getCurrentDate(zoneId);

if (isSinceDateProvided) {
sinceDate = TimeUtil.getZonedSinceDate(cliSinceDate.get(), zoneId);
sinceDate = TimeUtil.getZonedSinceDate(tempSinceDate.get(), zoneId);
} else {
sinceDate = isPeriodProvided
? TimeUtil.getDateMinusNDays(cliUntilDate, zoneId, cliPeriod.get())
: TimeUtil.getDateMinusAMonth(cliUntilDate, zoneId);
? TimeUtil.getDateMinusNDays(tempUntilDate, zoneId, tempPeriod.get())
: TimeUtil.getDateMinusAMonth(tempUntilDate, zoneId);
}

if (isUntilDateProvided) {
untilDate = TimeUtil.getZonedUntilDate(cliUntilDate.get(), zoneId);
untilDate = TimeUtil.getZonedUntilDate(tempUntilDate.get(), zoneId);
} else {
untilDate = (isSinceDateProvided && isPeriodProvided)
? TimeUtil.getDatePlusNDays(cliSinceDate, zoneId, cliPeriod.get())
? TimeUtil.getDatePlusNDays(tempSinceDate, zoneId, tempPeriod.get())
: currentDate;
}

Expand Down
161 changes: 161 additions & 0 deletions src/test/java/reposense/parser/ArgsParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ public class ArgsParserTest {
CONFIG_FOLDER_ABSOLUTE.resolve(AuthorConfigCsvParser.AUTHOR_CONFIG_FILENAME);
private static final String NONEXISTENT_DIRECTORY = "some_non_existent_dir/";

private static final Path REPORT_CONFIG_FOLDER_ABSOLUTE_ONE = loadResource(ArgsParserTest.class,
"report_config_test/one");
private static final Path REPORT_CONFIG_FOLDER_ABSOLUTE_TWO = loadResource(ArgsParserTest.class,
"report_config_test/two");
private static final Path REPORT_CONFIG_FOLDER_ABSOLUTE_THREE = loadResource(ArgsParserTest.class,
"report_config_test/three");

private static final InputBuilder DEFAULT_INPUT_BUILDER = new InputBuilder();

private static final String TEST_REPO_REPOSENSE = "https://github.com/reposense/RepoSense.git";
Expand Down Expand Up @@ -739,6 +746,160 @@ public void parse_withDatesAndTimezone_success() throws Exception {
Assert.assertEquals(expectedUntilDate, cliArguments.getUntilDate());
}

@Test
public void parse_reportConfigFolderOnly_success() throws Exception {
String input = new InputBuilder()
.addConfig(REPORT_CONFIG_FOLDER_ABSOLUTE_ONE)
.addTimezone(DEFAULT_TIMEZONE)
.build();
CliArguments cliArguments = ArgsParser.parse(translateCommandline(input));
Date expectedSinceDate = TestUtil.getSinceDate(2019, Calendar.MAY, 20);
Date expectedUntilDate = TestUtil.getUntilDate(2020, Calendar.NOVEMBER, 20);

Assert.assertTrue(cliArguments instanceof ConfigCliArguments);
Assert.assertEquals(expectedSinceDate, cliArguments.getSinceDate());
Assert.assertEquals(expectedUntilDate, cliArguments.getUntilDate());

input = new InputBuilder()
.addConfig(REPORT_CONFIG_FOLDER_ABSOLUTE_TWO)
.addTimezone(DEFAULT_TIMEZONE)
.build();
cliArguments = ArgsParser.parse(translateCommandline(input));
expectedSinceDate = TestUtil.getSinceDate(2019, Calendar.MAY, 20);
expectedUntilDate = TestUtil.getUntilDate(2019, Calendar.JUNE, 9);

Assert.assertTrue(cliArguments instanceof ConfigCliArguments);
Assert.assertEquals(expectedSinceDate, cliArguments.getSinceDate());
Assert.assertEquals(expectedUntilDate, cliArguments.getUntilDate());

input = new InputBuilder()
.addConfig(REPORT_CONFIG_FOLDER_ABSOLUTE_THREE)
.addTimezone(DEFAULT_TIMEZONE)
.build();
cliArguments = ArgsParser.parse(translateCommandline(input));
expectedSinceDate = TestUtil.getSinceDate(2020, Calendar.OCTOBER, 20);
expectedUntilDate = TestUtil.getUntilDate(2020, Calendar.NOVEMBER, 20);

Assert.assertTrue(cliArguments instanceof ConfigCliArguments);
Assert.assertEquals(expectedSinceDate, cliArguments.getSinceDate());
Assert.assertEquals(expectedUntilDate, cliArguments.getUntilDate());
}

@Test
public void parse_reportConfigAndCliNonConflict_success() throws Exception {
String input = new InputBuilder()
.addConfig(REPORT_CONFIG_FOLDER_ABSOLUTE_TWO)
.addTimezone(DEFAULT_TIMEZONE)
.addPeriod("5d")
.build();
CliArguments cliArguments = ArgsParser.parse(translateCommandline(input));
Date expectedSinceDate = TestUtil.getSinceDate(2019, Calendar.MAY, 20);
Date expectedUntilDate = TestUtil.getUntilDate(2019, Calendar.MAY, 24);

Assert.assertTrue(cliArguments instanceof ConfigCliArguments);
Assert.assertEquals(expectedSinceDate, cliArguments.getSinceDate());
Assert.assertEquals(expectedUntilDate, cliArguments.getUntilDate());
input = new InputBuilder()
.addConfig(REPORT_CONFIG_FOLDER_ABSOLUTE_THREE)
.addTimezone(DEFAULT_TIMEZONE)
.addSinceDate("30/07/2018")
.build();
cliArguments = ArgsParser.parse(translateCommandline(input));
expectedSinceDate = TestUtil.getSinceDate(2018, Calendar.JULY, 30);
expectedUntilDate = TestUtil.getUntilDate(2020, Calendar.NOVEMBER, 20);

Assert.assertTrue(cliArguments instanceof ConfigCliArguments);
Assert.assertEquals(expectedSinceDate, cliArguments.getSinceDate());
Assert.assertEquals(expectedUntilDate, cliArguments.getUntilDate());
}

@Test
public void parse_reportConfigAndCliConflict_ignoreReportConfig() throws Exception {
String input = new InputBuilder()
.addConfig(REPORT_CONFIG_FOLDER_ABSOLUTE_ONE)
.addTimezone(DEFAULT_TIMEZONE)
.addSinceDate("16/06/2018")
.build();
CliArguments cliArguments = ArgsParser.parse(translateCommandline(input));
Date expectedSinceDate = TestUtil.getSinceDate(2018, Calendar.JUNE, 16);
Date expectedUntilDate = TestUtil.getUntilDate(2020, Calendar.NOVEMBER, 20);

Assert.assertTrue(cliArguments instanceof ConfigCliArguments);
Assert.assertEquals(expectedSinceDate, cliArguments.getSinceDate());
Assert.assertEquals(expectedUntilDate, cliArguments.getUntilDate());

input = new InputBuilder()
.addConfig(REPORT_CONFIG_FOLDER_ABSOLUTE_TWO)
.addTimezone(DEFAULT_TIMEZONE)
.addPeriod("2w")
.build();
cliArguments = ArgsParser.parse(translateCommandline(input));
expectedSinceDate = TestUtil.getSinceDate(2019, Calendar.MAY, 20);
expectedUntilDate = TestUtil.getUntilDate(2019, Calendar.JUNE, 2);

Assert.assertTrue(cliArguments instanceof ConfigCliArguments);
Assert.assertEquals(expectedSinceDate, cliArguments.getSinceDate());
Assert.assertEquals(expectedUntilDate, cliArguments.getUntilDate());

input = new InputBuilder()
.addConfig(REPORT_CONFIG_FOLDER_ABSOLUTE_THREE)
.addTimezone(DEFAULT_TIMEZONE)
.addSinceDate("08/05/2018")
.addUntilDate("07/08/2018")
.build();
cliArguments = ArgsParser.parse(translateCommandline(input));
expectedSinceDate = TestUtil.getSinceDate(2018, Calendar.MAY, 8);
expectedUntilDate = TestUtil.getUntilDate(2018, Calendar.AUGUST, 7);

Assert.assertTrue(cliArguments instanceof ConfigCliArguments);
Assert.assertEquals(expectedSinceDate, cliArguments.getSinceDate());
Assert.assertEquals(expectedUntilDate, cliArguments.getUntilDate());
}

@Test
public void parse_reportConfigAndCliHaveSinceUntilDateAndPeriod_ignoreReportConfig() throws Exception {
String input = new InputBuilder()
.addConfig(REPORT_CONFIG_FOLDER_ABSOLUTE_ONE)
.addTimezone(DEFAULT_TIMEZONE)
.addPeriod("2w")
.addUntilDate("12/01/2019")
.build();
CliArguments cliArguments = ArgsParser.parse(translateCommandline(input));
Date expectedSinceDate = TestUtil.getSinceDate(2018, Calendar.DECEMBER, 30);
Date expectedUntilDate = TestUtil.getUntilDate(2019, Calendar.JANUARY, 12);

Assert.assertTrue(cliArguments instanceof ConfigCliArguments);
Assert.assertEquals(expectedSinceDate, cliArguments.getSinceDate());
Assert.assertEquals(expectedUntilDate, cliArguments.getUntilDate());

input = new InputBuilder()
.addConfig(REPORT_CONFIG_FOLDER_ABSOLUTE_TWO)
.addTimezone(DEFAULT_TIMEZONE)
.addUntilDate("23/04/2019")
.build();
cliArguments = ArgsParser.parse(translateCommandline(input));
expectedSinceDate = TestUtil.getSinceDate(2019, Calendar.MARCH, 23);
expectedUntilDate = TestUtil.getUntilDate(2019, Calendar.APRIL, 23);

Assert.assertTrue(cliArguments instanceof ConfigCliArguments);
Assert.assertEquals(expectedSinceDate, cliArguments.getSinceDate());
Assert.assertEquals(expectedUntilDate, cliArguments.getUntilDate());

input = new InputBuilder()
.addConfig(REPORT_CONFIG_FOLDER_ABSOLUTE_THREE)
.addTimezone(DEFAULT_TIMEZONE)
.addPeriod("5d")
.addUntilDate("16/02/2019")
.build();
cliArguments = ArgsParser.parse(translateCommandline(input));
expectedSinceDate = TestUtil.getSinceDate(2019, Calendar.FEBRUARY, 12);
expectedUntilDate = TestUtil.getUntilDate(2019, Calendar.FEBRUARY, 16);

Assert.assertTrue(cliArguments instanceof ConfigCliArguments);
Assert.assertEquals(expectedSinceDate, cliArguments.getSinceDate());
Assert.assertEquals(expectedUntilDate, cliArguments.getUntilDate());
}

@Test
public void parse_shallowCloning_success() throws Exception {
String input = new InputBuilder().addConfig(CONFIG_FOLDER_ABSOLUTE)
Expand Down
Loading