You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ctakes.apache.org by se...@apache.org on 2017/11/16 20:03:53 UTC

svn commit: r1815509 - /ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/PatientNoteStore.java

Author: seanfinan
Date: Thu Nov 16 20:03:53 2017
New Revision: 1815509

URL: http://svn.apache.org/viewvc?rev=1815509&view=rev
Log:
Rewrite of Patient view cache

Modified:
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/PatientNoteStore.java

Modified: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/PatientNoteStore.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/PatientNoteStore.java?rev=1815509&r1=1815508&r2=1815509&view=diff
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/PatientNoteStore.java (original)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/PatientNoteStore.java Thu Nov 16 20:03:53 2017
@@ -11,6 +11,7 @@ import org.apache.uima.fit.factory.JCasF
 import org.apache.uima.jcas.JCas;
 import org.apache.uima.util.CasCopier;
 
+import javax.annotation.concurrent.Immutable;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -32,41 +33,82 @@ public enum PatientNoteStore {
    static private final Logger LOGGER = Logger.getLogger( "PatientNoteStore" );
 
    private final Map<String, JCas> _patientMap;
+   private final Map<String, Collection<StoreViewInfo>> _patientViewInfo;
    private final Map<String, Integer> _wantedDocCounts;
-   private final Map<String, Integer> _storedDocCounts;
+
    private String _currentPatientName;
    private String _previousPatientName;
 
+   /**
+    * private
+    */
    PatientNoteStore() {
       _patientMap = new HashMap<>();
+      _patientViewInfo = new HashMap<>();
       _wantedDocCounts = new HashMap<>();
-      _storedDocCounts = new HashMap<>();
+   }
+
+   /////////////////    Get available patient, document, view names   ///////////////
+
+   /**
+    * @return identifiers for all stored patients
+    */
+   synchronized public Collection<String> getStoredPatientIds() {
+      return _patientMap.keySet().stream()
+            .sorted()
+            .collect( Collectors.toList() );
    }
 
    /**
-    * @return all patient identifiers in the cache
+    * @param patientId -
+    * @return identifiers for all stored documents for the given patient
     */
-   synchronized public Collection<String> getPatientIds() {
-      return Collections.unmodifiableList( new ArrayList<>( _patientMap.keySet() ) );
+   synchronized public Collection<String> getStoredDocIds( final String patientId ) {
+      return getViewInfos( patientId ).stream()
+            .map( StoreViewInfo::getDocId )
+            .sorted()
+            .collect( Collectors.toList() );
    }
 
    /**
+    * @param patientId -
+    * @param docId     -
+    * @return names for all stored views for the given patient and document
+    */
+   synchronized public Collection<String> getStoredViewNames( final String patientId, final String docId ) {
+      return getViewInfos( patientId ).stream()
+            .filter( vi -> vi.getDocId().equals( docId ) )
+            .map( StoreViewInfo::getViewName )
+            .sorted()
+            .collect( Collectors.toList() );
+   }
+
+   /////////////////    Completion Information    ///////////////
+
+   /**
     * @return all completed patient identifiers in the cache
     */
    synchronized public Collection<String> getCompletedPatientIds() {
-      return _wantedDocCounts.entrySet().stream()
-            .filter( e -> _storedDocCounts.getOrDefault( e.getKey(), 0 ).equals( e.getValue() ) )
-            .map( Map.Entry::getKey )
+      return getStoredPatientIds().stream()
+            .filter( pid -> getWantedDocCount( pid ) == getStoredDocCount( pid ) )
+            .sorted()
             .collect( Collectors.toList() );
    }
 
+   /**
+    * @param patientId -
+    * @return number of documents for the patient that have been completed and stored in the cache
+    */
+   synchronized public int getStoredDocCount( final String patientId ) {
+      return getStoredDocIds( patientId ).size();
+   }
 
    /**
     *
     * @param patientId -
     * @return number of documents that exist for the patient or -1 if unknown
     */
-   synchronized public int getDocCount( final String patientId ) {
+   synchronized public int getWantedDocCount( final String patientId ) {
       return _wantedDocCounts.getOrDefault( patientId, -1 );
    }
 
@@ -75,76 +117,114 @@ public enum PatientNoteStore {
     * @param patientId -
     * @param count number of documents that exist for the patient
     */
-   synchronized public void setDocCount( final String patientId, final int count ) {
+   synchronized public void setWantedDocCount( final String patientId, final int count ) {
       _wantedDocCounts.put( patientId, count );
    }
 
+   /////////////////    Get default patient, document names   ///////////////
+
    /**
-    * @param patientId -
-    * @return number of documents for the patient that have been completed and stored in the cache
+    * @return the default identifier for a view of the document's patient.
+    * If it has been set in the document metadata then that is used,
+    * otherwise it will come from the document's parent directory.
+    * @see SourceMetadataUtil#getPatientIdentifier(JCas)
+    * @see DocumentIDAnnotationUtil#getDocumentIdPrefix(JCas)
     */
-   synchronized public int getCompletedDocCount( final String patientId ) {
-      return _storedDocCounts.getOrDefault( patientId, 0 );
+   public String getDefaultPatientId( final JCas viewCas ) {
+      final String patientIdentifier = SourceMetadataUtil.getPatientIdentifier( viewCas );
+      if ( patientIdentifier != null && !patientIdentifier.isEmpty() && !patientIdentifier.equals( SourceMetadataUtil.UNKNOWN_PATIENT ) ) {
+         return patientIdentifier;
+      }
+      return DocumentIDAnnotationUtil.getDocumentIdPrefix( viewCas );
    }
 
    /**
-    * @return the default identifier for a view of the document.  {@link DocumentIDAnnotationUtil#getDocumentID(JCas)}
+    * @return the default identifier for a view of the document.
+    * @see DocumentIDAnnotationUtil#getDocumentID(JCas)
     */
    public String getDefaultDocumentId( final JCas viewCas ) {
       return DocumentIDAnnotationUtil.getDocumentID( viewCas );
    }
 
+   /////////////////    Store Views   ///////////////
+
    /**
-    * @return the default identifier for a view of the document's patient.  {@link DocumentIDAnnotationUtil#getDocumentIdPrefix(JCas)}
+    * Store all views in the source cas.  Patient Id and Document Id will be determined from the source cas.
+    * @param sourceCas source (document) cas
     */
-   public String getDefaultPatientId( final JCas viewCas ) {
-      final String patientIdentifier = SourceMetadataUtil.getPatientIdentifier( viewCas );
-      if ( patientIdentifier != null && !patientIdentifier.isEmpty() && !patientIdentifier.equals( SourceMetadataUtil.UNKNOWN_PATIENT ) ) {
-         return patientIdentifier;
-      }
-      return DocumentIDAnnotationUtil.getDocumentIdPrefix( viewCas );
+   synchronized public void storeAllViews( final JCas sourceCas ) {
+      PatientViewUtil.getAllViewNames( sourceCas ).forEach( n -> storeView( n, sourceCas ) );
    }
 
    /**
-    * @param goldCas ye olde containing the gold in the default view
+    * Store all views in the source cas.
+    * @param patientId -
+    * @param docId -
+    * @param sourceCas source (document) cas
     */
-   synchronized public void addGoldView( final JCas goldCas ) {
-      final int goldCount = getGoldViewNames( getDefaultPatientId( goldCas ) ).size();
-      addGoldView( "" + (goldCount + 1), goldCas );
+   synchronized public void storeAllViews( final String patientId, final String docId, final JCas sourceCas ) {
+      PatientViewUtil.getAllViewNames( sourceCas ).forEach( n -> storeView( patientId, docId, n, sourceCas ) );
    }
 
    /**
-    * @param goldName name to use for the cached gold view
-    * @param goldCas  ye olde containing the document in the default view
+    * Store the primary view under some different name.  Patient Id and Document Id will be determined from the source cas.
+    * @param storeViewName the name to use to store the primary view
+    * @param sourceCas source (document) cas
     */
-   synchronized public void addGoldView( final String goldName, final JCas goldCas ) {
-      addDocument( PatientViewUtil.GOLD_PREFIX + "_" + goldName, goldCas );
+   synchronized public void storePrimaryAsView( final String storeViewName, final JCas sourceCas ) {
+      storePrimaryAsView( getDefaultPatientId( sourceCas ), getDefaultDocumentId( sourceCas ),
+            storeViewName, sourceCas );
    }
 
    /**
-    * @param documentCas ye olde containing the document in the default view
+    * Store the primary view under some different name.
+    * @param patientId -
+    * @param docId -
+    * @param storeViewName the name to use to store the primary view
+    * @param sourceCas source (document) cas
     */
-   synchronized public void addDocument( final JCas documentCas ) {
-      addDocument( getDefaultDocumentId( documentCas ), documentCas );
+   synchronized public void storePrimaryAsView( final String patientId, final String docId, final String storeViewName,
+                                                final JCas sourceCas ) {
+      storeView( patientId, docId, storeViewName, PatientViewUtil.DEFAULT_VIEW, sourceCas );
    }
 
    /**
-    * @param viewName    name to use for the cached document view
-    * @param documentCas ye olde containing the document in the default view
+    * Store some single view with its own name.  Patient Id and Document Id will be determined from the source cas.
+    *
+    * @param sourceViewName the name of the view in the source cas
+    * @param sourceCas      source (document) cas
     */
-   synchronized public void addDocument( final String viewName, final JCas documentCas ) {
-      addDocument( getDefaultPatientId( documentCas ), viewName, documentCas );
+   synchronized public void storeView( final String sourceViewName, final JCas sourceCas ) {
+      storeView( getDefaultPatientId( sourceCas ), getDefaultDocumentId( sourceCas ),
+            sourceViewName, sourceViewName, sourceCas );
    }
 
    /**
-    * @param patientId name to use for the cached patient cas
-    * @param viewName    name to use for the cached document view
-    * @param documentCas ye olde containing the document in the default view
+    * Explicitly store some single view with its own name.
+    *
+    * @param patientId      -
+    * @param docId          -
+    * @param sourceViewName the name of the view in the source cas
+    * @param sourceCas      source (document) cas
     */
-   synchronized public void addDocument( final String patientId, final String viewName, final JCas documentCas ) {
-      if ( !patientId.equals( _currentPatientName ) ) {
-         _previousPatientName = _currentPatientName;
-         _currentPatientName = patientId;
+   synchronized public void storeView( final String patientId, final String docId, final String sourceViewName, final JCas sourceCas ) {
+      storeView( patientId, docId, sourceViewName, sourceViewName, sourceCas );
+   }
+
+   /**
+    * Explicitly store some single view under a different name.
+    *
+    * @param patientId      -
+    * @param docId          -
+    * @param storeViewName  the name to use to store the view
+    * @param sourceViewName the name of the view in the source cas
+    * @param sourceCas      source (document) cas
+    */
+   synchronized public void storeView( final String patientId, final String docId, final String storeViewName,
+                                       final String sourceViewName, final JCas sourceCas ) {
+      if ( getStoredViewNames( patientId, docId ).contains( storeViewName ) ) {
+         LOGGER.warn( "View already stored as " + patientId + " " + docId + " " + storeViewName );
+         LOGGER.warn( "Previously stored view will be replaced." );
       }
       // don't use putIfAbsent or computeIfAbsent to better handle exceptions and lazy instantiation
       JCas patientCas = _patientMap.get( patientId );
@@ -157,104 +237,166 @@ public enum PatientNoteStore {
             return;
          }
       }
-      LOGGER.info( "Caching " + viewName + " for patient " + patientId + " ..." );
+      // Cache view into patient using encoded view name
+      LOGGER.info( "Caching view for" + patientId + " " + docId + " " + sourceViewName
+            + (sourceViewName.equals( storeViewName ) ? "" : " as " + storeViewName) + " ..." );
+      final StoreViewInfo storeViewInfo = new StoreViewInfo( patientId, docId, storeViewName );
       try {
-         final JCas mainView = documentCas.getView( PatientViewUtil.DEFAULT_VIEW );
-         final CasCopier copier = new CasCopier( documentCas.getCas(), patientCas.getCas() );
-         copier.copyCasView( mainView.getCas(), viewName, true );
-         final int stored = _storedDocCounts.getOrDefault( patientId, 0 );
-         _storedDocCounts.put( patientId, stored + 1 );
+         final JCas sourceView = sourceCas.getView( sourceViewName );
+         final CasCopier copier = new CasCopier( sourceCas.getCas(), patientCas.getCas() );
+         copier.copyCasView( sourceView.getCas(), storeViewInfo.getViewName(), true );
+         _patientViewInfo.putIfAbsent( patientId, new ArrayList<>() );
+         _patientViewInfo.get( patientId ).add( storeViewInfo );
       } catch ( CASException | CASRuntimeException casE ) {
          LOGGER.error( casE.getMessage() );
       }
    }
 
-   /**
-    * @param patientId identifier of patient
-    * @return cached cas representing patient with documents and gold as views, or null if none
-    */
-   synchronized public JCas getPatientCas( final String patientId ) {
-      return _patientMap.get( patientId );
-   }
+   /////////////////    view fetchers   ///////////////
 
    /**
-    * @param patientId identifier of patient to remove from cache
-    */
-   synchronized public void removePatient( final String patientId ) {
-      _patientMap.remove( patientId );
-      _wantedDocCounts.remove( patientId );
-   }
-
-   /**
-    * @param patientId identifier of patient
-    * @return All views, including gold and default
+    * @param patientId -
+    * @param docId -
+    * @param viewName -
+    * @return Stored view for the parameters
     */
-   synchronized public Collection<JCas> getAllViews( final String patientId ) {
-      final JCas patientCas = getPatientCas( patientId );
+   synchronized public JCas getStoredView( final String patientId, final String docId, final String viewName ) {
+      final JCas patientCas = _patientMap.get( patientId );
       if ( patientCas == null ) {
-         return Collections.emptyList();
+         LOGGER.warn( "No patient with id " + patientId );
+         return null;
       }
-      return PatientViewUtil.getAllViews( patientCas );
+      final StoreViewInfo storeViewInfo = new StoreViewInfo( patientId, docId, viewName );
+      try {
+         return patientCas.getView( storeViewInfo.getStoreViewCode() );
+      } catch ( CASException casE ) {
+         LOGGER.error( casE.getMessage() );
+      }
+      return null;
    }
 
    /**
-    * @param patientId identifier of patient
-    * @return All document views, which are views that are not the default and not gold
+    * @param patientId -
+    * @param docId     -
+    * @return Map of ViewNames to Views
     */
-   synchronized public Collection<JCas> getDocumentViews( final String patientId ) {
-      final JCas patientCas = getPatientCas( patientId );
+   synchronized public Map<String, JCas> getStoredViews( final String patientId, final String docId ) {
+      final JCas patientCas = _patientMap.get( patientId );
       if ( patientCas == null ) {
-         return Collections.emptyList();
+         LOGGER.warn( "No patient with id " + patientId );
+         return null;
+      }
+      final Collection<String> viewNames = getStoredViewNames( patientId, docId );
+      final Map<String, JCas> viewMap = new HashMap<>();
+      try {
+         for ( String viewName : viewNames ) {
+            final StoreViewInfo viewInfo = new StoreViewInfo( viewName );
+            viewMap.put( viewInfo.getViewName(), patientCas.getView( viewName ) );
+         }
+      } catch ( CASException casE ) {
+         LOGGER.error( casE.getMessage() );
       }
-      return PatientViewUtil.getDocumentViews( patientCas );
+      return viewMap;
    }
 
    /**
-    * @param patientId identifier of patient
-    * @return All gold views, which are views with the prefix {@link PatientViewUtil#GOLD_PREFIX}
+    * @param patientId -
+    * @return Map of docIds to Map of ViewNames to Views
     */
-   synchronized public Collection<JCas> getGoldViews( final String patientId ) {
-      final JCas patientCas = getPatientCas( patientId );
-      if ( patientCas == null ) {
-         return Collections.emptyList();
+   synchronized public Map<String, Map<String, JCas>> getStoredViews( final String patientId ) {
+      final Map<String, Map<String, JCas>> viewMap = new HashMap<>();
+      final Collection<String> docIds = getStoredDocIds( patientId );
+      for ( String docId : docIds ) {
+         viewMap.put( docId, getStoredViews( patientId, docId ) );
       }
-      return PatientViewUtil.getGoldViews( patientCas );
+      return viewMap;
    }
 
+   /////////////////    patient cleanup - careful !   ///////////////
+
    /**
-    * @param patientId identifier of patient
-    * @return Names of all views, including gold and default
+    * @param patientId identifier of patient to remove from cache
     */
-   synchronized public Collection<String> getAllViewNames( final String patientId ) {
-      final JCas patientCas = getPatientCas( patientId );
-      if ( patientCas == null ) {
-         return Collections.emptyList();
-      }
-      return PatientViewUtil.getAllViewNames( patientCas );
+   synchronized public void removePatient( final String patientId ) {
+      _patientMap.remove( patientId );
+      _patientViewInfo.remove( patientId );
+      _wantedDocCounts.remove( patientId );
    }
 
+   /////////////////    Encoding for cached patient view names   ///////////////
+
    /**
-    * @param patientId identifier of patient
-    * @return Names of all document views, which are views that are not the default and not gold
+    * @param patientId -
+    * @return
     */
-   synchronized public Collection<String> getDocumentViewNames( final String patientId ) {
-      final JCas patientCas = getPatientCas( patientId );
-      if ( patientCas == null ) {
+   synchronized private Collection<StoreViewInfo> getViewInfos( final String patientId ) {
+      final Collection<StoreViewInfo> storeViewInfos = _patientViewInfo.get( patientId );
+      if ( storeViewInfos == null ) {
+         LOGGER.debug( "No patient with id " + patientId );
          return Collections.emptyList();
       }
-      return PatientViewUtil.getDocumentViewNames( patientCas );
+      return storeViewInfos;
    }
 
    /**
-    * @param patientId identifier of patient
-    * @return Names of all gold views, which are views with the prefix {@link PatientViewUtil#GOLD_PREFIX}
+    * Used to map pid, docId, view names to views for each patient.
     */
-   synchronized public Collection<String> getGoldViewNames( final String patientId ) {
-      final JCas patientCas = getPatientCas( patientId );
-      if ( patientCas == null ) {
-         return Collections.emptyList();
+   @Immutable
+   static private final class StoreViewInfo {
+      private final String _pid;
+      private final String _docId;
+      private final String _viewName;
+
+      private StoreViewInfo( final String storeViewCode ) {
+         this( getId( storeViewCode, "pid" ),
+               getId( storeViewCode, "docId" ),
+               getId( storeViewCode, "viewName" ) );
+      }
+
+      private StoreViewInfo( final String pid, final String docId, final String viewName ) {
+         _pid = pid;
+         _docId = docId;
+         _viewName = viewName;
+      }
+
+      public String getPid() {
+         return _pid;
+      }
+
+      public String getDocId() {
+         return _docId;
+      }
+
+      public String getViewName() {
+         return _viewName;
+      }
+
+      public String getStoreViewCode() {
+         return "<pid>" + _pid + "</pid><docId>" + _docId + "</docId><viewName>" + _viewName + "</viewName>";
+      }
+
+      @Override
+      public boolean equals( final Object object ) {
+         return object instanceof StoreViewInfo && ((StoreViewInfo) object).getStoreViewCode().equals( getStoreViewCode() );
+      }
+
+      @Override
+      public int hashCode() {
+         return getStoreViewCode().hashCode();
+      }
+
+      static private String getId( final String storeViewCode, final String tag ) {
+         final int i1 = storeViewCode.indexOf( "<" + tag + ">" );
+         if ( i1 < 0 ) {
+            return storeViewCode;
+         }
+         final int i2 = storeViewCode.indexOf( "</" + tag + ">" );
+         if ( i2 < 0 ) {
+            return storeViewCode;
+         }
+         return storeViewCode.substring( i1 + 2 + tag.length(), i2 );
       }
-      return PatientViewUtil.getGoldViewNames( patientCas );
    }
 
+
 }