diff --git a/plugins/Insights/InsightReport.php b/plugins/Insights/InsightReport.php index 7c62df9344d71a362caf61c4db2b69c9bbee786d..88644f265af4262759bc8f9d237d39e2a529fc29 100644 --- a/plugins/Insights/InsightReport.php +++ b/plugins/Insights/InsightReport.php @@ -37,13 +37,12 @@ class InsightReport */ public function generateMoverAndShaker($reportMetadata, $period, $date, $lastDate, $metric, $currentReport, $lastReport, $totalValue, $lastTotalValue, $orderBy, $limitIncreaser, $limitDecreaser) { - $totalEvolution = Piwik::getPercentageSafe($totalValue - $lastTotalValue, $lastTotalValue, 1); + $totalEvolution = $this->getTotalEvolution($totalValue, $lastTotalValue); $minMoversPercent = 1; if ($totalEvolution >= 100) { // eg change from 50 to 150 = 200% - // $evolutionReverse = Piwik::getPercentageSafe($totalValue > $lastTotal ? $lastTotal : $totalValue, $totalValue > $lastTotal ? $totalValue : $lastTotal, 1); $factor = (int) ceil($totalEvolution / 500); $minGrowthPercentPositive = $totalEvolution + ($factor * 40); // min +240% $minGrowthPercentNegative = -70; // min -70% @@ -64,7 +63,7 @@ class InsightReport $minNewPercent = 5; } - if ($totalValue < 200 && $totalValue > 0) { + if ($totalValue < 200 && $totalValue != 0) { // force at least a change of 2 visits $minMoversPercent = (int) ceil(2 / ($totalValue / 100)); $minNewPercent = max($minNewPercent, $minMoversPercent); @@ -73,9 +72,7 @@ class InsightReport $dataTable = $this->generateInsight($reportMetadata, $period, $date, $lastDate, $metric, $currentReport, $lastReport, $totalValue, $minMoversPercent, $minNewPercent, $minDisappearedPercent, $minGrowthPercentPositive, $minGrowthPercentNegative, $orderBy, $limitIncreaser, $limitDecreaser); - $dataTable->setMetadata('lastTotalValue', $lastTotalValue); - $dataTable->setMetadata('evolutionTotal', $totalEvolution); - $dataTable->setMetadata('evolutionDifference', $totalValue - $lastTotalValue); + $this->addMoversAndShakersMetadata($dataTable, $totalValue, $lastTotalValue); return $dataTable; } @@ -113,6 +110,8 @@ class InsightReport $row->setColumn('isMoverAndShaker', false); } } + + $this->addMoversAndShakersMetadata($insight, $totalValue, $lastTotalValue); } /** @@ -266,4 +265,18 @@ class InsightReport return (int) $minVisits; } + + private function addMoversAndShakersMetadata(DataTable $dataTable, $totalValue, $lastTotalValue) + { + $totalEvolution = $this->getTotalEvolution($totalValue, $lastTotalValue); + + $dataTable->setMetadata('lastTotalValue', $lastTotalValue); + $dataTable->setMetadata('evolutionTotal', $totalEvolution); + $dataTable->setMetadata('evolutionDifference', $totalValue - $lastTotalValue); + } + + private function getTotalEvolution($totalValue, $lastTotalValue) + { + return Piwik::getPercentageSafe($totalValue - $lastTotalValue, $lastTotalValue, 1); + } } diff --git a/plugins/Insights/lang/en.json b/plugins/Insights/lang/en.json index 3a7667db508c6766dc0fbc3a4f2af7864332b0da..5dc5bd260ca5b4c9e46abd42049e45bbdbf451a7 100644 --- a/plugins/Insights/lang/en.json +++ b/plugins/Insights/lang/en.json @@ -4,12 +4,30 @@ "WidgetCategory": "Insights", "NoResultMatchesCriteria": "No rows match the criteria", "MoversAndShakersWidgetTitle": "Movers and Shakers", - "TitleConsideredVisits": "Considered rows having a growth of at least %s%% compared to %s.", - "TitleConsideredChanges": "Considered movers only if they increased or decreased by more than %s visits, new entries only if they increase by more than %s visits, and disappeared rows if they decreased by more than %s visits based on %s total visits.", - "TitleReportBasedOn": "Based on %s %s, rows less than %s %s were ignored", - "TitleRowChangeDetails": "'%s' changed from %s (%s) to %s (%s) %s", - "TitleIgnoredChanges": "Changes affecting less than %s visits were ignored.", - "TitleConsideredMoversAndShakers": "Visits changed from %s to %s compared to %s. Based on this an evolution of each row of %s%% is expected.", - "TitleConsideredChangesMoversAndShakers": "Considered movers only if they grew by more than %s%% visits or less than %s%% visits, new entries only if they increased by more than %s%% visits (%s), and disappeared rows if they descreased by less than %s%% visits (%s)" + "TitleRowChangeDetails": "'%s' changed from %s (%s) to %s (%s) %s.", + "TitleRowNewDetails": "'%s' increased by %s and is new compared to %s.", + "TitleRowDisappearedDetails": "'%s' decreased by %s and disappeared in %s compared to %s.", + "TitleRowMoverAndShaker": "This row had an higher impact than the average.", + "IgnoredChanges": "Changes affecting less than %s visits were ignored.", + "TitleConsideredInsightsGrowth": "The following rows have a growth of at least %s%% compared to %s.", + "TitleConsideredInsightsChanges": "The rows increased or decreased by at least %s visits (%s%% of %s total visits).", + "TitleConsideredMoversAndShakersGrowth": "%s changed from %s to %s compared to %s. Based on this an evolution of each row of %s%% is expected.", + "TitleConsideredMoversAndShakersChanges": "Considered movers only if they grew by more than %s%% visits or shrank by less than %s%% visits, new entries only if they increased by more than %s%% visits (%s), and disappeared rows if they shrank by less than %s%% visits (%s).", + "DatePeriodCombinationNotSupported": "It is not possible to generate insights for this date / period combination.", + "ControlGrowthDescription": "Minimum growth of", + "ControlComparedToDescription": "compared to the", + "DayComparedToPreviousDay": "previous day", + "DayComparedToPreviousWeek": "same day in the previous week", + "DayComparedToPreviousYear": "same day in the previous year", + "WeekComparedToPreviousWeek": "previous week", + "MonthComparedToPreviousMonth": "previous month", + "MonthComparedToPreviousYear": "same month in the previous year", + "Filter": "Filter", + "FilterOnlyMovers": "Only movers", + "FilterOnlyNew": "Only new", + "FilterOnlyDisappeared": "Only disappeared", + "FilterIncreaserAndDecreaser": "Increaser & decreaser", + "FilterOnlyIncreaser": "Only increaser", + "FilterOnlyDecreaser": "Only decreaser" } } \ No newline at end of file diff --git a/plugins/Insights/stylesheets/insightVisualization.less b/plugins/Insights/stylesheets/insightVisualization.less index f70ea83ee5ccc0da2a857899d96c5775fa4c9812..2c983d514ef8883add3e057afab2674ad3dbbcd6 100644 --- a/plugins/Insights/stylesheets/insightVisualization.less +++ b/plugins/Insights/stylesheets/insightVisualization.less @@ -5,6 +5,13 @@ } .insightsDataTable { + + .controlSeparator { + height: 1px; + border: 0px; + background-color: #cccccc; + } + th.orderBy { width: 20%; } diff --git a/plugins/Insights/templates/cannotDisplayReport.twig b/plugins/Insights/templates/cannotDisplayReport.twig index d32a9d1a364b6abc98f8f87039b49454967787cd..7c996e28379416c45d6b80a0752a9e958f1bf2cf 100644 --- a/plugins/Insights/templates/cannotDisplayReport.twig +++ b/plugins/Insights/templates/cannotDisplayReport.twig @@ -1,3 +1,3 @@ <div class="pk-emptyDataTable"> - It is not possible to generate insights for this date / period combination. + {{ 'Insights_DatePeriodCombinationNotSupported'|translate }} </div> \ No newline at end of file diff --git a/plugins/Insights/templates/insightControls.twig b/plugins/Insights/templates/insightControls.twig index a572ee850736c1f33578bde691754bd598fc69f6..e9d1e3f27f3690bb3c1f69b49f61e7b7948de778 100644 --- a/plugins/Insights/templates/insightControls.twig +++ b/plugins/Insights/templates/insightControls.twig @@ -1,6 +1,7 @@ <div style="padding: 10px;padding-bottom: 0px;"> - Minimum growth of + {{ 'Insights_ControlGrowthDescription'|translate }} + <select size="1" name="minGrowthPercent"> {% for i in range(0, 1, 1) %} <option {% if i == properties.min_growth_percent %}selected{% endif %} value="{{ i }}">{{ i }}%</option> @@ -14,53 +15,63 @@ </select> {% if period == 'day' or period == 'month' or period == 'week' %} - compared to + + {{ 'Insights_ControlComparedToDescription'|translate }} + {% if period == 'day' %} <select size="1" name="comparedToXPeriodsAgo"> <option value="1" {% if properties.compared_to_x_periods_ago == 1 %}selected{% endif %}> - previous day + {{ 'Insights_DayComparedToPreviousDay'|translate }} </option> <option value="7" {% if properties.compared_to_x_periods_ago == 7 %}selected{% endif %}> - same day in previous week + {{ 'Insights_DayComparedToPreviousWeek'|translate }} </option> <option value="365" {% if properties.compared_to_x_periods_ago == 365 %}selected{% endif %}> - same day in previous year + {{ 'Insights_DayComparedToPreviousYear'|translate }} </option> </select> {% elseif period == 'month' %} <select size="1" name="comparedToXPeriodsAgo"> <option value="1" {% if properties.compared_to_x_periods_ago == 1 %}selected{% endif %}> - previous month + {{ 'Insights_MonthComparedToPreviousMonth'|translate }} </option> <option value="12" {% if properties.compared_to_x_periods_ago == 12 %}selected{% endif %}> - same month in previous year + {{ 'Insights_MonthComparedToPreviousYear'|translate }} </option> </select> {% elseif period == 'week' %} - previous week + {{ 'Insights_WeekComparedToPreviousWeek'|translate }} {% endif %} {% endif %} - <hr style="height: 1px;border: 0px;background-color: #cccccc;" /> + <hr class="controlSeparator" /> - Filter + {{ 'Insights_Filter'|translate }} <select size="1" name="filterBy" title="Show all, only movers, only new, only disappeared"> - <option {% if not properties.filter_by %}selected{% endif %} value="">All</option> - <option {% if properties.filter_by == 'movers' %}selected{% endif %} value="movers">Only movers</option> - <option {% if properties.filter_by == 'new' %}selected{% endif %} value="new">Only new</option> - <option {% if properties.filter_by == 'disappeared' %}selected{% endif %} value="disappeared">Only disappeared</option> + <option {% if not properties.filter_by %}selected{% endif %} value=""> + {{ 'General_All'|translate }} + </option> + <option {% if properties.filter_by == 'movers' %}selected{% endif %} value="movers"> + {{ 'Insights_FilterOnlyMovers'|translate }} + </option> + <option {% if properties.filter_by == 'new' %}selected{% endif %} value="new"> + {{ 'Insights_FilterOnlyNew'|translate }} + </option> + <option {% if properties.filter_by == 'disappeared' %}selected{% endif %} value="disappeared"> + {{ 'Insights_FilterOnlyDisappeared'|translate }} + </option> </select> <select size="1" name="showIncreaseOrDecrease" title="Show increaser and/or decreaser"> <option value="both" {% if properties.limit_increaser and properties.limit_decreaser %}selected{%endif%}> - Increaser & decreaser + {{ 'Insights_FilterIncreaserAndDecreaser'|translate }} </option> <option value="increase" {% if properties.limit_increaser and not properties.limit_decreaser %}selected{%endif%}> - Only increaser + {{ 'Insights_FilterOnlyIncreaser'|translate }} </option> <option value="decrease" {% if not properties.limit_increaser and properties.limit_decreaser %}selected{%endif%}> - Only decreaser + {{ 'Insights_FilterOnlyDecreaser'|translate }} </option> </select> </div> \ No newline at end of file diff --git a/plugins/Insights/templates/insightVisualization.twig b/plugins/Insights/templates/insightVisualization.twig index 196b3909095f6b595657dda044cbc36a5e1c9f56..6dd35e3697e33b636562c42d74214b7c29026412 100644 --- a/plugins/Insights/templates/insightVisualization.twig +++ b/plugins/Insights/templates/insightVisualization.twig @@ -2,14 +2,14 @@ {% include "@Insights/cannotDisplayReport.twig" %} {% else %} {% set metadata = dataTable.getAllTableMetadata%} - {% set consideredVisits = 'Insights_TitleConsideredVisits'|translate(metadata.minGrowthPercentPositive, metadata.lastDate|prettyDate(metadata.period)) %} + {% set consideredGrowth = 'Insights_TitleConsideredInsightsGrowth'|translate(metadata.minGrowthPercentPositive, metadata.lastDate|prettyDate(metadata.period)) %} {% set consideredChanges = '' %} {% if metadata.minChangeMovers and metadata.minChangeMovers > 1 %} - {% set consideredChanges = 'Insights_TitleIgnoredChanges'|translate(metadata.minChangeMovers) %} + {% set consideredChanges = 'Insights_IgnoredChanges'|translate(metadata.minChangeMovers) %} {% endif %} - <div class="insightsDataTable" title="{{ consideredVisits|e('html_attr') }} {{ consideredChanges|e('html_attr') }}"> + <div class="insightsDataTable" title="{{ consideredGrowth|e('html_attr') }} {{ consideredChanges|e('html_attr') }}"> {% if dataTable.getRowsCount %} <table class="dataTable"> diff --git a/plugins/Insights/templates/insightsOverviewWidget.twig b/plugins/Insights/templates/insightsOverviewWidget.twig index 8f99ba11ba797da3a580f390760f31b392f9e666..cf8bfe166d7ce5dea72dedf0e1fc4f3db64c2f20 100644 --- a/plugins/Insights/templates/insightsOverviewWidget.twig +++ b/plugins/Insights/templates/insightsOverviewWidget.twig @@ -1,10 +1,5 @@ {% set allMetadata = reports.getFirstRow.getAllTableMetadata %} - -{% set consideredVisits = 'Insights_TitleConsideredVisits'|translate(allMetadata.minGrowthPercentPositive, allMetadata.lastDate|prettyDate(allMetadata.period)) %} +{% set consideredGrowth = 'Insights_TitleConsideredInsightsGrowth'|translate(allMetadata.minGrowthPercentPositive, allMetadata.lastDate|prettyDate(allMetadata.period)) %} {% set consideredChanges = '' %} -{% if allMetadata.minChangeMovers or allMetadata.minIncreaseNew or allMetadata.minDecreaseDisappeared %} - {% set consideredChanges = 'Insights_TitleConsideredChanges'|translate(allMetadata.minChangeMovers, allMetadata.minIncreaseNew, allMetadata.minDecreaseDisappeared, allMetadata.totalValue) %} -{% endif %} - {% include "@Insights/overviewWidget.twig" %} \ No newline at end of file diff --git a/plugins/Insights/templates/moversAndShakersOverviewWidget.twig b/plugins/Insights/templates/moversAndShakersOverviewWidget.twig index 185a61b37ed76965b08df8f51b6eeaf9ff3b2a71..f2326df1eae3565c6f6445500beacff3806d9097 100644 --- a/plugins/Insights/templates/moversAndShakersOverviewWidget.twig +++ b/plugins/Insights/templates/moversAndShakersOverviewWidget.twig @@ -1,10 +1,6 @@ {% set allMetadata = reports.getFirstRow.getAllTableMetadata %} -{% set consideredVisits = 'Insights_TitleConsideredMoversAndShakers'|translate(allMetadata.lastTotalValue, allMetadata.totalValue, allMetadata.lastDate|prettyDate(allMetadata.period), allMetadata.evolutionTotal) %} -{% set consideredChanges = '' %} - -{% if allMetadata.minChangeMovers or allMetadata.minIncreaseNew or allMetadata.minDecreaseDisappeared %} - {% set consideredChanges = 'Insights_TitleConsideredChangesMoversAndShakers'|translate(allMetadata.minGrowthPercentPositive, allMetadata.minGrowthPercentNegative, allMetadata.minNewPercent, allMetadata.minIncreaseNew, allMetadata.minDisappearedPercent, allMetadata.minDecreaseDisappeared, allMetadata.totalValue) %} -{% endif %} +{% set consideredGrowth = 'Insights_TitleConsideredMoversAndShakersGrowth'|translate(allMetadata.metricName, allMetadata.lastTotalValue, allMetadata.totalValue, allMetadata.lastDate|prettyDate(allMetadata.period), allMetadata.evolutionTotal) %} +{% set consideredChanges = 'Insights_TitleConsideredMoversAndShakersChanges'|translate(allMetadata.minGrowthPercentPositive, allMetadata.minGrowthPercentNegative, allMetadata.minNewPercent, allMetadata.minIncreaseNew, allMetadata.minDisappearedPercent, allMetadata.minDecreaseDisappeared, allMetadata.totalValue) %} {% include "@Insights/overviewWidget.twig" %} \ No newline at end of file diff --git a/plugins/Insights/templates/overviewWidget.twig b/plugins/Insights/templates/overviewWidget.twig index d3c43b917ba2ecdc24a0c561d5a6877189b95064..648025f9136983d40dd018a3bbe348e6c3c6d028 100644 --- a/plugins/Insights/templates/overviewWidget.twig +++ b/plugins/Insights/templates/overviewWidget.twig @@ -1,14 +1,15 @@ <div class="insightsDataTable dataTable" data-table-type="InsightsDataTable" - data-table-onlyinsightsinit="1" - title="{{ consideredVisits|e('html_attr') }} {{ consideredChanges|e('html_attr') }}"> + data-table-onlyinsightsinit="1"> {% if reports.getColumns|length > 0 %} - <table class="dataTable"> - {% for dataTable in reports.getDataTables() if dataTable.getRowsCount > 0 %} - {% set metadata = dataTable.getAllTableMetadata %} + {% for dataTable in reports.getDataTables() if dataTable.getRowsCount > 0 %} + {% set metadata = dataTable.getAllTableMetadata %} - <thead> + <table class="dataTable" + title="{{ consideredGrowth|e('html_attr') }} {{ consideredChanges|e('html_attr') }}"> + + <thead > {% include "@Insights/table_header.twig" %} </thead> @@ -18,8 +19,8 @@ {% endfor %} </tbody> - {% endfor %} - </table> + </table> + {% endfor %} <script type="text/javascript" defer="defer"> $(document).ready(function () { @@ -29,7 +30,8 @@ {% else %} - <div class="pk-emptyDataTable"> + <div class="pk-emptyDataTable" + title="{{ consideredGrowth|e('html_attr') }} {{ consideredChanges|e('html_attr') }}"> {{ 'Insights_NoResultMatchesCriteria'|translate }} </div> diff --git a/plugins/Insights/templates/table_row.twig b/plugins/Insights/templates/table_row.twig index bb4e8e9eef8f6d274be40d7210d17726ed2b5c26..ab681e0850b8cfa27a062eb1f632f51810022827 100644 --- a/plugins/Insights/templates/table_row.twig +++ b/plugins/Insights/templates/table_row.twig @@ -1,19 +1,24 @@ -<tr title="{{ 'Insights_TitleRowChangeDetails'|translate(row.getColumn('label'), row.getColumn('value_old'), metadata.lastDate|prettyDate(metadata.period), row.getColumn('value_new'), metadata.date|prettyDate(metadata.period), metadata.metricName)|e('html_attr') }}" - {% if row.getColumn('isMoverAndShaker') %}class="isMoverAndShaker"{% endif %}> +{% if row.getColumn('isDisappeared') %} + {% set rowTitle = 'Insights_TitleRowDisappearedDetails'|translate(row.getColumn('label'), row.getColumn('value_old'), metadata.date|prettyDate(metadata.period), metadata.lastDate|prettyDate(metadata.period)) %} +{% elseif row.getColumn('isNew') %} + {% set rowTitle = 'Insights_TitleRowNewDetails'|translate(row.getColumn('label'), row.getColumn('value_new'), metadata.lastDate|prettyDate(metadata.period)) %} +{% else %} + {% set rowTitle = 'Insights_TitleRowChangeDetails'|translate(row.getColumn('label'), row.getColumn('value_old'), metadata.lastDate|prettyDate(metadata.period), row.getColumn('value_new'), metadata.date|prettyDate(metadata.period), metadata.metricName) %} +{% endif %} + +{% set rowTitleShaker = '' %} +{% if row.getColumn('isMoverAndShaker') %} + {% set rowTitleShaker = 'Insights_TitleRowMoverAndShaker'|translate %} +{% endif %} + +<tr title="{{ rowTitle|e('html_attr') }} {{ rowTitleShaker|e('html_attr') }}" + class="{% if row.getColumn('isMoverAndShaker') %}isMoverAndShaker{% endif %}"> <td class="label"> <span class="title"> {{ row.getColumn('label') }} </span> </td> - {% if row.getColumn('isMover') %} - {% set typeOfChange = 'Moved' %} - {% elseif row.getColumn('isNew') %} - {% set typeOfChange = 'New' %} - {% elseif row.getColumn('isDisappeared') %} - {% set typeOfChange = 'Disappeared' %} - {% endif %} - {% if row.getColumn('grown') %} <td>+{{ row.getColumn('difference') }}</td> <td class="grown">+{{ row.getColumn('growth_percent') }}</td> diff --git a/plugins/Insights/tests/InsightReportTest.php b/plugins/Insights/tests/InsightReportTest.php index 0bf1c9f2335a2651034e3173fd4b2bbabaf813a2..3e50a2ba416c7ef55f59f97c99d855971b89b550 100644 --- a/plugins/Insights/tests/InsightReportTest.php +++ b/plugins/Insights/tests/InsightReportTest.php @@ -290,6 +290,19 @@ class InsightReportTest extends \PHPUnit_Framework_TestCase $this->assertMoversAndShakers($report, $movers, $nonMovers); } + public function test_markMoversAndShakers_shouldAddMetadata() + { + $report = $this->generateInsight(2, 2, 2, 5, -5); + $this->insightReport->markMoversAndShakers($report, $this->currentTable, $this->pastTable, 200, 100); + + $metadata = $report->getAllTableMetadata(); + + $this->assertEquals(100, $metadata['lastTotalValue']); + $this->assertEquals(200, $metadata['totalValue']); + $this->assertEquals(100, $metadata['evolutionDifference']); + $this->assertEquals(100, $metadata['evolutionTotal']); + } + public function test_generateMoversAndShakers() { // increase by 60% --> minGrowth 80% @@ -413,6 +426,17 @@ class InsightReportTest extends \PHPUnit_Framework_TestCase $this->assertEquals(3, $metadata['minMoversPercent']); $this->assertEquals(5, $metadata['minNewPercent']); $this->assertEquals(7, $metadata['minDisappearedPercent']); + + + // make sure no division by zero issue + $report = $this->generateMoverAndShaker(0, 150); + $metadata = $report->getAllTableMetadata(); + + $this->assertEquals(120, $metadata['minGrowthPercentPositive']); + $this->assertEquals(-120, $metadata['minGrowthPercentNegative']); + $this->assertEquals(1, $metadata['minMoversPercent']); + $this->assertEquals(5, $metadata['minNewPercent']); + $this->assertEquals(7, $metadata['minDisappearedPercent']); } private function generateMoverAndShaker($totalValue, $lastTotalValue, $orderBy = null, $limitIncreaser = 99, $limitDecreaser = 99)