package com.QA; import com.QA.rank.Hit; import com.google.common.collect.Lists; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.hibernate.annotations.ForeignKey; import org.hibernate.annotations.Index; import org.hibernate.annotations.IndexColumn; import org.hibernate.annotations.Type; import org.hibernate.search.annotations.DocumentId; import org.hibernate.search.annotations.FieldBridge; import org.hibernate.search.bridge.builtin.IntegerBridge; import org.jblooming.agenda.CompanyCalendar; import org.jblooming.ontology.SecuredLoggableSupport; import org.jblooming.operator.User; import org.jblooming.oql.OqlQuery; import org.jblooming.persistence.PersistenceHome; import org.jblooming.persistence.exceptions.FindByPrimaryKeyException; import org.jblooming.persistence.exceptions.FindException; import org.jblooming.persistence.exceptions.StoreException; import org.jblooming.security.Permission; import org.jblooming.utilities.JSP; import org.jblooming.waf.settings.ApplicationState; import org.jblooming.waf.settings.I18n; import org.jblooming.waf.view.PageSeed; import javax.persistence.*; import java.io.Serializable; import java.util.*; @Entity @Table(name = "qa_question") public class Question extends SecuredLoggableSupport { private String subject; private String description; private String code; //for imports private String externalCode; private boolean deleted = false; private QAOperator owner; private Category category; //activity level * likes private int communityInterestRank; private List<Tag> tags = new ArrayList(); private List<QuestionRevision> questionRevisions = new ArrayList(); private JSONObject calderon = new JSONObject(); //de norm private Date answerAcceptedOn; private Answer acceptedAnswer; private Set<Answer> answers = new HashSet(); // inverse private List<Comment> comments = new ArrayList(); // inverse private Set<Upvote> upvotes = new HashSet(); // inverse //denormalized social weight private double totUpvotesFromQandA; public enum ContentRatingValue { CENSORED, SIGNALLED, OFFTOPIC, DUPLICATE, ARGUMENTATIVE } @Id @Type(type = "int") @GeneratedValue(strategy = GenerationType.AUTO) @DocumentId @FieldBridge(impl = IntegerBridge.class) public Serializable getId() { return super.getId(); } public String getExternalCode() { return externalCode; } public void setExternalCode(String externalCode) { this.externalCode = externalCode; } public double getTotUpvotesFromQandA() { return totUpvotesFromQandA; } public void setTotUpvotesFromQandA(double totUpvotesFromQandA) { this.totUpvotesFromQandA = totUpvotesFromQandA; } public Date getAnswerAcceptedOn() { return answerAcceptedOn; } protected void setAnswerAcceptedOn(Date answerAcceptedOn) { this.answerAcceptedOn = answerAcceptedOn; } @ManyToOne(targetEntity = Answer.class) @ForeignKey(name = "fk_quest_accans") @Index(name = "idx_quest_accans") public Answer getAcceptedAnswer() { return acceptedAnswer; } protected void setAcceptedAnswer(Answer acceptedAnswer) { this.acceptedAnswer = acceptedAnswer; } public int getCommunityInterestRank() { return communityInterestRank; } public void setCommunityInterestRank(int communityInterestRank) { this.communityInterestRank = communityInterestRank; } @OneToMany(targetEntity = Upvote.class) @JoinColumn(name = "question") @OrderBy("operator") public Set<Upvote> getUpvotes() { return upvotes; } private void setUpvotes(Set<Upvote> upvotes) { this.upvotes = upvotes; } @ManyToOne(targetEntity = Category.class) @ForeignKey(name = "fk_quest_cat") @Index(name = "idx_quest_cat") public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } @ManyToMany(cascade = {CascadeType.PERSIST}) @JoinTable(name = "qa_tagquest", joinColumns = {@JoinColumn(name = "id")}) @IndexColumn(name = "tagPosition", base = 0) public List<Tag> getTags() { return tags; } public void setTags(List<Tag> tags) { this.tags = tags; } @OneToMany(targetEntity = Comment.class, cascade = CascadeType.REMOVE) @JoinColumn(name = "question") public List<Comment> getComments() throws FindException { return comments; } public void setComments(List<Comment> comments) { this.comments = comments; } @Column(length = 900) public String getSubject() { return subject; } public void setSubject(String name) { this.subject = name; } @Lob public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @ManyToOne(targetEntity = QAOperator.class) @ForeignKey(name = "fk_quest_own") @Index(name = "idx_quest_own") @JoinColumn(name = "ownerx") public QAOperator getOwner() { return owner; } public void setOwner(QAOperator owner) { this.owner = owner; } @OneToMany(targetEntity = QuestionRevision.class, cascade = CascadeType.REMOVE) @JoinColumn(name = "revisionOf") public List<QuestionRevision> getQuestionRevisions() { return questionRevisions; } private void setQuestionRevisions(List<QuestionRevision> questionRevisions) { this.questionRevisions = questionRevisions; } @Transient public List<QuestionRevision> getRevisions() { List<QuestionRevision> qr = getQuestionRevisions(); return Lists.reverse(qr); } @OneToMany(targetEntity = Answer.class, cascade = CascadeType.REMOVE) @JoinColumn(name = "question") /** * @deprecated use getAnswersNotDeleted */ private Set<Answer> getAnswers() { return answers; } private void setAnswers(Set<Answer> ps) { this.answers = ps; } @Type(type = "org.jblooming.ontology.JSONObjectType") public JSONObject getCalderon() { return calderon; } public void setCalderon(JSONObject calderon) { this.calderon = calderon; } public String getCode() { return code; } public void setCode(String code) { if (code != null && code.length() > 250) code = code.substring(0, 250); this.code = code; } @Transient public static Question load(Serializable id) throws FindByPrimaryKeyException { return (Question) PersistenceHome.findByPrimaryKey(Question.class, id); } @Transient public static Question loadByExternalId(String extId) throws FindException { OqlQuery oqlQuery = new OqlQuery("from " + Question.class.getName() + " as question where question.externalCode = :extId"); oqlQuery.getQuery().setMaxResults(1); oqlQuery.getQuery().setParameter("extId", extId); Question question = (Question) oqlQuery.uniqueResultNullIfEmpty(); return question; } public boolean isDeleted() { return deleted; } public void setDeleted(boolean deleted) { this.deleted = deleted; } /*@Transient public static List<Question> getMostUsedManifestos(int maxResults, List<Integer> except) throws org.jblooming.persistence.exceptions.PersistenceException { String hql = "from " + Hit.class.getName() + " as hit where hit.manifestoId!=0 order by hit.when desc"; if (JSP.ex(except)) hql = hql + " and hit.teamId not in (:ids)"; hql = hql + " order by hit.when desc"; OqlQuery oql = new OqlQuery(hql); if (JSP.ex(except)) oql.getQuery().setParameterList("ids", except); oql.getQuery().setMaxResults(300); List<Hit> hits = oql.list(); List<EntityGroupRank> ranks = RankUtilities.getRanked(RankUtilities.computeWeightForManifestos(hits), maxResults * 5); List<Question> ret = new ArrayList(); for (EntityGroupRank egr : ranks) { Question manifesto = (Question) egr.getEntity(); if (manifesto != null && !manifesto.isDeleted() && !Question.ContentRatingValue.CENSORED.toString().equals(manifesto.getContentRating())) { ret.add(manifesto); } if (ret.size() > maxResults) break; } return ret; } */ @Transient public void hit(QAOperator operator, QAEvent event) throws StoreException { Hit hit = Hit.newHit(this, operator, event.toString(), event.value); hit.setQuestionId(this.getIntId()); hit.store(); if (operator != null) { operator.setKarma(operator.getKarma() + event.value); operator.store(); } } @Transient public void hitAndNotify(QAOperator logged, QAEvent QAEvent) throws StoreException { hit(logged, QAEvent); // brick text, brick link, op name, op link getOwner().sendNote( I18n.get(QAEvent.toString() + "_SUBJECT"), // server, brick id, brick text, server, user id, user name I18n.get(QAEvent.toString() + "_BODY_%%_%%_%%_%%", ApplicationState.serverURL, getId() + "", JSP.htmlEncodeApexesAndTags(JSP.limWr(getDescription(), 30)), ApplicationState.serverURL, logged.getLoginName(), logged.getDisplayName()), QAEvent.toString()); } @Transient public JSONObject jsonify(QAOperator logged, boolean getAnswers) { Question question = this; QAOperator owner = question.getOwner(); JSONObject jm = new JSONObject(); jm.element("queId", question.getId()); jm.element("queName", question.getSubject()); jm.element("queDescription", question.getDescription()); jm.element("queCode", question.getCode()); //jm.element("queUrl", question.getURL()); jm.element("queOwner", getOwner().jsonify()); jm.element("queOwnerName", getOwner().getDisplayName()); jm.element("queUpvotes", getUpvotes().size()); if (getAnswers) { JSONArray jsonArray = new JSONArray(); for (Answer prop : getAnswersNotDeleted()) { JSONObject jp = prop.jsonify(logged, true); jsonArray.add(jp); } jm.element("queProposals", jsonArray); } if (logged != null) jm.element("queIsOwner", getOwner().getIntId() == logged.getIntId()); return jm; } @Transient public PageSeed getURL() { PageSeed ps = new PageSeed("/question/" + getId()); ps.disableCache = false; return ps; } @Transient public boolean hasAcceptedAnswer() { boolean haa = false; for (Answer a : getAnswersNotDeleted()) { if (a.isAccepted()) { haa = true; break; } } return haa; } @Transient public boolean likedBy(QAOperator QAOperator) { boolean likedBy = false; for (Upvote a : getUpvotes()) { if (a.getOperator() != null && a.getOperator().equals(QAOperator)) { likedBy = true; break; } } return likedBy; } @Transient public List<Answer> getAnswersNotDeleted() { String hql = "from " + Answer.class.getName() + " as ans where ans.question=:qq and ans.deleted = false"; OqlQuery oql = new OqlQuery(hql); oql.getQuery().setEntity("qq", this); return oql.getQuery().list(); } @Transient public List<Answer> getAnswersByRelevance() { String hql= "select answ from "+ Answer.class.getName()+" as answ where answ.deleted=false and answ.question=:qst order by answ.totUpvotesAndAcceptance desc, answ.lastModified desc"; org.hibernate.Query query = new OqlQuery(hql).getQuery(); query.setEntity("qst",this); return query.list(); } @Transient public boolean hasPermissionFor(User u, Permission p) { QAOperator mpu = (QAOperator) u; boolean result = false; result = super.hasPermissionFor(mpu, p); if (!result) { String arq = ApplicationState.getApplicationSetting("RECAPTCHA_QUESTION"); boolean dealtBySpecialCase = false; //special case MpPermission.QUESTION_CREATE if (p.equals(QAPermission.QUESTION_CREATE) && mpu.getQuestionsNotDeletedSize() < 4) { result = true; dealtBySpecialCase = true; //special case MpPermission.QUESTION_CREATE_NO_RECAPTCHA //ALWAYS,NEVER,MAYBE } else if (p.equals(QAPermission.QUESTION_CREATE_NO_RECAPTCHA)) { if ("NEVER".equals(arq)) { result = true; dealtBySpecialCase = true; } else if ("ALWAYS".equals(arq)) { result = false; dealtBySpecialCase = true; } else if ("MAYBE".equals(arq)) { //&& u.getCreationDate().getTime()>CompanyCalendar.) { long crTime = u.getCreationDate().getTime(); CompanyCalendar cc = new CompanyCalendar(); cc.add(CompanyCalendar.DAY_OF_YEAR,-2); if (crTime>cc.getTime().getTime()) { result = false; } else result = true; dealtBySpecialCase = true; } else if (mpu.getQuestionsNotDeletedSize() == 0) { result = true; dealtBySpecialCase = true; } } else if (p.equals(QAPermission.ANSWER_CREATE) && mpu.getAnswersNotDeleted().size()<3) { result = true; dealtBySpecialCase = true; } else if (p.equals(QAPermission.ANSWER_CREATE_NO_RECAPTCHA)) { if ("NEVER".equals(arq)) { result = true; dealtBySpecialCase = true; } else if ("ALWAYS".equals(arq)) { result = false; dealtBySpecialCase = true; } else if (this.getAnswersNotDeleted().size() == 0) { result = true; dealtBySpecialCase = true; } } if (!dealtBySpecialCase) result = ((QAOperator) u).getKarma() > ((QAPermission) p).reputationRequired; } return result; } @Transient public List<Question> getRelatedQuestions() throws FindException { /* ------------- 5 BY SUB ---------------------------- */ /* about,after,all,also,an,and,another,any,are,as,at,be,because,been,before being,between,both,but,by,came,can,come,could,did,do,each,for,from,get got,has,had,he,have,her,here,him,himself,his,how,if,in,into,is,it,like make,many,me,might,more,most,much,must,my,never,now,of,on,only,or,other our,out,over,said,same,see,should,since,some,still,such,take,than,that the,their,them,then,there,these,they,this,those,through,to,too,under,up very,was,way,we,well,were,what,where,which,while,who,with,would,you,your,a b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$,1,2,3,4,5,6,7,8,9,0,_ */ /*List<Question> subResults = new ArrayList(); List<String> subtok = Arrays.asList(getSubject().split(" ")); int tok = 0; String hqlS= "select q from "+ Question.class.getName()+" as q where q.subject like :subtok "; hqlS += " and q != :tq order by q.totUpvotesFromQandA desc, q.lastModified desc"; OqlQuery oqlQs = new OqlQuery(hqlS); oqlQs.getQuery().setEntity("tq", this); oqlQs.getQuery().setMaxResults(3); */ /* ------------- 5 BY TAG ---------------------------- */ List<Question> tagResults = new ArrayList(); String hql= "select q from "+ Question.class.getName()+" as q where "; boolean isFirst = true; int tn = 0; for (Tag t : getTags()) { hql = hql + (isFirst?" ":" and ")+":tag"+tn+" in elements(q.tags)"; tn++; isFirst = false; } hql += " and q != :tq order by q.totUpvotesFromQandA desc, q.lastModified desc"; OqlQuery oqlQ = new OqlQuery(hql); tn = 0; for (Tag t : getTags()) { oqlQ.getQuery().setEntity("tag"+tn,t); tn++; } oqlQ.getQuery().setEntity("tq", this); oqlQ.getQuery().setMaxResults(5); tagResults.addAll(oqlQ.list()); if (tagResults.size()<5) { List<Tag> sort = new ArrayList(getTags()); //tag by inverse pop!!! Collections.sort(sort, new Comparator<Tag>() { public int compare(Tag o1, Tag o2) { return o1.getQuestionsCount()-o2.getQuestionsCount(); } }); for (Tag ts : sort) { List<Question> questions = ts.getQuestions(10); for (Question qts : questions) { if (!tagResults.contains(qts) && !this.equals(qts)) { tagResults.add(qts); if (tagResults.size()>9) break; } } if (tagResults.size()>=5) break; } } return tagResults; } public static List<Question> getHotQuestions(int maxResults, boolean onlyUnAnswered) throws FindException, FindByPrimaryKeyException { String hql = "select sum(hit.weight), questionId from " + Hit.class.getName() + " as hit where hit.when>:since and questionId<>null group by questionId order by sum(hit.weight) desc"; OqlQuery oql = new OqlQuery(hql); CompanyCalendar oneMonthAgo = new CompanyCalendar(); oneMonthAgo.add(CompanyCalendar.MONTH, -1); oneMonthAgo.setAndGetTimeToDayStart(); oql.getQuery().setLong("since", oneMonthAgo.getTime().getTime()); oql.getQuery().setMaxResults(maxResults*5); int picked = 0; List<Question> openQ = new ArrayList<Question>(); List<Object[]>os = oql.list(); for (Object[] oo : os) { Question qst = Question.load((Serializable) oo[1]); if (qst!=null && !qst.isDeleted() && (!onlyUnAnswered || !qst.hasAcceptedAnswer())) { openQ.add(qst); picked++; } if (picked>=maxResults) break; } return openQ; } public static List<Question> getTopQuestions(int maxResults, boolean onlyUnAnswered) throws FindException { String hql= "select q from "+ Question.class.getName()+" as q where q.deleted=false"; if (onlyUnAnswered) hql += " and q.acceptedAnswer = null"; hql += " order by q.totUpvotesFromQandA desc, q.lastModified desc"; org.hibernate.Query query = new OqlQuery(hql).getQuery(); query.setMaxResults(maxResults); return query.list(); } }