BrightSide Workbench Full Report + Source Code
IssueWrapper.java
Go to the documentation of this file.
1 /*
2  * TurrĂ³ i Cutiller Foundation. License notice.
3  * Copyright (C) 2011 Lluis TurrĂ³ Cutiller <http://www.turro.org/>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Affero General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Affero General Public License for more details.
14  *
15  * You should have received a copy of the GNU Affero General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 package org.turro.dossier.issue;
19 
20 import java.util.*;
21 import java.util.logging.Level;
22 import java.util.logging.Logger;
23 import org.turro.action.Contacts;
24 import org.turro.string.ObjectString;
25 import org.turro.string.Strings;
26 import org.turro.auth.Authentication;
27 import org.turro.dossier.db.DossierPU;
28 import org.turro.dossier.dossier.DossierData;
29 import org.turro.dossier.dossier.DossierValues;
30 import org.turro.dossier.dossier.DossierWrapper;
31 import org.turro.dossier.dw.DossierDWReport;
32 import org.turro.dossier.entity.*;
33 import org.turro.dossier.util.WorksheetUtil;
34 import org.turro.elephant.context.Application;
35 import org.turro.elephant.context.ElephantContext;
36 import org.turro.elephant.db.WhereClause;
37 import org.turro.i18n.I_;
38 import org.turro.jpa.Dao;
39 import org.turro.jpa.entity.DaoEntity;
40 import org.turro.plugin.contacts.IContact;
41 import org.turro.related.Relateds;
42 
47 public class IssueWrapper extends DaoEntity<Issue, Long> {
48 
49  private IContact contact;
50  private List<IssueParticipantRole> role;
51  private boolean hasIssueQA;
52  private DossierValues reality;
53 
54  public IssueWrapper(Issue issue) {
55  super(issue);
56  this.contact = Authentication.getIContact();
57  }
58 
59  public IssueWrapper(Issue issue, IContact contact) {
60  super(issue);
61  this.contact = contact;
62  }
63 
64  public String getTabLabel() {
65  return getSelfLabel() + " #" + (entity.getId() == null ? "" : entity.getId());
66  }
67 
68  public String getSelfLabel() {
69  return I_.get("Issue");
70  }
71 
72  public String getTooltiptext() {
73  try {
74  return entity.getFullDescription();
75  } catch(Exception ex) {
76  return null;
77  }
78  }
79 
80  public void setContact(IContact contact) {
81  this.contact = contact;
82  }
83 
84  public boolean isFullParticipant() {
85  if(!Contacts.isValid(contact)) return false;
86  if(isParticipant()) return true;
87  for(IDossierParticipant dp : getIssue().getDossier().getFullParticipants()) {
88  if(dp.getIdContact().equals(contact.getId()) && dp.isShowAllIssues()) {
89  return true;
90  }
91  }
92  return false;
93  }
94 
95  public boolean isParticipant() {
96  return !getRole().isEmpty();
97  }
98 
99  public boolean isReporter() {
100  return getRole().contains(IssueParticipantRole.ISSUE_REPORTER);
101  }
102 
103  public boolean isResponsible() {
104  return getRole().contains(IssueParticipantRole.ISSUE_RESPONSIBLE);
105  }
106 
107  public boolean isQA() {
108  return getRole().contains(IssueParticipantRole.ISSUE_QA);
109  }
110 
111  public boolean isAssistant() {
112  return getRole().contains(IssueParticipantRole.ISSUE_ASSISTANT);
113  }
114 
115  public boolean hasQA() {
116  getRole();
117  return hasIssueQA;
118  }
119 
120  public boolean getCanShowDossier() {
121  return Application.getApplication().isInRole("dossier:show") &&
122  entity.getDossier() != null &&
123  new DossierWrapper(entity.getDossier()).isParticipant();
124  }
125 
126  public Issue getIssue() {
127  return entity;
128  }
129 
130  public void setIssue(Issue issue) {
131  this.entity = issue;
132  }
133 
134  public String getTimmingImage() {
135  return "/_zul/images/timming" + getRelevanceOrderByContact() + ".png";
136  }
137 
138  public String getAlarmImage() {
139  if(hasPassedNow()) {
140  return "/_zul/images/date_surpassed.png";
141  }
142  if(hasPassedValues()) {
143  return "/_zul/images/value_surpassed.png";
144  }
145  return "";
146  }
147 
148  public String getPriorityImage() {
149  int p = entity.getPriority().getPriority();
150  if(p < 3) {
151  return "/_zul/images/priority" + p + ".png";
152  }
153  return "";
154  }
155 
157  return Math.min(getRelevanceByReporter(), Math.min(getRelevanceByResponsible(), getRelevanceByQA()));
158  }
159 
160  public String getOrderingValue() {
161  StringBuilder result = new StringBuilder();
162  try {
163  if(isUnrelated()) {
164  result.append("0");
165  }
166  else {
167  result.append("1");
168  }
169  if(hasPassedNow()) {
170  result.append("0");
171  }
172  else {
173  result.append("1");
174  }
176  result.append("0");
177  } else {
178  result.append("1");
179  }
180  result.append(getRelevanceOrderByContact());
181  result.append(entity.getPriority().getPriority());
182  result.append(entity.getType().getPriority());
183  result.append(reverse(ObjectString.formatObject(entity.getModification(), ObjectString.COMPRESSED_DATE_PATTERN, false)));
184  result.append(entity.getId());
185 
186  // TODO: shouldn't happen... but...
187  } catch(Exception ex) {
188  Logger.getLogger(IssueWrapper.class.getName()).log(Level.SEVERE, ElephantContext.logMsg(null), ex);
189  }
190 
191  return result.toString();
192  }
193 
195  if(reality == null) {
196  reality = new DossierValues();
197  // TODO: optimize using SQL aggregates
198  for(IssueComment icomment : entity.getComments()) {
199  reality.expenses += icomment.getExpenses();
200  reality.hours += icomment.getHours();
201  reality.price += icomment.getPrice();
202  }
203  }
204  return reality;
205  }
206 
207  public DossierData getData() {
208  DossierValues prevision = new DossierValues();
209  prevision.expenses = entity.getExpenses();
210  prevision.hours = entity.getHours();
211  prevision.price = entity.getPrice();
212  return new DossierData(prevision, getReality());
213  }
214 
216  DossierDWReport dr = new DossierDWReport();
217  dr.setReportValues(entity.getDossier());
218  return dr.getData();
219  }
220 
221  public boolean isUnrelated() {
222  return getIssue().getDossier() == null ||
223  !hasReporter() ||
224  !hasResponsible();
225  }
226 
227  public boolean hasReporter() {
228  for(IssueParticipant ip : entity.getParticipants()) {
229  if(ip.getRole().equals(IssueParticipantRole.ISSUE_REPORTER) &&
230  !Strings.isBlank(ip.getIdContact())) {
231  return true;
232  }
233  }
234  return false;
235  }
236 
237  public boolean hasResponsible() {
238  for(IssueParticipant ip : entity.getParticipants()) {
239  if(ip.getRole().equals(IssueParticipantRole.ISSUE_RESPONSIBLE) &&
240  !Strings.isBlank(ip.getIdContact())) {
241  return true;
242  }
243  }
244  return false;
245  }
246 
247  public boolean hasAssistant() {
248  for(IssueParticipant ip : entity.getParticipants()) {
249  if(ip.getRole().equals(IssueParticipantRole.ISSUE_ASSISTANT) &&
250  !Strings.isBlank(ip.getIdContact())) {
251  return true;
252  }
253  }
254  return false;
255  }
256 
257  public boolean hasPassedStartDate() {
258  Date now = new Date();
259  return entity.getStartDate() == null || now.after(entity.getStartDate());
260  }
261 
262  public boolean hasPassedNow() {
263  Date now = new Date();
264  return (entity.getControlDate() != null && now.after(entity.getControlDate())) ||
265  (entity.getDelivery() != null && now.after(entity.getDelivery()));
266  }
267 
268  public boolean hasPassedValues() {
269  return getReality().expenses > entity.getExpenses() ||
270  getReality().hours > entity.getHours() ||
271  getReality().price > entity.getPrice();
272  }
273 
274  public boolean hasAlarm() {
275  return hasPassedNow() || hasPassedValues();
276  }
277 
278  @Deprecated
279  public Set<IssuePredecessor> getSources() {
280  return entity.getSources();
281  }
282 
283  @Deprecated
284  public Set<IssuePredecessor> getTargets() {
285  return entity.getTargets();
286  }
287 
288  public String getResponsibleString() {
289  String result = "";
290  for(IssueParticipant ip : entity.getParticipants()) {
291  if(ip.getRole().equals(IssueParticipantRole.ISSUE_RESPONSIBLE)) {
292  result += (Strings.isEmpty(result) ? "" : ", ") + ip.getName();
293  }
294  }
295  return result;
296  }
297 
298  public String getReporterString() {
299  String result = "";
300  for(IssueParticipant ip : entity.getParticipants()) {
301  if(ip.getRole().equals(IssueParticipantRole.ISSUE_REPORTER)) {
302  result += (Strings.isEmpty(result) ? "" : ", ") + ip.getName();
303  }
304  }
305  return result;
306  }
307 
308  public String getQAString() {
309  String result = "";
310  for(IssueParticipant ip : entity.getParticipants()) {
311  if(ip.getRole().equals(IssueParticipantRole.ISSUE_QA)) {
312  result += (Strings.isEmpty(result) ? "" : ", ") + ip.getName();
313  }
314  }
315  return result;
316  }
317 
318  public String getAssistantString() {
319  String result = "";
320  for(IssueParticipant ip : entity.getParticipants()) {
321  if(ip.getRole().equals(IssueParticipantRole.ISSUE_ASSISTANT)) {
322  result += (Strings.isEmpty(result) ? "" : ", ") + ip.getName();
323  }
324  }
325  return result;
326  }
327 
328  public boolean canStartBySources() {
329  for(IssuePredecessor ip : entity.getSources()) {
330  if(ip.getType() == IssuePredecessorType.START_WHEN_STARTS && !ip.getSource().getStatus().isSeen()) {
331  return false;
332  } else if(ip.getType() == IssuePredecessorType.START_WHEN_ENDS && !ip.getSource().getStatus().isFinished()) {
333  return false;
334  }
335  }
336  return true;
337  }
338 
339  public List<String> getWaitingReason() {
340  List<String> l = new ArrayList<String>();
341  Date now = new Date();
342  if(entity.getStartDate() != null) {
343  if(now.before(entity.getStartDate())) {
344  l.add(I_.format("Waiting for date %tF", entity.getStartDate()));
345  }
346  }
347  StringBuffer sb = new StringBuffer();
348  for(IssuePredecessor ip : entity.getSources()) {
349  Issue source = ip.getSource();
350  if(ip.getType() == IssuePredecessorType.START_WHEN_STARTS && !source.getStatus().isSeen()) {
351  l.add(I_.byKey(ip.getType().toString()) + " #" + source.getId() + " " + source.getDescription() + "\n");
352  } else if(ip.getType() == IssuePredecessorType.START_WHEN_ENDS && !source.getStatus().isFinished()) {
353  l.add(I_.byKey(ip.getType().toString()) + " #" + source.getId() + " " + source.getDescription() + "\n");
354  }
355  }
356  return l;
357  }
358 
359  public int getKanbanColumn() {
360  IssueStatus status = entity.getStatus();
361  if(!status.isSeen()) {
362  return 1;
363  } else if(status.isWorking() || status.isStandBy()) {
364  return 2;
365  } else if(status.isFinished()) {
366  return 3;
367  } else {
368  return 0;
369  }
370  }
371 
372  private String reverse(String string) {
373  StringBuffer sb = new StringBuffer(string);
374  for(int i = 0; i < sb.length(); i++) {
375  sb.setCharAt(i, (char)('9' - sb.charAt(i) + '0'));
376  }
377  return sb.toString();
378  }
379 
380  private List<IssueParticipantRole> getRole() {
381  if(!contact.isValid()) return Collections.EMPTY_LIST;
382  if(role == null) {
383  role = new ArrayList<>();
384  for(IssueParticipant ip : getIssue().getParticipants()) {
385  if(!Strings.isBlank(ip.getIdContact())) {
386  if(ip.getIdContact().equals(contact.getId())) {
387  role.add(ip.getRole());
388  }
389  if(ip.getRole() == IssueParticipantRole.ISSUE_QA) {
390  hasIssueQA = true;
391  }
392  }
393  }
394  }
395  return role;
396  }
397 
398  private int getRelevanceByReporter() {
399  IssueStatus status = entity.getStatus();
400  if(isReporter()) {
401  if(status == IssueStatus.STATUS_INCOMPLETE ||
402  (status == IssueStatus.STATUS_VERIFIED && hasIssueQA) ||
403  (status == IssueStatus.STATUS_RESOLVED && !hasIssueQA)) {
404  return 1;
405  }
406  else if(status == IssueStatus.STATUS_REUNION) {
407  return 2;
408  }
409  else if(status == IssueStatus.STATUS_FROZEN) {
410  return 5;
411  }
412  else {
413  return 3;
414  }
415  }
416  return 4;
417  }
418 
419  private int getRelevanceByResponsible() {
420  IssueStatus status = entity.getStatus();
421  if(isResponsible()) {
422  if(status == IssueStatus.STATUS_NEW ||
423  status == IssueStatus.STATUS_STARTED ||
424  status == IssueStatus.STATUS_REOPENED) {
425  return 1;
426  }
427  else if(status == IssueStatus.STATUS_REUNION) {
428  return 2;
429  }
430  else if(status == IssueStatus.STATUS_FROZEN) {
431  return 5;
432  }
433  else {
434  return 3;
435  }
436  }
437  return 4;
438  }
439 
440  private int getRelevanceByQA() {
441  IssueStatus status = entity.getStatus();
442  if(isQA()) {
443  if(status == IssueStatus.STATUS_RESOLVED) {
444  return 1;
445  }
446  else if(status == IssueStatus.STATUS_REUNION) {
447  return 2;
448  }
449  else if(status == IssueStatus.STATUS_FROZEN) {
450  return 5;
451  }
452  else {
453  return 3;
454  }
455  }
456  return 4;
457  }
458 
459  public void clearEmpties() {
460  Iterator<IssueParticipant> iip = entity.getParticipants().iterator();
461  while(iip.hasNext()) {
462  if(Strings.isBlank(iip.next().getIdContact())) {
463  iip.remove();
464  }
465  }
466  }
467 
468  public Date getBeginDate() {
469  return entity.getStartDate();
470  }
471 
472  public Date getEndDate() {
473  return entity.getDelivery();
474  }
475 
476  public Date getControlDate() {
477  return entity.getControlDate();
478  }
479 
481  return new IssueDetailSet(entity);
482  }
483 
484  @Override
485  protected Dao createDao() {
486  return new DossierPU();
487  }
488 
489  @Override
490  public boolean delete() {
493  return super.delete();
494  }
495 
496  @Override
497  protected boolean shouldLog() {
498  return false;
499  }
500 
501  public String getLeaveAsLabel() {
502  return I_.get("Leave issue as") + ": " + I_.byKey(entity.getStatus().toString()) +
503  (entity.getStatus().isFinished() ? " " + I_.byKey(entity.getResolution().toString()) : "");
504  }
505 
506  public boolean getCanStart() {
508  .contains(entity.getStatus()) && isResponsible();
509  }
510 
511  public boolean getCanMarkAsIncomplete() {
512  return EnumSet.of(IssueStatus.STATUS_NEW, IssueStatus.STATUS_STARTED,
514  .contains(entity.getStatus()) && isResponsible();
515  }
516 
517  public boolean getCanFreeze() {
518  return EnumSet.of(IssueStatus.STATUS_NEW, IssueStatus.STATUS_STARTED,
521  .contains(entity.getStatus()) && (isResponsible() || isReporter());
522  }
523 
524  public boolean getCanResolve() {
528  .contains(entity.getStatus()) && (isFullParticipant());
529  }
530 
531  public boolean getCanReopen() {
535  .contains(entity.getStatus()) && isFullParticipant();
536  }
537 
538  public boolean getCanVerify() {
539  return EnumSet.of(IssueStatus.STATUS_RESOLVED)
540  .contains(entity.getStatus()) && hasQA() && isQA();
541  }
542 
543  public boolean getCanClose() {
544  return (EnumSet.of(IssueStatus.STATUS_RESOLVED)
545  .contains(entity.getStatus()) && !hasQA() && (isFullParticipant())) ||
546  (EnumSet.of(IssueStatus.STATUS_VERIFIED)
547  .contains(entity.getStatus()) && hasQA() && (isFullParticipant()));
548  }
549 
550  public boolean getCanCheckClose() {
553  .contains(entity.getStatus()) && !hasQA() &&
554  (entity.getId() == null || isReporter() || isResponsible()));
555  }
556 
557  public static void checkPendingRevision(Issue issue) {
558  IssueWrapper iw = new IssueWrapper(issue);
559  if(issue.getStatus().equals(IssueStatus.STATUS_CLOSED)) {
560  IssueComment last = null;
561  for(IssueComment ic : issue.getComments()) {
562  last = ic;
563  }
564  if(last != null) {
565  for(IssueParticipant ip : issue.getParticipants()) {
566  if(ip.getRole().equals(IssueParticipantRole.ISSUE_REPORTER)) {
567  if(Strings.isEmpty(last.getIdContact()) || !last.getIdContact().equals(ip.getIdContact())) {
568  WhereClause wc = new WhereClause();
569  wc.addClause("update IssueParticipant ip");
570  wc.addClause("set pendingRevision = true");
571  wc.addClause("where ip = :ip");
572  wc.addNamedValue("ip", ip);
573  iw.getDao().executeUpdate(wc);
574  }
575  }
576  }
577  }
578  } else {
579  WhereClause wc = new WhereClause();
580  wc.addClause("update IssueParticipant ip");
581  wc.addClause("set pendingRevision = false");
582  wc.addClause("where ip.issue = :issue");
583  wc.addNamedValue("issue", issue);
584  iw.getDao().executeUpdate(wc);
585  }
586  }
587 
588 }
static boolean isValid(IContact contact)
Definition: Contacts.java:52
static String getObjectPath(Object object)
Definition: DossierPU.java:66
Set< IssueComment > getComments()
Definition: Issue.java:122
Set< IssueParticipant > getParticipants()
Definition: Issue.java:114
IssueWrapper(Issue issue, IContact contact)
void setContact(IContact contact)
Set< IssuePredecessor > getSources()
Set< IssuePredecessor > getTargets()
static void checkPendingRevision(Issue issue)
static void removeWorksheet(String idContact)
void addNamedValue(String name, Object value)
static String format(String msg, Object... arguments)
Definition: I_.java:49
static String byKey(String key)
Definition: I_.java:83
static String get(String msg)
Definition: I_.java:41
int executeUpdate(String query)
Definition: Dao.java:463
Relateds removeAny(String removePath)
Definition: Relateds.java:100
static Relateds empty()
Definition: Relateds.java:149