Wiki source code of Activity Stream Demo

Last modified by Ludovic Dubost on 2013/06/03 09:04

Show last authors
1
2 {{velocity}}
3 #if(!$request.details)
4 This is a demo of a performant activity stream. The principle of this activity stream is to display results by groups of 5 minutes. A first query is run to detect all groups of 5 minutes that are needed to display 20 (configurable) groups. Then one other query is run to retrieve all the necessary data to display the 20 groups. For each group, the display is different based on the number of results.
5
6 If you click on a group the detailed changes are retrieved using an AJAX call.
7
8 Security wise, the display will check security for groups of less than 100 changes. Otherwise only the number of changes are displayed.
9
10 For now the display has not been made to look pretty.
11 #end
12 {{/velocity}}
13 {{groovy}}
14 import org.joda.time.DateTime;
15 import java.text.SimpleDateFormat;
16
17 spacefilter = "'XWiki','Scheduler', 'Sandbox', 'IRC'"
18 wikifilter = "'import'"
19 pagefilter = "'Main.ActStream', 'Main.ActStream2'"
20
21 def cache = new HashMap()
22 nbqueries = 0;
23 nbchecks = 0;
24
25 def getIntervals(nb, delay, secDelay) {
26 nbqueries++;
27 def hql = "select distinct round( (datediff(current_timestamp(), act.date)*24*3600 + (hour(current_timestamp())-hour(act.date))*3600 + (minute(current_timestamp())-minute(act.date))*60 + (second(current_timestamp()) + ${secDelay} -second(act.date)))/60/${delay} - 0.5) from ActivityEventImpl as act where act.space not in (${spacefilter}) and act.page not in (${pagefilter}) and act.wiki not in (${wikifilter}) and act.hidden<>1 order by act.date desc"
28 return xwiki.search(hql, nb * 2, 0);
29 }
30
31 def filter(list) {
32 if (list.size()>100)
33 return list;
34 def list1 = new ArrayList();
35 for (event in list) {
36 def pageName = "${event[0]}:${event[1]}"
37 nbchecks++;
38 def pagedoc = xwiki.getDocument(pageName)
39 if (pagedoc!=null) {
40 list1.add(event);
41 } else {
42 println "Got rid of ${pageName}"
43 }
44 }
45 return list1;
46 }
47
48 def getEvents(starti, endi, delay, currentDate) {
49 nbqueries++;
50 def sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm")
51 def nextDate = currentDate.minusMinutes(delay * starti);
52 def previousDate = currentDate.minusMinutes(delay * endi);
53 def spreviousDate = sdf.format(previousDate.toDate());
54 def snextDate = sdf.format(nextDate.toDate())
55
56 def hql = "select act.wiki, act.page, act.user, act.title, act.version, act.type, act.date, act.param2 from ActivityEventImpl as act where act.space not in (${spacefilter}) and act.page not in (${pagefilter}) and act.wiki not in (${wikifilter}) and act.date>'${spreviousDate}' and act.date<'${snextDate}' and act.hidden<>1 order by act.date desc"
57 if (request.debug) {
58 println "* ${starti} ${endi} ${delay} ${previousDate} ${spreviousDate} ${nextDate} ${snextDate} ${hql}";
59 }
60
61 return xwiki.search(hql);
62 }
63
64 def getEventsWithCache(i, starti, endi, delay, currentDate, nbgroups, cache, withadddelay) {
65 // let's check in cache
66 def result = cache.get(i)
67 if (result!=null) {
68 return filter(result)
69 }
70
71 def endi2 = calcEnd(i+nbgroups, withadddelay)
72 def sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm")
73 def nextDate = currentDate.minusMinutes(delay * starti);
74 def previousDate = currentDate.minusMinutes(delay * endi2);
75 def spreviousDate = sdf.format(previousDate.toDate());
76 def snextDate = sdf.format(nextDate.toDate())
77
78 def hql = "select act.wiki, act.page, act.user, act.title, act.version, act.type, act.date from ActivityEventImpl as act where act.space not in (${spacefilter}) and act.page not in (${pagefilter}) and act.wiki not in (${wikifilter}) and act.date>'${spreviousDate}' and act.page<>'Sandbox.PushTest3' and act.date<'${snextDate}' and act.hidden<>1 order by act.date desc"
79
80 // now we have more data so we need to put it in cache
81 def list = xwiki.search(hql)
82 nbqueries ++;
83 def start = i;
84 def list2 = new ArrayList()
85 for (event in list) {
86 def minDate = null;
87 while (minDate==null) {
88 minDate = currentDate.minusMinutes(delay * calcEnd(start, withadddelay));
89 if (event[6].time<minDate.toDate().time) {
90 cache.put(start, list2);
91 list2 = new ArrayList();
92 start++;
93 minDate = null;
94 }
95 }
96 list2.add(event);
97 }
98 cache.put(start, list2);
99 start++;
100 while (start<=i+nbgroups) {
101 cache.put(start, new ArrayList())
102 start++;
103 }
104 def newlist = cache.get(i);
105 return filter(newlist);
106 }
107
108 def calcEnd(i, withadddelay) {
109 return (withadddelay&&(i+1>6)) ? (i + 1 + (i - 6)) : (i+1)
110 }
111
112 def calcStart(i, withadddelay) {
113 return (withadddelay&&(i>6)) ? (i + (i - 6) -1) : i
114 }
115
116 def findComment(pagedoc, commentNb) {
117 def cobj = pagedoc.getObject("XWiki.XWikiComments", Integer.parseInt(commentNb))
118 if (cobj) {
119 pagedoc.use(cobj);
120 return pagedoc.getValue("comment")
121 }
122 return "";
123 }
124
125 def getMessage(list, nochange) {
126 def message = ""
127
128 def nb = (list==null) ? 0 : list.size();
129 if (nb==0) {
130 if (nochange) {
131 message = "no change";
132 }
133 } else if (nb==1) {
134 def res = list.get(0)
135 def author = xwiki.getUserName(res[2], false)
136 def changedDoc = xwiki.getDocument("${res[0]}:${res[1]}")
137 if (changedDoc!=null) {
138 def stitle = changedDoc.displayTitle
139 if (changedDoc.version=="1.1")
140 message = "1 create: ${stitle} in wiki ${res[0]} update by ${author}"
141 else
142 message = "1 update: ${stitle} in wiki ${res[0]} update by ${author}"
143 }
144 } else if (nb<30) {
145 def titleList = new ArrayList()
146 def wikis = new ArrayList()
147 for (item in list) {
148 def changedDoc = xwiki.getDocument("${item[0]}:${item[1]}")
149 if (changedDoc!=null) {
150 def stitle = changedDoc.displayTitle
151 if (!titleList.contains(stitle))
152 titleList.add(stitle)
153 if (!wikis.contains(item[0]))
154 wikis.add(item[0])
155 } else {
156 nb--;
157 }
158 }
159 def text = ""
160 if (titleList.size()<3) {
161 text = ": " + titleList.join(",")
162 } else {
163 text = "on " + titleList.size() + " pages"
164 }
165 if (wikis.size()==1) {
166 def wiki = wikis.get(0)
167 message = "${nb} changes in ${wiki} ${text}"
168 } else {
169 def nbwikis = wikis.size();
170 message = "${nb} changes in ${nbwikis} wikis ${text}"
171 }
172 } else if (nb>1) {
173 def wikis = new ArrayList()
174 for (item in list) {
175 if (!wikis.contains(item[0]))
176 wikis.add(item[0])
177 }
178 if (wikis.size()==1) {
179 def wiki = wikis.get(0)
180 message = "${nb} changes in ${wiki}"
181 } else if (wikis.size()<4) {
182 def swikis = wikis.join(",");
183 message = "${nb} changes in wikis: ${swikis}"
184 } else {
185 def nbwikis = wikis.size();
186 message = "${nb} changed in ${nbwikis} wikis"
187 }
188 }
189 return message;
190 }
191
192 // add jsx
193 xwiki.jsx.use(doc.fullName);
194
195 if (request.details) {
196 def starti = Integer.parseInt(request.start)
197 def endi = Integer.parseInt(request.end)
198 def delay = Integer.parseInt(request.delay)
199 def ctime = Long.parseLong(request.time)
200 def cdate = new DateTime(ctime);
201 def events = getEvents(starti, endi, delay, cdate)
202 if (events.size()>100) {
203 println "* Too many changes to show"
204 } else {
205 for (event in events) {
206 def pageName = "${event[0]}:${event[1]}"
207 def pagedoc = xwiki.getDocument(pageName)
208 if (pagedoc!=null) {
209 def authorName = xwiki.getUserName(event[2])
210 def comment = ""
211 if (event[5]=="addComment")
212 comment = findComment(pagedoc, event[7])
213 println """* Page [[$pagedoc.displayTitle>>${pageName}]] ${event[5]} by {{html}}${authorName}{{/html}} to version ${event[4]} on ${event[6]} ${comment}"""
214 }
215 }
216 }
217 } else {
218 def time1 = (new Date()).getTime();
219 println """{{html clean=false}}<p><ul>"""
220
221 def delay = 5;
222 if (request.delay)
223 delay = Integer.parseInt(request.delay)
224 def max = 20;
225 if (request.max)
226 max = Integer.parseInt(request.max)
227
228 def currentDate = new DateTime()
229 def sec = currentDate.getSecondOfDay()
230 def secDelay = (int) (600 - sec + 600*Math.floor(sec/600))
231 currentDate = currentDate.plusSeconds(secDelay);
232
233
234 def intervals = getIntervals(max, delay, secDelay);
235 def maxi = (int) ((intervals.size()>0) ? intervals.get(intervals.size()-1) : 1);
236
237 if (request.debug) {
238 println intervals;
239 println maxi
240 }
241
242 def st = 0;
243 for (interval in intervals) {
244 // we should stop when we have enough
245 if (st>=max)
246 break;
247
248 def i = (int) interval;
249 def starti = calcStart(i, false);
250 def endi = calcEnd(i, false);
251 if (request.debug) {
252 println "<li>Run ${i} ${starti} ${endi} ${delay} ${currentDate} ${maxi}</li>"
253 }
254 def list = getEventsWithCache(i, starti, endi, delay, currentDate, maxi , cache, false)
255 // def list = getEvents(starti, endi, delay, currentDate)
256 def message = getMessage(list, (request.withnochange=="1"));
257
258 if (message!="") {
259 def mn = starti*delay - (int) (secDelay/60)
260 def newDate = currentDate.minusMinutes(mn)
261 def stime = ""
262 if (newDate.getDayOfYear()+1==currentDate.getDayOfYear()) {
263 def sdf2 = new SimpleDateFormat("HH:mm")
264 stime = "Yesterday " + sdf2.format(newDate.toDate())
265 } else if (newDate.getDayOfYear()!=currentDate.getDayOfYear()) {
266 def sdf2 = new SimpleDateFormat("E MMM dd, HH:mm")
267 stime = sdf2.format(newDate.toDate())
268 } else {
269
270 if (mn>60) {
271 def hours = Math.floor(10*mn/60)/10;
272 if (hours>24) {
273 def rdays = (int) Math.floor(mn/60/24);
274 def rmn = mn - rdays*60*24;
275 def rhours = (int) Math.floor(rmn/60);
276 rmn = rmn - rhours*60;
277 stime = "${rdays} days ${rhours} hours and ${rmn} minutes ago";
278 } else {
279 def rhours = (int) Math.floor(mn/60);
280 def rmn = mn - 60*rhours;
281
282 stime = "${rhours} hours and ${rmn} minutes ago";
283 }
284 } else if (mn>0) {
285 stime = "${mn} minutes ago"
286 } else {
287 stime = "now"
288 }
289 }
290
291 println """<li><a href="javascript:void(0)" onclick="load('${doc.getURL("get")}', ${starti}, ${endi}, ${delay}, ${currentDate.toDate().time}); return false;">${stime}: ${message}</a>"""
292 println """<div id="data${starti}"></div></li>"""
293 st++;
294 }
295 }
296 def time2 = (new Date()).getTime();
297 def dtime = time2-time1
298
299 println """</ul></p><p><br />run in ${dtime} ms with ${nbqueries} queries ${nbchecks} security checks</p>"""
300 println "{{/html}}"
301 }
302
303 {{/groovy}}