001package com.astrolabsoftware.FinkBrowser.Januser;
002
003import com.Lomikel.HBaser.HBaseClient;
004import com.Lomikel.Utils.LomikelException;
005import com.astrolabsoftware.FinkBrowser.FinkPortalClient.FPC;
006import com.astrolabsoftware.FinkBrowser.HBaser.Clusteriser.ClusterFinder;
007
008// org.json
009import org.json.JSONArray;
010import org.json.JSONObject;
011
012// Java
013import java.util.Arrays;
014import java.util.Set;
015import java.util.TreeSet;
016import java.util.Map;
017import java.util.TreeMap;
018import java.io.IOException;
019
020// Log4J
021import org.apache.logging.log4j.Logger;
022import org.apache.logging.log4j.LogManager;
023
024/** <code>FeaturesClassifier</code> classifies sources according to
025  * HBase <tt>lc_features_*</tt> field.
026  * @opt attributes
027  * @opt operations
028  * @opt types
029  * @opt visibility
030  * @author <a href="mailto:Julius.Hrivnac@cern.ch">J.Hrivnac</a> */
031// BUG: jd should be String or long
032public class FeaturesClassifier implements Classifier {
033  
034  @Override
035  public void classify(FinkGremlinRecipies recipies,
036                       String              oid,
037                       boolean             enhance,
038                       String              columns) throws LomikelException {
039    log.info(oid);
040    double jd;
041    String cl;
042    Map<String, String> value;
043    String[] featuresS;
044    double[] featuresD;
045    String fg;
046    String fr;
047    Map<String, Set<Double>> classes; // cl -> [jd]
048    Set<Double> jds;
049    String key;
050    Set<Double> val;
051    double weight;
052    double totalWeight;
053    Map<String, Map<String, String>> alerts = recipies.fhclient().scan(null,
054                                                                       "key:key:" + oid + ":prefix",
055                                                                       "i:jd,d:lc_features_g,d:lc_features_r",
056                                                                       0,
057                                                                       0,
058                                                                       false,
059                                                                       false);
060    classes = new TreeMap<>();
061    // get all alerts (jd) and their classses
062    boolean isClassified = false;
063    for (Map.Entry<String, Map<String, String>> entry : alerts.entrySet()) {
064      value = entry.getValue();
065      jd = Double.parseDouble(value.get("i:jd"));
066      if (value.containsKey("d:lc_features_g") &&
067          value.containsKey("d:lc_features_r")) {
068        fg = value.get("d:lc_features_g").replaceFirst("\\[", "").replaceAll("]$", "");
069        fr = value.get("d:lc_features_r").replaceFirst("\\[", "").replaceAll("]$", "");
070        featuresS = (fg + "," + fr).replaceAll("null", "0.0").
071                                    replaceAll("NaN", "0.0").
072                                    split(",");
073        featuresD = Arrays.stream(featuresS).
074                           mapToDouble(Double::parseDouble).
075                           toArray();
076        cl = String.valueOf(finder().transformAndPredict(featuresD));                  
077        if (!cl.equals("-1")) {
078          if (classes.containsKey(cl)) {
079            jds = classes.get(cl);
080            jds.add(jd);
081            }
082          else {
083            jds = new TreeSet<Double>();
084            jds.add(jd);
085            classes.put(cl, jds);
086            }
087          }
088        isClassified = true;
089        }
090      else {
091        //log.warn("Alert " + entry.getKey() + " has no features");
092        }
093      }
094    totalWeight = 0;
095    for (Map.Entry<String, Set<Double>> cls : classes.entrySet()) {
096      totalWeight += cls.getValue().size();
097      }
098    for (Map.Entry<String, Set<Double>> cls : classes.entrySet()) {
099      key = "FC-" + cls.getKey();
100      val = cls.getValue();
101      weight = val.size() / totalWeight;
102      recipies.registerSourcesOfInterest(Classifiers.FEATURES, key, oid, weight, val, enhance, columns);
103      }
104    if (!isClassified) {
105      log.warn("Source " + oid + " cannot be classified because his alerts have no LC features");
106      }
107    }
108    
109  /** Give {@link ClusterFinder} to current database. Singleton.
110    * @return The corresponding {@link ClusterFinder}. 
111    * @throws LomikelExceltion If {@link ClusterFinder} cannot be created. */
112  private ClusterFinder finder() throws LomikelException {
113    if (_finder == null || _reset) {
114      if (_resourceName == null && _dirName == null) {
115        _resourceName = "Clusters/2025/13-50-known";
116        }
117      try {
118        if (_resourceName != null) {
119          ClassLoader classLoader = getClass().getClassLoader();
120          _finder = new ClusterFinder(classLoader.getResource(_resourceName + "/scaler_params.json"),
121                                      classLoader.getResource(_resourceName + "/pca_params.json"),
122                                      classLoader.getResource(_resourceName + "/cluster_centers.json"));
123          }
124        else {
125          _finder = new ClusterFinder(_dirName + "/scaler_params.json",
126                                      _dirName + "/pca_params.json",
127                                      _dirName + "/cluster_centers.json");
128          }
129        _reset = false;
130        }
131      catch (IOException e) {
132        throw new LomikelException("Cannot create Cluster Finder", e);
133        }
134      }
135    return _finder;
136    }
137    
138  /** Set the directory for model json files
139    * <tt>scaler_params.json, pca_params.json, cluster_centers.json</tt>.
140    * If not set, <tt>/tmp</tt> will be used.
141    * @param dirName The directory for model json files. */
142  public static void setModelDirectory(String dirName) {
143    _dirName = dirName;
144    _reset = true;
145    }
146    
147  /** Set the resource directory for model json files
148    * <tt>scaler_params.json, pca_params.json, cluster_centers.json</tt>.
149    * If not set, {@link #setModelDirectory} will be used.
150    * @param resourceName The resource directory for model json files. */
151  public static void setModelResource(String resourceName) {
152    _resourceName = resourceName;
153    _reset = true;
154    }
155  
156  private static ClusterFinder _finder;
157  
158  private static String _dirName;
159  
160  private static String _resourceName;
161  
162  private static boolean _reset;
163
164  /** Logging . */
165  private static Logger log = LogManager.getLogger(FeaturesClassifier.class);
166  
167  }
168           
169