11package org .labkey .jbrowse ;
22
3+ import org .apache .commons .lang3 .StringUtils ;
34import org .apache .logging .log4j .Logger ;
5+ import org .jetbrains .annotations .Nullable ;
46import org .json .JSONObject ;
57import org .labkey .api .collections .CaseInsensitiveHashMap ;
68import org .labkey .api .data .Container ;
9+ import org .labkey .api .data .SimpleFilter ;
10+ import org .labkey .api .data .TableInfo ;
11+ import org .labkey .api .data .TableSelector ;
12+ import org .labkey .api .exp .api .ExpData ;
13+ import org .labkey .api .exp .api .ExpRun ;
14+ import org .labkey .api .exp .api .ExperimentService ;
715import org .labkey .api .jbrowse .DemographicsSource ;
816import org .labkey .api .jbrowse .GroupsProvider ;
917import org .labkey .api .jbrowse .JBrowseFieldCustomizer ;
1018import org .labkey .api .jbrowse .JBrowseFieldDescriptor ;
1119import org .labkey .api .jbrowse .JBrowseService ;
20+ import org .labkey .api .module .ModuleLoader ;
1221import org .labkey .api .pipeline .PipeRoot ;
1322import org .labkey .api .pipeline .PipelineJobException ;
1423import org .labkey .api .pipeline .PipelineService ;
1524import org .labkey .api .pipeline .PipelineValidationException ;
25+ import org .labkey .api .query .FieldKey ;
26+ import org .labkey .api .query .QueryService ;
27+ import org .labkey .api .reader .Readers ;
1628import org .labkey .api .security .User ;
29+ import org .labkey .api .sequenceanalysis .SequenceOutputFile ;
30+ import org .labkey .api .util .PageFlowUtil ;
1731import org .labkey .api .util .logging .LogHelper ;
1832import org .labkey .jbrowse .model .JBrowseSession ;
1933import org .labkey .jbrowse .model .JsonFile ;
34+ import org .labkey .jbrowse .pipeline .IndexVariantsStep ;
2035import org .labkey .jbrowse .pipeline .JBrowseLucenePipelineJob ;
2136import org .labkey .jbrowse .pipeline .JBrowseSessionPipelineJob ;
2237
38+ import java .io .BufferedReader ;
2339import java .io .File ;
40+ import java .io .IOException ;
2441import java .util .ArrayList ;
2542import java .util .Collection ;
2643import java .util .Collections ;
3148import java .util .Map ;
3249import java .util .Set ;
3350import java .util .TreeSet ;
51+ import java .util .concurrent .atomic .AtomicReference ;
3452
3553/**
3654 * Created by bimber on 11/3/2016.
@@ -44,9 +62,11 @@ public class JBrowseServiceImpl extends JBrowseService
4462 private final List <GroupsProvider > _providers = new ArrayList <>();
4563 private final List <JBrowseFieldCustomizer > _customizers = new ArrayList <>();
4664
65+ private final List <LuceneIndexDetector > _detectors = new ArrayList <>();
66+
4767 private JBrowseServiceImpl ()
4868 {
49-
69+ this . registerLuceneIndexDetector ( new DefaultLuceneIndexDetector ());
5070 }
5171
5272 public static JBrowseServiceImpl get ()
@@ -148,9 +168,8 @@ public Map<String, Map<String, Object>> resolveSubjects(List<String> subjects, U
148168
149169 public void customizeField (User u , Container c , JBrowseFieldDescriptor field ) {
150170 // NOTE: providers will be registered on module startup, which will be in dependency order.
151- // Process them here in reverse dependency order, so we prioritize end modules
171+ // Process them here in this order, so end modules can override earlier ones:
152172 List <JBrowseFieldCustomizer > customizers = new ArrayList <>(_customizers );
153- Collections .reverse (customizers );
154173 for (JBrowseFieldCustomizer fc : customizers ) {
155174 if (fc .isAvailable (c , u )) {
156175 fc .customizeField (field , c , u );
@@ -265,4 +284,142 @@ public Map<String, String> getDemographicsFields(User u, Container c)
265284
266285 return ret ;
267286 }
287+
288+ @ Override
289+ public SequenceOutputFile findMatchingLuceneIndex (SequenceOutputFile vcfFile , List <String > infoFieldsToIndex , User u , @ Nullable Logger log ) throws PipelineJobException
290+ {
291+ // NOTE: These are registered in module dependency order, so process in reverse:
292+ List <LuceneIndexDetector > detectors = new ArrayList <>(_detectors );
293+ Collections .reverse (detectors );
294+ for (LuceneIndexDetector li : detectors )
295+ {
296+ if (li .isAvailable (vcfFile .getContainerObj ()))
297+ {
298+ SequenceOutputFile so = li .findMatchingLuceneIndex (vcfFile , infoFieldsToIndex , u , log );
299+ if (so != null )
300+ {
301+ return so ;
302+ }
303+ }
304+ }
305+
306+ return null ;
307+ }
308+
309+ @ Override
310+ public void registerLuceneIndexDetector (LuceneIndexDetector detector )
311+ {
312+ _detectors .add (detector );
313+ }
314+
315+ public static final class DefaultLuceneIndexDetector implements LuceneIndexDetector
316+ {
317+ @ Override
318+ public SequenceOutputFile findMatchingLuceneIndex (SequenceOutputFile vcfFile , List <String > infoFieldsToIndex , User u , @ Nullable Logger log ) throws PipelineJobException
319+ {
320+ if (vcfFile .getContainerObj () == null )
321+ {
322+ return null ;
323+ }
324+
325+ // This forces the index and VCF outputs to live in the same workbook:
326+ TableInfo ti = QueryService .get ().getUserSchema (u , vcfFile .getContainerObj (), JBrowseSchema .SEQUENCE_ANALYSIS ).getTable ("outputfiles" );
327+ SimpleFilter filter = new SimpleFilter (FieldKey .fromString ("category" ), IndexVariantsStep .CATEGORY );
328+ AtomicReference <SequenceOutputFile > idxDir = new AtomicReference <>();
329+ new TableSelector (ti , PageFlowUtil .set ("rowid" ), filter , null ).forEachResults (rs -> {
330+ SequenceOutputFile so = SequenceOutputFile .getForId (rs .getInt (FieldKey .fromString ("rowid" )));
331+ if (so .getFile () == null || !so .getFile ().exists ())
332+ {
333+ log .error ("Sequence output lacks a file: " + so .getRowid ());
334+ return ;
335+ }
336+
337+ if (so .getRunId () == null )
338+ {
339+ return ;
340+ }
341+
342+ ExpRun run = ExperimentService .get ().getExpRun (so .getRunId ());
343+ if (run == null )
344+ {
345+ return ;
346+ }
347+
348+ Map <ExpData , String > inputMap = run .getDataInputs ();
349+ if (inputMap == null )
350+ {
351+ return ;
352+ }
353+
354+ for (ExpData d : inputMap .keySet ())
355+ {
356+ if (!"Input VCF" .equals (inputMap .get (d )))
357+ {
358+ continue ;
359+ }
360+
361+ if (d .getFile () == null || !d .getFile ().exists ())
362+ {
363+ continue ;
364+ }
365+
366+ if (vcfFile .getFile ().getAbsoluteFile ().equals (d .getFile ().getAbsoluteFile ()))
367+ {
368+ File fieldsFile = new File (d .getFile ().getParentFile (), "fieldList.txt" );
369+ if (!fieldsFile .exists ())
370+ {
371+ continue ;
372+ }
373+
374+ List <String > fields = new ArrayList <>();
375+ try (BufferedReader reader = Readers .getReader (fieldsFile ))
376+ {
377+ String line ;
378+ while ((line = reader .readLine ()) != null )
379+ {
380+ line = StringUtils .trimToNull (line );
381+ if (line != null )
382+ {
383+ fields .add (line );
384+ }
385+ }
386+ }
387+ catch (IOException e )
388+ {
389+ if (log != null )
390+ {
391+ log .error ("Unable to read fieldList.txt for: " + d .getFile ().getPath (), e );
392+ continue ;
393+ }
394+ }
395+
396+ if (!infoFieldsToIndex .equals (fields ))
397+ {
398+ if (log != null )
399+ {
400+ log .info ("Partial index match found, but fields to index do not match: " + d .getFile ().getPath ());
401+ }
402+ continue ;
403+ }
404+
405+ if (log != null )
406+ {
407+ log .debug ("Identified pre-existing lucene index: " + so .getFile ().getPath ());
408+ }
409+
410+ idxDir .set (so );
411+ break ;
412+ }
413+ }
414+ });
415+
416+ return idxDir .get ();
417+ }
418+
419+ @ Override
420+ public boolean isAvailable (Container c )
421+ {
422+ return c .getActiveModules ().contains (ModuleLoader .getInstance ().getModule (JBrowseModule .class ));
423+ }
424+ }
268425}
0 commit comments