/******************************************************************************* * Copyright 2013 * Ubiquitous Knowledge Processing (UKP) Lab * Technische Universität Darmstadt * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package de.tudarmstadt.ukp.csniper.webapp.evaluation.page; import static java.util.Arrays.asList; import static java.util.Collections.singleton; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Random; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.uima.UIMAException; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxEventBehavior; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox; import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; import org.apache.wicket.extensions.ajax.markup.html.AjaxEditableLabel; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow.CloseButtonCallback; import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator; import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder; import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; import org.apache.wicket.feedback.FeedbackMessage; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.CheckBox; import org.apache.wicket.markup.html.form.CheckGroup; import org.apache.wicket.markup.html.form.ChoiceRenderer; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.IChoiceRenderer; import org.apache.wicket.markup.html.form.ListMultipleChoice; import org.apache.wicket.markup.html.form.NumberTextField; import org.apache.wicket.markup.html.form.Radio; import org.apache.wicket.markup.html.form.RadioChoice; import org.apache.wicket.markup.html.form.RadioGroup; import org.apache.wicket.markup.html.form.TextArea; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.request.resource.ContextRelativeResource; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.core.util.lang.PropertyResolver; import org.odlabs.wiquery.ui.tabs.Tabs; import org.radeox.api.engine.RenderEngine; import org.radeox.api.engine.context.RenderContext; import org.radeox.engine.BaseRenderEngine; import org.radeox.engine.context.BaseRenderContext; import org.springframework.dao.NonTransientDataAccessException; import org.springframework.security.core.context.SecurityContextHolder; import org.wicketstuff.progressbar.ProgressBar; import de.tudarmstadt.ukp.csniper.webapp.DefaultValues; import de.tudarmstadt.ukp.csniper.webapp.analysis.ParseTreeResource; import de.tudarmstadt.ukp.csniper.webapp.analysis.uima.ParsingPipeline; import de.tudarmstadt.ukp.csniper.webapp.evaluation.EvaluationRepository; import de.tudarmstadt.ukp.csniper.webapp.evaluation.MlPipeline; import de.tudarmstadt.ukp.csniper.webapp.evaluation.SortableEvaluationResultDataProvider; import de.tudarmstadt.ukp.csniper.webapp.evaluation.SortableEvaluationResultDataProvider.ResultFilter; import de.tudarmstadt.ukp.csniper.webapp.evaluation.model.AdditionalColumn; import de.tudarmstadt.ukp.csniper.webapp.evaluation.model.CachedParse; import de.tudarmstadt.ukp.csniper.webapp.evaluation.model.EvaluationItem; import de.tudarmstadt.ukp.csniper.webapp.evaluation.model.EvaluationResult; import de.tudarmstadt.ukp.csniper.webapp.evaluation.model.Mark; import de.tudarmstadt.ukp.csniper.webapp.evaluation.model.Query; import de.tudarmstadt.ukp.csniper.webapp.evaluation.model.SampleSet; import de.tudarmstadt.ukp.csniper.webapp.page.ApplicationPageBase; import de.tudarmstadt.ukp.csniper.webapp.project.ProjectRepository; import de.tudarmstadt.ukp.csniper.webapp.project.model.AnnotationType; import de.tudarmstadt.ukp.csniper.webapp.search.ContextProvider; import de.tudarmstadt.ukp.csniper.webapp.search.CorpusService; import de.tudarmstadt.ukp.csniper.webapp.search.PreparedQuery; import de.tudarmstadt.ukp.csniper.webapp.search.SearchEngine; import de.tudarmstadt.ukp.csniper.webapp.statistics.model.AggregatedEvaluationResult; import de.tudarmstadt.ukp.csniper.webapp.support.task.ITaskService; import de.tudarmstadt.ukp.csniper.webapp.support.task.Task; import de.tudarmstadt.ukp.csniper.webapp.support.task.TaskProgressionModel; import de.tudarmstadt.ukp.csniper.webapp.support.task.Task.Message; import de.tudarmstadt.ukp.csniper.webapp.support.uima.CasHolder; import de.tudarmstadt.ukp.csniper.webapp.support.wicket.AnalysisPanel; import de.tudarmstadt.ukp.csniper.webapp.support.wicket.CustomDataTable; import de.tudarmstadt.ukp.csniper.webapp.support.wicket.DbFieldMaxLengthValidator; import de.tudarmstadt.ukp.csniper.webapp.support.wicket.EmbeddableImage; import de.tudarmstadt.ukp.csniper.webapp.support.wicket.ExtendedIndicatingAjaxButton; import de.tudarmstadt.ukp.csniper.webapp.support.wicket.LocalizerUtil; import de.tudarmstadt.ukp.csniper.webapp.support.wicket.ThresholdLink; /** * Evaluation Page */ @SuppressWarnings({ "rawtypes", "unused" }) public class EvaluationPage extends ApplicationPageBase { private static final long serialVersionUID = 1L; private Tabs tabs; private ParentOptionsForm parentOptionsForm; private QueryForm queryForm; private ReviewForm reviewForm; private SamplesetForm samplesetForm; private FindForm findForm; private FilterForm filterForm; private ShowColumnsForm showColumnsForm; private LimitForm limitForm; private WebMarkupContainer contextViewsContainer; private ListView<ContextView> contextViews; private ExtendedIndicatingAjaxButton saveButton; private ExtendedIndicatingAjaxButton predictButton; private ExtendedIndicatingAjaxButton samplesetButton; private List<IColumn<EvaluationResult, String>> columns; private boolean showResultColumns; private Component resultTable; private ModalWindow samplesetModal; private ModalWindow predictionModal; private ModalWindow analysisModal; private ParsingPipeline pp; private SortableEvaluationResultDataProvider dataProvider; private boolean contextAvailable = false; private Map<AdditionalColumn, Boolean> showColumns; private static final int ROWS_PER_PAGE = 10; private static final int MAX_RESULTS = 1000; private static final int MIN_ITEMS_ANNOTATED = 10; @SpringBean(name = "evaluationRepository") private EvaluationRepository repository; @SpringBean(name = "projectRepository") private ProjectRepository projectRepository; @SpringBean(name = "corpusService") private CorpusService corpusService; @SpringBean(name = "contextProvider") private ContextProvider contextProvider; private class ReviewForm extends Form<ReviewFormModel> { private static final long serialVersionUID = 1L; public ReviewForm(String aId) { super(aId, new CompoundPropertyModel<ReviewFormModel>(new ReviewFormModel())); add(new CheckBox("disputedOnly")); add(new ExtendedIndicatingAjaxButton("reviewButton", new Model<String>( "Review evaluations"), new Model<String>("Running query ...")) { private static final long serialVersionUID = 1L; // { // setDefaultFormProcessing(false); // } @Override public void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm) { AnnotationType type = parentOptionsForm.typeInput.getModelObject(); if (type == null) { error(LocalizerUtil.getString(parentOptionsForm.typeInput, "Required")); aTarget.add(getFeedbackPanel()); return; } ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); String user = SecurityContextHolder.getContext().getAuthentication().getName(); List<EvaluationResult> results; if (ReviewForm.this.getModelObject().disputedOnly) { results = repository.listDisputedEvaluationResults(pModel.collectionId, pModel.type, user); } else { results = repository.listEvaluationResults(pModel.collectionId, pModel.type, user); } // persis. results: hide saveButton, show result columns and filter options limitForm.setVisible(false); filterForm.setChoices(ResultFilter.values()); // only show showColumnsForm.setVisible(true && !pModel.type.getAdditionalColumns() .isEmpty()); showResultColumns(true); saveButton.setVisible(false); predictButton.setVisible(true); samplesetButton.setVisible(true); // update dataprovider dataProvider = new SortableEvaluationResultDataProvider(results); dataProvider.setSort("item.documentId", SortOrder.ASCENDING); dataProvider.setFilter(ResultFilter.ALL); // then update the table resultTable = resultTable .replaceWith(new CustomDataTable<EvaluationResult>("resultTable", getAllColumns(pModel.type), dataProvider, ROWS_PER_PAGE)); contextAvailable = false; updateComponents(aTarget); } @Override public void onError(AjaxRequestTarget aTarget, Form<?> aForm) { super.onError(aTarget, aForm); // Make sure the feedback messages are rendered aTarget.add(getFeedbackPanel()); } }); } } private static class ReviewFormModel implements Serializable { private static final long serialVersionUID = 1L; private boolean disputedOnly = false; } private class QueryForm extends Form<QueryFormModel> { private static final long serialVersionUID = 1L; final DropDownChoice<SearchEngine> engineInput; final TextArea<String> queryInput; final TextField<String> commentInput; final DropDownChoice<Query> historyQueryInput; final ExtendedIndicatingAjaxButton queryButton; @SuppressWarnings({ "serial" }) public QueryForm(String id) { super(id, new CompoundPropertyModel<QueryFormModel>(new QueryFormModel())); // Tab contents engineInput = new DropDownChoice<SearchEngine>("engine", new LoadableDetachableModel<List<SearchEngine>>() { @Override protected List<SearchEngine> load() { ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); return corpusService.listEngines(pModel.collectionId); } }, new IChoiceRenderer<SearchEngine>() { @Override public Object getDisplayValue(SearchEngine aObject) { return aObject.getName(); } @Override public String getIdValue(SearchEngine aObject, int aIndex) { return aObject.getName(); } }) { @Override protected boolean wantOnSelectionChangedNotifications() { return true; } @Override protected void onSelectionChanged(SearchEngine aEngine) { historyQueryInput.setModelObject(null); queryInput.setModelObject(""); commentInput.setModelObject(""); } }; engineInput.setRequired(true); queryInput = new TextArea<String>("query"); queryInput.setRequired(true); queryInput.add(new DbFieldMaxLengthValidator(projectRepository, "Query", "query")); commentInput = new TextField<String>("comment"); commentInput.add(new DbFieldMaxLengthValidator(projectRepository, "Query", "comment")); historyQueryInput = (QueryDropDown) new QueryDropDown("historyQuery"); // submit button queryButton = new ExtendedIndicatingAjaxButton("queryButton", new Model<String>( "Submit query"), new Model<String>("Running query ...")) { @Override public void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm) { AnnotationType type = parentOptionsForm.typeInput.getModelObject(); if (type == null) { error(LocalizerUtil.getString(parentOptionsForm.typeInput, "Required")); aTarget.add(getFeedbackPanel()); return; } QueryFormModel model = QueryForm.this.getModelObject(); ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); String user = SecurityContextHolder.getContext().getAuthentication().getName(); List<EvaluationItem> items; // only execute query if it was set if (!StringUtils.isBlank(model.query)) { PreparedQuery query = null; try { query = model.engine.createQuery(pModel.type.getName(), pModel.collectionId, model.query); query.setMaxResults(MAX_RESULTS); items = query.execute(); int resultCount = query.size(); limitForm.setResultCount(resultCount); // new results: show limitForm if too many results, show saveButton limitForm.setVisible(resultCount > MAX_RESULTS); saveButton.setVisible(true); } catch (NonTransientDataAccessException e) { error("Error executing query " + model.query + ": " + e.getMessage()); items = new ArrayList<EvaluationItem>(); // error -> no results: hide limitForm, saveButton limitForm.setVisible(false); saveButton.setVisible(false); } finally { // IOUtils.closeQuietly(query); } // new items (or error), so show only item columns, no filters filterForm.setChoices(); showColumnsForm.setVisible(false); showResultColumns(false); predictButton.setVisible(false); samplesetButton.setVisible(false); } // else do not execute cqp, instead fetch all results for given type from db // TODO dead code (query is a required field) - do we want this functionality? else { items = repository.listEvaluationItems(pModel.collectionId, pModel.type.getName()); // persis. results: hide saveButton, show result columns and filter options limitForm.setVisible(false); filterForm.setChoices(ResultFilter.values()); showColumnsForm.setVisible(true && !pModel.type.getAdditionalColumns() .isEmpty()); showResultColumns(true); saveButton.setVisible(false); predictButton.setVisible(true); samplesetButton.setVisible(true); } // update dataprovider dataProvider = new SortableEvaluationResultDataProvider( createEvaluationResults(items)); dataProvider.setSort("item.documentId", SortOrder.ASCENDING); dataProvider.setFilter(ResultFilter.ALL); // then update the table resultTable = resultTable .replaceWith(new CustomDataTable<EvaluationResult>("resultTable", getAllColumns(pModel.type), dataProvider, ROWS_PER_PAGE)); contextAvailable = true; updateComponents(aTarget); } @Override public void onError(AjaxRequestTarget aTarget, Form<?> aForm) { super.onError(aTarget, aForm); // Make sure the feedback messages are rendered aTarget.add(getFeedbackPanel()); } }; add(engineInput); add(historyQueryInput); add(queryInput); add(commentInput); add(queryButton); } private class QueryDropDown extends DropDownChoice<Query> { private static final long serialVersionUID = 1L; private final Query examplesHeader = new Query(null, "-- Examples --", null, null); private final Query historyHeader = new Query(null, "-- History --", null, null); public QueryDropDown(String aId) { super(aId); setChoiceRenderer(new ChoiceRenderer<Query>() { private static final long serialVersionUID = 1L; @Override public Object getDisplayValue(Query aObject) { return aObject.getQuery(); } }); setChoices(new LoadableDetachableModel<List<Query>>() { private static final long serialVersionUID = 1L; @Override protected List<Query> load() { ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); QueryFormModel model = QueryForm.this.getModelObject(); List<Query> queries = new ArrayList<Query>(); if (model.engine != null) { String user = SecurityContextHolder.getContext().getAuthentication() .getName(); List<Query> exampleQueries = getExampleQueries(model.engine.getName(), pModel.collectionId, pModel.type); if (!exampleQueries.isEmpty()) { queries.add(examplesHeader); queries.addAll(exampleQueries); queries.add(historyHeader); } queries.addAll(repository.listQueries(model.engine.getName(), pModel.collectionId, pModel.type, user)); } return queries; } }); } @Override protected boolean wantOnSelectionChangedNotifications() { return true; } @Override protected void onSelectionChanged(Query newSelection) { if (newSelection.getType() != null) { queryInput.setModelObject(newSelection.getQuery()); commentInput.setModelObject(newSelection.getComment()); } } } } private static class QueryFormModel implements Serializable { private static final long serialVersionUID = 1L; private SearchEngine engine = null; private String query = ""; private String comment = ""; private Query historyQuery; private boolean randomize = false; } private class SamplesetForm extends Form<SamplesetFormModel> { private static final long serialVersionUID = 1L; public SamplesetForm(String aId) { super(aId, new CompoundPropertyModel<SamplesetFormModel>(new SamplesetFormModel())); SamplesetDropdown samplesetDropdown = new SamplesetDropdown("sampleset"); samplesetDropdown.setRequired(true); add(samplesetDropdown); add(new ExtendedIndicatingAjaxButton("samplesetButton", new Model<String>( "Load sampleset"), new Model<String>("Loading ...")) { private static final long serialVersionUID = 1L; @Override public void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm) { ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); SamplesetFormModel model = samplesetForm.getModelObject(); String user = SecurityContextHolder.getContext().getAuthentication().getName(); List<EvaluationResult> results = repository.listEvaluationResults(user, model.sampleset); // query results: show saveButton, hide result columns and filter options limitForm.setVisible(false); filterForm.setChoices(ResultFilter.values()); showColumnsForm.setVisible(true && !pModel.type.getAdditionalColumns() .isEmpty()); showResultColumns(true); saveButton.setVisible(false); predictButton.setVisible(true); samplesetButton.setVisible(true); // update dataprovider dataProvider = new SortableEvaluationResultDataProvider(results); dataProvider.setSort("item.documentId", SortOrder.ASCENDING); dataProvider.setFilter(ResultFilter.ALL); // then update the table resultTable = resultTable .replaceWith(new CustomDataTable<EvaluationResult>("resultTable", getAllColumns(pModel.type), dataProvider, ROWS_PER_PAGE)); updateComponents(aTarget); } @Override public void onError(AjaxRequestTarget aTarget, Form<?> aForm) { super.onError(aTarget, aForm); // Make sure the feedback messages are rendered aTarget.add(getFeedbackPanel()); } }); } } class SamplesetDropdown extends DropDownChoice<SampleSet> { private static final long serialVersionUID = 1L; public SamplesetDropdown(String aId) { super(aId); setChoices(new LoadableDetachableModel<List<SampleSet>>() { private static final long serialVersionUID = 1L; @Override protected List<SampleSet> load() { ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); String user = SecurityContextHolder.getContext().getAuthentication().getName(); List<SampleSet> samplesets = new ArrayList<SampleSet>(); // TODO what exactly? next time it would be helpful if i leave myself a note... if (pModel.collectionId == null || pModel.type == null) return samplesets; samplesets.addAll(repository.listSampleSets(pModel.collectionId, pModel.type.getName(), user)); return samplesets; } }); setChoiceRenderer(new ChoiceRenderer<SampleSet>("name")); } } public static class SamplesetFormModel implements Serializable { private static final long serialVersionUID = 1L; private boolean createNew = true; private String newname = ""; private String newcomment = ""; private SampleSet sampleset; } private class FindForm extends Form<Void> { private static final long serialVersionUID = 1L; private ProgressBar progressBar; private TaskProgressionModel progressionModel; private ExtendedIndicatingAjaxButton findButton; private AjaxLink stopButton; public FindForm(String aId) { super(aId); progressionModel = new TaskProgressionModel() { private static final long serialVersionUID = 1L; @Override protected ITaskService getTaskService() { return EvaluationPage.this.getTaskService(); } }; add(findButton = new ExtendedIndicatingAjaxButton("findButton", new Model<String>( "Find"), new Model<String>("Finding ...")) { private static final long serialVersionUID = 1L; @Override public void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm) { final ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); final String user = SecurityContextHolder.getContext().getAuthentication() .getName(); // update dataprovider dataProvider = new SortableEvaluationResultDataProvider(); dataProvider.setSort("item.documentId", SortOrder.ASCENDING); dataProvider.setFilter(ResultFilter.ALL); // then update the table resultTable = resultTable .replaceWith(new CustomDataTable<EvaluationResult>("resultTable", getAllColumns(pModel.type), dataProvider, ROWS_PER_PAGE)); resultTable.setOutputMarkupPlaceholderTag(true); aTarget.add(resultTable.setVisible(false)); contextAvailable = false; // disable button setVisible(false); aTarget.add(stopButton.setVisible(true)); aTarget.add(saveButton.setVisible(false)); aTarget.add(predictButton.setVisible(false)); aTarget.add(samplesetButton.setVisible(false)); aTarget.add(showColumnsForm.setVisible(false)); filterForm.setChoices(); aTarget.add(filterForm); aTarget.add(limitForm.setVisible(false)); aTarget.add(FindForm.this); showResultColumns(false); updateComponents(aTarget); // Schedule and start a new task Long taskId = EvaluationPage.this.getTaskService().scheduleAndStart(new Task() { @Override protected void run() { try { // get aggregated results List<AggregatedEvaluationResult> aggregatedResults = repository .listAggregatedResults(singleton(pModel.collectionId), singleton(pModel.type), new HashSet<String>( repository.listUsers()), 0.0, 0.0); if (aggregatedResults.isEmpty()) { return; } // create training list List<EvaluationResult> trainingList = MlPipeline .convertToSimple(aggregatedResults); File modelDir = MlPipeline.train(trainingList, repository); String language = corpusService.getCorpus(pModel.collectionId) .getLanguage(); int max = (int) repository.getCachedParsesCount(pModel.collectionId); // Create list of pages int pageSize = 1000; int[][] pages = repository.listCachedParsesPages( pModel.collectionId, pageSize); // Shuffle pages Random random = new Random(); for (int i = 0; i < pages.length; i++) { int randomPosition = random.nextInt(pages.length); int[] temp = pages[i]; pages[i] = pages[randomPosition]; pages[randomPosition] = temp; } int goal = 1000; setTotal(goal); for (int p = 0; p < pages.length; p++) { checkCanceled(); List<CachedParse> parses = repository.listCachedParses( pModel.collectionId, pages[p][0], pages[p][1]); // In the last iteration, the page size is cropped to the size // of the last page. pageSize = parses.size(); List<EvaluationResult> results = MlPipeline.classifyPreParsed( modelDir, parses, pModel.type.getName(), user); // Keep only the correct ones ListIterator<EvaluationResult> ri = results.listIterator(); while (ri.hasNext()) { EvaluationResult r = ri.next(); Mark mark = Mark.fromString(r.getResult()); if (mark != Mark.PRED_CORRECT) { ri.remove(); } } // Putting this here to avoid adding results of a still // running iteration to the table because this could tigger // a concurrent modification problem. Checking for cancelled // here again should reduce this risk to a minimum. checkCanceled(); // Add to table dataProvider.getResults().addAll(results); setCurrent(dataProvider.getResults().size()); // Check if goal was reached if (dataProvider.getResults().size() >= goal) { break; } } } catch (UIMAException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }); // Set taskId for model progressionModel.setTaskId(taskId); // Start the progress bar, will set visibility to true progressBar.start(aTarget); } @Override public void onError(AjaxRequestTarget aTarget, Form<?> aForm) { super.onError(aTarget, aForm); // Make sure the feedback messages are rendered aTarget.add(getFeedbackPanel()); } }); add(progressBar = new ProgressBar("progress", progressionModel) { private static final long serialVersionUID = 1L; @Override protected void onFinished(AjaxRequestTarget aTarget) { finishTask(aTarget); } }); // Hide progress bar initially progressBar.setVisible(false); add(stopButton = new AjaxLink("stopButton") { private static final long serialVersionUID = 1L; @Override public void onClick(AjaxRequestTarget aTarget) { cancel(aTarget); } }); stopButton.setVisible(false); } protected void cancel(AjaxRequestTarget aTarget) { getTaskService().cancel(progressionModel.getTaskId()); finishTask(aTarget); } protected void finishTask(AjaxRequestTarget aTarget) { for (Message m : getTaskService().getMessages(progressionModel.getTaskId())) { error(m.messageKey); } // finish the task! getTaskService().finish(progressionModel.getTaskId()); // Hide progress bar after finish progressBar.setVisible(false); // re-enable button aTarget.add(findButton.setVisible(true)); aTarget.add(stopButton.setVisible(false)); aTarget.add(saveButton.setVisible(true)); aTarget.add(resultTable.setVisible(true)); aTarget.add(FindForm.this); updateComponents(aTarget); } } public static class PredictionFormModel implements Serializable { private static final long serialVersionUID = 1L; private Set<String> users = new HashSet<String>(); private Double userThreshold = DefaultValues.DEFAULT_USER_THRESHOLD; private Double confidenceThreshold = DefaultValues.DEFAULT_CONFIDENCE_THRESHOLD; private boolean onlycurrent = true; } private class ParentOptionsForm extends Form<ParentOptionsFormModel> { private static final long serialVersionUID = 1L; final DropDownChoice<String> collectionIdInput; final DropDownChoice<AnnotationType> typeInput; @SuppressWarnings({ "serial" }) public ParentOptionsForm(String id) { super(id, new CompoundPropertyModel<ParentOptionsFormModel>( new ParentOptionsFormModel())); // collection dropdown collectionIdInput = new DropDownChoice<String>("collectionId", new LoadableDetachableModel<List<String>>() { @Override protected List<String> load() { return corpusService.listCorpora(); } }, new IChoiceRenderer<String>() { @Override public Object getDisplayValue(String aObject) { return corpusService.getCorpus(aObject).getName(); } @Override public String getIdValue(String aObject, int aIndex) { return corpusService.getCorpus(aObject).getId(); } }) { @Override protected boolean wantOnSelectionChangedNotifications() { return true; } @Override protected void onSelectionChanged(String aNewSelection) { queryForm.engineInput.setModelObject(null); queryForm.historyQueryInput.setModelObject(null); queryForm.queryInput.setModelObject(""); queryForm.commentInput.setModelObject(""); tabs.setVisible(aNewSelection != null && ParentOptionsForm.this.getModelObject().type != null); } }; typeInput = new DropDownChoice<AnnotationType>("type") { { setRequired(true); setChoices(new LoadableDetachableModel<List<AnnotationType>>() { private static final long serialVersionUID = 1L; @Override protected List<AnnotationType> load() { return projectRepository.listAnnotationTypes(); } }); setChoiceRenderer(new ChoiceRenderer<AnnotationType>("name")); } @Override protected boolean wantOnSelectionChangedNotifications() { return true; } @Override protected void onSelectionChanged(AnnotationType aNewSelection) { queryForm.engineInput.setModelObject(null); queryForm.historyQueryInput.setModelObject(null); queryForm.queryInput.setModelObject(""); queryForm.commentInput.setModelObject(""); // get default visibility settings for additional columns // projectRepository.refreshEntity(aNewSelection); // type changed: hide everything limitForm.setVisible(false); filterForm.setChoices(); showColumnsForm.setVisible(false); showResultColumns(false); saveButton.setVisible(false); predictButton.setVisible(false); samplesetButton.setVisible(false); tabs.setVisible(aNewSelection != null && ParentOptionsForm.this.getModelObject().collectionId != null); } }; add(typeInput).add(collectionIdInput); } } private static class ParentOptionsFormModel implements Serializable { private static final long serialVersionUID = 1L; private String collectionId; private AnnotationType type; } private class FilterForm extends Form { private static final long serialVersionUID = 1L; private ResultFilter filter = ResultFilter.ALL; private RadioChoice<ResultFilter> filterGroup; public FilterForm(String id) { super(id); filterGroup = new RadioChoice<ResultFilter>("filterGroup", new PropertyModel<ResultFilter>(this, "filter"), Arrays.asList(ResultFilter .values()), new ChoiceRenderer<ResultFilter>("label")) { private static final long serialVersionUID = 1L; @Override protected boolean wantOnSelectionChangedNotifications() { return true; } @Override protected void onSelectionChanged(final Object newSelection) { ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); filter = (ResultFilter) newSelection; // update results based on new filter dataProvider.setFilter(filter); resultTable = resultTable .replaceWith(new CustomDataTable<EvaluationResult>("resultTable", getAllColumns(pModel.type), dataProvider, ROWS_PER_PAGE)); } }; filterGroup.setSuffix("\n"); add(filterGroup); } public void setChoices(ResultFilter... aChoices) { if (aChoices.length == 0) { filter = ResultFilter.ALL; setVisible(false); filterGroup.setChoices(Collections.EMPTY_LIST); } else { setVisible(true); if (!Arrays.asList(aChoices).contains(filter)) { filter = aChoices[0]; } filterGroup.setChoices(Arrays.asList(aChoices)); } } } private class ShowColumnsForm extends Form<List<AdditionalColumn>> { private static final long serialVersionUID = 1L; private CheckGroup<AdditionalColumn> showColumnsGroup; private ListView<AdditionalColumn> lv; public ShowColumnsForm(String id) { super(id); showColumnsGroup = new CheckGroup<AdditionalColumn>("showColumnsGroup"); lv = new ListView<AdditionalColumn>("showColumnsList", new LoadableDetachableModel<List<AdditionalColumn>>() { private static final long serialVersionUID = 1L; @Override protected List<AdditionalColumn> load() { return parentOptionsForm.getModelObject().type.getAdditionalColumns(); } }) { private static final long serialVersionUID = 1L; @Override protected void populateItem(final ListItem<AdditionalColumn> aItem) { AdditionalColumn ac = aItem.getModelObject(); aItem.add(new AjaxCheckBox("checkbox", new PropertyModel<Boolean>(ac, "showColumn")) { private static final long serialVersionUID = 1L; @Override protected void onUpdate(AjaxRequestTarget aTarget) { aTarget.add(resultTable); } }.setLabel(new Model<String>(ac.getName()))); } }; showColumnsGroup.add(lv); showColumnsGroup.setOutputMarkupId(true); add(showColumnsGroup); } } private class LimitForm extends Form { private static final long serialVersionUID = 1L; private int resultLimit = 0; private Label limitLabel; public LimitForm(String id) { super(id); add(limitLabel = new Label("limitLabel", "")); add(new TextField<Integer>("limitInput", new PropertyModel<Integer>(this, "resultLimit"))); add(new ExtendedIndicatingAjaxButton("limitButton", new Model<String>("Show results"), new Model<String>("Retrieving results")) { private static final long serialVersionUID = 1L; @Override protected void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm) { QueryFormModel model = queryForm.getModelObject(); ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); PreparedQuery query = null; List<EvaluationItem> items; try { query = model.engine.createQuery(pModel.type.getName(), pModel.collectionId, model.query); query.setMaxResults(resultLimit); items = query.execute(); } catch (NonTransientDataAccessException e) { error("Error executing query " + model.query + ": " + e.getMessage()); items = new ArrayList<EvaluationItem>(); } finally { // IOUtils.closeQuietly(query); } // query results: show saveButton, hide result columns and filter options limitForm.setVisible(false); filterForm.setChoices(); showColumnsForm.setVisible(false); showResultColumns(false); saveButton.setVisible(true); predictButton.setVisible(false); samplesetButton.setVisible(false); // update dataprovider dataProvider = new SortableEvaluationResultDataProvider( createEvaluationResults(items)); dataProvider.setSort("item.documentId", SortOrder.ASCENDING); dataProvider.setFilter(ResultFilter.ALL); // then update the table resultTable = resultTable .replaceWith(new CustomDataTable<EvaluationResult>("resultTable", getAllColumns(pModel.type), dataProvider, ROWS_PER_PAGE)); updateComponents(aTarget); } @Override public void onError(AjaxRequestTarget aTarget, Form<?> aForm) { super.onError(aTarget, aForm); // Make sure the feedback messages are rendered aTarget.add(getFeedbackPanel()); } }); } public void setResultCount(int aResultCount) { resultLimit = aResultCount; limitLabel.setDefaultModelObject("There are " + aResultCount + " results in total. How many results shall be shown? "); } } private class PredictionPanel extends Panel { private static final long serialVersionUID = 1L; private Form<PredictionFormModel> form; private ProgressBar progressBar; private TaskProgressionModel progressionModel; public PredictionPanel(String aId) { super(aId); progressionModel = new TaskProgressionModel() { private static final long serialVersionUID = 1L; @Override protected ITaskService getTaskService() { return EvaluationPage.this.getTaskService(); } }; add(form = new Form<PredictionFormModel>("predictionForm", new CompoundPropertyModel<PredictionFormModel>(new PredictionFormModel()))); form.add(new AjaxCheckBox("onlycurrent") { private static final long serialVersionUID = 1L; @Override protected void onUpdate(AjaxRequestTarget aTarget) { boolean greyOut = !form.getModelObject().onlycurrent; form.get("users").setEnabled(greyOut); form.get("userThreshold").setEnabled(greyOut); form.get("confidenceThreshold").setEnabled(greyOut); aTarget.add(form.get("users"), form.get("userThreshold"), form.get("confidenceThreshold")); } }); List<String> users = repository.listUsers(); Collections.sort(users); form.add(new ListMultipleChoice<String>("users", users).setOutputMarkupId(true) .setEnabled(false)); form.add(new ThresholdLink("thresholdHelp")); form.add(new NumberTextField<Double>("userThreshold").setMinimum(0.0).setMaximum(1.0) .setOutputMarkupId(true).setEnabled(false)); form.add(new NumberTextField<Double>("confidenceThreshold").setMinimum(0.0) .setMaximum(1.0).setOutputMarkupId(true).setEnabled(false)); form.add(new ExtendedIndicatingAjaxButton("predictionOkButton", new Model<String>( "Predict results"), new Model<String>("Predicting...")) { private static final long serialVersionUID = 1L; @Override public void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm) { final ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); final PredictionFormModel model = form.getModelObject(); // Schedule and start a new task Long taskId = EvaluationPage.this.getTaskService().scheduleAndStart(new Task() { @Override protected void run() { List<EvaluationResult> results = dataProvider.getResults(); if (results.isEmpty()) { return; } String language = corpusService.getCorpus( results.get(0).getItem().getCollectionId()).getLanguage(); try { boolean result; String errorMsg; MlPipeline mlp = new MlPipeline(language); mlp.setTask(this); mlp.setRepostitory(repository); // if taking only current results into account, ignore user list if (model.onlycurrent) { // parse, obtain Penn trees, predict results result = mlp.predict(results, MIN_ITEMS_ANNOTATED); errorMsg = "You have not annotated enough items to use the " + "prediction feature. Please annotate at least [" + MIN_ITEMS_ANNOTATED + "] items manually."; } else { // parse, obtain Penn trees, predict results boolean b = result = mlp.predictAggregated(results, pModel.collectionId, pModel.type, model.users, model.userThreshold, model.userThreshold); errorMsg = "The options you selected did not lead to any results. Please respecify the options."; } if (result == false) { error(errorMsg); } } catch (UIMAException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }); // Set taskId for model progressionModel.setTaskId(taskId); // disable button setEnabled(false); setVisible(false); aTarget.add(form); aTarget.add(this); // Start the progress bar, will set visibility to true progressBar.start(aTarget); } @Override public void onError(AjaxRequestTarget aTarget, Form<?> aForm) { super.onError(aTarget, aForm); // Make sure the feedback messages are rendered // since we have a modal window, we don't want to show the messages on the // regular feedbackpanel for (FeedbackMessage fm : getFeedbackPanel().getFeedbackMessagesModel() .getObject()) { aTarget.appendJavaScript("alert('" + fm.getMessage().toString().replace("'", "\\'") + "');"); } } }); form.add(progressBar = new ProgressBar("progress", progressionModel) { private static final long serialVersionUID = 1L; @Override protected void onFinished(AjaxRequestTarget aTarget) { finishTask(aTarget); } }); // Hide progress bar initially progressBar.setVisible(false); form.add(new AjaxLink("predictionCancelButton") { private static final long serialVersionUID = 1L; @Override public void onClick(AjaxRequestTarget aTarget) { cancel(aTarget); } }); } protected void cancel(AjaxRequestTarget aTarget) { getTaskService().cancel(progressionModel.getTaskId()); form.detach(); finishTask(aTarget); } protected void finishTask(AjaxRequestTarget aTarget) { for (Message m : getTaskService().getMessages(progressionModel.getTaskId())) { error(m.messageKey); } // finish the task! getTaskService().finish(progressionModel.getTaskId()); // Hide progress bar after finish progressBar.setVisible(false); // re-enable button Component button = form.get("predictionOkButton"); button.setEnabled(true); button.setVisible(true); aTarget.add(button); aTarget.add(form); updateComponents(aTarget); predictionModal.close(aTarget); } } private class SamplesetPanel extends Panel { private static final long serialVersionUID = 1L; private Form<SamplesetFormModel> form; public SamplesetPanel(String aId) { super(aId); add(form = new Form<SamplesetFormModel>("samplesetForm", new CompoundPropertyModel<SamplesetFormModel>(new SamplesetFormModel())) { private static final long serialVersionUID = 1L; { final RadioGroup group = new RadioGroup<Boolean>("createNew"); final TextField<String> newnameField = new TextField<String>("newname"); newnameField.add(new DbFieldMaxLengthValidator(projectRepository, "SampleSet", "name")); final TextField<String> newcommentField = new TextField<String>("newcomment"); newcommentField.add(new DbFieldMaxLengthValidator(projectRepository, "SampleSet", "comment")); final SamplesetDropdown samplesetDD = new SamplesetDropdown("sampleset"); group.add(newnameField.setOutputMarkupId(true)); group.add(newcommentField.setOutputMarkupId(true)); group.add(samplesetDD.setOutputMarkupId(true)); samplesetDD.setEnabled(false); group.add(new Radio<Boolean>("true", new Model<Boolean>(true)) .add(new AjaxEventBehavior("onclick") { private static final long serialVersionUID = 1L; @Override protected void onEvent(AjaxRequestTarget aTarget) { newnameField.setEnabled(true); newcommentField.setEnabled(true); samplesetDD.setEnabled(false); aTarget.add(newnameField, newcommentField, samplesetDD); } })); group.add(new Radio<Boolean>("false", new Model<Boolean>(false)) .add(new AjaxEventBehavior("onclick") { private static final long serialVersionUID = 1L; @Override protected void onEvent(AjaxRequestTarget aTarget) { newnameField.setEnabled(false); newcommentField.setEnabled(false); samplesetDD.setEnabled(true); aTarget.add(newnameField, newcommentField, samplesetDD); } })); add(group); add(new AjaxSubmitLink("samplesetOkButton") { private static final long serialVersionUID = 1L; @Override public void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm) { ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); SamplesetFormModel model = form.getModelObject(); String user = SecurityContextHolder.getContext().getAuthentication() .getName(); List<EvaluationItem> items = dataProvider.getItems(); boolean setExists = false; if (model.createNew) { if (StringUtils.isEmpty(model.newname)) { aTarget.appendJavaScript("alert('Choose a name for your sampleset.');"); return; } for (SampleSet s : repository.listSampleSets()) { if (s.getName().equals(model.newname)) { setExists = true; break; } } if (!setExists) { repository.recordSampleSet(model.newname, pModel.collectionId, pModel.type.getName(), model.newcomment, user, items); form.setModelObject(new SamplesetFormModel()); samplesetModal.close(aTarget); aTarget.add(form, samplesetForm); } else { aTarget.appendJavaScript("alert('A sampleset with this name already exists.');"); } } else { if (model.sampleset == null) { aTarget.appendJavaScript("alert('Choose a sampleset from the dropdownmenu.');"); return; } repository.updateSampleSet(model.sampleset, items); form.setModelObject(new SamplesetFormModel()); samplesetModal.close(aTarget); aTarget.add(form); } } @Override protected void onError(AjaxRequestTarget aTarget, Form<?> aForm) { error("Error closing the modal sampleset window."); } }); add(new AjaxLink("samplesetCancelButton") { private static final long serialVersionUID = 1L; @Override public void onClick(AjaxRequestTarget aTarget) { form.detach(); samplesetModal.close(aTarget); } }); } }); } } /** * Constructor that is invoked when page is invoked without a session. */ @SuppressWarnings({ "serial" }) public EvaluationPage() { contextViewsContainer = new WebMarkupContainer("contextViewsContainer") { { contextViews = new ListView<ContextView>("contextViews") { @Override protected void populateItem(ListItem aItem) { aItem.add((Component) aItem.getModelObject()); } }; add(contextViews); } }; contextViewsContainer.setOutputMarkupId(true); add(contextViewsContainer); columns = new ArrayList<IColumn<EvaluationResult, String>>(); columns.add(new AbstractColumn<EvaluationResult, String>(new Model<String>("")) { @Override public void populateItem(final Item<ICellPopulator<EvaluationResult>> aCellItem, String aComponentId, final IModel<EvaluationResult> model) { EmbeddableImage iconContext = new EmbeddableImage(aComponentId, new ContextRelativeResource("images/context.png")); iconContext.add(new AjaxEventBehavior("onclick") { @Override protected void onEvent(AjaxRequestTarget aTarget) { try { contextViews.setList(asList(new ContextView(contextProvider, model .getObject().getItem()))); aTarget.add(contextViewsContainer); } catch (IOException e) { aTarget.add(getFeedbackPanel()); error("Unable to load context: " + e.getMessage()); } } }); iconContext.add(new AttributeModifier("class", new Model<String>("clickableElement"))); aCellItem.add(iconContext); } }); columns.add(new AbstractColumn<EvaluationResult, String>(new Model<String>("")) { @Override public void populateItem(final Item<ICellPopulator<EvaluationResult>> aCellItem, final String aComponentId, final IModel<EvaluationResult> model) { // PopupLink pl = new PopupLink(aComponentId, new AnalysisPage(model.getObject() // .getItem()), "analysis", "Analyse", 800, 600); // pl.add(new AttributeModifier("class", new Model<String>("clickableElement"))); // aCellItem.add(pl); EmbeddableImage iconAnalysis = new EmbeddableImage(aComponentId, new ContextRelativeResource("images/analysis.png")); iconAnalysis.add(new AjaxEventBehavior("onclick") { @Override protected void onEvent(AjaxRequestTarget aTarget) { EvaluationItem item = model.getObject().getItem(); CachedParse cachedTree = repository.getCachedParse(item); ParseTreeResource ptr; if (cachedTree != null) { ptr = new ParseTreeResource(cachedTree.getPennTree()); } else { if (pp == null) { pp = new ParsingPipeline(); } CasHolder ch = new CasHolder(pp.parseInput("stanfordParser", corpusService.getCorpus(item.getCollectionId()).getLanguage(), item.getCoveredText())); ptr = new ParseTreeResource(ch); } analysisModal.setContent(new AnalysisPanel(analysisModal.getContentId(), ptr)); analysisModal.show(aTarget); } }); iconAnalysis.add(new AttributeModifier("class", new Model<String>( "clickableElement"))); aCellItem.add(iconAnalysis); } }); // columns.add(new PropertyColumn(new Model<String>("ID"), "id", "id")); // columns.add(new PropertyColumn(new Model<String>("Collection"), "item.collectionId", // "item.collectionId")); columns.add(new PropertyColumn<EvaluationResult, String>(new Model<String>("Doc"), "item.documentId", "item.documentId")); // columns.add(new PropertyColumn(new Model<String>("Begin"), "item.beginOffset", // "item.beginOffset")); // columns.add(new PropertyColumn(new Model<String>("End"), "item.endOffset", // "item.endOffset")); columns.add(new PropertyColumn<EvaluationResult, String>(new Model<String>("Left"), "item.leftContext", "item.leftContext") { @Override public String getCssClass() { return contextAvailable ? "leftContext" : " hideCol"; } }); columns.add(new PropertyColumn<EvaluationResult, String>(new Model<String>("Match"), "item.match", "item.match") { @Override public String getCssClass() { return contextAvailable ? "match nowrap" : null; } }); columns.add(new PropertyColumn<EvaluationResult, String>(new Model<String>("Right"), "item.rightContext", "item.rightContext") { @Override public String getCssClass() { return contextAvailable ? "rightContext" : " hideCol"; } }); columns.add(new AbstractColumn<EvaluationResult, String>(new Model<String>("Label"), "result") { @Override public void populateItem(final Item<ICellPopulator<EvaluationResult>> aCellItem, String aComponentId, final IModel<EvaluationResult> model) { final Label resultLabel = new Label(aComponentId, new PropertyModel(model, "result")); resultLabel.setOutputMarkupId(true); aCellItem.add(resultLabel); if (showResultColumns) { aCellItem.add(AttributeModifier.replace("class", new Model<String>("editable " + model.getObject().getResult().toLowerCase()))); } aCellItem.add(new AjaxEventBehavior("onclick") { @Override protected void onEvent(AjaxRequestTarget aTarget) { EvaluationResult result = model.getObject(); // cycle to next result Mark newResult = Mark.fromString(result.getResult()).next(); // update database result.setResult(newResult.getTitle()); repository.updateEvaluationResult(result); // update DataTable aCellItem.add(AttributeModifier.replace("class", new Model<String>( "editable " + newResult.getTitle().toLowerCase()))); aTarget.add(resultLabel, aCellItem); } }); } @Override public String getCssClass() { return (showResultColumns ? "" : " hideCol"); } }); columns.add(new AbstractColumn<EvaluationResult, String>(new Model<String>("Comment"), "comment") { @Override public void populateItem(Item<ICellPopulator<EvaluationResult>> cellItem, String componentId, final IModel<EvaluationResult> model) { cellItem.add(new AjaxEditableLabel<String>(componentId, new PropertyModel<String>( model, "comment")) { @Override public void onSubmit(final AjaxRequestTarget aTarget) { super.onSubmit(aTarget); EvaluationResult result = model.getObject(); // get new comment String newComment = getEditor().getInput(); // update database result.setComment(newComment); repository.updateEvaluationResult(result); } @Override public void onError(AjaxRequestTarget aTarget) { super.onError(aTarget); aTarget.add(getFeedbackPanel()); } }.add(new DbFieldMaxLengthValidator(projectRepository, "EvaluationResult", "comment"))); } @Override public String getCssClass() { return "editable" + (showResultColumns ? "" : " hideCol"); } }); // collection and type add(parentOptionsForm = new ParentOptionsForm("parentOptions")); tabs = new Tabs("tabs"); tabs.setVisible(false); // query tab tabs.add(queryForm = new QueryForm("queryForm")); // revision tab tabs.add(reviewForm = new ReviewForm("reviewForm")); // completion tab tabs.add(new Form("completeForm") { { add(new ExtendedIndicatingAjaxButton("completeButton", new Model<String>("Complete"), new Model<String>("Running query ...")) { { setDefaultFormProcessing(false); } @Override public void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm) { AnnotationType type = parentOptionsForm.typeInput.getModelObject(); if (type == null) { error(LocalizerUtil.getString(parentOptionsForm.typeInput, "Required")); aTarget.add(getFeedbackPanel()); return; } ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); String user = SecurityContextHolder.getContext().getAuthentication() .getName(); List<String> otherUsers = new ArrayList<String>(repository.listUsers()); otherUsers.remove(user); // get items, create/persist results List<EvaluationItem> items = repository.listEvaluationResultsMissing( pModel.collectionId, pModel.type.getName(), user, otherUsers); List<EvaluationResult> results = createEvaluationResults(items); repository.writeEvaluationResults(results); // persis. results: hide saveButton, show result columns and filter options limitForm.setVisible(false); filterForm.setChoices(ResultFilter.values()); showColumnsForm.setVisible(true && !pModel.type.getAdditionalColumns() .isEmpty()); showResultColumns(true); saveButton.setVisible(false); predictButton.setVisible(true); samplesetButton.setVisible(true); // update dataprovider dataProvider = new SortableEvaluationResultDataProvider(results); dataProvider.setSort("item.documentId", SortOrder.ASCENDING); dataProvider.setFilter(ResultFilter.ALL); // then update the table resultTable = resultTable .replaceWith(new CustomDataTable<EvaluationResult>("resultTable", getAllColumns(pModel.type), dataProvider, ROWS_PER_PAGE)); contextAvailable = false; updateComponents(aTarget); } @Override public void onError(AjaxRequestTarget aTarget, Form<?> aForm) { super.onError(aTarget, aForm); // Make sure the feedback messages are rendered aTarget.add(getFeedbackPanel()); } }); } }); // sampleset tab tabs.add(samplesetForm = new SamplesetForm("samplesetForm")); // sampleset tab tabs.add(findForm = new FindForm("findForm")); add(tabs); add(new Label("description", new LoadableDetachableModel<String>() { @Override protected String load() { Object value = PropertyResolver.getValue("type.description", parentOptionsForm.getModelObject()); if (value != null) { RenderContext context = new BaseRenderContext(); RenderEngine engine = new BaseRenderEngine(); return engine.render(String.valueOf(value), context); } else { return getString("page.selectTypeHint"); } } }).setEscapeModelStrings(false)); add(filterForm = (FilterForm) new FilterForm("filterForm") .setOutputMarkupPlaceholderTag(true)); add(showColumnsForm = (ShowColumnsForm) new ShowColumnsForm("showColumnsForm") .setOutputMarkupPlaceholderTag(true)); add(resultTable = new Label("resultTable").setOutputMarkupId(true)); add(predictionModal = new ModalWindow("predictionModal")); final PredictionPanel predictionPanel = new PredictionPanel(predictionModal.getContentId()); predictionModal.setContent(predictionPanel); predictionModal.setTitle("Predict results"); predictionModal.setAutoSize(false); predictionModal.setInitialWidth(550); predictionModal.setInitialHeight(350); predictionModal.setCloseButtonCallback(new CloseButtonCallback() { @Override public boolean onCloseButtonClicked(AjaxRequestTarget aTarget) { predictionPanel.cancel(aTarget); return true; } }); add(samplesetModal = new ModalWindow("samplesetModal")); samplesetModal.setContent(new SamplesetPanel(samplesetModal.getContentId())); samplesetModal.setTitle("Create / Extend sampleset"); samplesetModal.setAutoSize(true); add(analysisModal = new ModalWindow("analysisModal")); analysisModal.setTitle("Parse tree"); analysisModal.setInitialWidth(65 * 16); analysisModal.setInitialHeight(65 * 9); // autosize does not work... // analysisModal.setAutoSize(true); add(new Form("saveForm") { { add(saveButton = (ExtendedIndicatingAjaxButton) new ExtendedIndicatingAjaxButton( "saveButton", new Model<String>("Start annotating"), new Model<String>( "Preparing ...")) { @Override protected void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm) { // persist items and results List<EvaluationItem> items = dataProvider.getItems(); items = repository.writeEvaluationItems(items); List<EvaluationResult> results = createEvaluationResults(items); dataProvider.setResults(results); repository.writeEvaluationResults(results); // save results, query ParentOptionsFormModel pModel = parentOptionsForm.getModelObject(); String user = SecurityContextHolder.getContext().getAuthentication() .getName(); QueryFormModel model = queryForm.getModelObject(); if (model.engine != null && !StringUtils.isBlank(model.query)) { repository.recordQuery(model.engine.getName(), model.query, pModel.collectionId, pModel.type.getName(), model.comment, user); } // hide saveButton, show result columns and filter options limitForm.setVisible(false); filterForm.setChoices(ResultFilter.values()); showColumnsForm.setVisible(true && !pModel.type.getAdditionalColumns() .isEmpty()); showResultColumns(true); saveButton.setVisible(false); predictButton.setVisible(true); samplesetButton.setVisible(true); updateComponents(aTarget); } }.setOutputMarkupPlaceholderTag(true)); add(predictButton = (ExtendedIndicatingAjaxButton) new ExtendedIndicatingAjaxButton( "predictButton", new Model<String>("Predict results"), new Model<String>( "Predicting ...")) { @Override protected void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm) { aTarget.appendJavaScript("Wicket.Window.unloadConfirmation = false;"); predictionModal.show(aTarget); } }.setOutputMarkupPlaceholderTag(true)); add(samplesetButton = (ExtendedIndicatingAjaxButton) new ExtendedIndicatingAjaxButton( "samplesetButton", new Model<String>("Save results as sampleset"), new Model<String>("Saving...")) { @Override public void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm) { samplesetModal.show(aTarget); } }.setOutputMarkupPlaceholderTag(true)); } }); add(limitForm = (LimitForm) new LimitForm("limit").setOutputMarkupPlaceholderTag(true)); // at start, don't show: save button, results columns, filter limitForm.setVisible(false); filterForm.setChoices(); showColumnsForm.setVisible(false); showResultColumns(false); saveButton.setVisible(false); predictButton.setVisible(false); samplesetButton.setVisible(false); } /** * Set whether to show the result columns. */ private void showResultColumns(boolean doShow) { showResultColumns = doShow; } // TODO load from db instead of hardcoding here private List<Query> getExampleQueries(String aEngine, String aCollectionId, AnnotationType aType) { String user = SecurityContextHolder.getContext().getAuthentication().getName(); List<Query> queries = new ArrayList<Query>(); // add BNC examples if (aCollectionId.equals("BNC") && aEngine.equals("cqp")) { if (aType.getName().equals("It-cleft")) { queries.add(new Query("cqp", "\"It\" /VCC[] /PP[] /RC[]", "BNC", "It-cleft", user)); queries.add(new Query("cqp", "\"It\" /VCC[] /NP[] /RC[]", "BNC", "It-cleft", user)); queries.add(new Query("cqp", "\"It\" /VCC[] /RC[] \",\" /PP[]", "BNC", "It-cleft", user)); } if (aType.getName().equals("There-cleft")) { queries.add(new Query("cqp", "[pos=\"EX0\"] /VCF[] /NP[] [pos=\"V.*\"]* /PP[]", "BNC", "There-cleft", user)); queries.add(new Query("cqp", "[pos=\"EX0\"] /VCF[] /PP[] /NP[]", "BNC", "There-cleft", user)); } if (aType.getName().equals("Wh-cleft")) { queries.add(new Query("cqp", "/RC[] /VCC[] /PP[]", "BNC", "Wh-cleft", user)); queries.add(new Query("cqp", "/RC[] /VCC[] /NP[]", "BNC", "Wh-cleft", user)); } } return queries; } private List<EvaluationResult> createEvaluationResults(List<EvaluationItem> aItems) { String user = SecurityContextHolder.getContext().getAuthentication().getName(); List<EvaluationResult> results = new ArrayList<EvaluationResult>(); for (EvaluationItem item : new HashSet<EvaluationItem>(aItems)) { results.add(new EvaluationResult(item, user, "")); } return results; } private void updateComponents(AjaxRequestTarget aTarget) { aTarget.add(getFeedbackPanel(), limitForm, filterForm, showColumnsForm, resultTable, saveButton, predictButton, samplesetButton); } /** * Get additional result table columns for the specified type. * * @param aType * the type to get additional columns for * @return additional columns */ private List<IColumn<EvaluationResult, String>> getAllColumns(AnnotationType aType) { List<IColumn<EvaluationResult, String>> ac = new ArrayList<IColumn<EvaluationResult, String>>(); // add "standard" columns ac.addAll(columns); // add type dependent columns for (final AdditionalColumn ad : aType.getAdditionalColumns()) { ac.add(new AbstractColumn<EvaluationResult, String>(new Model<String>(ad.getName()), "additionalColumnValue(" + ad.getId() + ")") { private static final long serialVersionUID = 1L; @Override public void populateItem(Item<ICellPopulator<EvaluationResult>> aCellItem, String aComponentId, final IModel<EvaluationResult> aRowModel) { aCellItem.add(new AjaxEditableLabel<String>(aComponentId, new LoadableDetachableModel<String>() { private static final long serialVersionUID = 1L; @Override protected String load() { EvaluationResult result = aRowModel.getObject(); return result.getAdditionalColumns().get(ad); } }) { private static final long serialVersionUID = 1L; @Override public void onSubmit(AjaxRequestTarget aTarget) { super.onSubmit(aTarget); EvaluationResult result = aRowModel.getObject(); // get new value String newValue = getEditor().getInput(); // update database if (newValue.isEmpty()) { result.getAdditionalColumns().remove(ad); } else { result.getAdditionalColumns().put(ad, newValue); } repository.updateEvaluationResult(result); } @Override public void onError(AjaxRequestTarget aTarget) { super.onError(aTarget); aTarget.add(getFeedbackPanel()); } }.setType(String.class).add( new DbFieldMaxLengthValidator(projectRepository, "EvaluationResult_additionalColumns", "additionalColumns"))); } @Override public String getCssClass() { List<String> classes = new ArrayList<String>(); classes.add("editable"); if (!showResultColumns || !ad.getShowColumn()) { classes.add("hideCol"); } return StringUtils.join(classes, " "); } }); } return ac; } }