Added review statistics management (#2910).

This commit is contained in:
Christian P. MOMON 2018-05-16 09:27:01 +02:00
parent f3f18fc395
commit cf119c5b92
13 changed files with 2256 additions and 0 deletions

313
resources/reviewstats.csv Normal file
View File

@ -0,0 +1,313 @@
20110930-12h00 6
20111007-12h00 6
20111014-12h00 8
20111021-12h00 5
20111028-12h00 10
20111104-12h00 8
20111111-12h00 3
20111118-12h00 7
20111125-12h00 7
20111202-12h00 7
20111209-12h00 3
20111216-12h00 7
20111223-12h00 7
20111230-12h00 7
20120106-12h00 8
20120113-12h00 6
20120120-12h00 7
20120217-12h00 8
20120302-12h00 10
20120309-12h00 8
20120316-12h00 3
20120323-12h00 6
20120330-12h00 6
20120406-12h00 7
20120413-12h00 7
20120420-12h00 5
20120504-12h00 8
20120511-12h00 6
20120518-12h00 4
20120525-12h00 7
20120601-12h00 3
20120608-12h00 6
20120615-12h00 7
20120622-12h00 9
20120629-12h00 4
20120706-12h00 6
20120720-12h00 8
20120727-12h00 5
20120803-12h00 6
20120810-12h00 6
20120817-12h00 6
20120824-12h00 8
20120831-12h00 10
20120907-12h00 12
20120914-12h00 8
20120921-12h00 4
20120928-12h00 8
20121012-12h00 6
20121019-12h00 7
20121026-12h00 7
20121102-12h00 8
20121109-12h00 7
20121116-12h00 8
20121123-12h00 5
20121130-12h00 13
20121207-12h00 10
20121214-12h00 14
20121221-12h00 10
20121228-12h00 4
20130104-12h00 7
20130111-12h00 6
20130118-12h00 10
20130125-12h00 10
20130201-12h00 8
20130208-12h00 11
20130215-12h00 6
20130222-12h00 7
20130301-12h00 8
20130308-12h00 4
20130315-12h00 6
20130322-12h00 4
20130329-12h00 7
20130405-12h00 10
20130412-12h00 8
20130419-12h00 6
20130426-12h00 5
20130503-12h00 8
20130510-12h00 3
20130517-12h00 8
20130524-12h00 9
20130531-12h00 7
20130607-12h00 8
20130614-12h00 9
20130628-12h00 9
20130705-12h00 9
20130712-12h00 7
20130719-12h00 11
20130726-12h00 7
20130802-12h00 11
20130809-12h00 10
20130816-12h00 6
20130823-12h00 6
20130830-12h00 6
20130906-12h00 11
20130913-12h00 8
20130920-12h00 6
20130927-12h00 6
20131004-12h00 3
20131011-12h00 5
20131018-12h00 5
20131025-12h00 3
20131101-12h00 6
20131108-12h00 4
20131115-12h00 3
20131122-12h00 6
20131129-12h00 2
20131206-12h00 6
20131213-12h00 5
20131220-12h00 2
20131227-12h00 5
20140103-12h00 4
20140110-12h00 5
20140117-12h00 8
20140124-12h00 6
20140131-12h00 7
20140207-12h00 5
20140214-12h00 6
20140221-12h00 6
20140228-12h00 8
20140307-12h00 5
20140314-12h00 6
20140321-12h00 5
20140328-12h00 6
20140404-12h00 6
20140411-12h00 5
20140418-12h00 6
20140425-12h00 5
20140502-12h00 5
20140509-12h00 6
20140516-12h00 4
20140523-12h00 2
20140613-12h00 6
20140620-12h00 4
20140627-12h00 5
20140704-12h00 6
20140711-12h00 7
20140718-12h00 7
20140725-12h00 3
20140801-12h00 6
20140808-12h00 5
20140822-12h00 4
20140829-12h00 7
20140905-12h00 6
20140912-12h00 5
20140919-12h00 5
20140926-12h00 3
20141010-12h00 5
20141017-12h00 7
20141024-12h00 6
20141031-12h00 6
20141107-12h00 3
20141114-12h00 7
20141121-12h00 7
20141128-12h00 6
20141205-12h00 7
20141212-12h00 7
20141219-12h00 6
20150109-12h00 4
20150116-12h00 1
20150123-12h00 4
20150130-12h00 6
20150206-12h00 4
20150213-12h00 2
20150220-12h00 7
20150227-12h00 4
20150306-12h00 11
20150313-12h00 10
20150320-12h00 8
20150327-12h00 5
20150403-12h00 6
20150410-12h00 8
20150417-12h00 4
20150424-12h00 3
20150507-12h00 5
20150515-12h00 8
20150522-12h00 7
20150529-12h00 5
20150605-12h00 10
20150612-12h00 6
20150619-12h00 3
20150626-12h00 5
20150703-12h00 5
20150710-12h00 5
20150717-12h00 6
20150724-12h00 7
20150731-12h00 5
20150807-12h00 4
20150814-12h00 4
20150828-12h00 5
20150904-12h00 5
20150918-12h00 6
20150925-12h00 5
20151002-12h00 7
20151009-12h00 5
20151016-12h00 8
20151023-12h00 6
20151030-12h00 8
20151106-12h00 5
20151113-12h00 9
20151120-12h00 9
20151127-12h00 7
20151204-12h00 10
20151211-12h00 7
20151218-12h00 10
20160108-12h00 8
20160219-12h00 6
20160226-12h00 9
20160318-12h00 6
20160401-12h00 6
20160408-12h00 8
20160415-12h00 6
20160422-12h00 7
20160429-12h00 7
20160506-12h00 7
20160513-12h00 6
20160520-12h00 6
20160527-12h00 8
20160603-12h00 3
20160610-12h00 4
20160617-12h00 7
20160624-12h00 6
20160701-12h00 6
20160708-12h00 7
20160715-12h00 4
20160722-12h00 6
20160805-12h00 4
20160812-12h00 3
20160819-12h00 4
20160826-12h00 4
20160902-12h00 6
20160909-12h00 7
20160916-12h00 7
20160923-12h00 7
20160930-12h00 4
20161007-12h00 6
20161021-12h00 7
20161028-12h00 6
20161104-12h00 7
20161110-12h00 7
20161118-12h00 7
20161125-12h00 7
20161202-12h00 4
20161209-12h00 7
20161216-12h00 9
20161223-12h00 5
20170106-12h00 7
20170113-12h00 4
20170120-12h00 8
20170127-12h00 7
20170203-12h00 6
20170210-12h00 6
20170217-12h00 8
20170224-12h00 8
20170303-12h00 7
20170310-12h00 7
20170317-12h00 4
20170324-12h00 6
20170331-12h00 5
20170407-12h00 4
20170414-12h00 4
20170421-12h00 9
20170428-12h00 7
20170505-12h00 6
20170512-12h00 7
20170519-12h00 6
20170526-12h00 5
20170602-12h00 8
20170609-12h00 10
20170616-12h00 9
20170623-12h00 7
20170630-12h00 5
20170707-12h00 12
20170713-12h00 4
20170721-12h00 6
20170727-12h00 6
20170818-12h00 5
20170825-12h00 3
20170901-12h00 6
20170908-12h00 5
20170915-12h00 7
20170922-12h00 5
20170929-12h00 10
20171006-12h00 9
20171013-12h00 9
20171020-12h00 7
20171027-12h00 8
20171103-12h00 10
20171110-12h00 10
20171117-12h00 8
20171124-12h00 8
20171201-12h00 9
20171208-12h00 10
20171215-12h00 11
20171222-12h00 7
20180105-12h00 7
20180112-12h00 10
20180118-12h00 1
20180119-12h00 12
20180126-12h00 10
20180202-12h00 9
20180209-12h00 8 17
20180216-12h00 8 16
20180223-12h00 5 14
20180302-12h00 6 19
20180309-12h00 11 20
20180316-12h00 9 19
20180323-12h00 9 17
20180330-12h00 10 19
20180406-12h00 9 17
20180413-12h00 11 15
20180420-12h00 6 17
20180427-12h00 7 17
20180504-12h00 7 17
Can't render this file because it has a wrong number of fields in line 62.

View File

@ -40,6 +40,10 @@ import org.april.hebdobot.model.review.IndividualTopic;
import org.april.hebdobot.model.review.Message; import org.april.hebdobot.model.review.Message;
import org.april.hebdobot.model.review.Review; import org.april.hebdobot.model.review.Review;
import org.april.hebdobot.model.review.Topic; import org.april.hebdobot.model.review.Topic;
import org.april.hebdobot.model.stats.ReviewData;
import org.april.hebdobot.model.stats.ReviewDatas;
import org.april.hebdobot.model.stats.ReviewDatasFile;
import org.april.hebdobot.model.stats.ReviewStatsReporter;
import org.april.hebdobot.pastebin.PastebinClient; import org.april.hebdobot.pastebin.PastebinClient;
import org.april.hebdobot.pastebin.PastebinSettings; import org.april.hebdobot.pastebin.PastebinSettings;
import org.april.hebdobot.pastebin.Private; import org.april.hebdobot.pastebin.Private;
@ -364,6 +368,36 @@ public class Hebdobot extends PircBot
String participants = StringUtils.join(this.review.getParticipants(), " "); String participants = StringUtils.join(this.review.getParticipants(), " ");
sendMessage("% " + participants + ", pensez à noter votre bénévalo : http://www.april.org/my?action=benevalo"); sendMessage("% " + participants + ", pensez à noter votre bénévalo : http://www.april.org/my?action=benevalo");
// Display statistics. This feature has to not break
// Hebdobot.
try
{
File reviewDataFile = new File(this.reviewDirectory, "reviewstats.csv");
if (reviewDataFile.exists())
{
ReviewDatas datas = ReviewDatasFile.load(reviewDataFile);
datas.clean();
sendMessage("% " + ReviewStatsReporter.reportNewMaxUserCount(datas, this.review.getParticipants().size()));
ReviewData currentReview = new ReviewData(LocalDateTime.now(), this.review.getParticipants().size(),
(int) this.review.getDurationInMinutes());
datas.add(currentReview);
sendMessage("% " + ReviewStatsReporter.reportUserCount(datas, currentReview.getUserCount()));
sendMessage("% " + ReviewStatsReporter.reportDuration(datas, currentReview.getUserCount()));
ReviewDatasFile.append(reviewDataFile, currentReview);
}
else
{
logger.warn("Statistic file not found [{}]", reviewDataFile.getAbsolutePath());
sendMessage("% Fichier de statistiques absent.");
}
}
catch (Exception exception)
{
logger.warn("Exception during statistics work.", exception);
sendMessage("% Impossible d'afficher des statistiques.");
}
sendMessage("% Fin de la revue hebdomadaire"); sendMessage("% Fin de la revue hebdomadaire");
sendMessage(this.review.getOwner(), "Revue finie."); sendMessage(this.review.getOwner(), "Revue finie.");

View File

@ -0,0 +1,301 @@
/**
* Copyright (C) 2018 Christian Pierre MOMON <cmomon@april.org>
*
* This file is part of (April) Hebdobot.
*
* Hebdobot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hebdobot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hebdobot. If not, see <http://www.gnu.org/licenses/>
*/
package org.april.hebdobot.model.stats;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import fr.devinsy.util.strings.StringList;
/**
* The Class Integers.
*/
public class IntegerBoard implements Iterable<IntegerStat>
{
private Map<Integer, IntegerStat> integers;
private boolean isUptodate;
private IntegerStats board;
/**
* Instantiates a new distribution.
*/
public IntegerBoard()
{
this.integers = new HashMap<Integer, IntegerStat>(30);
this.isUptodate = false;
this.board = new IntegerStats(this.integers.size());
}
/**
* Adds the.
*
* @param value
* the value
* @return true, if successful
*/
public void add(final Integer value)
{
if (value != null)
{
IntegerStat stat = this.integers.get(value);
if (stat == null)
{
stat = new IntegerStat(value);
this.integers.put(value, stat);
}
stat.inc();
}
}
/**
* Gets the.
*
* @param value
* the value
* @return the integer stat
*/
public IntegerStat get(final int value)
{
IntegerStat result;
result = this.integers.get(value);
//
return result;
}
/**
* Gets the average.
*
* @return the average
*/
public double getAverage()
{
double result;
int numerator = 0;
int denumerator = 0;
for (IntegerStat stat : this.integers.values())
{
numerator += stat.getCount() * stat.getValue();
denumerator += stat.getCount();
}
result = (numerator * 1. / denumerator);
//
return result;
}
/**
* Gets the count sum.
*
* @return the count sum
*/
public int getCountSum()
{
int result;
result = 0;
for (IntegerStat stat : this.integers.values())
{
result += stat.getCount();
}
//
return result;
}
public int getMaxValue()
{
int result;
if (isEmpty())
{
result = 0;
}
else
{
result = Integer.MIN_VALUE;
for (IntegerStat stat : this.integers.values())
{
if (stat.getValue() > result)
{
result = stat.getValue();
}
}
}
//
return result;
}
/**
* Gets the min.
*
* @return the min
*/
public int getMinValue()
{
int result;
if (isEmpty())
{
result = 0;
}
else
{
result = Integer.MAX_VALUE;
for (IntegerStat stat : this.integers.values())
{
if (stat.getValue() < result)
{
result = stat.getValue();
}
}
}
//
return result;
}
/**
* Gets the index of.
*
* @param value
* the value
* @return the index of
*/
public Integer getPositionOf(final int search)
{
Integer result;
update();
int index = 0;
boolean ended = false;
result = null;
while (!ended)
{
if (index < this.board.size())
{
int value = this.board.get(index).getValue();
if (value == search)
{
result = index + 1;
ended = true;
}
else
{
index += 1;
}
}
else
{
ended = true;
result = null;
}
}
//
return result;
}
/**
* Checks if is empty.
*
* @return true, if is empty
*/
public boolean isEmpty()
{
boolean result;
if (this.integers.size() == 0)
{
result = true;
}
else
{
result = false;
}
//
return result;
}
/**
* Iterator.
*
* @return the iterator
*/
@Override
public Iterator<IntegerStat> iterator()
{
Iterator<IntegerStat> result;
update();
result = this.board.iterator();
//
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
String result;
update();
StringList buffer = new StringList();
for (IntegerStat stat : this.board)
{
buffer.append(String.format("%d (%d)", stat.getValue(), stat.getCount()));
}
result = buffer.toStringWithBracket();
//
return result;
}
/**
* Update.
*/
public void update()
{
if (!this.isUptodate)
{
this.board.clear();
for (IntegerStat stat : this.integers.values())
{
this.board.add(stat);
}
this.board.sortByValue();
Collections.reverse(this.board);
this.isUptodate = false;
}
}
}

View File

@ -0,0 +1,99 @@
/**
* Copyright (C) 2018 Christian Pierre MOMON <cmomon@april.org>
*
* This file is part of (April) Hebdobot.
*
* Hebdobot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hebdobot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hebdobot. If not, see <http://www.gnu.org/licenses/>
*/
package org.april.hebdobot.model.stats;
/**
* The Class Stat.
*/
public class IntegerStat
{
private int value;
private int count;
/**
* Instantiates a new distribution pair.
*
* @param value
* the value
*/
public IntegerStat(final int value)
{
this(value, 0);
}
/**
* Instantiates a new distribution pair.
*
* @param value
* the value
* @param count
* the count
*/
public IntegerStat(final int value, final int count)
{
this.value = value;
this.count = count;
}
/**
* Decrease count.
*/
public void dec()
{
this.count -= 1;
}
/**
* Gets the count.
*
* @return the count
*/
public int getCount()
{
int result;
result = this.count;
//
return result;
}
/**
* Gets the value.
*
* @return the value
*/
public int getValue()
{
int result;
result = this.value;
//
return result;
}
/**
* Increments count.
*/
public void inc()
{
this.count += 1;
}
}

View File

@ -0,0 +1,217 @@
/**
* Copyright (C) 2018 Christian Pierre MOMON <cmomon@april.org>
*
* This file is part of (April) Hebdobot.
*
* Hebdobot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hebdobot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hebdobot. If not, see <http://www.gnu.org/licenses/>
*/
package org.april.hebdobot.model.stats;
import java.util.Comparator;
/**
* The Class Stat.
*/
public class IntegerStatComparator implements Comparator<IntegerStat>
{
public enum Sorting
{
VALUE,
COUNT
}
private Sorting sorting;
/**
* Instantiates a new review data comparator.
*
* @param sorting
* the sorting
*/
public IntegerStatComparator(final Sorting sorting)
{
this.sorting = sorting;
}
/* (non-Javadoc)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(final IntegerStat alpha, final IntegerStat bravo)
{
int result;
result = compare(alpha, bravo, this.sorting);
//
return result;
}
/**
* Compare.
*
* @param alpha
* the alpha
* @param bravo
* the bravo
* @return the int
*/
public static int compare(final Integer alpha, final Integer bravo)
{
int result;
//
if ((alpha == null) && (bravo == null))
{
result = 0;
}
else if (alpha == null)
{
result = -1;
}
else if (bravo == null)
{
result = +1;
}
else
{
result = alpha.compareTo(bravo);
}
//
return result;
}
/**
* Compare.
*
* @param alpha
* the alpha
* @param bravo
* the bravo
* @param sorting
* the sorting
* @return the int
*/
public static int compare(final IntegerStat alpha, final IntegerStat bravo, final Sorting sorting)
{
int result;
if (sorting == null)
{
result = 0;
}
else
{
switch (sorting)
{
case VALUE:
result = compare(getValue(alpha), getValue(bravo));
break;
case COUNT:
result = compare(getCount(alpha), getCount(bravo));
break;
default:
result = 0;
}
}
//
return result;
}
/**
* Compare.
*
* @param alpha
* the alpha
* @param bravo
* the bravo
* @return the int
*/
public static int compare(final Long alpha, final Long bravo)
{
int result;
//
if ((alpha == null) && (bravo == null))
{
result = 0;
}
else if (alpha == null)
{
result = -1;
}
else if (bravo == null)
{
result = +1;
}
else
{
result = alpha.compareTo(bravo);
}
//
return result;
}
/**
* Gets the user count.
*
* @param source
* the source
* @return the user count
*/
public static Integer getCount(final IntegerStat source)
{
Integer result;
if (source == null)
{
result = null;
}
else
{
result = source.getCount();
}
//
return result;
}
/**
* Gets the duration.
*
* @param source
* the source
* @return the duration
*/
public static Integer getValue(final IntegerStat source)
{
Integer result;
if (source == null)
{
result = null;
}
else
{
result = source.getValue();
}
//
return result;
}
}

View File

@ -0,0 +1,75 @@
/**
* Copyright (C) 2017-2018 Christian Pierre MOMON <cmomon@april.org>
*
* This file is part of (April) Hebdobot.
*
* Hebdobot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hebdobot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hebdobot. If not, see <http://www.gnu.org/licenses/>
*/
package org.april.hebdobot.model.stats;
import java.util.ArrayList;
import java.util.Collections;
import org.april.hebdobot.model.stats.IntegerStatComparator.Sorting;
/**
* The Class Stats.
*/
public class IntegerStats extends ArrayList<IntegerStat>
{
private static final long serialVersionUID = 2632624619156256161L;
/**
* Instantiates a new integer stats.
*/
public IntegerStats()
{
super();
}
/**
* Instantiates a new integer stats.
*
* @param initialCapacity
* the initial capacity
*/
public IntegerStats(final int initialCapacity)
{
super(initialCapacity);
}
/**
* Reverse.
*/
public void reverse()
{
Collections.reverse(this);
}
/**
* Sort by user count.
*/
public void sortByCount()
{
Collections.sort(this, new IntegerStatComparator(Sorting.COUNT));
}
/**
* Sort by duration.
*/
public void sortByValue()
{
Collections.sort(this, new IntegerStatComparator(Sorting.VALUE));
}
}

View File

@ -0,0 +1,92 @@
/**
* Copyright (C) 2017-2018 Christian Pierre MOMON <cmomon@april.org>
*
* This file is part of (April) Hebdobot.
*
* Hebdobot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hebdobot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hebdobot. If not, see <http://www.gnu.org/licenses/>
*/
package org.april.hebdobot.model.stats;
import java.time.LocalDateTime;
/**
* The Class ReviewStat.
*/
public class ReviewData
{
private LocalDateTime date;
private int userCount;
private Integer duration;
/**
* Instantiates a new stat.
*
* @param date
* the date
* @param userCount
* the user count
* @param duration
* the duration
*/
public ReviewData(final LocalDateTime date, final int userCount, final Integer duration)
{
this.date = date;
this.userCount = userCount;
this.duration = duration;
}
public LocalDateTime getDate()
{
return this.date;
}
public Integer getDuration()
{
return this.duration;
}
public int getUserCount()
{
return this.userCount;
}
public void setDate(final LocalDateTime date)
{
this.date = date;
}
public void setDuration(final Integer duration)
{
this.duration = duration;
}
public void setUserCount(final int userCount)
{
this.userCount = userCount;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
String result;
result = String.format("%s\t%d\t%d", this.date.toString(), this.userCount, this.duration);
//
return result;
}
}

View File

@ -0,0 +1,273 @@
/**
* Copyright (C) 2017-2018 Christian Pierre MOMON <cmomon@april.org>
*
* This file is part of (April) Hebdobot.
*
* Hebdobot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hebdobot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hebdobot. If not, see <http://www.gnu.org/licenses/>
*/
package org.april.hebdobot.model.stats;
import java.time.LocalDateTime;
import java.util.Comparator;
/**
* The Class Stat.
*/
public class ReviewDataComparator implements Comparator<ReviewData>
{
public enum Sorting
{
DATE,
USERCOUNT,
DURATION
}
private Sorting sorting;
/**
* Instantiates a new review data comparator.
*
* @param sorting
* the sorting
*/
public ReviewDataComparator(final Sorting sorting)
{
this.sorting = sorting;
}
/* (non-Javadoc)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(final ReviewData alpha, final ReviewData bravo)
{
int result;
result = compare(alpha, bravo, this.sorting);
//
return result;
}
/**
* Compare.
*
* @param alpha
* the alpha
* @param bravo
* the bravo
* @return the int
*/
public static int compare(final Integer alpha, final Integer bravo)
{
int result;
//
if ((alpha == null) && (bravo == null))
{
result = 0;
}
else if (alpha == null)
{
result = -1;
}
else if (bravo == null)
{
result = +1;
}
else
{
result = alpha.compareTo(bravo);
}
//
return result;
}
public static int compare(final LocalDateTime alpha, final LocalDateTime bravo)
{
int result;
//
if ((alpha == null) && (bravo == null))
{
result = 0;
}
else if (alpha == null)
{
result = -1;
}
else if (bravo == null)
{
result = +1;
}
else
{
result = alpha.compareTo(bravo);
}
//
return result;
}
/**
* Compare.
*
* @param alpha
* the alpha
* @param bravo
* the bravo
* @return the int
*/
public static int compare(final Long alpha, final Long bravo)
{
int result;
//
if ((alpha == null) && (bravo == null))
{
result = 0;
}
else if (alpha == null)
{
result = -1;
}
else if (bravo == null)
{
result = +1;
}
else
{
result = alpha.compareTo(bravo);
}
//
return result;
}
/**
* Compare.
*
* @param alpha
* the alpha
* @param bravo
* the bravo
* @param sorting
* the sorting
* @return the int
*/
public static int compare(final ReviewData alpha, final ReviewData bravo, final Sorting sorting)
{
int result;
if (sorting == null)
{
result = 0;
}
else
{
switch (sorting)
{
case DATE:
result = compare(getDate(alpha), getDate(bravo));
break;
case USERCOUNT:
result = compare(getUserCount(alpha), getUserCount(bravo));
break;
case DURATION:
result = compare(getDuration(alpha), getDuration(bravo));
break;
default:
result = 0;
}
}
//
return result;
}
/**
* Gets the date.
*
* @param source
* the source
* @return the date
*/
public static LocalDateTime getDate(final ReviewData source)
{
LocalDateTime result;
if (source == null)
{
result = null;
}
else
{
result = source.getDate();
}
//
return result;
}
/**
* Gets the duration.
*
* @param source
* the source
* @return the duration
*/
public static Integer getDuration(final ReviewData source)
{
Integer result;
if (source == null)
{
result = null;
}
else
{
result = source.getDuration();
}
//
return result;
}
/**
* Gets the user count.
*
* @param source
* the source
* @return the user count
*/
public static Integer getUserCount(final ReviewData source)
{
Integer result;
if (source == null)
{
result = null;
}
else
{
result = source.getUserCount();
}
//
return result;
}
}

View File

@ -0,0 +1,186 @@
/**
* Copyright (C) 2017-2018 Christian Pierre MOMON <cmomon@april.org>
*
* This file is part of (April) Hebdobot.
*
* Hebdobot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hebdobot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hebdobot. If not, see <http://www.gnu.org/licenses/>
*/
package org.april.hebdobot.model.stats;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import org.april.hebdobot.model.stats.ReviewDataComparator.Sorting;
/**
* The Class Stats.
*/
public class ReviewDatas extends ArrayList<ReviewData>
{
private static final long serialVersionUID = 326880908257489774L;
/**
* Instantiates a new ReviewDatas.
*/
public ReviewDatas()
{
super();
}
/**
* Clean.
*
* @return the int
*/
public int clean()
{
int result;
result = 0;
Iterator<ReviewData> iterator = iterator();
while (iterator.hasNext())
{
if (iterator.next().getUserCount() < 2)
{
iterator.remove();
result += 1;
}
}
//
return result;
}
/**
* Gets the last by max user count.
*
* @return the last by max user count
*/
public ReviewData getLastByMaxUserCount()
{
ReviewData result;
if (isEmpty())
{
result = null;
}
else
{
int max = Integer.MIN_VALUE;
result = null;
for (ReviewData data : this)
{
if (data.getUserCount() > max)
{
max = data.getUserCount();
result = data;
}
}
}
//
return result;
}
/**
* Gets the max user count.
*
* @return the max user count
*/
public int getMaxUserCount()
{
int result;
if (isEmpty())
{
result = 0;
}
else
{
result = Integer.MIN_VALUE;
for (ReviewData data : this)
{
if (data.getUserCount() > result)
{
result = data.getUserCount();
}
}
}
//
return result;
}
/**
* Gets the min user count.
*
* @return the min user count
*/
public int getMinUserCount()
{
int result;
if (isEmpty())
{
result = 0;
}
else
{
result = Integer.MAX_VALUE;
for (ReviewData data : this)
{
if (data.getUserCount() < result)
{
result = data.getUserCount();
}
}
}
//
return result;
}
/**
* Reverse.
*/
public void reverse()
{
Collections.reverse(this);
}
/**
* Sort by date.
*/
public void sortByDate()
{
Collections.sort(this, new ReviewDataComparator(Sorting.DATE));
}
/**
* Sort by duration.
*/
public void sortByDuration()
{
Collections.sort(this, new ReviewDataComparator(Sorting.DURATION));
}
/**
* Sort by user count.
*/
public void sortByUserCount()
{
Collections.sort(this, new ReviewDataComparator(Sorting.USERCOUNT));
}
}

View File

@ -0,0 +1,210 @@
/**
* Copyright (C) 2017-2018 Christian Pierre MOMON <cmomon@april.org>
*
* This file is part of (April) Hebdobot.
*
* Hebdobot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hebdobot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hebdobot. If not, see <http://www.gnu.org/licenses/>
*/
package org.april.hebdobot.model.stats;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.april.hebdobot.HebdobotException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Class StatsFile.
*/
public class ReviewDatasFile
{
private static final Logger logger = LoggerFactory.getLogger(ReviewDatasFile.class);
public static final String DEFAULT_CHARSET_NAME = "UTF-8";
/**
* Instantiates a new stats file.
*/
private ReviewDatasFile()
{
}
/**
* Append.
*
* @param file
* the target
* @param stat
* the stat
* @throws HebdobotException
*/
public static void append(final File file, final ReviewData stat) throws HebdobotException
{
RandomAccessFile out = null;
try
{
out = new RandomAccessFile(file, "rw");
out.seek(out.length());
out.writeUTF(String.format("%s\t%d\t%d\n", stat.getDate(), stat.getUserCount(), stat.getDuration()));
}
catch (FileNotFoundException exception)
{
logger.error("File Not Found: " + exception.getMessage(), exception);
throw new HebdobotException("File Not Found appending file [" + file + "]");
}
catch (IOException exception)
{
logger.error("IO error: " + exception.getMessage(), exception);
throw new HebdobotException("IO Error appending file [" + file + "]");
}
finally
{
IOUtils.closeQuietly(out);
}
}
/**
* Load.
*
* @param source
* the source
* @return the stats
* @throws HebdobotException
*/
public static ReviewDatas load(final File source) throws HebdobotException
{
ReviewDatas result;
//
if (source == null)
{
logger.error("Attempt to read null file.");
throw new IllegalArgumentException("Null parameter.");
}
else
{
BufferedReader in = null;
try
{
in = new BufferedReader(new InputStreamReader(new FileInputStream(source), DEFAULT_CHARSET_NAME));
result = read(in);
}
catch (UnsupportedEncodingException exception)
{
logger.error("Encoding error reading file: " + exception.getMessage(), exception);
throw new HebdobotException("Encodding error reading file [" + source + "]");
}
catch (FileNotFoundException exception)
{
logger.error("File Not Found: " + exception.getMessage(), exception);
throw new HebdobotException("File Not Found opening file:[" + source + "]");
}
catch (IOException exception)
{
logger.error("IO error: " + exception.getMessage(), exception);
throw new HebdobotException("IO Error opening file: [" + source + "]");
}
finally
{
IOUtils.closeQuietly(in);
}
}
//
return result;
}
/**
* Read.
*
* @param source
* the source
* @return the stats
* @throws IOException
*/
public static ReviewDatas read(final BufferedReader in) throws IOException
{
ReviewDatas result;
result = new ReviewDatas();
boolean ended = false;
while (!ended)
{
String line = in.readLine();
if (line == null)
{
ended = true;
}
else if ((StringUtils.isNotBlank(line)) && (!line.startsWith("#")))
{
// System.out.println("line=" + line);
try
{
String[] tokens = line.split("[ \\s]+");
if ((tokens[0] == null) || (!tokens[0].matches("\\d{8}-\\d{2}h(\\d{2})+")))
{
logger.warn("Bad line in review stat file: [{}]", line);
}
else
{
int year = Integer.parseInt(tokens[0].substring(0, 4));
int month = Integer.parseInt(tokens[0].substring(4, 6));
int day = Integer.parseInt(tokens[0].substring(6, 8));
int hour = Integer.parseInt(tokens[0].substring(9, 11));
int minute = Integer.parseInt(tokens[0].substring(12, 14));
LocalDateTime date = LocalDateTime.of(year, month, day, hour, minute);
int userCount = Integer.parseInt(tokens[1]);
Integer duration;
if (tokens.length == 2)
{
duration = null;
}
else
{
duration = Integer.parseInt(tokens[2]);
}
ReviewData stat = new ReviewData(date, userCount, duration);
result.add(stat);
}
}
catch (Exception exception)
{
logger.warn("Decode failed for line: [{}]", line, exception);
}
}
}
//
return result;
}
}

View File

@ -0,0 +1,168 @@
/**
* Copyright (C) 2017-2018 Christian Pierre MOMON <cmomon@april.org>
*
* This file is part of (April) Hebdobot.
*
* Hebdobot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hebdobot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hebdobot. If not, see <http://www.gnu.org/licenses/>
*/
package org.april.hebdobot.model.stats;
/**
* The Class ReviewStatReporter.
*/
public class ReviewStatsReporter
{
/**
* Instantiates a new review stat reporter.
*/
private ReviewStatsReporter()
{
}
/**
* Percent.
*
* @param a
* the a
* @param b
* the b
* @return the double
*/
private static double percent(final double a, final double b)
{
double result;
if (b == 0.)
{
result = 0.;
}
else
{
result = Math.round(10000. * (a / b)) / 100.;
}
//
return result;
}
/**
* Percent.
*
* @param a
* the a
* @param b
* the b
* @return the double
*/
private static double percent(final int a, final int b)
{
double result;
result = percent(new Double(a).doubleValue(), new Double(b).doubleValue());
//
return result;
}
/**
* Report duration.
*
* @param datas
* the datas
* @param currentDuration
* the current duration
* @return the string
*/
public static String reportDuration(final ReviewDatas datas, final int currentDuration)
{
String result;
IntegerBoard board = new IntegerBoard();
for (ReviewData data : datas)
{
board.add(data.getDuration());
}
System.out.println("Duration board: " + board.toString());
IntegerStat stat = board.get(currentDuration);
int total = board.getCountSum();
result = String.format(
"Statistiques sur la durée de la revue (%d mn) : position %d (min=%d mn,moy=%.1f mn,max=%d mn), fréquence %d/%d (%.0f %%)",
stat.getValue(), board.getPositionOf(stat.getValue()), board.getMinValue(), board.getAverage(), board.getMaxValue(), stat.getCount(),
total, percent(stat.getCount(), total));
//
return result;
}
/**
* Report new max.
*
* @param datas
* the datas
* @param currentUserCount
* the current user count
* @return the string
*/
public static String reportNewMaxUserCount(final ReviewDatas datas, final int currentUserCount)
{
String result;
datas.sortByDate();
ReviewData last = datas.getLastByMaxUserCount();
if (currentUserCount < last.getUserCount())
{
result = "Pas de nouveau record de participation.";
}
else
{
result = "Nouveau record de participation.";
}
result = String.format("%s Dernier record %d, le %s.", result, last.getUserCount(), last.getDate().toString());
//
return result;
}
/**
* Report user count.
*
* @param datas
* the datas
* @param currentUserCount
* the current user count
* @return the string
*/
public static String reportUserCount(final ReviewDatas datas, final int currentUserCount)
{
String result;
IntegerBoard board = new IntegerBoard();
for (ReviewData data : datas)
{
board.add(data.getUserCount());
}
System.out.println("User count board: " + board.toString());
IntegerStat stat = board.get(currentUserCount);
int total = board.getCountSum();
result = String.format("Statistiques sur le nombre de participants (%d) : position %d (min=%d,moy=%.1f,max=%d), fréquence %d/%d (%.0f %%)",
stat.getValue(), board.getPositionOf(stat.getValue()), board.getMinValue(), board.getAverage(), board.getMaxValue(), stat.getCount(),
total, percent(stat.getCount(), total));
//
return result;
}
}

View File

@ -0,0 +1,169 @@
/**
* Copyright (C) 2018 Christian Pierre MOMON <cmomon@april.org>
*
* This file is part of (April) Hebdobot.
*
* Hebdobot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hebdobot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hebdobot. If not, see <http://www.gnu.org/licenses/>
*/
package org.april.hebdobot.model.stats;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import fr.devinsy.util.strings.StringList;
/**
* The Class Integers.
*/
public class SimpleIntegerBoard implements Iterable<Integer>
{
private Set<Integer> integers;
private boolean isUptodate;
private List<Integer> board;
/**
* Instantiates a new distribution.
*/
public SimpleIntegerBoard()
{
this.integers = new HashSet<Integer>(30);
this.isUptodate = false;
this.board = new ArrayList<Integer>(30);
}
/**
* Adds the.
*
* @param value
* the value
* @return true, if successful
*/
public boolean add(final Integer value)
{
boolean result;
if (value == null)
{
result = false;
}
else
{
result = this.integers.add(value);
}
//
return result;
}
/**
* Gets the index of.
*
* @param value
* the value
* @return the index of
*/
public Integer getPositionOf(final int search)
{
Integer result;
update();
int index = 0;
boolean ended = false;
result = null;
while (!ended)
{
if (index < this.board.size())
{
int value = this.board.get(index);
if (value == search)
{
result = index + 1;
ended = true;
}
else
{
index += 1;
}
}
else
{
ended = true;
result = null;
}
}
//
return result;
}
/**
* Iterator.
*
* @return the iterator
*/
@Override
public Iterator<Integer> iterator()
{
Iterator<Integer> result;
update();
result = this.board.iterator();
//
return result;
}
@Override
public String toString()
{
String result;
update();
StringList buffer = new StringList();
for (Integer value : this.board)
{
buffer.append(value);
}
result = buffer.toStringWithBracket();
//
return result;
}
/**
* Update.
*/
public void update()
{
if (!this.isUptodate)
{
this.board.clear();
for (Integer value : this.integers)
{
this.board.add(value);
}
Collections.sort(this.board);
Collections.reverse(this.board);
this.isUptodate = false;
}
}
}

View File

@ -0,0 +1,119 @@
/**
* Copyright (C) 2011-2013,2017 Nicolas Vinot <aeris@imirhil.fr>
* Copyright (C) 2017 Christian Pierre MOMON <cmomon@april.org>
*
* This file is part of (April) Hebdobot.
*
* Hebdobot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hebdobot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hebdobot. If not, see <http://www.gnu.org/licenses/>
*/
package org.april.hebdobot.reviewstats;
import java.io.File;
import java.time.LocalDateTime;
import org.apache.log4j.BasicConfigurator;
import org.april.hebdobot.model.stats.ReviewData;
import org.april.hebdobot.model.stats.ReviewDatas;
import org.april.hebdobot.model.stats.ReviewDatasFile;
import org.april.hebdobot.model.stats.ReviewStatsReporter;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Class BotMock.
*/
public class ReviewStatsTest
{
private static final Logger logger = LoggerFactory.getLogger(ReviewStatsTest.class);
@Before
public void init()
{
BasicConfigurator.configure();
logger.info("Basic log configuration done.");
}
/**
* Test load.
*
* @throws Exception
* the exception
*/
@Test
public void testLoad() throws Exception
{
System.out.println("================");
ReviewDatas datas = ReviewDatasFile.load(new File("resources/reviewstats.csv"));
for (ReviewData data : datas)
{
System.out.println(data.toString());
}
}
/**
* Test report duration.
*
* @throws Exception
* the exception
*/
@Test
public void testReportDuration() throws Exception
{
System.out.println("================");
ReviewDatas datas = ReviewDatasFile.load(new File("resources/reviewstats.csv"));
datas.clean();
System.out.println("File loaded.");
ReviewData currentReview = new ReviewData(LocalDateTime.now(), 12, 17);
datas.add(currentReview);
System.out.println(ReviewStatsReporter.reportDuration(datas, currentReview.getDuration()));
}
/**
* Test report new max user count.
*
* @throws Exception
* the exception
*/
@Test
public void testReportNewMaxUserCount() throws Exception
{
System.out.println("================");
ReviewDatas datas = ReviewDatasFile.load(new File("resources/reviewstats.csv"));
datas.clean();
System.out.println("File loaded.");
ReviewData currentReview = new ReviewData(LocalDateTime.now(), 12, 17);
System.out.println(ReviewStatsReporter.reportNewMaxUserCount(datas, currentReview.getUserCount()));
}
/**
* Test report user count.
*
* @throws Exception
* the exception
*/
@Test
public void testReportUserCount() throws Exception
{
System.out.println("================");
ReviewDatas datas = ReviewDatasFile.load(new File("resources/reviewstats.csv"));
datas.clean();
System.out.println("File loaded.");
ReviewData currentReview = new ReviewData(LocalDateTime.now(), 12, 17);
datas.add(currentReview);
System.out.println(ReviewStatsReporter.reportUserCount(datas, currentReview.getUserCount()));
}
}