r359 - in trunk/chorem-webmotion/src/main: java/org/chorem/webmotion/actions/project webapp/WEB-INF/jsp webapp/js
Author: meynier Date: 2013-07-03 17:30:35 +0200 (Wed, 03 Jul 2013) New Revision: 359 Url: http://chorem.org/projects/chorem/repository/revisions/359 Log: Added task calculations and enhanced alerts Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/DashboardProjectAction.java trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/QuotationCalculation.java trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardMultiProject.jsp trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp trunk/chorem-webmotion/src/main/webapp/js/chorem.js trunk/chorem-webmotion/src/main/webapp/js/jquery.fn.gantt.js Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/DashboardProjectAction.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/DashboardProjectAction.java 2013-07-02 16:04:44 UTC (rev 358) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/DashboardProjectAction.java 2013-07-03 15:30:35 UTC (rev 359) @@ -53,11 +53,11 @@ * @return */ public Render singleQuotationFilter(ChoremClient client, String id, String quotationFilter) { - + //Fetch the quotation from the filter WikittyQuery quotationQuery = new WikittyQueryMaker().ideq(quotationFilter).end(); WikittyQueryResult<Quotation> quotationResult = client.findAllByQuery(Quotation.class, quotationQuery); - + //If some quotation has been found if(quotationResult != null) { Quotation quotation = quotationResult.get(0); @@ -68,16 +68,37 @@ HashMap<Project, List<Quotation>> quotations = new HashMap<Project, List<Quotation>>(); HashMap<Quotation, List<Task>> taskMap = new HashMap<Quotation, List<Task>>(); HashMap<Quotation, List<String>> alertsMap = new HashMap<Quotation, List<String>>(); - + HashMap<Quotation, QuotationCalculation> calculations = new HashMap<Quotation, QuotationCalculation>(); + HashMap<Task, TaskCalculation> taskCalc = new HashMap<Task, TaskCalculation>(); + quotations.put(projectResult.get(0), quotationResult.getAll()); - - taskMap.put(quotation, fetchTasks(quotation, client)); + Configuration config = client.findAllByQuery(Configuration.class, + (new WikittyQueryMaker()).exteq(Configuration.EXT_CONFIGURATION).end() + ).get(0); + + List<Task> tasks = fetchTasks(quotation, client); + taskMap.put(quotation, tasks); + for(Task t : tasks) { + TaskCalculation tc = new TaskCalculation(t, client); + tc.setSrp(config.getDailyReturn()); + tc.calculate(); + taskCalc.put(t, tc); + } + taskMap.put(quotation, tasks); alertsMap.put(quotation, getAlerts(client, quotation)); + QuotationCalculation calc = new QuotationCalculation(quotation, client); + calc.setSrp(config.getDailyReturn()); + calc.calculate(); + calculations.put(quotation, calc); - return renderView("dashboardSingleProject.jsp", + + + + return renderView("dashboardSingleProject.jsp", "locale", client.getUserLocale(), "title", "Tableau de bord projet", "projects", projectResult.getAll(), "quotations", quotations, "taskMap", taskMap, "extensions", - Extensions.extensions, "alerts", alertsMap); + Extensions.extensions, "alerts", alertsMap, "calculations", calculations, + "taskCalc", taskCalc); } else { @@ -100,6 +121,9 @@ HashMap<Project, List<Quotation>> quotations = new HashMap<Project, List<Quotation>>(); HashMap<Quotation, List<Task>> taskMap = new HashMap<Quotation, List<Task>>(); HashMap<Quotation, List<String>> alertsMap = new HashMap<Quotation, List<String>>(); + HashMap<Quotation, QuotationCalculation> calculations = new HashMap<Quotation, QuotationCalculation>(); + HashMap<Task, TaskCalculation> taskCalc = new HashMap<Task, TaskCalculation>(); + //Fetch the project from the id WikittyQueryMaker projectQueryMaker = new WikittyQueryMaker(); if(id != null && !id.equals("")) @@ -110,6 +134,10 @@ projectResult = client.findAllByQuery(Project.class, projectQuery); + Configuration config = client.findAllByQuery(Configuration.class, + (new WikittyQueryMaker()).exteq(Configuration.EXT_CONFIGURATION).end() + ).get(0); + //Fetch the quotations form the project (if there is one) if(projectResult.size() != 0) { for(Project project : projectResult.getAll()) { @@ -125,8 +153,8 @@ else if (quotationFilter.equals("all")){ wqm.eq(Quotation.ELEMENT_FIELD_QUOTATION_PROJECT, project); } - + quotationQuery = wqm.end(); quotationQuery.setLimit(20); quotationQuery.addSortDescending(Quotation.ELEMENT_FIELD_INTERVAL_BEGINDATE); @@ -136,23 +164,38 @@ //Fetch the tasks form the quotations (if there are some) if(quotationResult != null) { for(Quotation quote : quotationResult.getAll()) { - taskMap.put(quote, fetchTasks(quote, client)); + List<Task> tasks = fetchTasks(quote, client); + taskMap.put(quote, tasks); + for(Task t : tasks) { + TaskCalculation tc = new TaskCalculation(t, client); + tc.setSrp(config.getDailyReturn()); + tc.calculate(); + taskCalc.put(t, tc); + } + alertsMap.put(quote, getAlerts(client, quote)); + + QuotationCalculation calc = new QuotationCalculation(quote, client); + calc.setSrp(config.getDailyReturn()); + calc.calculate(); + calculations.put(quote, calc); } } + } } - return renderView("dashboardSingleProject.jsp", + return renderView("dashboardSingleProject.jsp", "locale", client.getUserLocale(), "title", "Tableau de bord projet", "projects", projectResult.getAll(), "quotations", quotations, "taskMap", taskMap, "extensions", - Extensions.extensions, "alerts", alertsMap); + Extensions.extensions, "alerts", alertsMap, "calculations", calculations, + "taskCalc", taskCalc); } - + /** * Fetch the task from the given quotation * Simple wikitty query @@ -168,41 +211,39 @@ WikittyQueryResult taskResult = client.findAllByQuery(Task.class, taskQuery); return taskResult.getAll(); } - + private List<String> getAlerts(ChoremClient client, Quotation q) { List<Task> tasks = fetchTasks(q, client); Calendar now = new GregorianCalendar(); List<String> messages = new ArrayList<String>(); for(Task t : tasks) { - - System.out.println("Starting task alerts for " + t.getName()); - System.out.println("Task is " + t.getStatus()); - System.out.println(now.getTime()); - //Test if the statuses are correct - if(t.getStatus().equals("Scheduled")) { - if(t.getBeginDate().before(now.getTime())) { - System.out.println("should be started"); - messages.add( "Task " + t.getName() + " should be started"); + if(t.getBeginDate() != null && t.getEndDate() != null) { + //Test if the statuses are correct + if(t.getStatus().equalsIgnoreCase("Scheduled")) { + if(t.getBeginDate().before(now.getTime())) { + System.out.println("should be started"); + messages.add( "Task " + t.getName() + " should be started"); + } } - } - else if(t.getStatus().equals("Started")) { - if(t.getBeginDate().after(now.getTime())) { - System.out.println("started in advance"); - messages.add( "Task " + t.getName() + " has been started in advance"); + else if(t.getStatus().equalsIgnoreCase("Started")) { + if(t.getBeginDate().after(now.getTime())) { + System.out.println("started in advance"); + messages.add( "Task " + t.getName() + " has been started in advance"); + } + else if(t.getEndDate().before(now.getTime())) { + System.out.println("should be ended"); + messages.add( "Task " + t.getName() + " should have ended by now"); + } } - else if(t.getEndDate().before(now.getTime())) { - System.out.println("should be ended"); - messages.add( "Task " + t.getName() + " should have ended by now"); + else if(t.getStatus().equalsIgnoreCase("Finished")) { + + if(t.getEndDate().after(now.getTime())) { + System.out.println("finished in advance"); + messages.add( "Task " + t.getName() + " has been finished in advance"); + } } } - else if(t.getStatus().equals("Finished")) { - - if(t.getEndDate().after(now.getTime())) { - System.out.println("fnished in advance"); - messages.add( "Task " + t.getName() + " has been finished in advance"); - } - } - + } return messages; } @@ -247,6 +288,7 @@ return renderView("dashboardMultiProject.jsp", "title", "Tableau de bord projets", + "locale", client.getUserLocale(), "quotations", quotations, "calculations", calculations); @@ -308,7 +350,7 @@ // v= new Values[2]; //else v = new Values[1]; - v[0] = new Values(t.getBeginDate(), t.getEndDate(), t.getName(), customClass); + v[0] = new Values(t.getBeginDate(), t.getEndDate(), t.getName(), customClass, t.getWikittyId()); //if(t.getReestimatedEnd() != null) // v[1]= new Values(t.getEndDate(), t.getReestimatedEnd(), "Reestimated end", "ganttOrange"); @@ -391,11 +433,13 @@ private String to; private String label; private String customClass; - public Values(Date from, Date to, String label, String customClass) { + private String dataObj; + public Values(Date from, Date to, String label, String customClass, String dataObj) { this.from = "/Date(" + from.getTime() + ")/"; this.to = "/Date(" + to.getTime() + ")/"; this.label = label; this.customClass = customClass; + this.dataObj = dataObj; } @Override public int compareTo(Object o) { Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/QuotationCalculation.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/QuotationCalculation.java 2013-07-02 16:04:44 UTC (rev 358) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/QuotationCalculation.java 2013-07-03 15:30:35 UTC (rev 359) @@ -2,10 +2,8 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Date; import org.chorem.ChoremClient; -import org.chorem.entities.Interval; import org.chorem.entities.Quotation; import org.chorem.entities.Task; import org.chorem.entities.Time; @@ -13,57 +11,19 @@ import org.nuiton.wikitty.query.WikittyQueryMaker; import org.nuiton.wikitty.query.WikittyQueryResult; -public class QuotationCalculation { +public class QuotationCalculation extends Calculation<Quotation> { - private Quotation q; - private ChoremClient client; - private static final int SEC_PER_HOUR = 3600; - private static final int WORKING_HOURS_PER_DAY = 7; - private double srp = 1; - private Double tjm = null; - private Double realDays = null; - private Double deltaDays = null; - private Double realTjm = null; - private Double expectedProfit = null; - private Double lossOrProfit = null; - private Double resultPerDay = null; - - - public QuotationCalculation(Quotation q, ChoremClient client) { - this.q = q; - this.client = client; + super(q, q.getAmount(), q.getEstimatedDays(), client); } - - public void calculate() { - this.tjm = tjm(); - this.realDays = realDays(); - this.deltaDays = deltaDays(); - this.realTjm = realTjm(); - this.expectedProfit = expectedProfit(); - this.lossOrProfit = lossOrProfit(); - this.resultPerDay = resultPerDay(); - } - - public double tjm() { - double amount = q.getAmount(); - double nbDays = q.getEstimatedDays(); - - return amount/nbDays; - - } - - private double getPeriodInSeconds(Date start, Date end) { - return (end.getTime()-start.getTime())/1000; - } - + @Override public double realDays() { //test if quotation is finished - if(q.getExtensionNames().contains("Closed")) { + if(e.getExtensionNames().contains("Closed")) { //Calculate the days from the times objects WikittyQuery taskQuery = new WikittyQueryMaker() - .eq(Task.ELEMENT_FIELD_TASK_QUOTATION, q) + .eq(Task.ELEMENT_FIELD_TASK_QUOTATION, e) .end(); WikittyQueryResult<Task> result = @@ -73,7 +33,7 @@ WikittyQuery timeQuery = new WikittyQueryMaker() .eq(Time.ELEMENT_FIELD_TIME_TASK, t) .end(); - WikittyQueryResult timeResult = client.findAllByQuery(Time.class, timeQuery); + WikittyQueryResult<Time> timeResult = client.findAllByQuery(Time.class, timeQuery); times.addAll(timeResult.getAll()); } double totalTime = 0; @@ -87,7 +47,7 @@ else { //nbDays + day extensions from tasks WikittyQuery taskQuery = new WikittyQueryMaker() - .eq(Task.ELEMENT_FIELD_TASK_QUOTATION, q) + .eq(Task.ELEMENT_FIELD_TASK_QUOTATION, e) .end(); WikittyQueryResult<Task> result = @@ -97,80 +57,9 @@ for(Task t : result.getAll()) { totalDayExt += t.getDayExtension(); } - return q.getEstimatedDays() + totalDayExt; + return nbDays + totalDayExt; } } - public double deltaDays() { - return getRealDays() - q.getEstimatedDays(); - } - - public double realTjm() { - double amount = q.getAmount(); - double nbDays = getRealDays(); - - return amount/nbDays; - } - - public double expectedProfit() { - double nbDays = q.getEstimatedDays(); - return (getTjm() - srp) * nbDays; - } - - public double lossOrProfit() { - double amount = q.getAmount(); - return amount - (getRealDays() * srp); - } - - public double resultPerDay() { - return getLossOrProfit() / getRealDays(); - } - - public double getTjm() { - if(tjm == null) - return tjm(); - else - return tjm; - } - public double getRealDays() { - if(realDays == null) - return realDays(); - else - return realDays; - } - public double getDeltaDays() { - if(deltaDays == null) - return deltaDays; - else - return deltaDays; - } - public double getRealTjm() { - if(realTjm == null) - return realTjm(); - else - return realTjm; - } - public double getExpectedProfit() { - if(expectedProfit == null) - return expectedProfit(); - else - return expectedProfit; - } - public double getLossOrProfit() { - if(lossOrProfit == null) - return lossOrProfit(); - else - return lossOrProfit; - } - public double getResultPerDay() { - if(resultPerDay == null) - return resultPerDay(); - else - return resultPerDay; - } - public void setSrp(double val) { - this.srp = val; - } - } Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardMultiProject.jsp =================================================================== --- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardMultiProject.jsp 2013-07-02 16:04:44 UTC (rev 358) +++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardMultiProject.jsp 2013-07-03 15:30:35 UTC (rev 359) @@ -26,7 +26,7 @@ <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> <%@ taglib uri="/WEB-INF/wikitty.tld" prefix="w"%> - +<f:setLocale value="${locale}"/> <link rel="stylesheet" href="<c:url value='/css/jquery.fn.gantt.css'/>" /> <h1>${title}</h1> <form class="well form-inline" method="GET" id="projectSearch"> @@ -59,7 +59,7 @@ </thead> <c:forEach var="q" items="${quotations}"> <tbody> - <tr><f:setLocale value="fr_FR"/> + <tr> <td><w:display wikitty="${q.wikitty}" fqfield="Quotation.customer" label=""/></td> <td><w:display wikitty="${q.wikitty}" fqfield="Quotation.project" label=""/> <a href="<c:url value="/wikitty/edit/${q.wikittyId}"/>"><i Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp =================================================================== --- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp 2013-07-02 16:04:44 UTC (rev 358) +++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp 2013-07-03 15:30:35 UTC (rev 359) @@ -26,7 +26,7 @@ <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> <%@ taglib uri="/WEB-INF/wikitty.tld" prefix="w"%> - +<f:setLocale value="${locale}" /> <link rel="stylesheet" href="<c:url value='/css/jquery.fn.gantt.css'/>" /> <h1>${title}</h1> @@ -69,10 +69,10 @@ </script> <input type="text" id="project_text" name="project_name" - value="${param.project_name}" placeholder="project name" /> - <input type="hidden" id="project_hidden" name="project_id" + value="${param.project_name}" placeholder="project name" /> <input + type="hidden" id="project_hidden" name="project_id" value='<c:if test='${!param.project_name.equals("") }'>${param.project_id}</c:if>' /> - + </div> </div> @@ -108,16 +108,17 @@ -1]} </p> - + <table class="table table-striped table-bordered table-condensed"> <thead> <tr> <th>Responsable Code Lutin</th> <th>Responsable entreprise</th> - <th>Estimated days</th> - <th>Conversion hope</th> - <th>Hoped price day</th> - <th>Amount</th> + <th>Jours estimés</th> + <th>TJM estimé</th> + <th>TJM réel</th> + <th>Montant</th> + <th>Gain/perte</th> </tr> </thead> @@ -131,63 +132,86 @@ fqfield="Quotation.customer" label="" /></td> <td class="number"><w:display wikitty="${q.wikitty}" fqfield="Quotation.estimatedDays" label="" /></td> - <td class="percent"><w:display wikitty="${q.wikitty}" - fqfield="Quotation.conversionHope" label="" /></td> <td class="currency"><f:formatNumber type="currency" - value="${q.amount / q.estimatedDays}" /></td> + maxFractionDigits="2" value="${calculations[q].getTjm()}" /></td> + <td class="currency"><f:formatNumber type="currency" + maxFractionDigits="2" value="${calculations[q].getRealTjm()}" /></td> <td class="currency"><w:display wikitty="${q.wikitty}" fqfield="Quotation.amount" label="" /></td> + <td class="currency"><f:formatNumber type="currency" + maxFractionDigits="2" + value="${calculations[q].getLossOrProfit()}" /></td> </tr> </tbody> </table> - ${alerts[q]} + + + + <c:forEach var="a" items="${alerts[q]}"> + + <div class="alert-error alert"> + <div class="alert-block">${a}</div> + </div> + + </c:forEach> + <div class="gantt gantt-${q.wikittyId}" wikittyId="${q.wikittyId}" data=''>Pas de tâche</div> - <!-- - <p>Liste des tâches :</p> - <c:forEach items="${taskMap}" var="entry"> - <c:if test="${entry.key.equals(q)}"> - <ul> - <c:forEach items="${entry.value}" var="task"> - <li><a - href="<c:url value="/wikitty/view/${task.wikittyId}"/>">${task.name} - : ${task.description}</a></li> - </c:forEach> - </ul> - </c:if> + <table class="table table-striped table-bordered table-condensed"> + <thead> + <tr> + <th>Nom</th> + <th>Montant</th> + <th>Jours estimés</th> + <th>Jours réels</th> + <th>Différence estimation/réel</th> + <th>TJM estimé</th> + <th>TJM réel</th> + <th>Gain attendu</th> + <th>Gain/perte</th> + </tr> + </thead> + <tbody> + <c:forEach items="${taskMap[q]}" var="task"> + <tr> + <td><a href="<c:url value="/wikitty/view/${task.wikittyId}"/>"> + <w:display wikitty="${task.wikitty}" fqfield="Task.name" + label="" /></a></td> + <td><w:display wikitty="${task.wikitty}" + fqfield="Task.price" label="" /></td> + <td class="number"><w:display wikitty="${task.wikitty}" + fqfield="Task.estimatedDays" label="" /></td> + <td class="number"><f:formatNumber type="number" + maxFractionDigits="2" value="${taskCalc[task].getRealDays()}" /></td> + <td class="number"><f:formatNumber type="number" + maxFractionDigits="2" value="${taskCalc[task].getDeltaDays()}" /></td> + <td class="number"><f:formatNumber type="number" + maxFractionDigits="2" value="${taskCalc[task].getTjm()}" /></td> - </c:forEach> - <p> - <select class="extBox" name="extList" wikittyId="${q.wikittyId}"> - <option>Transformer en...</option> - <c:forEach items="${extensions}" var="ext"> - <c:set var="contains" value="false" /> - <c:forEach var="item" items="${q.extensionNames}"> - <c:if test="${item eq ext}"> - <c:set var="contains" value="true" /> - </c:if> - </c:forEach> - <c:if test="${contains == false}"> - <option>${ext}</option> - </c:if> + <td class="number"><f:formatNumber type="number" + maxFractionDigits="2" value="${taskCalc[task].getRealTjm()}" /></td> + + <td class="currency"><f:formatNumber type="currency" + maxFractionDigits="2" + value="${taskCalc[task].getExpectedProfit()}" /></td> + <td class="currency"><f:formatNumber type="currency" + maxFractionDigits="2" + value="${taskCalc[task].getLossOrProfit()}" /></td> + </tr> + + </c:forEach> - </select> - </p> - <div id="upgradeFields-${q.wikittyId}"></div> ---> + </tbody> + </table> + </c:forEach> </c:if> </c:forEach> -<script src="<c:url value='/js/jquery.fn.gantt.js'/>"></script> - - - - - +<script src="<c:url value='/js/jquery.fn.gantt.js'/>"></script> \ No newline at end of file Modified: trunk/chorem-webmotion/src/main/webapp/js/chorem.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/chorem.js 2013-07-02 16:04:44 UTC (rev 358) +++ trunk/chorem-webmotion/src/main/webapp/js/chorem.js 2013-07-03 15:30:35 UTC (rev 359) @@ -761,7 +761,6 @@ $.get(createUrl("/project/json/getGanttInfo/",id), function(ret){ var data = ret['data']; - console.log(data); if(data['source'].length != 0) { $(".gantt-"+id).gantt({ source: data['source'], @@ -774,7 +773,7 @@ minScale: "days", itemsPerPage: 10, onItemClick: function(data) { - window.location ="wikitty/edit/" + this.source[0].wikittyId; + window.location ="wikitty/edit/" + data; }, onAddClick: function(dt, rowId) { alert("Empty space clicked - add an item!"); Modified: trunk/chorem-webmotion/src/main/webapp/js/jquery.fn.gantt.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jquery.fn.gantt.js 2013-07-02 16:04:44 UTC (rev 358) +++ trunk/chorem-webmotion/src/main/webapp/js/jquery.fn.gantt.js 2013-07-03 15:30:35 UTC (rev 359) @@ -368,13 +368,17 @@ var entries = []; $.each(element.data, function (i, entry) { if (i >= element.pageNum * settings.itemsPerPage && i < (element.pageNum * settings.itemsPerPage + settings.itemsPerPage)) { - - entries.push('<div class="row name row' + i + (entry.desc ? '' : ' fn-wide') + '" id="rowheader' + i + '" offset="' + i % settings.itemsPerPage * tools.getCellSize() + '">'); + entries.push('<div class="row name row' + i + (entry.desc ? '' : ' fn-wide') + + '" id="rowheader' + i + '" offset="' + + i % settings.itemsPerPage * tools.getCellSize() + '" dataObj="' + + entry.wikittyId + '">'); entries.push('<span class="fn-label' + (entry.cssClass ? ' ' + entry.cssClass : '') + '">' + entry.name + '</span>'); entries.push('</div>'); if (entry.desc) { - entries.push('<div class="row desc row' + i + ' " id="RowdId_' + i + '" data-id="' + entry.id + '">'); + entries.push('<div class="row desc row' + i + ' " id="RowdId_' + i + '" data-id="' + + entry.id + '" dataObj="' + + entry.wikittyId + '">'); entries.push('<span class="fn-label' + (entry.cssClass ? ' ' + entry.cssClass : '') + '">' + entry.desc + '</span>'); @@ -388,7 +392,12 @@ ganttLeftPanel.find('.row:not(.spacer)').mouseover(core.mover) .mouseout(core.mout) .mousemove(core.mmove) - .attr('over', 'text'); + .attr('over', 'text') + .click(function (e) { + e.stopPropagation(); + settings.onItemClick($(this).attr("dataObj")); + }); + return ganttLeftPanel; },
participants (1)
-
meynier@users.chorem.org