Merge lp:~frankencode/ubuntu-calendar-app/data-service into lp:ubuntu-calendar-app
- data-service
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 11 |
Proposed branch: | lp:~frankencode/ubuntu-calendar-app/data-service |
Merge into: | lp:ubuntu-calendar-app |
Diff against target: |
907 lines (+729/-50) (has conflicts) 8 files modified
DiaryView.qml (+82/-0) EventListModel.qml (+26/-0) EventView.qml (+19/-13) MonthView.qml (+9/-3) calendar.qml (+26/-34) calendarTests.qml (+14/-0) dataService.js (+361/-0) dataServiceTests.js (+192/-0) Text conflict in MonthView.qml |
To merge this branch: | bzr merge lp:~frankencode/ubuntu-calendar-app/data-service |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Phone Apps Jenkins Bot | continuous-integration | Needs Fixing | |
Frank Mertens | Pending | ||
Review via email: mp+154240@code.launchpad.net |
Commit message
Added a data model for the local store, some simulation data and thin API on top, which is called DataService.
Description of the change
Added a data model for the local store, some simulation data and thin API on top, which is called DataService. The idea behind: abstract away the access to events, contacts and places. Especially the last two will be delivered by some system service in a later version. For now this is only simulated. But the user of the DataService API shouldn't need to worry about.
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
- 11. By Frank Mertens
-
Added more complex test case in preparation for integration of the diary view.
To run the tests and load the test data run 'qmlscene calendarTest.qml' once.
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:11
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 12. By Frank Mertens
-
Use singleton pattern to improve startup performance
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:12
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 13. By Frank Mertens
-
Extended the database model by an events category and added a basic diary view.
The events category allows to use section headers.
The basic diary view uses the standard Ubuntu component ListItem.Standard to show
the events of the current day. - 14. By Frank Mertens
-
Removed testing icon and added new icons needed for the diary view.
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:14
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 15. By Frank Mertens
-
Missing file.
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:15
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 16. By Frank Mertens
-
Allow proper interactions with the compressed event view.
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:16
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === added file 'DiaryView.qml' |
2 | --- DiaryView.qml 1970-01-01 00:00:00 +0000 |
3 | +++ DiaryView.qml 2013-03-27 23:13:20 +0000 |
4 | @@ -0,0 +1,82 @@ |
5 | +import QtQuick 2.0 |
6 | +import Ubuntu.Components 0.1 |
7 | +import Ubuntu.Components.ListItems 0.1 as ListItem |
8 | +import "dateExt.js" as DateExt |
9 | +import "colorUtils.js" as Color |
10 | + |
11 | +ListView { |
12 | + id: diaryView |
13 | + |
14 | + property var dayStart: new Date() |
15 | + |
16 | + property bool expanded: false |
17 | + |
18 | + property bool expanding: false |
19 | + property bool compressing: false |
20 | + |
21 | + signal expand() |
22 | + signal compress() |
23 | + |
24 | + clip: true |
25 | + |
26 | + model: EventListModel { |
27 | + termStart: dayStart |
28 | + termLength: Date.msPerDay |
29 | + } |
30 | + |
31 | + section { |
32 | + property: "category" |
33 | + // labelPositioning: ViewSection.CurrentLabelAtStart // FIXME, unreliable |
34 | + delegate: ListItem.Header { |
35 | + text: i18n.tr(section) |
36 | + MouseArea { |
37 | + anchors.fill: parent |
38 | + onClicked: { |
39 | + if (expanded) |
40 | + compress() |
41 | + else |
42 | + expand() |
43 | + } |
44 | + } |
45 | + } |
46 | + } |
47 | + |
48 | + delegate: ListItem.Standard { |
49 | + text: startTime.toLocaleTimeString(Qt.locale(i18n.language), Locale.ShortFormat) + " " + title |
50 | + } |
51 | + |
52 | + footer: ListItem.Standard { |
53 | + text: i18n.tr("(+) New Event / Todo") |
54 | + } |
55 | + |
56 | + onContentYChanged: { |
57 | + // console.log(expanded, expanding, compressing, dragging, flicking, moving, contentY) |
58 | + if (expanding || compressing || !dragging) return |
59 | + |
60 | + if (expanded) { |
61 | + if (contentY < -units.gu(0.5)) { |
62 | + compressing = true |
63 | + expanding = false |
64 | + } |
65 | + } |
66 | + else { |
67 | + if (contentY < -units.gu(0.5)) { |
68 | + expanding = true |
69 | + compressing = false |
70 | + } |
71 | + } |
72 | + } |
73 | + |
74 | + onDraggingChanged: { |
75 | + if (dragging) return |
76 | + |
77 | + if (expanding) { |
78 | + expanding = false |
79 | + expand() |
80 | + } |
81 | + else if (compressing) { |
82 | + compressing = false |
83 | + compress() |
84 | + } |
85 | + } |
86 | +} |
87 | |
88 | === added file 'EventListModel.qml' |
89 | --- EventListModel.qml 1970-01-01 00:00:00 +0000 |
90 | +++ EventListModel.qml 2013-03-27 23:13:20 +0000 |
91 | @@ -0,0 +1,26 @@ |
92 | +import QtQuick 2.0 |
93 | +import "dateExt.js" as DateExt |
94 | +import "dataService.js" as DataService |
95 | + |
96 | +ListModel { |
97 | + id: model |
98 | + |
99 | + property var termStart: new Date() |
100 | + property var termLength: Date.msPerDay |
101 | + |
102 | + signal reload |
103 | + |
104 | + onReload: { |
105 | + var t0 = termStart.getTime() |
106 | + var t1 = t0 + termLength |
107 | + model.clear() |
108 | + DataService.getEvents(t0, t1, model) |
109 | + // for (var i = 0; i < model.count; ++i) |
110 | + // DataService.printEvent(model.get(i)) |
111 | + } |
112 | + Component.onCompleted: { |
113 | + reload() |
114 | + DataService.eventsNotifier().dataChanged.connect(reload) |
115 | + termStartChanged.connect(reload) |
116 | + } |
117 | +} |
118 | |
119 | === modified file 'EventView.qml' |
120 | --- EventView.qml 2013-03-07 00:13:06 +0000 |
121 | +++ EventView.qml 2013-03-27 23:13:20 +0000 |
122 | @@ -1,6 +1,6 @@ |
123 | import QtQuick 2.0 |
124 | import Ubuntu.Components 0.1 |
125 | -import "DateLib.js" as DateLib |
126 | +import "dateExt.js" as DateExt |
127 | |
128 | PathView { |
129 | id: eventView |
130 | @@ -10,6 +10,11 @@ |
131 | signal incrementCurrentDay |
132 | signal decrementCurrentDay |
133 | |
134 | + property bool expanded: false |
135 | + |
136 | + signal compress() |
137 | + signal expand() |
138 | + |
139 | readonly property real visibleHeight: parent.height - y |
140 | |
141 | QtObject { |
142 | @@ -43,7 +48,7 @@ |
143 | |
144 | path: Path { |
145 | startX: -eventView.width; startY: eventView.height / 2 |
146 | - PathLine { relativeX: eventView.width; relativeY: 0 } |
147 | + PathLine { relativeX: eventView.width; relativeY: 0 } |
148 | PathLine { relativeX: eventView.width; relativeY: 0 } |
149 | PathLine { relativeX: eventView.width; relativeY: 0 } |
150 | } |
151 | @@ -52,21 +57,22 @@ |
152 | |
153 | model: 3 |
154 | |
155 | - delegate: Rectangle { |
156 | - property var dayStart: { |
157 | + delegate: DiaryView { |
158 | + id: diaryView |
159 | + |
160 | + width: eventView.width |
161 | + height: eventView.height |
162 | + |
163 | + dayStart: { |
164 | if (index == intern.currentIndex) return intern.currentDayStart |
165 | var previousIndex = intern.currentIndex > 0 ? intern.currentIndex - 1 : 2 |
166 | if (index == previousIndex) return intern.currentDayStart.addDays(-1) |
167 | return intern.currentDayStart.addDays(1) |
168 | } |
169 | - width: eventView.width |
170 | - height: eventView.height |
171 | - color: index == 0 ? "#FFFFFF" : index == 1 ? "#EEEEEE" : "#DDDDDD" |
172 | - Label { |
173 | - anchors.horizontalCenter: parent.horizontalCenter |
174 | - y: units.gu(4) |
175 | - text: i18n.tr("No events for") + "\n" + Qt.formatDate(dayStart) |
176 | - fontSize: "large" |
177 | - } |
178 | + |
179 | + expanded: eventView.expanded |
180 | + |
181 | + onExpand: eventView.expand() |
182 | + onCompress: eventView.compress() |
183 | } |
184 | } |
185 | |
186 | === modified file 'MonthView.qml' |
187 | --- MonthView.qml 2013-03-18 20:39:06 +0000 |
188 | +++ MonthView.qml 2013-03-27 23:13:20 +0000 |
189 | @@ -1,6 +1,6 @@ |
190 | import QtQuick 2.0 |
191 | import Ubuntu.Components 0.1 |
192 | -import "DateLib.js" as DateLib |
193 | +import "dateExt.js" as DateExt |
194 | import "colorUtils.js" as Color |
195 | |
196 | ListView { |
197 | @@ -11,7 +11,8 @@ |
198 | readonly property var currentDayStart: intern.currentDayStart |
199 | |
200 | property bool compressed: false |
201 | - property real compressedHeight: intern.squareUnit + intern.verticalMargin * 2 |
202 | + readonly property real compressedHeight: intern.squareUnit + intern.verticalMargin * 2 |
203 | + readonly property real expandedHeight: intern.squareUnit * 6 + intern.verticalMargin * 2 |
204 | |
205 | signal incrementCurrentDay |
206 | signal decrementCurrentDay |
207 | @@ -90,7 +91,7 @@ |
208 | |
209 | property int squareUnit: monthView.width / 8 |
210 | property int verticalMargin: units.gu(1) |
211 | - property int weekstartDay: Qt.locale().firstDayOfWeek |
212 | + property int weekstartDay: Qt.locale(i18n.language).firstDayOfWeek |
213 | property int monthCount: 49 // months for +-2 years |
214 | |
215 | property var today: (new Date()).midnight() // TODO: update at midnight |
216 | @@ -99,8 +100,13 @@ |
217 | property var monthStart0: today.monthStart() |
218 | } |
219 | |
220 | +<<<<<<< TREE |
221 | width: parent.width > 0 ? parent.width : 1 |
222 | height: intern.squareUnit * 6 + intern.verticalMargin * 2 |
223 | +======= |
224 | + width: parent.width |
225 | + height: compressed ? compressedHeight : expandedHeight |
226 | +>>>>>>> MERGE-SOURCE |
227 | |
228 | interactive: !compressed |
229 | clip: true |
230 | |
231 | === added file 'avatar@8.png' |
232 | Binary files avatar@8.png 1970-01-01 00:00:00 +0000 and avatar@8.png 2013-03-27 23:13:20 +0000 differ |
233 | === removed file 'avatar@8.png' |
234 | Binary files avatar@8.png 2013-02-12 16:18:48 +0000 and avatar@8.png 1970-01-01 00:00:00 +0000 differ |
235 | === modified file 'calendar.qml' |
236 | --- calendar.qml 2013-03-18 11:39:23 +0000 |
237 | +++ calendar.qml 2013-03-27 23:13:20 +0000 |
238 | @@ -31,55 +31,47 @@ |
239 | onSelectedTabIndexChanged: monthView.gotoNextMonth(selectedTabIndex) |
240 | } |
241 | |
242 | + Rectangle { |
243 | + anchors.fill: monthView |
244 | + color: "white" |
245 | + } |
246 | + |
247 | MonthView { |
248 | id: monthView |
249 | onMonthStartChanged: tabs.selectedTabIndex = monthStart.getMonth() |
250 | y: pageArea.y |
251 | + onMovementEnded: eventView.currentDayStart = currentDayStart |
252 | + onCurrentDayStartChanged: if (!(dragging || flicking)) eventView.currentDayStart = currentDayStart |
253 | + Component.onCompleted: eventView.currentDayStart = currentDayStart |
254 | } |
255 | |
256 | EventView { |
257 | id: eventView |
258 | + |
259 | property real minY: pageArea.y + monthView.compressedHeight |
260 | - property real maxY: pageArea.y + monthView.height |
261 | + property real maxY: pageArea.y + monthView.expandedHeight |
262 | + |
263 | y: maxY |
264 | width: mainView.width |
265 | - height: parent.height - monthView.compressedHeight |
266 | - currentDayStart: monthView.currentDayStart |
267 | + height: parent.height - y |
268 | + |
269 | + expanded: monthView.compressed |
270 | + |
271 | Component.onCompleted: { |
272 | incrementCurrentDay.connect(monthView.incrementCurrentDay) |
273 | decrementCurrentDay.connect(monthView.decrementCurrentDay) |
274 | } |
275 | - MouseArea { |
276 | - id: drawer |
277 | - property bool compression: true |
278 | - anchors.fill: parent |
279 | - drag { |
280 | - axis: Drag.YAxis |
281 | - target: eventView |
282 | - minimumY: monthView.y + monthView.compressedHeight |
283 | - maximumY: monthView.y + monthView.height |
284 | - onActiveChanged: { |
285 | - if (compression) { |
286 | - if (drag.active) { |
287 | - monthView.compressed = true |
288 | - } |
289 | - else { |
290 | - yBehavior.enabled = true |
291 | - eventView.y = Qt.binding(function() { return eventView.minY }) |
292 | - compression = false |
293 | - } |
294 | - } |
295 | - else { |
296 | - if (drag.active) {} |
297 | - else{ |
298 | - eventView.y = Qt.binding(function() { return eventView.maxY }) |
299 | - monthView.compressed = false |
300 | - compression = true |
301 | - } |
302 | - } |
303 | - } |
304 | - } |
305 | - } |
306 | + |
307 | + onExpand: { |
308 | + monthView.compressed = true |
309 | + yBehavior.enabled = true |
310 | + y = minY |
311 | + } |
312 | + onCompress: { |
313 | + monthView.compressed = false |
314 | + y = maxY |
315 | + } |
316 | + |
317 | Behavior on y { |
318 | id: yBehavior |
319 | enabled: false |
320 | |
321 | === added file 'calendarTests.qml' |
322 | --- calendarTests.qml 1970-01-01 00:00:00 +0000 |
323 | +++ calendarTests.qml 2013-03-27 23:13:20 +0000 |
324 | @@ -0,0 +1,14 @@ |
325 | +import QtQuick 2.0 |
326 | +import Ubuntu.Components 0.1 |
327 | +import "dataServiceTests.js" as DataServiceTests |
328 | + |
329 | +MainView { |
330 | + width: units.gu(20) |
331 | + height: units.gu(20) |
332 | + Button { |
333 | + anchors.fill: parent |
334 | + anchors.margins: units.gu(5) |
335 | + text: "Close" |
336 | + onClicked: Qt.quit() |
337 | + } |
338 | +} |
339 | |
340 | === added file 'dataService.js' |
341 | --- dataService.js 1970-01-01 00:00:00 +0000 |
342 | +++ dataService.js 2013-03-27 23:13:20 +0000 |
343 | @@ -0,0 +1,361 @@ |
344 | +.pragma library |
345 | + |
346 | +.import QtQuick.LocalStorage 2.0 as LS |
347 | + |
348 | +Array.prototype.append = function(x) { this.push(x) } |
349 | + |
350 | +var CATEGORY_EVENT = 0 |
351 | +var CATEGORY_TODO = 1 |
352 | + |
353 | +function getEvents(termStart, termEnd, events) |
354 | +{ |
355 | + var result = null |
356 | + |
357 | + db().readTransaction( |
358 | + function(tx) { |
359 | + result = tx.executeSql('\ |
360 | +select * from Event \ |
361 | +where (? <= startTime and startTime < ?) or \ |
362 | + (? < endTime and endTime <= ?) or \ |
363 | + (startTime <= ? and ? <= endTime) \ |
364 | +order by startTime', |
365 | + [ termStart, termEnd, termStart, termEnd, termStart, termEnd ] |
366 | + ) |
367 | + } |
368 | + ) |
369 | + |
370 | + events = events || [] |
371 | + |
372 | + for (var i = 0; i < result.rows.length; ++i) { |
373 | + var e = result.rows.item(i) |
374 | + e.startTime = new Date(e.startTime) |
375 | + e.endTime = new Date(e.endTime) |
376 | + events.append(e) |
377 | + } |
378 | + |
379 | + return events |
380 | +} |
381 | + |
382 | +function getAttendees(event, attendees) |
383 | +{ |
384 | + var result = null; |
385 | + |
386 | + db().readTransaction( |
387 | + function(tx) { |
388 | + result = tx.executeSql('\ |
389 | +select c.* from Attendance a, Contact c \ |
390 | +where a.eventId = ? and a.contactId = c.id \ |
391 | +order by c.name', |
392 | + [ event.id ] |
393 | + ) |
394 | + } |
395 | + ) |
396 | + |
397 | + attendees = attendees || [] |
398 | + |
399 | + for (var i = 0; i < result.rows.length; ++i) |
400 | + attendees.append(result.rows.item(i)) |
401 | + |
402 | + return attendees |
403 | +} |
404 | + |
405 | +function addEvent(event) |
406 | +{ |
407 | + var result = null |
408 | + |
409 | + db().transaction( |
410 | + function(tx) { |
411 | + result = tx.executeSql('\ |
412 | +insert into Event(title, message, startTime, endTime) \ |
413 | +values (?, ?, ?, ?)', |
414 | + [ event.title, event.message, event.startTime, event.endTime ] |
415 | + ) |
416 | + } |
417 | + ) |
418 | + |
419 | + event.id = result.insertId |
420 | + |
421 | + eventsNotifier().dataChanged() |
422 | + |
423 | + return event |
424 | +} |
425 | + |
426 | +function removeEvent(event) |
427 | +{ |
428 | + db().transaction( |
429 | + function(tx) { |
430 | + tx.executeSql( |
431 | + 'delete from Event where id = ?', |
432 | + [ event.id ] |
433 | + ) |
434 | + tx.executeSql( |
435 | + 'delete from Attendance where eventId = ?', |
436 | + [ event.id ] |
437 | + ) |
438 | + tx.executeSql( |
439 | + 'delete from Venue where eventId = ?', |
440 | + [ event.id ] |
441 | + ) |
442 | + } |
443 | + ) |
444 | + |
445 | + delete event.id |
446 | + |
447 | + eventsNotifier().dataChanged() |
448 | +} |
449 | + |
450 | +function getContacts(contacts) |
451 | +{ |
452 | + var result = null |
453 | + |
454 | + db().readTransaction( |
455 | + function(tx) { |
456 | + result = tx.executeSql('select * from Contact order by name') |
457 | + } |
458 | + ) |
459 | + |
460 | + contacts = contacts || [] |
461 | + |
462 | + for (var i = 0; i < result.rows.length; ++i) |
463 | + contacts.append(result.rows.item(i)) |
464 | + |
465 | + return contacts |
466 | +} |
467 | + |
468 | +function addAttendee(event, contact) |
469 | +{ |
470 | + db().transaction( |
471 | + function(tx) { |
472 | + tx.executeSql( |
473 | + 'insert into Attendance(eventId, contactId) values (?, ?)', |
474 | + [ event.id, contact.id ] |
475 | + ) |
476 | + } |
477 | + ) |
478 | +} |
479 | + |
480 | +function removeAttendee(event, contact) |
481 | +{ |
482 | + db().transaction( |
483 | + function(tx) { |
484 | + tx.executeSql( |
485 | + 'delete from Attendance where eventId = ? and contactId = ?', |
486 | + [ event.id, contact.id ] |
487 | + ) |
488 | + } |
489 | + ) |
490 | +} |
491 | + |
492 | +function getPlaces(places) |
493 | +{ |
494 | + var result = null |
495 | + |
496 | + db().readTransaction( |
497 | + function(tx) { |
498 | + result = tx.executeSql('select * from Place') |
499 | + } |
500 | + ) |
501 | + |
502 | + places = places || [] |
503 | + |
504 | + for (var i = 0; i < result.rows.length; ++i) |
505 | + places.append(result.rows.item(i)) |
506 | + |
507 | + return places |
508 | +} |
509 | + |
510 | +function addPlace(place) |
511 | +{ |
512 | + var result = null |
513 | + |
514 | + if (typeof place.address == 'undefined') place.address = null |
515 | + if (typeof place.latitude == 'undefined') place.latitude = null |
516 | + if (typeof place.longitude == 'undefined') place.longitude = null |
517 | + |
518 | + db().transaction( |
519 | + function(tx) { |
520 | + result = tx.executeSql( |
521 | + 'insert into Place(name, address, latitude, longitude) values(?, ?, ?, ?)', |
522 | + [ place.name, place.address, place.latitude, place.longitude ] |
523 | + ) |
524 | + } |
525 | + ) |
526 | + |
527 | + place.id = result.insertId |
528 | + |
529 | + return place |
530 | +} |
531 | + |
532 | +function removePlace(place) |
533 | +{ |
534 | + db().transaction( |
535 | + function(tx) { |
536 | + tx.executeSql( |
537 | + 'delete from Place where id = ?', |
538 | + [ place.id ] |
539 | + ) |
540 | + tx.executeSql( |
541 | + 'delete from Venue where placeId = ?', |
542 | + [ place.id ] |
543 | + ) |
544 | + } |
545 | + ) |
546 | + |
547 | + delete place.id |
548 | +} |
549 | + |
550 | +function addVenue(event, place) |
551 | +{ |
552 | + db().transaction( |
553 | + function(tx) { |
554 | + tx.executeSql( |
555 | + 'insert into Venue(eventId, placeId) values(?, ?)', |
556 | + [ event.id, place.id ] |
557 | + ) |
558 | + } |
559 | + ) |
560 | +} |
561 | + |
562 | +function removeVenue(event, place) |
563 | +{ |
564 | + db().transaction( |
565 | + function(tx) { |
566 | + tx.executeSql( |
567 | + 'delete from Venue where eventId = ? and placeId = ?', |
568 | + [ event.id, place.id ] |
569 | + ) |
570 | + } |
571 | + ) |
572 | +} |
573 | + |
574 | +function getVenues(event, venues) |
575 | +{ |
576 | + var result = null |
577 | + |
578 | + db().readTransaction( |
579 | + function(tx) { |
580 | + result = tx.executeSql('\ |
581 | +select p.* \ |
582 | +from Venue v, Place p \ |
583 | +where v.eventId = ? and p.id = v.placeId \ |
584 | +order by p.name', |
585 | + [ event.id ] |
586 | + ) |
587 | + } |
588 | + ) |
589 | + |
590 | + venues = venues || [] |
591 | + |
592 | + for (var i = 0; i < result.rows.length; ++i) |
593 | + venues.append(result.rows.item(i)) |
594 | + |
595 | + return venues |
596 | +} |
597 | + |
598 | +function printEvent(event) |
599 | +{ |
600 | + console.log('Event', event) |
601 | + console.log(' id:', event.id) |
602 | + console.log(' title:', event.title) |
603 | + console.log(' message:', event.message) |
604 | + console.log(' startTime:', new Date(event.startTime).toLocaleString()) |
605 | + console.log(' endTime:', new Date(event.endTime).toLocaleString()) |
606 | + |
607 | + var attendees = [] |
608 | + var venues = [] |
609 | + getAttendees(event, attendees) |
610 | + getVenues(event, venues) |
611 | + for (var j = 0; j < attendees.length; ++j) |
612 | + printContact(attendees[j]) |
613 | + for (var j = 0; j < venues.length; ++j) |
614 | + printPlace(venues[j]) |
615 | + console.log('') |
616 | +} |
617 | + |
618 | +function printContact(contact) |
619 | +{ |
620 | + console.log('Contact', contact) |
621 | + console.log(' id:', contact.id) |
622 | + console.log(' name:', contact.name) |
623 | + console.log(' surname:', contact.surname) |
624 | + console.log(' avatar:', contact.avatar) |
625 | +} |
626 | + |
627 | +function printPlace(place) |
628 | +{ |
629 | + console.log('Place', place) |
630 | + console.log(' name:', place.name) |
631 | + console.log(' address:', place.address) |
632 | + console.log(' latitude:', place.latitude) |
633 | + console.log(' longitude:', place.longitude) |
634 | +} |
635 | + |
636 | +function __createFirstTime(tx) |
637 | +{ |
638 | + var schema = '\ |
639 | +create table Event(\ |
640 | + id integer primary key,\ |
641 | + title text,\ |
642 | + message text,\ |
643 | + startTime integer,\ |
644 | + endTime integer,\ |
645 | + category text default "Events"\ |
646 | +);\ |
647 | +\ |
648 | +create index EventStartTimeIndex on Event(startTime);\ |
649 | +create index EventEndTimeIndex on Event(endTime);\ |
650 | +\ |
651 | +create table Place(\ |
652 | + id integer primary key,\ |
653 | + name text,\ |
654 | + address text,\ |
655 | + latitude real,\ |
656 | + longitude real\ |
657 | +);\ |
658 | +\ |
659 | +create table Contact(\ |
660 | + id integer primary key,\ |
661 | + name text,\ |
662 | + surname text,\ |
663 | + avatar text\ |
664 | +);\ |
665 | +\ |
666 | +create table Attendance(\ |
667 | + id integer primary key,\ |
668 | + eventId integer references Event(id) on delete cascade,\ |
669 | + contactId integer references Contact(id) on delete cascade,\ |
670 | + placeId integer references Place(id) on delete set null\ |
671 | +);\ |
672 | +\ |
673 | +create table Venue(\ |
674 | + id integer primary key,\ |
675 | + eventId integer references Event(id) on delete cascade,\ |
676 | + placeId integer references Place(id) on delete cascade\ |
677 | +);\ |
678 | +\ |
679 | +'.split(';') |
680 | + |
681 | + for (var i = 0; i < schema.length; ++i) { |
682 | + var sql = schema[i] |
683 | + if (sql != "") { |
684 | + console.log(sql) |
685 | + tx.executeSql(sql) |
686 | + } |
687 | + } |
688 | +} |
689 | + |
690 | +function eventsNotifier() |
691 | +{ |
692 | + if (!eventsNotifier.hasOwnProperty("instance")) |
693 | + eventsNotifier.instance = Qt.createQmlObject('import QtQuick 2.0; QtObject { signal dataChanged }', Qt.application, 'DataService.eventsNotifier()') |
694 | + return eventsNotifier.instance |
695 | +} |
696 | + |
697 | +function db() |
698 | +{ |
699 | + if (!db.hasOwnProperty("instance")) { |
700 | + db.instance = LS.LocalStorage.openDatabaseSync("Calendar", "", "Offline Calendar", 100000) |
701 | + if (db.instance.version == "") db.instance.changeVersion("", "0.1", __createFirstTime) |
702 | + } |
703 | + return db.instance |
704 | +} |
705 | |
706 | === added file 'dataServiceTests.js' |
707 | --- dataServiceTests.js 1970-01-01 00:00:00 +0000 |
708 | +++ dataServiceTests.js 2013-03-27 23:13:20 +0000 |
709 | @@ -0,0 +1,192 @@ |
710 | +.pragma library |
711 | +.import "dateExt.js" as DateExt |
712 | +.import "dataService.js" as DataService |
713 | + |
714 | +function clearData(tx) { |
715 | + var deletes = '\ |
716 | +delete from Event;\ |
717 | +delete from Place;\ |
718 | +delete from Contact;\ |
719 | +delete from Attendance;\ |
720 | +delete from Venue\ |
721 | +'.split(';') |
722 | + for (var i = 0; i < deletes.length; ++i) |
723 | + tx.executeSql(deletes[i]) |
724 | +} |
725 | + |
726 | +function loadTestDataSimple(tx) |
727 | +{ |
728 | + clearData(tx) |
729 | + |
730 | + var inserts = '\ |
731 | +insert into Contact(id, name, surname) values (1, "John", "Smith");\ |
732 | +insert into Contact(id, name, surname) values (2, "Jane", "Smith");\ |
733 | +insert into Contact(id, name, surname, avatar) values (3, "Frank", "Mertens", "http://www.gravatar.com/avatar/6d96fd4a98bba7b8779661d5db391ab6");\ |
734 | +insert into Contact(id, name, surname) values (4, "Kunal", "Parmar");\ |
735 | +insert into Contact(id, name, surname) values (5, "Mario", "Boikov");\ |
736 | +insert into Place(id, name, address) values (1, "Quan Sen", "Pasing Arcaden, München");\ |
737 | +insert into Place(id, name, address) values (2, "Jashan", "Landsberger Straße 84, 82110 Germering");\ |
738 | +insert into Place(id, name, latitude, longitude) values (3, "Café Moskau", 52.521339, 13.42279);\ |
739 | +insert into Place(id, name, address) values (4, "Santa Clara Marriott", "2700 Mission College Boulevard, Santa Clara, California");\ |
740 | +insert into Place(id, name, address) values (5, "embeddedworld", "Messezentrum, 90471 Nürnberg");\ |
741 | +insert into Event(id, title, message, startTime, endTime) values (1, "Team Meeting", "Bring your gear...", 1364648400000, 1364650200000);\ |
742 | +insert into Event(id, title, message, startTime, endTime) values (2, "Jane\'s Birthday Party", "this year: southern wine", 1364061600000, 1364068800000);\ |
743 | +insert into Event(id, title, startTime, endTime) values (3, "embeddedworld 2013", 1361836800000, 1362009600000);\ |
744 | +insert into Attendance(eventId, contactId, placeId) values (1, 1, 1);\ |
745 | +insert into Attendance(eventId, contactId, placeId) values (1, 2, 1);\ |
746 | +insert into Attendance(eventId, contactId, placeId) values (1, 3, 1);\ |
747 | +insert into Attendance(eventId, contactId, placeId) values (1, 4, 3);\ |
748 | +insert into Attendance(eventId, contactId, placeId) values (1, 5, 3);\ |
749 | +insert into Attendance(eventId, contactId) values (2, 1);\ |
750 | +insert into Attendance(eventId, contactId) values (2, 2);\ |
751 | +insert into Attendance(eventId, contactId) values (2, 3);\ |
752 | +insert into Venue(eventId, placeId) values (2, 3);\ |
753 | +insert into Venue(eventId, placeId) values (3, 5)\ |
754 | +'.split(';') |
755 | + |
756 | + for (var i = 0; i < inserts.length; ++i) { |
757 | + var sql = inserts[i] |
758 | + if (sql != "") { |
759 | + console.log(sql) |
760 | + tx.executeSql(sql) |
761 | + } |
762 | + } |
763 | +} |
764 | + |
765 | +function loadTestDataComplex(tx) |
766 | +{ |
767 | + clearData(tx) |
768 | + |
769 | + function t(d, h, m) { |
770 | + if (typeof t.today == "undefined") t.today = new Date().midnight() |
771 | + return t.today.addDays(d).setHours(h, m) |
772 | + } |
773 | + |
774 | + var places = [ |
775 | + { id: 1, name: "Moskau A" }, |
776 | + { id: 2, name: "Moskau B" }, |
777 | + { id: 3, name: "Bischkek" }, |
778 | + { id: 4, name: "Asgabat A" }, |
779 | + { id: 5, name: "Asgabat B" }, |
780 | + { id: 6, name: "Vilnius" }, |
781 | + { id: 7, name: "Riga" } |
782 | + ] |
783 | + |
784 | + var speaker = [ |
785 | + { id: 1, name: "Sean", surname: "Harmer" }, |
786 | + { id: 2, name: "Marc", surname: "Lutz" }, |
787 | + { id: 3, name: "David", surname: "Faure" }, |
788 | + { id: 4, name: "Volker", surname: "Krause" }, |
789 | + { id: 5, name: "Kevin", surname: "Krammer" }, |
790 | + { id: 6, name: "Tobias", surname: "Nätterlund" }, |
791 | + { id: 7, name: "Steffen", surname: "Hansen" }, |
792 | + { id: 8, name: "Tommi", surname: "Laitinen" }, |
793 | + { id: 9, name: "Lars", surname: "Knoll" }, |
794 | + { id: 10, name: "Roland", surname: "Krause" }, |
795 | + { id: 11, name: "Jens", surname: "Bache-Wiig" }, |
796 | + { id: 12, name: "Michael", surname: "Wagner" }, |
797 | + { id: 13, name: "Helmut", surname: "Sedding" }, |
798 | + { id: 14, name: "Jeff", surname: "Tranter" }, |
799 | + { id: 15, name: "Simon", surname: "Hausmann" }, |
800 | + { id: 16, name: "Stephen", surname: "Kelly" }, |
801 | + { id: 17, name: "Tam", surname: "Hanna" }, |
802 | + { id: 18, name: "Mirko", surname: "Boehm" }, |
803 | + { id: 19, name: "Till", surname: "Adam" }, |
804 | + { id: 20, name: "Thomas", surname: "Senyk" } |
805 | + ] |
806 | + |
807 | + var events = [ |
808 | + { id: 1, room: 1, speaker: [ 1 ], title: "Modern OpenGL with Qt5", message: "hands-on training", startTime: t(0, 10, 00), endTime: t(0, 12 ,00) }, |
809 | + { id: 2, room: 2, speaker: [ 2 ], title: "What's new in C++11", message: "focus on Qt5", startTime: t(0, 13, 00), endTime: t(0, 14, 30) }, |
810 | + { id: 3, room: 3, speaker: [ 3 ], title: "Model/view Programming using Qt", message: "hands-on training", startTime: t(0, 14, 45), endTime: t(0, 16, 15) }, |
811 | + { id: 4, room: 4, speaker: [ 4 ], title: "Introduction to Qt Quick", message: "hands-on training", startTime: t(0, 16, 30), endTime: t(0, 17, 45) }, |
812 | + { id: 5, room: 1, speaker: [ 8 ], title: "Keynote: Qt – Gearing up for the Future", message: "", startTime: t(1, 9, 15), endTime: t(1, 9, 30) }, |
813 | + { id: 6, room: 1, speaker: [ 9 ], title: "Keynote: Qt 5 Roadmap", message: "", startTime: t(1, 9, 30), endTime: t(1, 10, 30) }, |
814 | + { id: 7, room: 3, speaker: [ 10 ], title: "Qt and the Google APIs", message: "", startTime: t(1, 10, 45), endTime: t(1, 11, 45) }, |
815 | + { id: 8, room: 7, speaker: [ 11 ], title: "Desktop Components for QtQuick", message: "", startTime: t(1, 10, 45), endTime: t(1, 11, 45) }, |
816 | + { id: 9, room: 2, speaker: [ 9 ], title: "The Future of Qt on Embedded Linux", message: "", startTime: t(1, 12, 45), endTime: t(1, 13, 45) }, |
817 | + { id: 10, room: 7, speaker: [ 12, 13 ], title: "QML for desktop apps", message: "", startTime: t(1, 12, 45), endTime: t(1, 13, 34) }, |
818 | + { id: 11, room: 2, speaker: [ 14 ], title: "Qt on Raspberry Pi", message: "", startTime: t(1, 14, 00), endTime: t(1, 15, 00) }, |
819 | + { id: 12, room: 3, speaker: [ 15 ], title: "What's new in QtWebKit in 5.0", message: "", startTime: t(1, 14, 00), endTime: t(1, 15, 00) }, |
820 | + { id: 13, room: 1, speaker: [ 16 ], title: "In Depth – QMetaType and QMetaObject", message: "", startTime: t(1, 15, 30), endTime: t(1, 16, 30) }, |
821 | + { id: 14, room: 2, speaker: [ 17 ], title: "Using Qt as mobile cross-platform system", message: "", startTime: t(1, 15, 30), endTime: t(1, 16, 30) }, |
822 | + { id: 15, room: 1, speaker: [ 18, 19 ], title: "Intentions good, warranty void: Using Qt in unexpected ways", message: "", startTime: t(1, 16, 45), endTime: t(1, 17, 45) }, |
823 | + { id: 16, room: 2, speaker: [ 20 ], title: "Porting Qt 5 to embedded hardware", message: "", startTime: t(1, 16, 45), endTime: t(1, 17, 45) } |
824 | + ] |
825 | + |
826 | + for (var i = 0; i < places.length; ++i) { |
827 | + var p = places[i] |
828 | + tx.executeSql( |
829 | + 'insert into Place(id, name, address) values (?, ?, ?)', |
830 | + [ p.id, p.name, "Cafe Moskau, Berlin, Germany" ] |
831 | + ) |
832 | + } |
833 | + |
834 | + for (var i = 0; i < speaker.length; ++i) { |
835 | + var s = speaker[i] |
836 | + tx.executeSql( |
837 | + 'insert into Contact(id, name, surname) values (?, ?, ?)', |
838 | + [ s.id, s.name, s.surname ] |
839 | + ) |
840 | + } |
841 | + |
842 | + for (var i = 0; i < events.length; ++i) { |
843 | + var e = events[i] |
844 | + tx.executeSql( |
845 | + 'insert into Event(id, title, message, startTime, endTime) values (?, ?, ?, ?, ?)', |
846 | + [ e.id, e.title, e.message, e.startTime, e.endTime ] |
847 | + ) |
848 | + tx.executeSql( |
849 | + 'insert into Venue(eventId, placeId) values (?, ?)', |
850 | + [ e.id, e.room ] |
851 | + ) |
852 | + for (var j = 0; j < e.speaker.length; ++j) { |
853 | + tx.executeSql( |
854 | + 'insert into Attendance(eventId, contactId) values (?, ?)', |
855 | + [ e.id, e.speaker[j] ] |
856 | + ) |
857 | + } |
858 | + } |
859 | +} |
860 | + |
861 | +function runTestSimple(tx) |
862 | +{ |
863 | + loadTestDataSimple(tx) |
864 | + |
865 | + var contacts = [] |
866 | + DataService.getContacts(contacts) |
867 | + for (var i = 0; i < contacts.length; ++i) |
868 | + DataService.printContact(contacts[i]) |
869 | + console.log('') |
870 | + |
871 | + var testEvent = DataService.addEvent({ |
872 | + title: 'Critical Review', |
873 | + message: '', |
874 | + startTime: new Date(2013, 2, 30, 10, 00).getTime(), |
875 | + endTime: new Date(2013, 2, 30, 10, 30).getTime() |
876 | + }) |
877 | + DataService.addAttendee(testEvent, contacts[1]) |
878 | + DataService.addAttendee(testEvent, contacts[2]) |
879 | + DataService.addAttendee(testEvent, contacts[0]) |
880 | + DataService.removeAttendee(testEvent, contacts[0]) |
881 | + var testPlace = DataService.addPlace({ name: 'Jane\'s bar' }) |
882 | + DataService.addVenue(testEvent, testPlace) |
883 | + console.log('Added new event with id', testEvent.id) |
884 | + console.log('') |
885 | + |
886 | + var events = [] |
887 | + var dayStart = new Date(2013, 2, 30) |
888 | + DataService.getEvents(dayStart.getTime(), dayStart.addDays(1).getTime(), events) |
889 | + for (var i = 0; i < events.length; ++i) |
890 | + DataService.printEvent(events[i]) |
891 | + |
892 | + DataService.removeEvent(testEvent) |
893 | + DataService.removePlace(testPlace) |
894 | +} |
895 | + |
896 | +DataService.db().transaction( |
897 | + function (tx) { |
898 | + runTestSimple(tx) |
899 | + loadTestDataComplex(tx) |
900 | + } |
901 | +) |
902 | |
903 | === renamed file 'DateLib.js' => 'dateExt.js' |
904 | === added file 'icon-contacts@8.png' |
905 | Binary files icon-contacts@8.png 1970-01-01 00:00:00 +0000 and icon-contacts@8.png 2013-03-27 23:13:20 +0000 differ |
906 | === added file 'icon-location@18.png' |
907 | Binary files icon-location@18.png 1970-01-01 00:00:00 +0000 and icon-location@18.png 2013-03-27 23:13:20 +0000 differ |
PASSED: Continuous integration, rev:10 91.189. 93.125: 8080/job/ ubuntu- calendar- app-ci/ 4/ 91.189. 93.125: 8080/job/ ubuntu- calendar- app-quantal- amd64-ci/ 4/console 91.189. 93.125: 8080/job/ ubuntu- calendar- app-raring- amd64-ci/ 4/console
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: 91.189. 93.125: 8080/job/ ubuntu- calendar- app-ci/ 4/rebuild
http://