Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
In progress: creating version locale statistic cache, new statistic m…
Browse files Browse the repository at this point in the history
…odal
  • Loading branch information
Alex Eng committed Oct 23, 2013
1 parent e2a19d1 commit 1c1b39f
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 37 deletions.
25 changes: 5 additions & 20 deletions zanata-war/src/main/java/org/zanata/dao/DocumentDAO.java
Expand Up @@ -272,7 +272,7 @@ public Map<LocaleId, ContainerTranslationStatistics> getStatistics(
new HashMap<String, TransUnitWords>();

StringBuilder query = new StringBuilder();
query.append("select new map (tft.state as state, count(tft) as count, ");
query.append("select new map (tft.state as state, count(tft) as msgCount, ");
query.append(" sum(tft.textFlow.wordCount) as wordCount, "
+ "tft.locale.localeId as locale) ");
query.append("from HTextFlowTarget tft ");
Expand Down Expand Up @@ -300,7 +300,7 @@ public Map<LocaleId, ContainerTranslationStatistics> getStatistics(
// Collect the results for all states
for (Map<String, Object> row : stats) {
ContentState state = (ContentState) row.get("state");
Long count = (Long) row.get("count");
Long msgCount = (Long) row.get("msgCount");
Long wordCount = (Long) row.get("wordCount");
LocaleId localeId = (LocaleId) row.get("locale");

Expand All @@ -318,33 +318,18 @@ public Map<LocaleId, ContainerTranslationStatistics> getStatistics(
transUnitWordsMap.put(localeId.getId(), transUnitWords);
}

transUnitCount.set(state, count.intValue());
transUnitCount.set(state, msgCount.intValue());
transUnitWords.set(state, wordCount.intValue());
}

Map<String, Object> totalCounts =
(Map<String, Object>) session
.createQuery(
"select new map ( count(tf) as count, "
+ "sum(tf.wordCount) as wordCount ) "
+ "from HTextFlow tf "
+ "where tf.document.id = :id "
+ "and tf.obsolete = false")
.setParameter("id", docId)
.setComment(
"DocumentDAO.getStatisticsMultipleLocales-words")
.setCacheable(true).uniqueResult();

// Calculate the 'New' counts
Long totalCount = (Long) totalCounts.get("count");
Long totalWordCount = (Long) totalCounts.get("wordCount");
for (TransUnitCount stat : transUnitCountMap.values()) {
stat.set(ContentState.New,
StatisticsUtil.calculateUntranslated(totalCount, stat));
StatisticsUtil.calculateUntranslated(new Long(stat.getTotal()), stat));
}
for (TransUnitWords stat : transUnitWordsMap.values()) {
stat.set(ContentState.New,
StatisticsUtil.calculateUntranslated(totalWordCount, stat));
StatisticsUtil.calculateUntranslated(new Long(stat.getTotal()), stat));
}

// Merge into a single Stats object
Expand Down
Expand Up @@ -36,9 +36,11 @@ public final class TextFlowTargetStateEvent {
public static final String EVENT_NAME =
"org.zanata.event.HTextFlowTranslated";

private final Long versionId;
private final Long documentId;
private final Long textFlowId;
private final LocaleId localeId;
private final Long textFlowTargetId;
private final ContentState newState;
private final ContentState previousState;
}
45 changes: 45 additions & 0 deletions zanata-war/src/main/java/org/zanata/service/VersionStateCache.java
@@ -0,0 +1,45 @@
/*
*
* * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the
* * @author tags. See the copyright.txt file in the distribution for a full
* * listing of individual contributors.
* *
* * This is free software; you can redistribute it and/or modify it under the
* * terms of the GNU Lesser General Public License as published by the Free
* * Software Foundation; either version 2.1 of the License, or (at your option)
* * any later version.
* *
* * This software 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 Lesser General Public License for more
* * details.
* *
* * You should have received a copy of the GNU Lesser General Public License
* * along with this software; if not, write to the Free Software Foundation,
* * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
* * site: http://www.fsf.org.
*/

package org.zanata.service;

import org.zanata.common.AbstractTranslationCount;
import org.zanata.common.LocaleId;
import org.zanata.events.TextFlowTargetStateEvent;

/**
* Defines a Cache Service for project version states.
*
* @author Alex Eng <a href="mailto:aeng@redhat.com">aeng@redhat.com</a>
*/
public interface VersionStateCache {

/**
* Informs the cache that a text flow has changed its state in a given
* locale. (It's really a Text Flow Target state)
*
*/
void textFlowStateUpdated(TextFlowTargetStateEvent event);

AbstractTranslationCount getVersionStatistic(Long versionId,
LocaleId localeId);
}
Expand Up @@ -48,6 +48,7 @@
import org.zanata.rest.service.StatisticsResource;
import org.zanata.rest.service.ZPathService;
import org.zanata.service.TranslationStateCache;
import org.zanata.service.VersionStateCache;
import org.zanata.util.DateUtil;
import org.zanata.webtrans.shared.model.DocumentStatus;

Expand Down Expand Up @@ -77,6 +78,9 @@ public class StatisticsServiceImpl implements StatisticsResource {
@In
private TranslationStateCache translationStateCacheImpl;

@In
private VersionStateCache versionStateCacheImpl;

// TODO Need to refactor this method to get Message statistic by default.
// This is to be consistance with UI which uses message stats, and for
// calculating remaining hours.
Expand Down Expand Up @@ -346,16 +350,21 @@ public ContainerTranslationStatistics getDocStatistics(Long documentId,
TranslationStatistics wordStatistics =
result.getStats(localeId.getId(), StatUnit.WORD);

double remainingHours =
getRemainingHours(wordStatistics.getDraft(),
wordStatistics.getUntranslated());
// double remainingHours =
// getRemainingHours(wordStatistics.getDraft(),
// wordStatistics.getUntranslated());

wordStatistics.setRemainingHours(remainingHours);
// wordStatistics.setRemainingHours(remainingHours);

TranslationStatistics msgStatistics =
result.getStats(localeId.getId(), StatUnit.MESSAGE);
msgStatistics.setRemainingHours(remainingHours);
msgStatistics.setRemainingHours(wordStatistics.getRemainingHours());

return result;
}

public TranslationStatistics getVersionLocaleStatistic(Long versionId,
LocaleId localeId) {
return versionStateCacheImpl.getVersionStatistic(versionId, localeId);
}
}
Expand Up @@ -293,32 +293,34 @@ private HLocale validateLocale(LocaleId localeId,
/**
* Sends out an event to signal that a Text Flow target has been translated
*/
private void signalPostTranslateEvent(HTextFlowTarget hTextFlowTarget) {
private void signalPostTranslateEvent(HTextFlowTarget hTextFlowTarget,
ContentState oldState) {
if (Events.exists()) {
HTextFlow textFlow = hTextFlowTarget.getTextFlow();
HDocument document = textFlow.getDocument();
Long documentId = textFlow.getDocument().getId();
Long versionId =
textFlow.getDocument().getProjectIteration().getId();
// TODO remove hasError from DocumentStatus, so that we can pass
// everything else directly to cache
// DocumentStatus docStatus = new DocumentStatus(
// new DocumentId(document.getId(), document.getDocId()), hasError,
// hTextFlowTarget.getLastChanged(),
// hTextFlowTarget.getLastModifiedBy().getAccount().getUsername());

Events.instance()
.raiseTransactionSuccessEvent(
TextFlowTargetStateEvent.EVENT_NAME,
new TextFlowTargetStateEvent(document.getId(),
textFlow.getId(), hTextFlowTarget
.getLocale().getLocaleId(),
hTextFlowTarget.getId(), hTextFlowTarget
.getState()));
Events.instance().raiseTransactionSuccessEvent(
TextFlowTargetStateEvent.EVENT_NAME,
new TextFlowTargetStateEvent(versionId, documentId,
textFlow.getId(), hTextFlowTarget.getLocale()
.getLocaleId(), hTextFlowTarget.getId(),
hTextFlowTarget.getState(), oldState));
}
}

private boolean translate(@Nonnull HTextFlowTarget hTextFlowTarget,
@Nonnull List<String> contentsToSave, ContentState requestedState,
int nPlurals, Boolean requireTranslationReview) {
boolean targetChanged = false;
ContentState currentState = hTextFlowTarget.getState();
targetChanged |= setContentIfChanged(hTextFlowTarget, contentsToSave);
targetChanged |=
setContentStateIfChanged(requestedState, hTextFlowTarget,
Expand All @@ -338,7 +340,7 @@ private boolean translate(@Nonnull HTextFlowTarget hTextFlowTarget,

// fire event after flush
if (targetChanged || hTextFlowTarget.getVersionNum() == 0) {
this.signalPostTranslateEvent(hTextFlowTarget);
this.signalPostTranslateEvent(hTextFlowTarget, currentState);
}

return targetChanged;
Expand Down Expand Up @@ -659,6 +661,7 @@ protected Boolean work() throws Exception {
HTextFlowTarget hTarget =
textFlowTargetDAO.getTextFlowTarget(
textFlow, hLocale);
ContentState currentState = hTarget.getState();

if (mergeType == MergeType.IMPORT) {
removedTargets.remove(hTarget);
Expand Down Expand Up @@ -713,7 +716,7 @@ protected Boolean work() throws Exception {
}
textFlowTargetDAO.makePersistent(hTarget);
}
signalPostTranslateEvent(hTarget);
signalPostTranslateEvent(hTarget, currentState);
}

personDAO.flush();
Expand Down
@@ -0,0 +1,139 @@
/*
*
* * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the
* * @author tags. See the copyright.txt file in the distribution for a full
* * listing of individual contributors.
* *
* * This is free software; you can redistribute it and/or modify it under the
* * terms of the GNU Lesser General Public License as published by the Free
* * Software Foundation; either version 2.1 of the License, or (at your option)
* * any later version.
* *
* * This software 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 Lesser General Public License for more
* * details.
* *
* * You should have received a copy of the GNU Lesser General Public License
* * along with this software; if not, write to the Free Software Foundation,
* * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
* * site: http://www.fsf.org.
*/

package org.zanata.service.impl;

import java.io.Serializable;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import net.sf.ehcache.CacheManager;

import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Observer;
import org.jboss.seam.annotations.Scope;
import org.zanata.cache.CacheWrapper;
import org.zanata.cache.EhcacheWrapper;
import org.zanata.common.AbstractTranslationCount;
import org.zanata.common.LocaleId;
import org.zanata.common.TransUnitWords;
import org.zanata.dao.ProjectIterationDAO;
import org.zanata.events.TextFlowTargetStateEvent;
import org.zanata.rest.dto.stats.TranslationStatistics;
import org.zanata.service.VersionStateCache;

import com.google.common.cache.CacheLoader;

/**
* @author Alex Eng <a href="mailto:aeng@redhat.com">aeng@redhat.com</a>
*/
@Name("versionStateCacheImpl")
@Scope(ScopeType.APPLICATION)
public class VersionStateCacheImpl implements VersionStateCache {
private static final String BASE = VersionStateCacheImpl.class.getName();

private static final String VERSION_STATISTIC_CACHE_NAME = BASE
+ ".versionStatisticCache";

private CacheManager cacheManager;

private CacheWrapper<VersionLocaleKey, AbstractTranslationCount> versionStatisticCache;
private CacheLoader<VersionLocaleKey, AbstractTranslationCount> versionStatisticLoader;

public VersionStateCacheImpl() {
// constructor for Seam
this.versionStatisticLoader = new VersionStatisticLoader();
}

@Create
public void create() {
cacheManager = CacheManager.create();
versionStatisticCache =
EhcacheWrapper.create(VERSION_STATISTIC_CACHE_NAME,
cacheManager, versionStatisticLoader);
}

@Destroy
public void destroy() {
cacheManager.shutdown();
}

@Observer(TextFlowTargetStateEvent.EVENT_NAME)
@Override
public void textFlowStateUpdated(TextFlowTargetStateEvent event) {
VersionLocaleKey key =
new VersionLocaleKey(event.getVersionId(), event.getLocaleId());
AbstractTranslationCount stats = versionStatisticCache.get(key);

if (stats != null) {
stats.decrement(event.getPreviousState(), 1);
stats.increment(event.getNewState(), 1);
versionStatisticCache.put(key, stats);
}
}

@Override
public AbstractTranslationCount getVersionStatistic(Long versionId,
LocaleId localeId) {
return versionStatisticCache.getWithLoader(new VersionLocaleKey(
versionId, localeId));
}

private static class VersionStatisticLoader extends
CacheLoader<VersionLocaleKey, AbstractTranslationCount> {

ProjectIterationDAO getProjectIterationDAO() {
return (ProjectIterationDAO) Component
.getInstance(ProjectIterationDAO.class);
}

@Override
public AbstractTranslationCount load(VersionLocaleKey key)
throws Exception {

TransUnitWords transUnitWords =
getProjectIterationDAO().getWordStatsForContainer(
key.getVersionId(), key.getLocaleId());

// return new AbstractTranslationCount(transUnitWords, key.getLocaleId()
// .getId());
return null; //TODO: Create new stats modal (memory efficiency).
}
}

@AllArgsConstructor
@EqualsAndHashCode
public static final class VersionLocaleKey implements Serializable {
private static final long serialVersionUID = 1L;

@Getter
private Long versionId;

@Getter
private LocaleId localeId;
}
}
11 changes: 11 additions & 0 deletions zanata-war/src/main/resources/ehcache.xml
Expand Up @@ -80,4 +80,15 @@
statistics="true"
/>

<cache
name="org.zanata.service.impl.VersionStateCacheImpl.versionStatisticCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="86400"
timeToLiveSeconds="0"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"
statistics="true"
/>

</ehcache>

0 comments on commit 1c1b39f

Please sign in to comment.