Author: fdesbois Date: 2010-02-10 22:04:27 +0000 (Wed, 10 Feb 2010) New Revision: 345 Added: trunk/suiviobsmer-ui/src/main/webapp/img/synthesis-ind-contact-states.png Modified: trunk/suiviobsmer-business/src/main/java/fr/ifremer/suiviobsmer/services/ServiceSynthesisImpl.java trunk/suiviobsmer-business/src/main/xmi/suiviobsmer.zargo trunk/suiviobsmer-business/src/test/java/fr/ifremer/suiviobsmer/services/ServiceSynthesisImplTest.java trunk/suiviobsmer-ui/src/main/java/fr/ifremer/suiviobsmer/ui/pages/Synthesis.java trunk/suiviobsmer-ui/src/main/resources/fr/ifremer/suiviobsmer/ui/pages/Synthesis.properties trunk/suiviobsmer-ui/src/main/webapp/Synthesis.tml trunk/suiviobsmer-ui/src/main/webapp/css/synthesis.css Log: Evo #1986 : Integrate indicator contact states in synthesis page Modified: trunk/suiviobsmer-business/src/main/java/fr/ifremer/suiviobsmer/services/ServiceSynthesisImpl.java =================================================================== --- trunk/suiviobsmer-business/src/main/java/fr/ifremer/suiviobsmer/services/ServiceSynthesisImpl.java 2010-02-10 15:43:48 UTC (rev 344) +++ trunk/suiviobsmer-business/src/main/java/fr/ifremer/suiviobsmer/services/ServiceSynthesisImpl.java 2010-02-10 22:04:27 UTC (rev 345) @@ -258,17 +258,17 @@ TopiaQuery query = dao.createQueryDoneContactsFromDate(null, null); - String contactKey = query.getMainAlias(); - String sampleRowKey = contactKey + "." + Contact.SAMPLE_ROW; - String companyKey = sampleRowKey + "." + SampleRow.COMPANY; - String companyName = companyKey + "." + Company.NAME; + String contact = query.getMainAlias(); + String sampleRow = contact + "." + Contact.SAMPLE_ROW; + String companyProp = sampleRow + "." + SampleRow.COMPANY; + String companyName = companyProp + "." + Company.NAME; // Only for unfinished sampleRows Date current = SuiviObsmerContext.getCurrentDate(); - query.add(sampleRowKey + "." + SampleRow.PERIOD_END, Op.GE, current); + query.add(sampleRow + "." + SampleRow.PERIOD_END, Op.GE, current); if (company != null) { - query.add(companyKey, company); + query.add(companyProp, company); } // Prepare results @@ -282,8 +282,8 @@ // Use the same query and add the constraint of non compliance //i.e. nbObservantsReal (contact) < nbObservantsExpected (sampleRow) - query.add(contactKey + "." + Contact.NB_OBSERVANTS + " < " + - sampleRowKey + "." + SampleRow.NB_OBSERVANTS); + query.add(contact + "." + Contact.NB_OBSERVANTS + " < " + + sampleRow + "." + SampleRow.NB_OBSERVANTS); if (log.isDebugEnabled()) { log.debug("Exec query for result : " + query); @@ -336,43 +336,44 @@ @Override public Collection<ContactStateStatistics> getContactStateStatistics(Company company, PeriodDates period) throws SuiviObsmerException { TopiaContext transaction = null; - Map<Company, ContactStateStatistics> results = new HashMap<Company, ContactStateStatistics>(); + Map<String, ContactStateStatistics> results = new HashMap<String, ContactStateStatistics>(); try { transaction = rootContext.beginTransaction(); ContactDAO dao = SuiviObsmerModelDAOHelper.getContactDAO(transaction); // Only for contacts not refused by the program - String contactKey = "C"; - String validationProgramKey = contactKey + "." + Contact.VALIDATION_PROGRAM; - TopiaQuery query = dao.createQuery(contactKey). - add(validationProgramKey + " IS NULL " + - "OR " + validationProgramKey + " = :booleanTrue"). + String contact = "C"; + String validationProgram = contact + "." + Contact.VALIDATION_PROGRAM; + TopiaQuery query = dao.createQuery(contact). + add(validationProgram + " IS NULL " + + "OR " + validationProgram + " = :booleanTrue"). addParam("booleanTrue", Boolean.TRUE); - String companyKey = contactKey + "." + Contact.OBSERVER + "." + WaoUser.COMPANY; + String companyProp = contact + "." + Contact.OBSERVER + "." + WaoUser.COMPANY; if (company != null) { - query.add(companyKey, company); + query.add(companyProp, company); } if (period != null) { // Contacts include in the period - String tideBeginDateKey = contactKey + "." + Contact.TIDE_BEGIN_DATE; - String createDateKey = contactKey + "." + TopiaEntity.TOPIA_CREATE_DATE; - query.add("(" + tideBeginDateKey + " IS NOT NULL " + - "AND " + tideBeginDateKey + " BETWEEN :fromDate AND :thruDate)" + - " OR (" + tideBeginDateKey + " IS NULL " + - "AND " + createDateKey + " BETWEEN :fromDate AND :thruDate)"); + String tideBeginDate = contact + "." + Contact.TIDE_BEGIN_DATE; + String createDate = contact + "." + TopiaEntity.TOPIA_CREATE_DATE; + query.add("(" + tideBeginDate + " IS NOT NULL " + + "AND " + tideBeginDate + " BETWEEN :fromDate AND :thruDate)" + + " OR (" + tideBeginDate + " IS NULL " + + "AND " + createDate + " BETWEEN :fromDate AND :thruDate)"); query.addParam("fromDate", period.getFromDate()). addParam("thruDate", period.getThruDate()); } - String stateKey = contactKey + "." + Contact.STATE; - query.addGroup(companyKey, stateKey). - addOrder(companyKey). - setSelect(companyKey, stateKey, "COUNT(*)"); + String state = contact + "." + Contact.STATE; + String companyName = companyProp + "." + Company.NAME; + query.addGroup(companyName, state). + addOrder(companyName). + setSelect(companyName, state, "COUNT(*)"); if (log.isDebugEnabled()) { log.debug("Exec query : " + query); @@ -381,17 +382,17 @@ List<Object[]> res = query.execute(); for (Object[] row : res) { - Company rowCompany = (Company)row[0]; + String rowCompanyName = (String)row[0]; String rowState = (String)row[1]; Long rowCount = (Long)row[2]; if (log.isDebugEnabled()) { - log.debug("res : " + rowCompany.getName() + " _ " + rowState + " _ " + rowCount); + log.debug("res : " + rowCompanyName + " _ " + rowState + " _ " + rowCount); } - ContactStateStatistics stats = results.get(rowCompany); + ContactStateStatistics stats = results.get(rowCompanyName); if (stats == null) { stats = new ContactStateStatisticsImpl(); - stats.setCompany(rowCompany); - results.put(rowCompany, stats); + stats.setCompanyName(rowCompanyName); + results.put(rowCompanyName, stats); } stats.addResult(rowState, rowCount.intValue()); } Modified: trunk/suiviobsmer-business/src/main/xmi/suiviobsmer.zargo =================================================================== (Binary files differ) Modified: trunk/suiviobsmer-business/src/test/java/fr/ifremer/suiviobsmer/services/ServiceSynthesisImplTest.java =================================================================== --- trunk/suiviobsmer-business/src/test/java/fr/ifremer/suiviobsmer/services/ServiceSynthesisImplTest.java 2010-02-10 15:43:48 UTC (rev 344) +++ trunk/suiviobsmer-business/src/test/java/fr/ifremer/suiviobsmer/services/ServiceSynthesisImplTest.java 2010-02-10 22:04:27 UTC (rev 345) @@ -389,7 +389,7 @@ result = null; for (ContactStateStatistics stats : results) { - if (stats.getCompany().equals(company2)) { + if (stats.getCompanyName().equals(company2.getName())) { result = stats; break; } Modified: trunk/suiviobsmer-ui/src/main/java/fr/ifremer/suiviobsmer/ui/pages/Synthesis.java =================================================================== --- trunk/suiviobsmer-ui/src/main/java/fr/ifremer/suiviobsmer/ui/pages/Synthesis.java 2010-02-10 15:43:48 UTC (rev 344) +++ trunk/suiviobsmer-ui/src/main/java/fr/ifremer/suiviobsmer/ui/pages/Synthesis.java 2010-02-10 22:04:27 UTC (rev 345) @@ -25,6 +25,8 @@ import fr.ifremer.suiviobsmer.bean.BoardingResult; import fr.ifremer.suiviobsmer.bean.BoatFilter; import fr.ifremer.suiviobsmer.bean.BoatFilterImpl; +import fr.ifremer.suiviobsmer.bean.ContactState; +import fr.ifremer.suiviobsmer.bean.ContactStateStatistics; import fr.ifremer.suiviobsmer.entity.Company; import fr.ifremer.suiviobsmer.entity.WaoUser; import fr.ifremer.suiviobsmer.services.ServiceSynthesis; @@ -37,6 +39,7 @@ import java.text.DateFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -115,6 +118,14 @@ return ChartType.class.getName(); } + public NumberFormat getPercentFormat() { + return NumberFormat.getPercentInstance(); + } + + public DateFormat getDateFormat() { + return new SimpleDateFormat("dd/MM/yyyy"); + } + @Log Object onAction(String actionType, String actionValue) { // Delegator action (menu) @@ -275,10 +286,6 @@ return DateUtils.createDateAfterToday(0, -12, 0); } - public DateFormat getDateFormat() { - return new SimpleDateFormat("dd/MM/yyyy"); - } - @Log void onSuccessFromFilterCompanyForBoarding() throws SuiviObsmerException { companyForBoarding = getCompanySelectModel().findObject(companyIdForBoarding); @@ -286,6 +293,8 @@ /********************* INDICATOR : NONCOMPLIANCEBOARDING ******************/ + private Map<String, Double> nonComplianceBoarding; + @Property private Map.Entry<String, Double> nonComplianceBoardingEntry; @@ -297,34 +306,98 @@ * @return a Map with companies and there values for nonComplianceBoarding * @throws SuiviObsmerException */ - public Map<String, Double> getNonComplianceBoarding() throws SuiviObsmerException { - Company company = !user.getAdmin() ? user.getCompany() : null; - return serviceSynthesis.getNonComplianceBoardingIndicator(company); + public Map<String, Double> getNonComplianceBoarding() + throws SuiviObsmerException { + if (nonComplianceBoarding == null) { + Company company = !user.getAdmin() ? user.getCompany() : null; + nonComplianceBoarding = + serviceSynthesis.getNonComplianceBoardingIndicator(company); + } + return nonComplianceBoarding; } - public NumberFormat getPercentFormat() { - return NumberFormat.getPercentInstance(); + /** + * Return the value of nonComplianceBoarding indicator. Only useful for + * observer user. + * + * @return the value of the indicator + * @throws SuiviObsmerException + */ + public Double getNonComplianceBoardingValue() throws SuiviObsmerException { + return getNonComplianceBoarding().get(user.getCompany().getName()); } + /********************* INDICATOR : NONCOMPLIANCEBOARDING ******************/ + + private Collection<ContactStateStatistics> contactStateStatistics; + + @Persist + private PeriodDates periodForContactStates; + + @Property + private ContactStateStatistics contactStateStats; + + @Property + private ContactState contactState; + /** - * Return the company name of the current entry (in the loop) for - * nonComplianceBoarding indicator. Only useful for admin user. + * Get all existing contactStates. * - * @return the company name - * @throws SuiviObsmerException + * @return ContactState[] */ -// public String getNonComplianceBoardingCompanyName() throws SuiviObsmerException { -// return nonComplianceBoardingEntry.getKey().getName(); -// } + public ContactState[] getContactStates() { + return ContactState.values(); + } /** - * Return the value of nonComplianceBoarding indicator. Only useful for - * observer user. + * Get all contactStateStatistics from serviceSynthesis. There will be + * only one result if the current user is an observer. * - * @return the value of the indicator + * @return all contactStateStatistics depends on company and selected period * @throws SuiviObsmerException */ - public Double getNonComplianceBoardingValue() throws SuiviObsmerException { - return getNonComplianceBoarding().get(user.getCompany().getName()); + public Collection<ContactStateStatistics> getContactStateStatistics() + throws SuiviObsmerException { + if (contactStateStatistics == null) { + Company company = !user.getAdmin() ? user.getCompany() : null; + contactStateStatistics = + serviceSynthesis.getContactStateStatistics(company, getPeriodForContactStates()); + } + return contactStateStatistics; } + + public PeriodDates getPeriodForContactStates() { + if (periodForContactStates == null) { + periodForContactStates = PeriodDates.createMonthsPeriodFromToday(-12); + } + return periodForContactStates; + } + + /** + * Get the value for current contactStateStats in table row (by company) + * and current contactState in table column. + * + * @return the number of contact states for the current contactState + */ + public int getNbContactStates() { + return contactStateStats.getData().get(contactState); + } + + /** + * Get the percent value for the current contactStateStats in table row + * (by company) and current contactState in table column. + * + * @return the percent value of contact states for the current contactState + */ + public double getPercentContactStates() { + if (log.isDebugEnabled()) { + log.debug("nbContactStates : " + getNbContactStates()); + log.debug("total : " + contactStateStats.getTotal()); + } + return (double)getNbContactStates() / (double)contactStateStats.getTotal(); + } + +// void onSuccessFromFilterPeriodForContactStates() { +// } + } Modified: trunk/suiviobsmer-ui/src/main/resources/fr/ifremer/suiviobsmer/ui/pages/Synthesis.properties =================================================================== --- trunk/suiviobsmer-ui/src/main/resources/fr/ifremer/suiviobsmer/ui/pages/Synthesis.properties 2010-02-10 15:43:48 UTC (rev 344) +++ trunk/suiviobsmer-ui/src/main/resources/fr/ifremer/suiviobsmer/ui/pages/Synthesis.properties 2010-02-10 22:04:27 UTC (rev 345) @@ -9,4 +9,7 @@ company-label: Soci\u00E9t\u00E9 programName-label: Programme -companyForBoarding-label: Soci\u00E9t\u00E9 \ No newline at end of file +companyForBoarding-label: Soci\u00E9t\u00E9 + +periodBeginForContactStates-label: du +periodEndForContactStates-label: au \ No newline at end of file Modified: trunk/suiviobsmer-ui/src/main/webapp/Synthesis.tml =================================================================== --- trunk/suiviobsmer-ui/src/main/webapp/Synthesis.tml 2010-02-10 15:43:48 UTC (rev 344) +++ trunk/suiviobsmer-ui/src/main/webapp/Synthesis.tml 2010-02-10 22:04:27 UTC (rev 345) @@ -2,73 +2,40 @@ <t:layout t:pageTitle="Synthèse et indicateurs" t:contentId="so-synthesis" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" xmlns:p="tapestry:parameter"> -<!-- <div class="clearfix" id="so-synthesis-indicators"> - <div class="indicator fleft"> - <p class="title"> - <span t:type="ck/Tooltip" title="Description" t:effect="appear" - t:value="pourcentage de marées réalisés par rapport aux prévus dans le plan - d'échantillonnage"> - Embarquements 2009 - </span> - </p> - <p class="number">85 %</p> + <!-- MENU : delegator --> + <div class="clearfix"> + <div class="fleft" id="so-synthesis-menu"> + <ul> + <li> + <a t:type="actionlink" t:context="[actionSynthesisId,'GRAPH_SAMPLING']" t:zone="so-synthesis-main"> + <img src="${asset:context:}/img/synthesis-graph-sampling.png" alt="Diagramme en bâtons" + title="Graphique des données des marées (réalisé / planifié)" /> + </a> + </li> + <li> + <a t:type="actionlink" t:context="[actionSynthesisId,'GRAPH_BOARDING']" t:zone="so-synthesis-main"> + <img src="${asset:context:}/img/synthesis-graph-boarding.png" alt="Diagramme en bâtons" + title="Graphique des sollicitations des navires" /> + </a> + </li> + <li> + <a t:type="actionlink" t:context="[actionSynthesisId,'IND_NON_COMPLIANCE_BOARDING']" t:zone="so-synthesis-main"> + <img src="${asset:context:}/img/synthesis-ind-non-compliance-boarding.png" alt="Indicateur" + title="Indicateur de non respect du nombre d'observateurs embarqués" /> + </a> + </li> + <li> + <a t:type="actionlink" t:context="[actionSynthesisId,'IND_CONTACT_STATE']" t:zone="so-synthesis-main"> + <img src="${asset:context:}/img/synthesis-ind-contact-states.png" alt="Indicateur" + title="Indicateur sur les états des contacts" /> + </a> + </li> + </ul> </div> - <div class="indicator fleft"> - <p class="title"> - <span t:type="ck/Tooltip" title="Description" t:effect="appear" - t:value="nombre de contacts ayant aboutis en 2009"> - Contacts 2009 - </span> - </p> - <p class="number">120</p> + <div t:type="zone" t:id="delegator" class="fleft" t:update="show" id="so-synthesis-main"> + <t:delegate t:to="activeBlock" /> </div> - <div class="indicator fleft"> - <p class="title"> - <span t:type="ck/Tooltip" title="Description" t:effect="appear" - t:value="navire le plus sollicité ce mois-ci"> - Top navire déc2009 - </span> - </p> - <p class="number">MAYFLOWERS (18)</p> - </div> - - </div>--> -<!-- <div class="graph"> - <img src="${asset:context:}/tmp/graph1.png" alt="graphe"/> - <p class="description">Proin eu molestie dolor. Aliquam eget ultrices lorem. Phasellus molestie iaculis eleifend. - Aenean vehicula elit eget felis vestibulum ut tempor est congue. Ut nulla felis, - pulvinar at faucibus nec, lacinia in justo. Aliquam erat volutpat. Curabitur - risus libero, tincidunt eget aliquam sit amet, auctor id libero. Proin in - lectus nunc. Proin molestie molestie augue aliquet suscipit. Sed id enim diam, - sed rutrum libero. </p> - </div>--> -<div class="clearfix"> - <div class="fleft" id="so-synthesis-menu"> - <ul> - <li> - <a t:type="actionlink" t:context="[actionSynthesisId,'GRAPH_SAMPLING']" t:zone="so-synthesis-main"> - <img src="${asset:context:}/img/synthesis-graph-sampling.png" alt="Diagramme en bâtons" - title="Graphique des données des marées (réalisé / planifié)" /> - </a> - </li> - <li> - <a t:type="actionlink" t:context="[actionSynthesisId,'GRAPH_BOARDING']" t:zone="so-synthesis-main"> - <img src="${asset:context:}/img/synthesis-graph-boarding.png" alt="Diagramme en bâtons" - title="Graphique des sollicitations des navires" /> - </a> - </li> - <li> - <a t:type="actionlink" t:context="[actionSynthesisId,'IND_NON_COMPLIANCE_BOARDING']" t:zone="so-synthesis-main"> - <img src="${asset:context:}/img/synthesis-ind-non-compliance-boarding.png" alt="Indicateur" - title="Indicateur de non respect du nombre d'observateurs embarqués" /> - </a> - </li> - </ul> </div> - <div t:type="zone" t:id="delegator" class="fleft" t:update="show" id="so-synthesis-main"> - <t:delegate t:to="activeBlock" /> - </div> -</div> <!-- GRAPH1 : DATA_SAMPLING --> <t:block t:id="graph1"> @@ -140,7 +107,7 @@ </div> </t:block> <!-- GRAPH2 : BOARDING_BOAT --> - <t:block t:id="graph2"> + <t:block t:id="graph2"> <div class="acenter"> <h2>Sollicitations des navires</h2> <br /> @@ -165,22 +132,19 @@ </t:block> <!-- IND1 : NON_COMPLIANCE_BOARDING --> <t:block t:id="ind1"> - <div class="acenter" id="so-noncomplianceboarding"> + <div class="ind-table acenter" id="so-noncomplianceboarding"> <h2>Non respect du nombre d'observateurs embarqués</h2> <br /> - <p> - Ce résultat concerne l'ensemble des contrats en cours - <t:unless t:test="user.admin"> - pour votre société - </t:unless> - ayant des données réelles - </p> <t:if t:test="user.admin"> + <p> + Ces résultats concernent l'ensemble des contrats en cours + ayant des données réelles. + </p> <table class="t-data-grid"> <thead> <tr> - <td class="company">Société</td> - <td>Indicateur</td> + <th class="company">Société</th> + <th>Indicateur</th> </tr> </thead> <tbody> @@ -193,13 +157,73 @@ </tbody> </table> <p:else> + <p> + Ce résultat concerne l'ensemble des contrats en cours + pour votre société ayant des données réelles. + </p> <div class="indicator"> <p class="number"> - <t:output t:value="nonComplianceBoardingValue" t:format="percentFormat"/> + <span t:type="output" t:value="nonComplianceBoardingValue" t:format="percentFormat"> + ${nonComplianceBoardingValue} + </span> </p> </div> </p:else> </t:if> </div> </t:block> + <!-- IND2 : CONTACT_STATE --> + <t:block t:id="ind2"> + <div class="ind-table acenter" id="so-contactstate"> + <h2>Etats des contacts</h2> + <br /> + <p> + Ces résultats concernent l'ensemble des états des contacts<br /> excepté + ceux qui ont été refusés par le programme. + </p> + <br /> + <p> + <form t:type="form" t:id="filterPeriodForContactStates"> + <label>Période : </label> + <t:label t:for="periodBeginForContactStates" /> + <input t:type="datefield" class="width70" t:id="periodBeginForContactStates" t:value="periodForContactStates.fromDate" t:format="MM/yyyy"/> + <t:label t:for="periodEndForContactStates" /> + <input t:type="datefield" class="width70" t:id="periodEndForContactStates" t:value="periodForContactStates.thruDate" t:format="MM/yyyy" /> + <input t:type="submit" class="ico search-32px" t:id="searchForContactStates" value="Search" + title="Rechercher les états des contacts sur cette période"/> + </form> + </p> + <table class="t-data-grid"> + <thead> + <tr> + <t:if t:test="user.admin"> + <th class="company">Société</th> + </t:if> + <th t:type="loop" t:source="contactStates" t:value="contactState"> + ${contactState} + </th> + <th>Total</th> + </tr> + </thead> + <tbody> + <tr t:type="loop" t:source="contactStateStatistics" t:value="contactStateStats"> + <t:if t:test="user.admin"> + <td class="company">${contactStateStats.companyName}</td> + </t:if> + <td t:type="loop" t:source="contactStates" t:value="contactState" + class="number-contactstate"> + <t:if t:test="nbContactStates"> + ${nbContactStates} + (<span t:type="output" t:value="percentContactStates" t:format="percentFormat">${percentContactStates}</span>) + <p:else> + - + </p:else> + </t:if> + </td> + <td>${contactStateStats.total}</td> + </tr> + </tbody> + </table> + </div> + </t:block> </t:layout> Modified: trunk/suiviobsmer-ui/src/main/webapp/css/synthesis.css =================================================================== --- trunk/suiviobsmer-ui/src/main/webapp/css/synthesis.css 2010-02-10 15:43:48 UTC (rev 344) +++ trunk/suiviobsmer-ui/src/main/webapp/css/synthesis.css 2010-02-10 22:04:27 UTC (rev 345) @@ -54,7 +54,7 @@ div#so-synthesis-menu { border-right: 2px solid #133852; padding-left: 15px; - height: 400px; + height: 406px; width: 10%; } @@ -82,6 +82,13 @@ font-weight: bold; } +.number-contactstate { + color: #007CC2; + text-align: center; + font-size: 1em; + font-weight: bold; +} + div#so-datasampling fieldset { width: 70%; margin-left: auto; @@ -106,23 +113,37 @@ border: 1px solid #133852; } -div#so-noncomplianceboarding table { +div#so-synthesis div.ind-table table { margin-top: 20px; - width: 60%; margin-left: auto; margin-right: auto; } -div#so-noncomplianceboarding td { - padding: 3px; -} - -div#so-noncomplianceboarding table thead tr { +div#so-synthesis div.ind-table table thead tr { background-color: #19a28d; font-weight: bold; } -div#so-noncomplianceboarding table td.company { +/*div#so-synthesis div.ind-table table td { + padding: 3px; +}*/ + +div#so-synthesis div.ind-table table th.company, +div#so-synthesis div.ind-table table td.company { text-align: left; } +div#so-noncomplianceboarding table { + width: 50%; +} + +div#so-contactstate table { + width: 90%; +} + +div#so-contactstate table thead th { + width: 100px; + white-space: normal; + text-align: center; +} + Added: trunk/suiviobsmer-ui/src/main/webapp/img/synthesis-ind-contact-states.png =================================================================== (Binary files differ) Property changes on: trunk/suiviobsmer-ui/src/main/webapp/img/synthesis-ind-contact-states.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream