<commit_before><template>
    <div id="builds">

        <div class="ui left vertical labeled icon small inverted sidebar menu">
            <div class="item"><i>Existing configurations</i></div>
            <a class="ui buildconfigs dropdown item" v-for="(conf,conf_name) in build_configs">
                <i class="dropdown icon"></i>
                <div :class="['ui', build_colors[conf_name], 'empty circular label']"></div>
                {{conf_name}} <b v-if="conf.error" class="red"><i class="red bell icon"></i></b>
                <div class="ui inverted menu">
                    <div v-if="!conf.error" class="item" :conf-name="conf_name" @click="newBuild($event)"><i class="cube icon"></i> Create new build</div>
                    <div class="item" :conf-name="conf_name" @click="editConfiguration($event)">
                        <i class="edit outline icon"></i> Edit configuration <b v-if="conf.error" class="red"><i class="red bell icon"></i></b>
                    </div>
                    <div class="item" :conf-name="conf_name" @click="deleteConfiguration($event)"><i class="trash alternate outline icon"></i> Delete configuration</div>
                </div>
            </a>
            <div class="item"><i>Other actions</i></div>
            <a class="item"  v-on:click="createConfiguration">
                <i class="big icons">
                    <i class="configure icon"></i>
                    <i class="huge corner add icon"></i>
                </i>
                <div>New configuration</div>
            </a>
            <a class="item" v-on:click="toggleShowArchivedConfigs">
                <i class="big icons">
                    <i class="archive icon"></i>
                </i>
                <div>{{include_archived_configs ? "Hide" : "Show"}}  archived configurations</div>
            </a>
        </div>
        <div class="pusher">
            <div class="ui main container">
                <div class="ui segment">
                    <div class="ui secondary small menu">
                        <a class="item" id="side_menu">
                            <i class="sidebar icon"></i>
                            Menu
                        </a>
                        <a class="right aligned item">
                            <button class="ui clearconffilter button" v-if="conf_filter" @click="clearFilter">
                                Clear 
                            </button>
                            <select class="ui filterbuilds dropdown" v-model="conf_filter">
                                <option value="">Filter</option>
                                <option :value="name" v-for="(conf,name) in build_configs">
                                    <div :class="['ui', build_colors[name], 'empty circular label']"></div>
                                    {{name}}
                                </option>
                            </select>
                        </a>
                        <a class="item">
                                <div class="ui includearchived checkbox">
                                    <input type="checkbox" name="includearchived" v-model="only_archived">
                                    <label>Show archived builds only</label>
                                </div>
                            </a>
                          </div>
                    </div>
                    <div class="ui centered grid">
                        <div class="ui five wide column" v-for="build in builds">
                            <build v-bind:pbuild="build" v-bind:color="build_colors[build.build_config.name]"></build>
                        </div>
                    </div>
                </div>
            </div>

            <!-- create new build configuration -->
            <div class="ui basic buildconfiguration modal">
                <h3 class="ui icon">
                    <i class="configure icon"></i>
                    Create/edit build configuration
                </h3>
                <div class="content">
                    <div class="ui buildconfiguration form">
                        <div class="ui centered grid">
                            <div class="eight wide column">

                                <div v-if="buildconf_error" class="ui negative message">
                                  <div class="header">
                                    Error in build configuration
                                  </div>
                                  <p>{{buildconf_error}}</p>
                                </div>

                                <label>Enter a build configuration name</label>
                                <input type="text" id="conf_name" placeholder="Configuration name" autofocus>
                                <br>
                                <br>

                                <label>Enter a name for the type of stored documents ("gene", "variant", ...)</label>
                                <input type="text" id="doc_type" placeholder="Document type" autofocus>
                                <br>
                                <br>

                                <label>Select the sources used to create merged data</label>
                                <select class="ui fluid sources dropdown" id="selected_sources" multiple="">
                                    <option value="">Available sources</option>
                                    <option v-for="_id in sources">{{_id}}</option>
                                </select>
                                <br>

                                <label>Once sources are selected, choose sources providing root documents</label>
                                <select class="ui fluid rootsources dropdown" id="root_sources" multiple="">
                                    <option value="">Root sources</option>
                                </select>
                                <br>

                                <label>Select a builder type</label>
                                <select class="ui fluid builders dropdown" id="builders">
                                    <option v-for="val,classpath in builder_classes" :value="classpath">{{classpath}} <i v-if="val.default">(default)</i></option>
                                </select>
                                <div class="ui blue message">
                                  <div class="header">
                                    About this builder
                                  </div>
                                  <p id="builder_doc">{{builder_doc}}</p>
                                </div>
                                <br>

                                <p>Optional parameters can be added to the configuration (usefull to customize builder behavior). Enter a JSON object structure</p>
                                <textarea id="optionals">{}</textarea>

                                <div class="ui teal message">
                                    <b>Note</b>: sources providing root documents, or <i>root sources</i>, are sources allowed to
                                    create a new document in a build (merged data). If a root source is declared, data from other sources will only be merged <b>if</b>
                                    documents previously exist with same IDs (documents coming from root sources). If not, data is discarded. Finally, if no root source
                                    is declared, any data sources can generate a new document in the merged data.
                                </div>

                                <label>Remove this configuration from list by default</label>
                                <div><i>You can still access it by clicking on "show/hide archived configuration" in the menu on the side</i></div>
                                <div class="ui checkbox">
                                  <input type="checkbox" name="archive_conf">
                                  <label class="white">Archive</label>
                                </div>

                            </div>
                        </div>
                    </div>
                </div>

                <div class="ui error message" v-if="errors.length">
                    <ul class="ui list">
                        <li v-for="err in errors">{{err}}</li>
                    </ul>
                </div>

                <div class="actions">
                    <div class="ui red basic cancel inverted button">
                        <i class="remove icon"></i>
                        Cancel
                    </div>
                    <div class="ui green ok inverted button">
                        <i class="checkmark icon"></i>
                        OK
                    </div>
                </div>
            </div>

            <div class="ui basic deleteconf modal">
                <div class="ui icon header">
                    <i class="trash alternate icon"></i>
                    Delete configuration
                </div>
                <div class="content">
                    <p>Are you sure you want to delete this build configuration ?</p>
                </div>
                <div class="actions">
                    <div class="ui red basic cancel inverted button">
                        <i class="remove icon"></i>
                        No
                    </div>
                    <div class="ui green ok inverted button">
                        <i class="checkmark icon"></i>
                        Yes
                    </div>
                </div>
            </div>

            <div class="ui basic newbuild modal">
                <div class="ui icon header">
                    <i class="cube icon"></i>
                    Create new build
                </div>
                <div class="ui newbuild form">
                    <div class="ui centered grid">
                        <div class="ten wide column">
                            <p>Enter a name for the merged data collection or leave it empty to generate a random one</p>
                            <input type="text" id="target_name" placeholder="Collection name" autofocus>
                            <div class="ui forcebuild checkbox">
                                <br/>
                                <input type="checkbox" name="force_build" id="force_build">
                                <label class="white">Skip sanity checks and force build (not recommended)</label>
                            </div>
                        </div>
                        <div class="six wide column">
                        </div>
                    </div>
                </div>
                <div class="actions">
                    <div class="ui red basic cancel inverted button">
                        <i class="remove icon"></i>
                        Cancel
                    </div>
                    <div class="ui green ok inverted button">
                        <i class="checkmark icon"></i>
                        OK
                    </div>
                </div>
            </div>

        </div>


    </template>

    <script>
    import axios from 'axios'
    import Build from './Build.vue'
    import Loader from './Loader.vue'
    import bus from './bus.js'


    export default {
        name: 'build-grid',
        mixins: [ Loader, ],
        mounted () {
            var self = this;
            console.log("BuildGrid mounted");
            $('.ui.filterbuilds.dropdown').dropdown();
            $('.ui.buildconfigs.dropdown').dropdown();
            $('.ui.rootsources.dropdown').dropdown();
            $('.ui.sources.dropdown').dropdown({
                onChange: function(addedValue, addedText, $addedChoice) {
                    var selected_sources = $('.ui.sources.dropdown').dropdown('get value');
                    var fmt = []
                    for(var i in selected_sources) {
                        var x = selected_sources[i];
                        var d = {"name":x,"text":x,"value":x};
                        fmt.push(d);
                    }
                    $('.ui.rootsources.dropdown').dropdown("clear");
                    $('.ui.rootsources.dropdown').dropdown("setup menu",{"values" : fmt}).dropdown("refresh");
                },
            });

            $('.ui.builders.dropdown').dropdown({
              onChange: function(value, text) {
                self.setBuilderDoc(value);
              }
            });
            $('#builds .ui.sidebar')
            .sidebar({context:$('#builds')})
            .sidebar('setting', 'transition', 'overlay')
            .sidebar('attach events', '#side_menu');
            $('.ui.form').form();
            $('.ui.includearchived.checkbox').checkbox();
            $(".ui.forcebuild.checkbox").checkbox();
    },
    updated() {
        // there's some kind of race-condition regarding dropdown init, if
        // in mounted() they won't get init, prob. because data changed and needs to
        // be re-rendered
        $('.ui.buildconfigs.dropdown').dropdown();
    },
    created() {
        // load sources to build dropdown list when creating a new config
        this.loading();
        this.getSourceList();
        // builds & configs
        this.getBuildConfigs();
        this.getBuilds();
        bus.$on('change_source',this.onSourceChanged);
        bus.$on('change_build',this.onBuildChanged);
        bus.$on('change_build_config',this.onBuildConfigChanged);
    },
    beforeDestroy() {
        clearInterval(this.interval);
        // hacky to remove modal from div outside of app, preventing having more than one
        // modal displayed when getting back to that page. https://github.com/Semantic-Org/Semantic-UI/issues/4049
        $('.ui.basic.deleteconf.modal').remove();
        $('.ui.basic.newbuild.modal').remove();
        $('.ui.basic.buildconfiguration.modal').remove();
        bus.$off('change_source',this.onSourceChanged);
        bus.$off('change_build',this.onBuildChanged);
        bus.$off('change_build_config',this.onBuildConfigChanged);
    },
    watch: {
        conf_filter: function(newv,oldv) {
            if(newv != oldv) {
                this.getBuilds();
            }
        },
        only_archived: function(newv,oldv) {
            if(newv != oldv) {
                this.getBuilds();
            }
        },
    },
    data () {
        return  {
            builds: [],
            sources : [],
            all_build_configs: {}, // from API
            build_configs: {}, // displayed
            builder_classes: {},
            default_builder_class: null,
            builder_doc: "No documentation for this builder", // selected builder doc
            buildconf_error: null,
            errors: [],
            build_colors : {},
            // build colors for easy visual id
            colors: ["orange","green","yellow","olive","teal","violet","blue","pink","purple"],
            color_idx : 0,
            conf_filter : "",
            only_archived : false,
            include_archived_configs: false,
        }
    },
    computed: {
    },
    components: { Build, },
    methods: {
      setBuilderDoc: function(classpath) {
        var bclass = this.builder_classes[classpath];
        if(bclass) {
            this.builder_doc = bclass["desc"];
        }
        if(!this.builder_doc)
          this.builder_doc = "No documentation for this builder";
      },
        updateRootSources: function() {
            var avail_roots = $(".ui.buildconfiguration.form").form('get field', "selected_sources").val();
            $('#root_sources').dropdown("clear");
            $('#root_sources').empty();
            $.each(avail_roots, function(i) {
                var val = avail_roots[i];
                $('#root_sources').append(
                    $('<option></option>').val(val).html(val)
                );
            });
            $('#root_sources').dropdown().dropdown("refresh");
        },
        getBuilds: function() {
            var filter = this.conf_filter == "" ? '' : `?conf_name=${this.conf_filter}`;
            // https://vuejs.org/v2/guide/list.html#Caveats:
            // "Vue implements some smart heuristics to maximize DOM element reuse, so replacing an 
            //  array with another array containing overlapping objects is a very efficient operation."
            // Well, if only one element changes, like a deleted build, it seems that smart heuristic
            // is somehow dumb enough to partially render the list, so we need to empty that list here
            // (and if emptied in "response", I guess there's a race condition because builds aren't
            // rendered properly again...). Anyway, I don't know if it's related but that's the only
            // explanation I have...
            if(this.only_archived) {
                if(filter == "")
                    filter += "?only_archived=1";
                else
                    filter += "&only_archived=1";
            }
            this.loading();
            this.builds = [];
            axios.get(axios.defaults.baseURL + '/builds' + filter)
            .then(response => {
                this.builds = response.data.result;
                this.loaded();
            })
            .catch(err => {
                console.log("Error getting builds information: " + err);
                this.loaderror(err);
            })
        },
        setBuildConfigs: function() {
            this.build_configs = {};
            // make sure we always give the same color for a given build config
            var keys = Object.keys(this.all_build_configs);
            for(var k in keys) {
                if(!this.include_archived_configs && this.all_build_configs[keys[k]].build_config["archived"])
                    continue;
                this.build_configs[keys[k]] = this.all_build_configs[keys[k]];
            }
        },
        getBuildConfigs: function() {
            var self = this;
            // reset colors (if configs haven't changed we should get the same colors
            // as we sort the build config by name)
            self.build_colors = {};
            self.color_idx = 0;
            this.loading();
            axios.get(axios.defaults.baseURL + '/build_manager')
            .then(response => {
              // depending on API version, build configs are under "build_configs" key
              // along with "builder_classes", or at the root (no builder_classes)
              // TODO: version hub API and check version number there
              if(response.data.result.build_configs) {
                self.all_build_configs = response.data.result.build_configs;
                self.builder_classes = response.data.result.builder_classes;
                for(var k in self.builder_classes) {
                    if(self.builder_classes[k]["default"])
                        self.default_builder_class = k;
                }
              } else {
                self.all_build_configs = response.data.result;
                self.builder_classes = [];
              }
              var keys = Object.keys(self.all_build_configs);
              for(var k in keys) {
                self.build_colors[keys[k]] = self.colors[self.color_idx++];
                if(self.color_idx + 1 >= Object.keys(self.colors).length) {
                  self.color_idx = 0;
                }
              }
              self.setBuildConfigs();
              this.loaded();
            })
            .catch(err => {
                console.log("Error getting builds information: " + err);
                this.loaderror(err);
            })
        },
        getSourceList: function() {
            var self = this;
            this.loading();
            axios.get(axios.defaults.baseURL + '/sources')
            .then(response => {
                $(response.data.result).each(function(i,e) {
                    if(e["upload"]) {
                        for(var k in e["upload"]["sources"]) {
                            self.sources.push(k);
                        }
                    }
                });
                self.sources.sort();
                this.loaded();
            })
            .catch(err => {
                console.log("Error listing sources: " + err);
                this.loaderror(err);
            })<commit_msg>fix rare cases where double call to api could be made and produce duplicates in list of sources<commit_after><template>
    <div id="builds">

        <div class="ui left vertical labeled icon small inverted sidebar menu">
            <div class="item"><i>Existing configurations</i></div>
            <a class="ui buildconfigs dropdown item" v-for="(conf,conf_name) in build_configs">
                <i class="dropdown icon"></i>
                <div :class="['ui', build_colors[conf_name], 'empty circular label']"></div>
                {{conf_name}} <b v-if="conf.error" class="red"><i class="red bell icon"></i></b>
                <div class="ui inverted menu">
                    <div v-if="!conf.error" class="item" :conf-name="conf_name" @click="newBuild($event)"><i class="cube icon"></i> Create new build</div>
                    <div class="item" :conf-name="conf_name" @click="editConfiguration($event)">
                        <i class="edit outline icon"></i> Edit configuration <b v-if="conf.error" class="red"><i class="red bell icon"></i></b>
                    </div>
                    <div class="item" :conf-name="conf_name" @click="deleteConfiguration($event)"><i class="trash alternate outline icon"></i> Delete configuration</div>
                </div>
            </a>
            <div class="item"><i>Other actions</i></div>
            <a class="item"  v-on:click="createConfiguration">
                <i class="big icons">
                    <i class="configure icon"></i>
                    <i class="huge corner add icon"></i>
                </i>
                <div>New configuration</div>
            </a>
            <a class="item" v-on:click="toggleShowArchivedConfigs">
                <i class="big icons">
                    <i class="archive icon"></i>
                </i>
                <div>{{include_archived_configs ? "Hide" : "Show"}}  archived configurations</div>
            </a>
        </div>
        <div class="pusher">
            <div class="ui main container">
                <div class="ui segment">
                    <div class="ui secondary small menu">
                        <a class="item" id="side_menu">
                            <i class="sidebar icon"></i>
                            Menu
                        </a>
                        <a class="right aligned item">
                            <button class="ui clearconffilter button" v-if="conf_filter" @click="clearFilter">
                                Clear 
                            </button>
                            <select class="ui filterbuilds dropdown" v-model="conf_filter">
                                <option value="">Filter</option>
                                <option :value="name" v-for="(conf,name) in build_configs">
                                    <div :class="['ui', build_colors[name], 'empty circular label']"></div>
                                    {{name}}
                                </option>
                            </select>
                        </a>
                        <a class="item">
                                <div class="ui includearchived checkbox">
                                    <input type="checkbox" name="includearchived" v-model="only_archived">
                                    <label>Show archived builds only</label>
                                </div>
                            </a>
                          </div>
                    </div>
                    <div class="ui centered grid">
                        <div class="ui five wide column" v-for="build in builds">
                            <build v-bind:pbuild="build" v-bind:color="build_colors[build.build_config.name]"></build>
                        </div>
                    </div>
                </div>
            </div>

            <!-- create new build configuration -->
            <div class="ui basic buildconfiguration modal">
                <h3 class="ui icon">
                    <i class="configure icon"></i>
                    Create/edit build configuration
                </h3>
                <div class="content">
                    <div class="ui buildconfiguration form">
                        <div class="ui centered grid">
                            <div class="eight wide column">

                                <div v-if="buildconf_error" class="ui negative message">
                                  <div class="header">
                                    Error in build configuration
                                  </div>
                                  <p>{{buildconf_error}}</p>
                                </div>

                                <label>Enter a build configuration name</label>
                                <input type="text" id="conf_name" placeholder="Configuration name" autofocus>
                                <br>
                                <br>

                                <label>Enter a name for the type of stored documents ("gene", "variant", ...)</label>
                                <input type="text" id="doc_type" placeholder="Document type" autofocus>
                                <br>
                                <br>

                                <label>Select the sources used to create merged data</label>
                                <select class="ui fluid sources dropdown" id="selected_sources" multiple="">
                                    <option value="">Available sources</option>
                                    <option v-for="_id in sources">{{_id}}</option>
                                </select>
                                <br>

                                <label>Once sources are selected, choose sources providing root documents</label>
                                <select class="ui fluid rootsources dropdown" id="root_sources" multiple="">
                                    <option value="">Root sources</option>
                                </select>
                                <br>

                                <label>Select a builder type</label>
                                <select class="ui fluid builders dropdown" id="builders">
                                    <option v-for="val,classpath in builder_classes" :value="classpath">{{classpath}} <i v-if="val.default">(default)</i></option>
                                </select>
                                <div class="ui blue message">
                                  <div class="header">
                                    About this builder
                                  </div>
                                  <p id="builder_doc">{{builder_doc}}</p>
                                </div>
                                <br>

                                <p>Optional parameters can be added to the configuration (usefull to customize builder behavior). Enter a JSON object structure</p>
                                <textarea id="optionals">{}</textarea>

                                <div class="ui teal message">
                                    <b>Note</b>: sources providing root documents, or <i>root sources</i>, are sources allowed to
                                    create a new document in a build (merged data). If a root source is declared, data from other sources will only be merged <b>if</b>
                                    documents previously exist with same IDs (documents coming from root sources). If not, data is discarded. Finally, if no root source
                                    is declared, any data sources can generate a new document in the merged data.
                                </div>

                                <label>Remove this configuration from list by default</label>
                                <div><i>You can still access it by clicking on "show/hide archived configuration" in the menu on the side</i></div>
                                <div class="ui checkbox">
                                  <input type="checkbox" name="archive_conf">
                                  <label class="white">Archive</label>
                                </div>

                            </div>
                        </div>
                    </div>
                </div>

                <div class="ui error message" v-if="errors.length">
                    <ul class="ui list">
                        <li v-for="err in errors">{{err}}</li>
                    </ul>
                </div>

                <div class="actions">
                    <div class="ui red basic cancel inverted button">
                        <i class="remove icon"></i>
                        Cancel
                    </div>
                    <div class="ui green ok inverted button">
                        <i class="checkmark icon"></i>
                        OK
                    </div>
                </div>
            </div>

            <div class="ui basic deleteconf modal">
                <div class="ui icon header">
                    <i class="trash alternate icon"></i>
                    Delete configuration
                </div>
                <div class="content">
                    <p>Are you sure you want to delete this build configuration ?</p>
                </div>
                <div class="actions">
                    <div class="ui red basic cancel inverted button">
                        <i class="remove icon"></i>
                        No
                    </div>
                    <div class="ui green ok inverted button">
                        <i class="checkmark icon"></i>
                        Yes
                    </div>
                </div>
            </div>

            <div class="ui basic newbuild modal">
                <div class="ui icon header">
                    <i class="cube icon"></i>
                    Create new build
                </div>
                <div class="ui newbuild form">
                    <div class="ui centered grid">
                        <div class="ten wide column">
                            <p>Enter a name for the merged data collection or leave it empty to generate a random one</p>
                            <input type="text" id="target_name" placeholder="Collection name" autofocus>
                            <div class="ui forcebuild checkbox">
                                <br/>
                                <input type="checkbox" name="force_build" id="force_build">
                                <label class="white">Skip sanity checks and force build (not recommended)</label>
                            </div>
                        </div>
                        <div class="six wide column">
                        </div>
                    </div>
                </div>
                <div class="actions">
                    <div class="ui red basic cancel inverted button">
                        <i class="remove icon"></i>
                        Cancel
                    </div>
                    <div class="ui green ok inverted button">
                        <i class="checkmark icon"></i>
                        OK
                    </div>
                </div>
            </div>

        </div>


    </template>

    <script>
    import axios from 'axios'
    import Build from './Build.vue'
    import Loader from './Loader.vue'
    import bus from './bus.js'


    export default {
        name: 'build-grid',
        mixins: [ Loader, ],
        mounted () {
            var self = this;
            console.log("BuildGrid mounted");
            $('.ui.filterbuilds.dropdown').dropdown();
            $('.ui.buildconfigs.dropdown').dropdown();
            $('.ui.rootsources.dropdown').dropdown();
            $('.ui.sources.dropdown').dropdown({
                onChange: function(addedValue, addedText, $addedChoice) {
                    var selected_sources = $('.ui.sources.dropdown').dropdown('get value');
                    var fmt = []
                    for(var i in selected_sources) {
                        var x = selected_sources[i];
                        var d = {"name":x,"text":x,"value":x};
                        fmt.push(d);
                    }
                    $('.ui.rootsources.dropdown').dropdown("clear");
                    $('.ui.rootsources.dropdown').dropdown("setup menu",{"values" : fmt}).dropdown("refresh");
                },
            });

            $('.ui.builders.dropdown').dropdown({
              onChange: function(value, text) {
                self.setBuilderDoc(value);
              }
            });
            $('#builds .ui.sidebar')
            .sidebar({context:$('#builds')})
            .sidebar('setting', 'transition', 'overlay')
            .sidebar('attach events', '#side_menu');
            $('.ui.form').form();
            $('.ui.includearchived.checkbox').checkbox();
            $(".ui.forcebuild.checkbox").checkbox();
    },
    updated() {
        // there's some kind of race-condition regarding dropdown init, if
        // in mounted() they won't get init, prob. because data changed and needs to
        // be re-rendered
        $('.ui.buildconfigs.dropdown').dropdown();
    },
    created() {
        // load sources to build dropdown list when creating a new config
        this.loading();
        this.getSourceList();
        // builds & configs
        this.getBuildConfigs();
        this.getBuilds();
        bus.$on('change_source',this.onSourceChanged);
        bus.$on('change_build',this.onBuildChanged);
        bus.$on('change_build_config',this.onBuildConfigChanged);
    },
    beforeDestroy() {
        clearInterval(this.interval);
        // hacky to remove modal from div outside of app, preventing having more than one
        // modal displayed when getting back to that page. https://github.com/Semantic-Org/Semantic-UI/issues/4049
        $('.ui.basic.deleteconf.modal').remove();
        $('.ui.basic.newbuild.modal').remove();
        $('.ui.basic.buildconfiguration.modal').remove();
        bus.$off('change_source',this.onSourceChanged);
        bus.$off('change_build',this.onBuildChanged);
        bus.$off('change_build_config',this.onBuildConfigChanged);
    },
    watch: {
        conf_filter: function(newv,oldv) {
            if(newv != oldv) {
                this.getBuilds();
            }
        },
        only_archived: function(newv,oldv) {
            if(newv != oldv) {
                this.getBuilds();
            }
        },
    },
    data () {
        return  {
            builds: [],
            sources : [],
            all_build_configs: {}, // from API
            build_configs: {}, // displayed
            builder_classes: {},
            default_builder_class: null,
            builder_doc: "No documentation for this builder", // selected builder doc
            buildconf_error: null,
            errors: [],
            build_colors : {},
            // build colors for easy visual id
            colors: ["orange","green","yellow","olive","teal","violet","blue","pink","purple"],
            color_idx : 0,
            conf_filter : "",
            only_archived : false,
            include_archived_configs: false,
        }
    },
    computed: {
    },
    components: { Build, },
    methods: {
      setBuilderDoc: function(classpath) {
        var bclass = this.builder_classes[classpath];
        if(bclass) {
            this.builder_doc = bclass["desc"];
        }
        if(!this.builder_doc)
          this.builder_doc = "No documentation for this builder";
      },
        updateRootSources: function() {
            var avail_roots = $(".ui.buildconfiguration.form").form('get field', "selected_sources").val();
            $('#root_sources').dropdown("clear");
            $('#root_sources').empty();
            $.each(avail_roots, function(i) {
                var val = avail_roots[i];
                $('#root_sources').append(
                    $('<option></option>').val(val).html(val)
                );
            });
            $('#root_sources').dropdown().dropdown("refresh");
        },
        getBuilds: function() {
            var filter = this.conf_filter == "" ? '' : `?conf_name=${this.conf_filter}`;
            // https://vuejs.org/v2/guide/list.html#Caveats:
            // "Vue implements some smart heuristics to maximize DOM element reuse, so replacing an 
            //  array with another array containing overlapping objects is a very efficient operation."
            // Well, if only one element changes, like a deleted build, it seems that smart heuristic
            // is somehow dumb enough to partially render the list, so we need to empty that list here
            // (and if emptied in "response", I guess there's a race condition because builds aren't
            // rendered properly again...). Anyway, I don't know if it's related but that's the only
            // explanation I have...
            if(this.only_archived) {
                if(filter == "")
                    filter += "?only_archived=1";
                else
                    filter += "&only_archived=1";
            }
            this.loading();
            this.builds = [];
            axios.get(axios.defaults.baseURL + '/builds' + filter)
            .then(response => {
                this.builds = response.data.result;
                this.loaded();
            })
            .catch(err => {
                console.log("Error getting builds information: " + err);
                this.loaderror(err);
            })
        },
        setBuildConfigs: function() {
            this.build_configs = {};
            // make sure we always give the same color for a given build config
            var keys = Object.keys(this.all_build_configs);
            for(var k in keys) {
                if(!this.include_archived_configs && this.all_build_configs[keys[k]].build_config["archived"])
                    continue;
                this.build_configs[keys[k]] = this.all_build_configs[keys[k]];
            }
        },
        getBuildConfigs: function() {
            var self = this;
            // reset colors (if configs haven't changed we should get the same colors
            // as we sort the build config by name)
            self.build_colors = {};
            self.color_idx = 0;
            this.loading();
            axios.get(axios.defaults.baseURL + '/build_manager')
            .then(response => {
              // depending on API version, build configs are under "build_configs" key
              // along with "builder_classes", or at the root (no builder_classes)
              // TODO: version hub API and check version number there
              if(response.data.result.build_configs) {
                self.all_build_configs = response.data.result.build_configs;
                self.builder_classes = response.data.result.builder_classes;
                for(var k in self.builder_classes) {
                    if(self.builder_classes[k]["default"])
                        self.default_builder_class = k;
                }
              } else {
                self.all_build_configs = response.data.result;
                self.builder_classes = [];
              }
              var keys = Object.keys(self.all_build_configs);
              for(var k in keys) {
                self.build_colors[keys[k]] = self.colors[self.color_idx++];
                if(self.color_idx + 1 >= Object.keys(self.colors).length) {
                  self.color_idx = 0;
                }
              }
              self.setBuildConfigs();
              this.loaded();
            })
            .catch(err => {
                console.log("Error getting builds information: " + err);
                this.loaderror(err);
            })
        },
        getSourceList: function() {
            var self = this;
            this.loading();
            self.sources = [];
            axios.get(axios.defaults.baseURL + '/sources')
            .then(response => {
                $(response.data.result).each(function(i,e) {
                    if(e["upload"]) {
                        for(var k in e["upload"]["sources"]) {
                            self.sources.push(k);
                        }
                    }
                });
                self.sources.sort();
                this.loaded();
            })
            .catch(err => {
                console.log("Error listing sources: " + err);
                this.loaderror(err);
            })
{'fraction_non_alphanumeric': 0.1180864783317517, 'fraction_numerical': 0.0006326495851278682, 'mean_word_length': 1.2406498746047323, 'pattern_counts': {'":': 6, '<': 511, '<?xml version=': 0, '>': 525, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 46, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1995071', 'n_tokens_mistral': 10023, 'n_tokens_neox': 9416, 'n_words': 2454}