View Javadoc
1   /*
2    * Copyright 2018-2024 Medical Inrderation Systems Research Group (https://medical.zcu.cz),
3    * Department of Computer Science and Engineering, University of West Bohemia.
4    * Address: Univerzitni 8, 306 14 Plzen, Czech Republic.
5    *
6    * Author Petr Vcelak (vcelak@kiv.zcu.cz).
7    *
8    * This file is part of MRE SPARQL QueryBuilder project.
9    *
10   * MRE SPARQL QueryBuilder is free software: you can redistribute it and/or modify
11   * it under the terms of the GNU General Public License as published by
12   * the Free Software Foundation, either version 3 of the License.
13   *
14   * MRE SPARQL QueryBuilder is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17   * GNU General Public License for more details.
18   *
19   * You should have received a copy of the GNU General Public License
20   * along with MRE SPARQL QueryBuilder. If not, see <http://www.gnu.org/licenses/>.
21   */
22  package cz.zcu.mre.qbuilder.controller;
23  
24  import cz.zcu.mre.data.rdf.ResourceWrapper;
25  import cz.zcu.mre.qbuilder.core.QBuilder;
26  import cz.zcu.mre.qbuilder.helper.OntologyHelper;
27  import cz.zcu.mre.qbuilder.model.ActiveForm;
28  import cz.zcu.mre.qbuilder.model.Condition;
29  import cz.zcu.mre.qbuilder.model.Operator;
30  import cz.zcu.mre.qbuilder.model.OrderBy;
31  import cz.zcu.mre.qbuilder.model.Property;
32  import cz.zcu.mre.qbuilder.model.QueryData;
33  import cz.zcu.mre.qbuilder.model.Timepoint;
34  import cz.zcu.mre.qbuilder.model.Type;
35  import cz.zcu.mre.qbuilder.model.TypeConversion;
36  import cz.zcu.mre.qbuilder.model.Variable;
37  import cz.zcu.mre.service.data.Language;
38  import cz.zcu.mre.service.data.LanguageService;
39  import cz.zcu.mre.vocab.STROKE;
40  import jakarta.servlet.http.HttpServletRequest;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  import org.springframework.beans.factory.annotation.Autowired;
44  import org.springframework.stereotype.Controller;
45  import org.springframework.ui.Model;
46  import org.springframework.web.bind.annotation.ModelAttribute;
47  import org.springframework.web.bind.annotation.RequestMapping;
48  import org.springframework.web.bind.annotation.RequestMethod;
49  import org.springframework.web.bind.annotation.RequestParam;
50  import org.springframework.web.servlet.mvc.support.RedirectAttributes;
51  
52  /**
53   * The QBuilder Controller.
54   *
55   * @author Petr Vcelak (vcelak@kiv.zcu.cz)
56   */
57  @Controller
58  public class QBuilderController {
59  
60      private static final Logger LOG = LoggerFactory.getLogger(QBuilderController.class);
61  
62      public static final String HOME_ANON = "/index";
63      public static final String PATH_IMPORT = "/api/import";
64      public static final String PATH_EXPORT = "/api/export";
65  
66      public static int updateOrderIndex = 0;
67      public static int updateConditionIndex = 0;
68  
69      boolean variableEditMode = false;		// Are we editing a variable item? Used to decide whether to update or delete associated orderings and filters.
70      boolean conditionEditMode = false;		// Are we editing a condition item? Used to decide whether to clear the conditions form or not.
71      
72      // Former attributes of edited variable.
73      Timepoint editedTimepoint;
74      Type editedType;
75      Property editedProperty;
76      
77      // Attributes of new variable or updated attributes of edited variable.
78      Timepoint newTimepoint;
79      Type newType;
80      Property newProperty;
81      
82      
83      @Autowired
84      private OntologyHelper ontologyHelper;
85  
86      @Autowired
87      private LanguageService languageService;
88  
89      @Autowired
90      private QBuilder qBuilder;
91  
92      @Autowired
93      private ActiveForm active;
94  
95      @RequestMapping(value = "/index", method = RequestMethod.GET)
96      public String index(Model model) {
97          LOG.info("QBuilderController.index");
98  
99          return root(model);
100     }
101 
102     @RequestMapping(value = "/", method = RequestMethod.GET)
103     public String root(Model model) {
104         LOG.info("QBuilderController.root");
105 
106         initializeModel(model, null, null, null, null);
107 
108         return HOME_ANON;
109     }
110 
111     @RequestMapping(value = "/", method = {RequestMethod.POST})
112     public String queryDataUpdate(
113             HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
114             @ModelAttribute QueryData qData,
115             @ModelAttribute Variable variable,
116             @ModelAttribute OrderBy order,
117             @ModelAttribute Condition condition,
118             @ModelAttribute(value = "language") String langString,
119             @RequestParam(value = "action", required = false, defaultValue = "") String action,
120             @RequestParam(value = "action2", required = false, defaultValue = "") String action2,
121             @RequestParam(value = "action3", required = false, defaultValue = "") String action3) {
122 
123         // update languageService to actual language
124         Language language = Language.getLanguage(langString);
125         LOG.info("Language: {}, {}", langString, language);
126         if (language != null) {
127             languageService.setLanguage(language);
128         }
129 
130         if (action == null) {
131             action = "";
132         }
133         if (action2 == null) {
134             action2 = "";
135         }
136         if (action3 == null) {
137             action3 = "";
138         }
139 
140         String currentAction = "";
141 
142         LOG.info("Model contents: {}", model.asMap());
143         LOG.info("/n/n/n");
144         LOG.info("List: {}", qData.getOrdering());
145 //        System.out.println("ACTION:" + action);
146 //        System.out.println("Working directory: " + System.getProperty("user.dir"));
147 
148         if (!action.isEmpty()) {
149             variable = processActionBuilder(action, request, variable, qData);
150         } else if (!action2.isEmpty()) {
151             order = processActionBuilder(action2, request, order, qData);
152         } else {
153             condition = processActionBuilder(action3, request, condition, qData);
154         }
155 
156         // if serialization/deserialization request
157         if (((((action.equals("import") || action.equals("export")) || action2.equals("import")) || action2.equals("export"))
158                 || action3.equals("import")) || action3.equals("export")) {
159             initializeModel(redirectAttributes, qData, variable, order, condition);
160         } else {
161             initializeModel(model, qData, variable, order, condition);
162         }
163 
164         if (action.isEmpty() && action2.isEmpty()) {
165             currentAction = action3;
166         } else if (action.isEmpty()) {
167             currentAction = action2;
168         } else {
169             currentAction = action;
170         }
171 
172         return processActionSerialization(currentAction, qData, redirectAttributes);
173     }
174 
175     private Variable processActionBuilder(String action, HttpServletRequest request, Variable variable, QueryData qData) {
176 
177         // get variable index in the list
178         String stringIndex = request.getParameter("variableIndex");
179         Integer variableIndex = null;
180         if (stringIndex != null) {
181             variableIndex = Integer.valueOf(stringIndex);
182         }
183 
184         LOG.info("ACTION: " + action);
185 
186         // what action requested?
187         switch (action) {
188             case "save" -> {
189             	
190             	variableEditMode = false;
191             	
192                 LOG.info("Require action - add variable");
193                 qData.add(variable);
194 
195                 // create new Variable instance initialized by previously used timepoint, type and property
196                 Variable newVariable = new Variable();
197                                 
198                 newVariable.setTimepoint(variable.getTimepoint());
199                 newVariable.setType(variable.getType());                
200                 newVariable.setProperty(variable.getProperty());		
201                 
202                 newTimepoint = newVariable.getTimepoint();
203                 newType = newVariable.getType();
204                 newProperty = newVariable.getProperty();                  
205             	
206                 // Editing of a variable. 
207                 // If the property is changed then the associated filters and orderings are deleted.
208                 // If the timepoint is changed then only the timepoint value is changed for associated filters and orderings.
209                 if (newProperty != null && editedProperty != null && !newProperty.equals(editedProperty)) {
210                 	                	
211                 	for (int i = 0; i < qData.getConditions().size(); i++) {
212 	                  	if (editedTimepoint.equals(qData.getConditions().get(i).getTimepointCondition()) && editedProperty.equals(qData.getConditions().get(i).getFilter())) {
213 	                      	qData.getConditions().remove(i);
214 	                      	i--;
215 	                  	}
216 	              	}
217 		    
218 	              	for (int j = 0; j < qData.getOrdering().size(); j++) {
219 	                  	if (editedTimepoint.equals(qData.getOrdering().get(j).getTimepointOrder()) && editedProperty.equals(qData.getOrdering().get(j).getAttribute())) {
220 	                      qData.getOrdering().remove(j);
221 	                      	j--;
222 	                  	}
223 	              	}       
224                 	
225                 }
226                 else if (newTimepoint != null && editedTimepoint != null && !newTimepoint.equals(editedTimepoint)) {
227 
228                 	for (int i = 0; i < qData.getConditions().size(); i++) {
229 	                	if (editedProperty.equals(qData.getConditions().get(i).getFilter()) && editedTimepoint.equals(qData.getConditions().get(i).getTimepointCondition())) {	                
230 	                		qData.getConditions().get(i).setTimepointCondition(newTimepoint);	                		
231 	                	}
232           			}                
233                 	
234                 	for (int j = 0; j < qData.getOrdering().size(); j++) {
235 	                	if (editedProperty.equals(qData.getOrdering().get(j).getAttribute()) && editedTimepoint.equals(qData.getOrdering().get(j).getTimepointOrder())) {	                
236 	                		qData.getOrdering().get(j).setTimepointOrder(newTimepoint);
237 	                	}
238           			}
239                 }
240                             
241                 editedTimepoint = null;
242                 editedType = null;
243                 editedProperty = null;
244                 
245                 newTimepoint = null;
246                 newType = null;
247                 newProperty = null;
248                 
249                 return newVariable;
250             }
251 
252             case "edit" -> {
253                 
254             	variableEditMode = true;
255             	
256             	LOG.info("Require action - edit variable");
257 
258                 if (variableIndex != null) {
259                     Variable var = qData.getVariables().get(variableIndex);
260                                     
261                     editedTimepoint = qData.getVariables().get(variableIndex).getTimepoint();
262                     editedType = qData.getVariables().get(variableIndex).getType();
263                     editedProperty = qData.getVariables().get(variableIndex).getProperty();
264                      	
265                     // remove from list, because it will be in editor
266                     qData.getVariables().remove(variableIndex.intValue());
267                     
268                     return var;
269                 }
270             }
271 
272             case "remove" -> {
273                 
274             	variableEditMode = false;
275             	
276             	LOG.info("Require action - remove variable");
277                 LOG.info("Variable index: " + variableIndex);
278 
279                 if (variableIndex != null) {
280 
281                 	Timepoint removedTimepoint= qData.getVariables().get(variableIndex).getTimepoint();
282                     Property removedProperty = qData.getVariables().get(variableIndex).getProperty();
283                     
284 
285                     qData.getVariables().remove(variableIndex.intValue());
286 
287                     // Pokud je smazana vlastnost ze seznamu 'variables', jsou take smazany vsechny k ni prislusejici polozky razeni a filtrovani
288                     for (int i = 0; i < qData.getConditions().size(); i++) {
289                         if (removedProperty.equals(qData.getConditions().get(i).getFilter()) && removedTimepoint.equals(qData.getConditions().get(i).getTimepointCondition())) {
290                             qData.getConditions().remove(i);
291                             i--;
292                         }
293                     }
294 
295                     for (int j = 0; j < qData.getOrdering().size(); j++) {
296                         if (removedProperty.equals(qData.getOrdering().get(j).getAttribute()) && removedTimepoint.equals(qData.getOrdering().get(j).getTimepointOrder()) ) {
297                             qData.getOrdering().remove(j);
298                             j--;
299                         }
300                     }
301                 }
302 
303             }
304 
305             case "up" -> {
306 
307                 LOG.info("Require action - move variable up");
308                 processActionBuilderVariableMove(variableIndex, true, qData);
309             }
310             case "down" -> {
311 
312                 LOG.info("Require action - move variable down");
313                 processActionBuilderVariableMove(variableIndex, false, qData);
314             }
315             default -> {
316                 LOG.warn("Found unknown action: {}", action);
317             }
318         }
319 
320         return variable;
321     }
322 
323     private OrderBy processActionBuilder(String action, HttpServletRequest request, OrderBy order, QueryData qData) {
324 
325         // get ordering index in the list
326         String stringIndex = request.getParameter("orderingIndex");
327         Integer orderingIndex = null;
328         if (stringIndex != null) {
329             orderingIndex = Integer.valueOf(stringIndex);
330         }
331 
332         // what action requested?
333         switch (action) {
334             case "save" -> {
335                 LOG.info("Require action - add ordering");
336                 qData.add(order);
337             }
338 
339             case "change" -> {
340                 qData.getOrdering().add(updateOrderIndex, order);
341             }
342 
343             case "edit" -> {
344                 LOG.info("Require action - edit ordering");
345 
346                 if (orderingIndex != null) {
347                     OrderBy ord = qData.getOrdering().get(orderingIndex);
348 
349                     // remove from list, because it will be in editor
350                     qData.getOrdering().remove(orderingIndex.intValue());
351 
352                     updateOrderIndex = orderingIndex;
353 
354                     return ord;
355                 }
356             }
357 
358             case "remove" -> {
359                 LOG.info("Require action - remove ordering");
360                 LOG.info("Ordering index: " + orderingIndex);
361 
362                 if (orderingIndex != null) {
363                     qData.getOrdering().remove(orderingIndex.intValue());
364                 }
365 
366             }
367 
368             case "up" -> {
369 
370                 LOG.info("Require action - move ordering up");
371                 processActionBuilderOrderingMove(orderingIndex, true, qData);
372             }
373             case "down" -> {
374 
375                 LOG.info("Require action - move ordering down");
376                 processActionBuilderOrderingMove(orderingIndex, false, qData);
377             }
378             default -> {
379                 LOG.warn("Found unknown action: {}", action);
380             }
381         }
382 
383         return order;
384     }
385 
386     private Condition processActionBuilder(String action, HttpServletRequest request, Condition condition, QueryData qData) {
387 
388         // get condition index in the list
389         String stringIndex = request.getParameter("conditionIndex");
390         Integer conditionIndex = null;
391         if (stringIndex != null) {
392             conditionIndex = Integer.valueOf(stringIndex);
393         }
394 
395         // what action requested?
396         switch (action) {
397             case "save" -> {
398                 LOG.info("Require action - add condition");
399 
400                 conditionEditMode = false;
401 
402                 qData.add(condition);
403 
404             }
405 
406             case "change" -> {
407                 conditionEditMode = false;
408 
409                 qData.getConditions().add(updateConditionIndex, condition);
410             }
411 
412             case "edit" -> {
413                 LOG.info("Require action - edit condition");
414 
415                 conditionEditMode = true;
416                 
417                 if (conditionIndex != null) {
418                     Condition cond = qData.getConditions().get(conditionIndex);
419 
420                     // remove from list, because it will be in editor
421                     qData.getConditions().remove(conditionIndex.intValue());
422 
423                     updateConditionIndex = conditionIndex;
424 
425                     return cond;
426                 }
427             }
428 
429             case "remove" -> {
430 
431                 LOG.info("Require action - remove condition");
432                 LOG.info("Condition index: " + conditionIndex);
433 
434                 if (conditionIndex != null) {
435                     qData.getConditions().remove(conditionIndex.intValue());
436                 }
437 
438             }
439 
440             case "up" -> {
441 
442                 LOG.info("Require action - move condition up");
443                 processActionBuilderConditionMove(conditionIndex, true, qData);
444             }
445             case "down" -> {
446 
447                 LOG.info("Require action - move condition down");
448                 processActionBuilderConditionMove(conditionIndex, false, qData);
449             }
450             default -> {
451                 LOG.warn("Found unknown action: {}", action);
452             }
453         }
454 
455         return condition;
456     }
457 
458     private void processActionBuilderVariableMove(Integer variableIndex, boolean moveUp, QueryData qData) {
459 
460         if (variableIndex == null) {
461             return;
462         }
463 
464         Variable var = qData.getVariables().get(variableIndex);
465         qData.getVariables().remove(variableIndex.intValue());
466 
467         int newIndex = (moveUp ? variableIndex - 1 : variableIndex + 1);
468         qData.getVariables().add(newIndex, var);
469     }
470 
471     private void processActionBuilderOrderingMove(Integer orderingIndex, boolean moveUp, QueryData qData) {
472 
473         if (orderingIndex == null) {
474             return;
475         }
476 
477         OrderBy ord = qData.getOrdering().get(orderingIndex);
478         qData.getOrdering().remove(orderingIndex.intValue());
479 
480         int newIndex = (moveUp ? orderingIndex - 1 : orderingIndex + 1);
481         qData.getOrdering().add(newIndex, ord);
482     }
483 
484     private void processActionBuilderConditionMove(Integer conditionIndex, boolean moveUp, QueryData qData) {
485 
486         if (conditionIndex == null) {
487             return;
488         }
489 
490         Condition cond = qData.getConditions().get(conditionIndex);
491         qData.getConditions().remove(conditionIndex.intValue());
492 
493         int newIndex = (moveUp ? conditionIndex - 1 : conditionIndex + 1);
494         qData.getConditions().add(newIndex, cond);
495     }
496 
497     private String processActionSerialization(String action, QueryData queryData, RedirectAttributes redirectAttributes) {
498 
499         switch (action) {
500 
501             case "import" -> {
502 
503                 LOG.info("Require action - import");
504                 // TODO implement JSON import
505                 return "redirect:" + PATH_IMPORT;
506             }
507             case "export" -> {
508 
509                 LOG.info("Require action - export");
510                 redirectAttributes.addFlashAttribute("query", queryData);
511                 return "redirect:" + PATH_EXPORT;
512             }
513         }
514 
515         return HOME_ANON;
516     }
517 
518     private void initializeModel(Model model, QueryData queryData, Variable variable, OrderBy order, Condition condition) {
519         LOG.info("Initialize QBuilder model");
520 
521         // query data
522         if (queryData == null) {
523             queryData = new QueryData();
524 
525             // add LanguageService.
526             queryData.setLanguageService(languageService);
527 
528             // Add Pivot class.
529             queryData.setPivotClass(new ResourceWrapper(STROKE.STROKE_REPORT, languageService));
530 
531             /* Initial variables for StrokeReport */
532             // add FNPL Local ID
533             queryData.add(new Variable(new Type(STROKE.STROKE_REPORT), null, new Property(STROKE.ID), "STR"));
534             // add SITS Treatment ID
535             queryData.add(new Variable(new Type(STROKE.STROKE_REPORT), null, new Property(STROKE.ID_SITS), "STR"));
536 
537             //queryData.add(new Variable(new Type(STROKE.CT), new Timepoint(STROKE.T_BASELINE), new Property(STROKE.CT_OCCLUSION_TICI_SCORE1), "OBJ"));
538             //queryData.add(new Variable(new Type(STROKE.PATIENT), new Timepoint(), new Property(STROKE.AGE), "INT"));
539         }
540 
541         addModelAttributes(model, queryData, variable, order, condition, languageService.getLanguage());
542     }
543 
544     private void addModelAttributes(Model model, QueryData queryData, Variable variable, OrderBy order, Condition condition, Language language) {
545 
546         model.addAttribute("language", language);
547 
548         LOG.debug("QBuilder model - query data {}", queryData);
549         queryData.setQuery(qBuilder.buildSelect(queryData));
550         if (model instanceof RedirectAttributes redirectAttributes) {
551             // the only redirect attribute is query data instance.
552             redirectAttributes.addFlashAttribute("queryData", queryData);
553 
554         } else {
555             // query data
556             model.addAttribute("query", queryData);
557 
558             model.addAttribute("active", active);
559 
560             // variable
561             if (variable == null) {
562                 variable = new Variable();
563             }
564             LOG.debug("QBuilder model - variable {}", variable);
565             if (model instanceof RedirectAttributes) {
566             } else {
567                 model.addAttribute("variable", variable);
568             }
569 
570             // order
571             if (order == null) {
572                 order = new OrderBy();
573             }
574             LOG.debug("QBuilder model - order {}", order);
575             if (model instanceof RedirectAttributes) {
576             } else {
577                 model.addAttribute("order", order);
578             }
579 
580             // condition
581             if (condition == null) {
582                 condition = new Condition();
583             }
584             LOG.debug("QBuilder model - condition {}", condition);
585 
586             if (model instanceof RedirectAttributes) {
587             } else {
588 
589                 if (conditionEditMode) {
590                     model.addAttribute("condition", condition);
591                 }
592                 if (queryData.getConditionsSize() > 0) {
593                     if (queryData.getConditions().contains(condition)) {
594                         model.addAttribute("condition", new Condition());
595                     }
596                 } else {
597                     model.addAttribute("condition", condition);
598                 }
599             }
600             LOG.debug("QBuilder model - condition {}", condition);
601 
602             // helper
603             LOG.debug("QBuilder model - ontologyHelper {}", ontologyHelper);
604             if (model instanceof RedirectAttributes) {
605             } else {
606                 model.addAttribute("ontologyHelper", ontologyHelper);
607             }
608 
609             System.out.println("");
610             System.out.println("QueryData:\n" + queryData + "\n\n Variable:\n" + variable + "\n\n Order:\n" + order + "\n\n Condition:\n" + condition);
611             System.out.println("\n\n VariableEditMode: \n" + variableEditMode);
612             
613 //            System.out.println("\n\n");
614 //            System.out.println("\n\n EditedTimepoint: \n" + editedTimepoint);
615 //            System.out.println("\n\n EditedType: \n" + editedType);
616 //            System.out.println("\n\n EditedProperty: \n" + editedProperty);
617 //            System.out.println("\n\n");
618 ////            System.out.println("\n\n UpdatedTimepoint: \n" + updatedTimepoint);
619 //            System.out.println("\n\n UpdatedType: \n" + updatedType);
620 //            System.out.println("\n\n UpdatedProperty: \n" + updatedProperty);
621             
622             System.out.println("");
623 
624         }
625 
626     }
627 
628 }