<commit_before>/** @jsx React.DOM */
"use strict";
var _ = require("underscore");
var $ = require("jquery");

var ClusterStrainsWorker = require("./cluster_strains.jsx");
var staticStrainMetadata = require("./strain_metadata");
var work = require("webworkify");

var LOCI_SEARCH_BASE_URL = "/search_sequence_objects";
var LOCUS_SHOW_BASE_URL = "/get_sequence_object";
var REFERENCE_STRAIN_ID = 1;

// internal data, initial state
var isApiError = false;
var allLociData = [];
var allLociTotal = 0;
var totalLoci = 0;
var filteredlociData = [];
var clusteredStrainData = null;
var strainClusterIndexes = null;
var isProteinMode = false;
var query = "";
var strainMetaData = [];
var visibleLocusData = null;
var visibleStrainIds = null;
var heatmapZoom = 16; // px per node
var sortBy = "position"; // or "entropy"

module.exports = class VariantViewerStore {

	// *** mutators ***
	setQuery (newQuery) {
		query = newQuery;
		return;
	}

	// cb(err)
	setVisibleStrainIds (_visibleStrainIds, cb) {
		// must contain reference
		if (_visibleStrainIds.indexOf(REFERENCE_STRAIN_ID) < 0) _visibleStrainIds.push(REFERENCE_STRAIN_ID);
		_visibleStrainIds = _.sortBy(_visibleStrainIds, d => { return d.id });
		visibleStrainIds = _visibleStrainIds;
		if (typeof cb === "function") return this.clusterStrains(cb);
		return;
	}

	setIsProteinMode (_isProteinMode) { isProteinMode = _isProteinMode; return; }

	setSortBy (_sortBy) {
		sortBy = _sortBy;
		this.sortLoci();
		return;
	}

	sortLoci () {
		var sortFn;
		// reference genetic position
		if (sortBy === "position") {
			sortFn = d => {
				return d.absolute_genetic_start;
			};
		// entropy
		} else {
			sortFn = (d, _i) => {
				var n;
				var sigma = 0;
				for (var i = d.dna_scores.length - 1; i >= 0; i--) {
					n = d.dna_scores[i];
					if(typeof n === "number") sigma += n * (Math.log(n) / Math.log(2));
				};
				return sigma;
			};
		}
		allLociData = _.sortBy(allLociData, sortFn);
		filteredlociData = _.sortBy(filteredlociData, sortFn);
		return;
	}

	zoomHeatmap (isIn) {
		heatmapZoom += isIn;
		heatmapZoom = Math.max(1, heatmapZoom);
	}

	// *** accessors ***
	getQuery () { return query; }

	getLociData () { return query === "" ? allLociData : filteredlociData; }

	getLocusData () { return visibleLocusData; }

	getClusteredStrainData () { return clusteredStrainData; }

	getHeatmapData () {
		// first, create array of indexes for cluster sorted data
		var strainObj;
		var indexArr = strainClusterIndexes.map( _id => {
			strainObj = _.findWhere(strainMetaData, { id: _id } );
			return strainMetaData.indexOf(strainObj);
		});

		var unsorted, sorted;
		return this.getLociData().map( d => {
			// sort heatmap nodes to match the dendrogram cluster
			unsorted = (isProteinMode ? d.protein_scores : d.dna_scores);
			sorted = indexArr.map( _d => {
				return unsorted[_d];
			});
			return {
				name: d.name,
				id: d.sgdid,
				data: sorted,
				href: d.href
			}
		});
	}

	getHeatmapStrainData () {
		var strainMeta;
		return visibleStrainIds
			.map( d => {
				strainMeta = _.findWhere(strainMetaData, { id: d });
				return {
					name: strainMeta.display_name,
					id: strainMeta.id
				};
			});
	}

	getHeatmapZoom () { return heatmapZoom; }

	getVisibleStrainIds () { return visibleStrainIds; }

	getSortBy () { return sortBy; }

	getStrainMetaData () { return staticStrainMetadata.strains; }

	getAllLociTotal () { return allLociTotal; }

	getTotal () { return totalLoci; }

	getNumVisibleLoci () {
		return Math.min(totalLoci, Math.round(SCROLL_CONTAINER_HEIGHT / heatmapZoom));
	}

	// *** fetchers, calculators ***

	// cb (err) gets called twice
	fetchInitialData (cb) {
		strainMetaData = staticStrainMetadata.strains;
		clusteredStrainData = staticStrainMetadata.clusterData;
		strainClusterIndexes = this.getStrainIndexes(clusteredStrainData);
		this.setVisibleStrainIds(strainMetaData.map( d => { return d.id; }));
		var quickUrl = `${LOCI_SEARCH_BASE_URL}?limit=200`;
		var longUrl = `${LOCI_SEARCH_BASE_URL}?limit=6500`;
		$.getJSON(quickUrl, data => {
			totalLoci = data.total;
			allLociTotal = data.total;
			allLociData = data.loci;
			if (typeof cb === "function") cb(null);
			$.getJSON(longUrl, data => {
				allLociData = data.loci;
				this.sortLoci();
				if (typeof cb === "function") return cb(null);
				return;
			});
		});
	}

	// cb(err, data)
	fetchSearchResults (cb) {
		// if blank query, just exec callback
		if (query === "") {
			if (typeof cb === "function") return cb(null, allLociData);
			return;
		}
		var url = `${LOCI_SEARCH_BASE_URL}?query=${query}`;
		$.getJSON(url, data => {
			totalLoci = data.total;
			filteredlociData = data.loci;
			this.sortLoci();
			if (typeof cb === "function") return cb(null, data);
			return;
		});
	}

	// cb(err, data)
	fetchLocusData (id, cb) {
		var url = `${LOCUS_SHOW_BASE_URL}/${id}`;
		$.getJSON(url, data => {
			return cb(null, data);
		});
	}

	// cb (err)
	clusterStrains (cb) {
		// initial state, use precalculated cluster
		if (query === "" && visibleStrainIds.length === staticStrainMetadata.strains.length) {
			clusteredStrainData = staticStrainMetadata.clusterData;
		} else {
			var visibleStrainMetaData = visibleStrainIds
				.map( d => {<commit_msg>recluster when static clustering is needed<commit_after>/** @jsx React.DOM */
"use strict";
var _ = require("underscore");
var $ = require("jquery");

var ClusterStrainsWorker = require("./cluster_strains.jsx");
var staticStrainMetadata = require("./strain_metadata");
var work = require("webworkify");

var LOCI_SEARCH_BASE_URL = "/search_sequence_objects";
var LOCUS_SHOW_BASE_URL = "/get_sequence_object";
var REFERENCE_STRAIN_ID = 1;

// internal data, initial state
var isApiError = false;
var allLociData = [];
var allLociTotal = 0;
var totalLoci = 0;
var filteredlociData = [];
var clusteredStrainData = null;
var strainClusterIndexes = null;
var isProteinMode = false;
var query = "";
var strainMetaData = [];
var visibleLocusData = null;
var visibleStrainIds = null;
var heatmapZoom = 16; // px per node
var sortBy = "position"; // or "entropy"

module.exports = class VariantViewerStore {

	// *** mutators ***
	setQuery (newQuery) {
		query = newQuery;
		return;
	}

	// cb(err)
	setVisibleStrainIds (_visibleStrainIds, cb) {
		// must contain reference
		if (_visibleStrainIds.indexOf(REFERENCE_STRAIN_ID) < 0) _visibleStrainIds.push(REFERENCE_STRAIN_ID);
		_visibleStrainIds = _.sortBy(_visibleStrainIds, d => { return d.id });
		visibleStrainIds = _visibleStrainIds;
		if (typeof cb === "function") return this.clusterStrains(cb);
		return;
	}

	setIsProteinMode (_isProteinMode) { isProteinMode = _isProteinMode; return; }

	setSortBy (_sortBy) {
		sortBy = _sortBy;
		this.sortLoci();
		return;
	}

	sortLoci () {
		var sortFn;
		// reference genetic position
		if (sortBy === "position") {
			sortFn = d => {
				return d.absolute_genetic_start;
			};
		// entropy
		} else {
			sortFn = (d, _i) => {
				var n;
				var sigma = 0;
				for (var i = d.dna_scores.length - 1; i >= 0; i--) {
					n = d.dna_scores[i];
					if(typeof n === "number") sigma += n * (Math.log(n) / Math.log(2));
				};
				return sigma;
			};
		}
		allLociData = _.sortBy(allLociData, sortFn);
		filteredlociData = _.sortBy(filteredlociData, sortFn);
		return;
	}

	zoomHeatmap (isIn) {
		heatmapZoom += isIn;
		heatmapZoom = Math.max(1, heatmapZoom);
	}

	// *** accessors ***
	getQuery () { return query; }

	getLociData () { return query === "" ? allLociData : filteredlociData; }

	getLocusData () { return visibleLocusData; }

	getClusteredStrainData () { return clusteredStrainData; }

	getHeatmapData () {
		// first, create array of indexes for cluster sorted data
		var strainObj;
		var indexArr = strainClusterIndexes.map( _id => {
			strainObj = _.findWhere(strainMetaData, { id: _id } );
			return strainMetaData.indexOf(strainObj);
		});

		var unsorted, sorted;
		return this.getLociData().map( d => {
			// sort heatmap nodes to match the dendrogram cluster
			unsorted = (isProteinMode ? d.protein_scores : d.dna_scores);
			sorted = indexArr.map( _d => {
				return unsorted[_d];
			});
			return {
				name: d.name,
				id: d.sgdid,
				data: sorted,
				href: d.href
			}
		});
	}

	getHeatmapStrainData () {
		var strainMeta;
		return visibleStrainIds
			.map( d => {
				strainMeta = _.findWhere(strainMetaData, { id: d });
				return {
					name: strainMeta.display_name,
					id: strainMeta.id
				};
			});
	}

	getHeatmapZoom () { return heatmapZoom; }

	getVisibleStrainIds () { return visibleStrainIds; }

	getSortBy () { return sortBy; }

	getStrainMetaData () { return staticStrainMetadata.strains; }

	getAllLociTotal () { return allLociTotal; }

	getTotal () { return totalLoci; }

	getNumVisibleLoci () {
		return Math.min(totalLoci, Math.round(SCROLL_CONTAINER_HEIGHT / heatmapZoom));
	}

	// *** fetchers, calculators ***

	// cb (err) gets called twice
	fetchInitialData (cb) {
		strainMetaData = staticStrainMetadata.strains;
		clusteredStrainData = staticStrainMetadata.clusterData;
		strainClusterIndexes = this.getStrainIndexes(clusteredStrainData);
		this.setVisibleStrainIds(strainMetaData.map( d => { return d.id; }));
		var quickUrl = `${LOCI_SEARCH_BASE_URL}?limit=200`;
		var longUrl = `${LOCI_SEARCH_BASE_URL}?limit=6500`;
		$.getJSON(quickUrl, data => {
			totalLoci = data.total;
			allLociTotal = data.total;
			allLociData = data.loci;
			if (typeof cb === "function") cb(null);
			$.getJSON(longUrl, data => {
				allLociData = data.loci;
				this.sortLoci();
				if (typeof cb === "function") return cb(null);
				return;
			});
		});
	}

	// cb(err, data)
	fetchSearchResults (cb) {
		// if blank query, just exec callback
		if (query === "") {
			if (typeof cb === "function") return cb(null, allLociData);
			return;
		}
		var url = `${LOCI_SEARCH_BASE_URL}?query=${query}`;
		$.getJSON(url, data => {
			totalLoci = data.total;
			filteredlociData = data.loci;
			this.sortLoci();
			if (typeof cb === "function") return cb(null, data);
			return;
		});
	}

	// cb(err, data)
	fetchLocusData (id, cb) {
		var url = `${LOCUS_SHOW_BASE_URL}/${id}`;
		$.getJSON(url, data => {
			return cb(null, data);
		});
	}

	// cb (err)
	clusterStrains (cb) {
		// initial state, use precalculated cluster
		if (query === "" && visibleStrainIds.length === staticStrainMetadata.strains.length) {
			clusteredStrainData = staticStrainMetadata.clusterData;
			cb(null);
		} else {
			var visibleStrainMetaData = visibleStrainIds
				.map( d => {
{'fraction_non_alphanumeric': 0.14262607040913416, 'fraction_numerical': 0.0034253092293054233, 'mean_word_length': 3.7304230423042304, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6804033', 'n_tokens_mistral': 4199, 'n_tokens_neox': 3684, 'n_words': 816}