aiai.ai.launchpad.experiment.ExperimentsController.java Source code

Java tutorial

Introduction

Here is the source code for aiai.ai.launchpad.experiment.ExperimentsController.java

Source

/*
 * AiAi, Copyright (C) 2017-2018  Serge Maslyukov
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package aiai.ai.launchpad.experiment;

import aiai.ai.Enums;
import aiai.ai.Globals;
import aiai.ai.core.ExecProcessService;
import aiai.ai.launchpad.beans.*;
import aiai.ai.launchpad.repositories.*;
import aiai.ai.launchpad.snippet.SnippetCache;
import aiai.ai.launchpad.snippet.SnippetService;
import aiai.ai.snippet.SnippetCode;
import aiai.ai.utils.ControllerUtils;
import aiai.ai.utils.SimpleSelectOption;
import aiai.ai.yaml.console.SnippetExec;
import aiai.ai.yaml.console.SnippetExecUtils;
import aiai.apps.commons.yaml.snippet.SnippetVersion;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Profile;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.*;
import java.util.stream.Collectors;

/**
 * User: Serg
 * Date: 12.06.2017
 * Time: 20:22
 */
@Controller
@RequestMapping("/launchpad")
@Slf4j
@Profile("launchpad")
public class ExperimentsController {

    @Data
    public static class Result {
        public Slice<Experiment> items;
    }

    @Data
    public static class TasksResult {
        public Slice<Task> items;
    }

    @Data
    public static class ConsoleResult {
        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        public static class SimpleConsoleOutput {
            public int exitCode;
            public boolean isOk;
            public String console;
        }

        public final List<SimpleConsoleOutput> items = new ArrayList<>();
    }

    @Data
    public static class SnippetResult {
        public List<SimpleSelectOption> selectOptions = new ArrayList<>();
        public List<ExperimentSnippet> snippets = new ArrayList<>();

        public void sortSnippetsByOrder() {
            //            snippets.sort(Comparator.comparingInt(ExperimentSnippet::getOrder));
        }
    }

    @Data
    public static class ExperimentResult {
        public final List<SimpleSelectOption> allDatasetOptions = new ArrayList<>();
        public List<ExperimentFeature> features;
        public boolean isCanBeLaunched;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class SimpleExperiment {
        public String name;
        public String description;
        public String code;
        public int seed;
        public String epoch;
        public long id;

        public static SimpleExperiment to(Experiment e) {
            return new SimpleExperiment(e.getName(), e.getDescription(), e.getCode(), e.getSeed(), e.getEpoch(),
                    e.getId());
        }
    }

    private final Globals globals;

    private final SnippetRepository snippetRepository;
    private final SnippetService snippetService;
    private final ExperimentRepository experimentRepository;
    private final ExperimentService experimentService;
    private final ExperimentHyperParamsRepository experimentHyperParamsRepository;
    private final ExperimentSnippetRepository experimentSnippetRepository;
    private final ExperimentFeatureRepository experimentFeatureRepository;
    private final TaskRepository taskRepository;
    private final SnippetCache snippetCache;

    public ExperimentsController(Globals globals, SnippetRepository snippetRepository,
            ExperimentRepository experimentRepository,
            ExperimentHyperParamsRepository experimentHyperParamsRepository, SnippetService snippetService,
            ExperimentService experimentService, ExperimentSnippetRepository experimentSnippetRepository,
            ExperimentFeatureRepository experimentFeatureRepository, TaskRepository taskRepository,
            SnippetCache snippetCache) {
        this.globals = globals;
        this.snippetRepository = snippetRepository;
        this.experimentRepository = experimentRepository;
        this.experimentHyperParamsRepository = experimentHyperParamsRepository;
        this.snippetService = snippetService;
        this.experimentService = experimentService;
        this.experimentSnippetRepository = experimentSnippetRepository;
        this.experimentFeatureRepository = experimentFeatureRepository;
        this.taskRepository = taskRepository;
        this.snippetCache = snippetCache;
    }

    @GetMapping("/experiments")
    public String init(@ModelAttribute Result result, @PageableDefault(size = 5) Pageable pageable,
            @ModelAttribute("errorMessage") final String errorMessage) {
        pageable = ControllerUtils.fixPageSize(globals.experimentRowsLimit, pageable);
        result.items = experimentRepository.findAll(pageable);
        return "launchpad/experiments";
    }

    // for AJAX
    @PostMapping("/experiments-part")
    public String getExperiments(@ModelAttribute Result result, @PageableDefault(size = 5) Pageable pageable) {
        pageable = ControllerUtils.fixPageSize(globals.experimentRowsLimit, pageable);
        result.items = experimentRepository.findAll(pageable);
        return "launchpad/experiments :: table";
    }

    @PostMapping("/experiment-feature-progress-part/{experimentId}/{featureId}/{params}/part")
    public String getSequncesPart(Model model, @PathVariable Long experimentId, @PathVariable Long featureId,
            @PathVariable String[] params, @PageableDefault(size = 10) Pageable pageable) {
        Experiment experiment = experimentRepository.findById(experimentId).orElse(null);
        ExperimentFeature feature = experimentFeatureRepository.findById(featureId).orElse(null);

        TasksResult result = new TasksResult();
        result.items = experimentService.findTasks(ControllerUtils.fixPageSize(10, pageable), experiment, feature,
                params);

        model.addAttribute("result", result);
        model.addAttribute("experiment", experiment);
        model.addAttribute("feature", feature);
        model.addAttribute("consoleResult", new ConsoleResult());

        return "launchpad/experiment-feature-progress :: fragment-table";
    }

    @PostMapping("/experiment-feature-plot-data-part/{experimentId}/{featureId}/{params}/{paramsAxis}/part")
    public @ResponseBody ExperimentService.PlotData getPlotData(Model model, @PathVariable Long experimentId,
            @PathVariable Long featureId, @PathVariable String[] params, @PathVariable String[] paramsAxis) {
        Experiment experiment = experimentRepository.findById(experimentId).orElse(null);
        ExperimentFeature feature = experimentFeatureRepository.findById(featureId).orElse(null);

        //noinspection UnnecessaryLocalVariable
        ExperimentService.PlotData data = experimentService.findExperimentSequenceForPlot(experiment, feature,
                params, paramsAxis);
        return data;
    }

    @PostMapping("/experiment-feature-progress-console-part/{id}")
    public String getSequencesConsolePart(Model model, @PathVariable(name = "id") Long sequenceId) {
        ConsoleResult result = new ConsoleResult();
        Task sequence = taskRepository.findById(sequenceId).orElse(null);
        if (sequence != null) {
            SnippetExec snippetExec = SnippetExecUtils.toSnippetExec(sequence.getSnippetExecResults());
            final ExecProcessService.Result execResult = snippetExec.getExec();
            result.items.add(new ConsoleResult.SimpleConsoleOutput(execResult.exitCode, execResult.isOk,
                    execResult.console));
        }
        model.addAttribute("consoleResult", result);

        return "launchpad/experiment-feature-progress :: fragment-console-table";
    }

    @GetMapping(value = "/experiment-feature-progress/{experimentId}/{featureId}")
    public String getSequences(Model model, @PathVariable Long experimentId, @PathVariable Long featureId,
            final RedirectAttributes redirectAttributes) {
        Experiment experiment = experimentRepository.findById(experimentId).orElse(null);
        if (experiment == null) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#280.01 experiment wasn't found, experimentId: " + experimentId);
            return "redirect:/launchpad/experiments";
        }

        ExperimentFeature experimentFeature = experimentFeatureRepository.findById(featureId).orElse(null);
        if (experimentFeature == null) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#280.02 feature wasn't found, experimentFeatureId: " + featureId);
            return "redirect:/launchpad/experiments";
        }

        Map<String, Object> map = experimentService.prepareExperimentFeatures(experiment, experimentFeature);
        model.addAllAttributes(map);

        return "launchpad/experiment-feature-progress";
    }

    @GetMapping(value = "/experiment-add")
    public String add(@ModelAttribute("experiment") Experiment experiment) {
        experiment.setSeed(1);
        return "launchpad/experiment-add-form";
    }

    @GetMapping(value = "/experiment-info/{id}")
    public String info(@PathVariable Long id, Model model, final RedirectAttributes redirectAttributes,
            @ModelAttribute("errorMessage") final String errorMessage) {
        Experiment experiment = experimentRepository.findById(id).orElse(null);
        if (experiment == null) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#282.01 experiment wasn't found, experimentId: " + id);
            return "redirect:/launchpad/experiments";
        }
        for (ExperimentHyperParams hyperParams : experiment.getHyperParams()) {
            if (StringUtils.isBlank(hyperParams.getValues())) {
                continue;
            }
            ExperimentUtils.NumberOfVariants variants = ExperimentUtils
                    .getNumberOfVariants(hyperParams.getValues());
            hyperParams.setVariants(variants.status ? variants.count : 0);
        }
        if (experiment.getFlowInstanceId() == null) {
            model.addAttribute("infoMessages", Collections.singleton("Launch is disabled, dataset isn't assigned"));
        }

        ExperimentResult experimentResult = new ExperimentResult();
        experimentResult.features = experimentFeatureRepository.findByExperimentId(experiment.getId());
        experimentResult.features.sort(
                (ExperimentFeature o1, ExperimentFeature o2) -> (Boolean.compare(o2.isFinished, o1.isFinished)));

        model.addAttribute("experiment", experiment);
        model.addAttribute("experimentResult", experimentResult);
        return "launchpad/experiment-info";
    }

    @GetMapping(value = "/experiment-edit/{id}")
    public String edit(@PathVariable Long id, Model model,
            @ModelAttribute("errorMessage") final String errorMessage,
            final RedirectAttributes redirectAttributes) {
        final Experiment experiment = experimentRepository.findById(id).orElse(null);
        if (experiment == null) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#275.01 experiment wasn't found, experimentId: " + id);
            return "redirect:/launchpad/experiments";
        }
        Iterable<Snippet> snippets = snippetRepository.findAll();
        SnippetResult snippetResult = new SnippetResult();

        List<ExperimentSnippet> experimentSnippets = snippetService
                .getTaskSnippetsForExperiment(experiment.getId());
        snippetResult.snippets = experimentSnippets;
        final List<String> types = Arrays.asList("fit", "predict");
        snippetResult.selectOptions = snippetService.getSelectOptions(snippets, snippetResult.snippets.stream()
                .map(o -> new SnippetCode(o.getId(), o.getSnippetCode())).collect(Collectors.toList()), (s) -> {
                    if (!types.contains(s.type)) {
                        return true;
                    }
                    if ("fit".equals(s.type) && snippetService.hasFit(experimentSnippets)) {
                        return true;
                    }
                    if ("predict".equals(s.type) && snippetService.hasPredict(experimentSnippets)) {
                        return true;
                    }
                    return false;
                });

        ExperimentResult experimentResult = new ExperimentResult();

        snippetResult.sortSnippetsByOrder();
        model.addAttribute("experiment", experiment);
        model.addAttribute("simpleExperiment", SimpleExperiment.to(experiment));
        model.addAttribute("experimentResult", experimentResult);
        model.addAttribute("snippetResult", snippetResult);
        return "launchpad/experiment-edit-form";
    }

    @PostMapping("/experiment-add-form-commit")
    public String addFormCommit(Model model, Experiment experiment, final RedirectAttributes redirectAttributes) {
        return processCommit(model, experiment, "launchpad/experiment-add-form", "redirect:/launchpad/experiments",
                false, redirectAttributes);
    }

    @PostMapping("/experiment-edit-form-commit")
    public String editFormCommit(Model model, SimpleExperiment simpleExperiment,
            final RedirectAttributes redirectAttributes) {
        Experiment experiment = experimentRepository.findById(simpleExperiment.id).orElse(null);
        if (experiment == null) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#281.01 experiment wasn't found, experimentId: " + simpleExperiment.id);
            return "redirect:/launchpad/experiments";
        }
        experiment.setName(simpleExperiment.getName());
        experiment.setDescription(simpleExperiment.getDescription());
        experiment.setSeed(simpleExperiment.getSeed());
        experiment.setEpoch(simpleExperiment.getEpoch());
        experiment.setCode(simpleExperiment.getCode());
        String target = "redirect:/launchpad/experiment-edit/" + experiment.getId();
        return processCommit(model, experiment, target, target, true, redirectAttributes);
    }

    private String processCommit(Model model, Experiment experiment, String errorTarget, String normalTarget,
            boolean isErrorWithRedirect, final RedirectAttributes redirectAttributes) {
        if (StringUtils.isBlank(experiment.getName())) {
            prepareErrorMessage(model, "#330.01 Name of experiment is blank.", isErrorWithRedirect,
                    redirectAttributes);
            return errorTarget;
        }
        if (StringUtils.isBlank(experiment.getCode())) {
            prepareErrorMessage(model, "#330.04 Code of experiment is blank.", isErrorWithRedirect,
                    redirectAttributes);
            return errorTarget;
        }
        if (StringUtils.isBlank(experiment.getDescription())) {
            prepareErrorMessage(model, "#330.07 Description of experiment is blank.", isErrorWithRedirect,
                    redirectAttributes);
            return errorTarget;
        }
        if (StringUtils.isBlank(experiment.getEpoch())) {
            prepareErrorMessage(model, "#330.10 Epochs of experiment isn't specified.", isErrorWithRedirect,
                    redirectAttributes);
            return errorTarget;
        }
        ExperimentUtils.NumberOfVariants numberOfVariants = ExperimentUtils
                .getNumberOfVariants(experiment.getEpoch());
        if (!numberOfVariants.status) {
            prepareErrorMessage(model, numberOfVariants.getError(), isErrorWithRedirect, redirectAttributes);
            return errorTarget;
        }
        experiment.setEpochVariant(numberOfVariants.getCount());
        experiment.strip();
        experimentRepository.save(experiment);
        return normalTarget;
    }

    private void prepareErrorMessage(Model model, String msg, boolean isErrorWithRedirect,
            final RedirectAttributes redirectAttributes) {
        if (isErrorWithRedirect) {
            redirectAttributes.addFlashAttribute("errorMessage", msg);
        } else {
            model.addAttribute("errorMessage", msg);
        }
    }

    public static void sortSnippetsByType(List<ExperimentSnippet> snippets) {
        snippets.sort(Comparator.comparing(ExperimentSnippet::getType));
    }

    @PostMapping("/experiment-metadata-add-commit/{id}")
    public String metadataAddCommit(@PathVariable Long id, String key, String value,
            final RedirectAttributes redirectAttributes) {
        Experiment experiment = experimentRepository.findById(id).orElse(null);
        if (experiment == null) {
            return "redirect:/launchpad/experiments";
        }
        if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#289.51 hyper param's key and value must not be null, key: " + key + ", value: " + value);
            return "redirect:/launchpad/experiment-edit/" + id;
        }
        if (experiment.getHyperParams() == null) {
            experiment.setHyperParams(new ArrayList<>());
        }
        String keyFinal = key.trim();
        boolean isExist = experiment.getHyperParams().stream().map(ExperimentHyperParams::getKey)
                .anyMatch(keyFinal::equals);
        if (isExist) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#289.52 hyper parameter " + key + " already exist");
            return "redirect:/launchpad/experiment-edit/" + id;
        }

        ExperimentHyperParams m = new ExperimentHyperParams();
        m.setExperiment(experiment);
        m.setKey(keyFinal);
        m.setValues(value.trim());
        experiment.getHyperParams().add(m);

        experimentRepository.save(experiment);
        return "redirect:/launchpad/experiment-edit/" + id;
    }

    @PostMapping("/experiment-metadata-edit-commit/{id}")
    public String metadataEditCommit(@PathVariable Long id, String key, String value,
            final RedirectAttributes redirectAttributes) {
        Experiment experiment = experimentRepository.findById(id).orElse(null);
        if (experiment == null) {
            redirectAttributes.addFlashAttribute("errorMessage", "#289.01 experiment wasn't found, id: " + id);
            return "redirect:/launchpad/experiments";
        }
        if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#289.02 hyper param's key and value must not be null, key: " + key + ", value: " + value);
            return "redirect:/launchpad/experiment-edit/" + id;
        }
        if (experiment.getHyperParams() == null) {
            experiment.setHyperParams(new ArrayList<>());
        }
        ExperimentHyperParams m = null;
        String keyFinal = key.trim();
        for (ExperimentHyperParams hyperParam : experiment.getHyperParams()) {
            if (hyperParam.getKey().equals(keyFinal)) {
                m = hyperParam;
                break;
            }
        }
        if (m == null) {
            m = new ExperimentHyperParams();
            m.setExperiment(experiment);
            m.setKey(keyFinal);
            experiment.getHyperParams().add(m);
        }
        m.setValues(value.trim());

        experimentRepository.save(experiment);
        return "redirect:/launchpad/experiment-edit/" + id;
    }

    @PostMapping("/experiment-snippet-add-commit/{id}")
    public String snippetAddCommit(@PathVariable Long id, String code,
            final RedirectAttributes redirectAttributes) {
        Experiment experiment = experimentRepository.findById(id).orElse(null);
        if (experiment == null) {
            redirectAttributes.addFlashAttribute("errorMessage", "#290.01 experiment wasn't found, id: " + id);
            return "redirect:/launchpad/experiments";
        }
        Long experimentId = experiment.getId();
        List<ExperimentSnippet> experimentSnippets = snippetService.getTaskSnippetsForExperiment(experimentId);

        SnippetVersion version = SnippetVersion.from(code);
        Snippet snippet = snippetCache.findByNameAndSnippetVersion(version.name, version.version);

        ExperimentSnippet ts = new ExperimentSnippet();
        ts.setExperimentId(experimentId);
        ts.setSnippetCode(code);
        ts.setType(snippet.type);

        List<ExperimentSnippet> list = new ArrayList<>(experimentSnippets);
        list.add(ts);

        sortSnippetsByType(list);

        experimentSnippetRepository.saveAll(list);
        return "redirect:/launchpad/experiment-edit/" + id;
    }

    @GetMapping("/experiment-metadata-delete-commit/{experimentId}/{id}")
    public String metadataDeleteCommit(@PathVariable long experimentId, @PathVariable Long id,
            final RedirectAttributes redirectAttributes) {
        ExperimentHyperParams hyperParams = experimentHyperParamsRepository.findById(id).orElse(null);
        if (hyperParams == null || experimentId != hyperParams.getExperiment().getId()) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#291.01 Hyper parameters misconfigured, try again.");
            return "redirect:/launchpad/experiment-edit/" + experimentId;
        }
        experimentHyperParamsRepository.deleteById(id);
        return "redirect:/launchpad/experiment-edit/" + experimentId;
    }

    @GetMapping("/experiment-metadata-default-add-commit/{experimentId}")
    public String metadataDefaultAddCommit(@PathVariable long experimentId,
            final RedirectAttributes redirectAttributes) {
        Experiment experiment = experimentRepository.findById(experimentId).orElse(null);
        if (experiment == null) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#292.01 experiment wasn't found, id: " + experimentId);
            return "redirect:/launchpad/experiments";
        }
        if (experiment.getHyperParams() == null) {
            experiment.setHyperParams(new ArrayList<>());
        }

        add(experiment, "RNN", "[LSTM, GRU, SimpleRNN]");
        add(experiment, "activation",
                "[hard_sigmoid, softplus, softmax, softsign, relu, tanh, sigmoid, linear, elu]");
        add(experiment, "optimizer", "[sgd, nadam, adagrad, adadelta, rmsprop, adam, adamax]");
        add(experiment, "batch_size", "[20, 40, 60]");
        add(experiment, "time_steps", "[5, 40, 60]");

        experimentRepository.save(experiment);
        return "redirect:/launchpad/experiment-edit/" + experimentId;
    }

    private void add(Experiment experiment, String key, String value) {
        ExperimentHyperParams param = getParams(experiment, key, value);
        List<ExperimentHyperParams> params = experiment.getHyperParams();
        for (ExperimentHyperParams p1 : params) {
            if (p1.getKey().equals(param.getKey())) {
                p1.setValues(param.getValues());
                return;
            }
        }
        params.add(param);
    }

    private ExperimentHyperParams getParams(Experiment experiment, String key, String value) {
        ExperimentHyperParams params = new ExperimentHyperParams();
        params.setExperiment(experiment);
        params.setKey(key);
        params.setValues(value);
        return params;
    }

    @GetMapping("/experiment-snippet-delete-commit/{experimentId}/{id}")
    public String snippetDeleteCommit(@PathVariable long experimentId, @PathVariable Long id,
            final RedirectAttributes redirectAttributes) {
        ExperimentSnippet snippet = experimentSnippetRepository.findById(id).orElse(null);
        if (snippet == null || experimentId != snippet.getExperimentId()) {
            redirectAttributes.addFlashAttribute("errorMessage", "#293.01 Snippet is misconfigured. Try again");
            return "redirect:/launchpad/experiment-edit/" + experimentId;
        }
        experimentSnippetRepository.deleteById(id);
        return "redirect:/launchpad/experiment-edit/" + experimentId;
    }

    @GetMapping("/experiment-delete/{id}")
    public String delete(@PathVariable Long id, Model model, final RedirectAttributes redirectAttributes) {
        Experiment experiment = experimentRepository.findById(id).orElse(null);
        if (experiment == null) {
            redirectAttributes.addFlashAttribute("errorMessage", "#294.01 experiment wasn't found, id: " + id);
            return "redirect:/launchpad/experiments";
        }
        model.addAttribute("experiment", experiment);
        return "launchpad/experiment-delete";
    }

    @PostMapping("/experiment-delete-commit")
    public String deleteCommit(Long id, final RedirectAttributes redirectAttributes) {
        Experiment experiment = experimentRepository.findById(id).orElse(null);
        if (experiment == null) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#283.01 experiment wasn't found, experimentId: " + id);
            return "redirect:/launchpad/experiments";
        }
        experimentSnippetRepository.deleteByExperimentId(id);
        experimentFeatureRepository.deleteByExperimentId(id);
        experimentRepository.deleteById(id);
        return "redirect:/launchpad/experiments";
    }

    @PostMapping("/task-rerun/{id}")
    public @ResponseBody boolean rerunSequence(@PathVariable long id) {
        if (true)
            throw new IllegalStateException("Not implemented yet");
        /*
                Task seq = taskRepository.findById(id).orElse(null);
                if (seq == null) {
        log.warn("#291.01 Can't re-run sequence {}, sequence with such id wasn't found", id);
        return false;
                }
                ExperimentFeature feature = experimentFeatureRepository.findById(seq.experimentFeatureId).orElse(null);
                if (feature == null) {
        log.warn("#292.01 Can't re-run sequence {}, ExperimentFeature wasn't found for this sequence", id);
        return false;
                }
                Experiment experiment = experimentRepository.findById(seq.getExperimentId()).orElse(null);
                if (experiment == null) {
        log.warn("#293.01 Can't re-run sequence {}, this sequence is orphan and doesn't belong to any experiment", id);
        return false;
                }
            
                seq.setCompletedOn(null);
                seq.setCompleted(false);
                seq.setMetrics(null);
                seq.setSnippetExecResults(null);
                seq.setStationId(null);
                seq.setAssignedOn(null);
                taskRepository.save(seq);
            
                feature.setExecStatus(FeatureExecStatus.unknown.code);
                feature.setFinished(false);
                feature.setInProgress(true);
                experimentFeatureRepository.save(feature);
            
        */
        return true;
    }

    @GetMapping("/experiment-launch/{experimentId}")
    public String launch(@PathVariable long experimentId, final RedirectAttributes redirectAttributes) {
        Experiment experiment = experimentRepository.findById(experimentId).orElse(null);
        if (experiment == null) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#284.01 experiment wasn't found, experimentId: " + experimentId);
            return "redirect:/launchpad/experiments";
        }
        if (experiment.isLaunched()) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#284.02 experiment was already launched, experimentId: " + experimentId);
            return "redirect:/launchpad/experiment-info/" + experimentId;
        }

        experiment.setLaunched(true);
        experiment.setLaunchedOn(System.currentTimeMillis());
        experiment.setExecState(Enums.FlowInstanceExecState.STARTED.code);
        experimentRepository.save(experiment);

        return "redirect:/launchpad/experiment-info/" + experimentId;
    }

    @GetMapping("/experiment-target-exec-state/{state}/{experimentId}")
    public String stop(@PathVariable String state, @PathVariable long experimentId,
            final RedirectAttributes redirectAttributes) {
        Experiment experiment = experimentRepository.findById(experimentId).orElse(null);
        if (experiment == null) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#285.01 experiment wasn't found, experimentId: " + experimentId);
            return "redirect:/launchpad/experiments";
        }
        if (experiment.getFlowInstanceId() == null) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#285.03 Flow instance wasn't assigned, experimentId: " + experimentId);
            return "redirect:/launchpad/experiments";
        }
        if (!experiment.isLaunched()) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#285.04 Experiment wasn't started yet, experimentId: " + experimentId);
            return "redirect:/launchpad/experiment-info/" + experimentId;
        }
        Enums.FlowInstanceExecState execState = Enums.FlowInstanceExecState.valueOf(state.toUpperCase());

        if ((execState == Enums.FlowInstanceExecState.STARTED
                && experiment.getExecState() == Enums.FlowInstanceExecState.STARTED.code)
                || (execState == Enums.FlowInstanceExecState.STOPPED
                        && experiment.getExecState() == Enums.FlowInstanceExecState.STOPPED.code)) {
            redirectAttributes.addFlashAttribute("errorMessage",
                    "#285.05 Experiment is already in target state: " + execState.toString());
            return "redirect:/launchpad/experiment-info/" + experimentId;
        }

        experiment.setExecState(execState.code);
        experimentRepository.save(experiment);

        return "redirect:/launchpad/experiment-info/" + experimentId;
    }

}