Plugin QML Type
The Plugin type describes a Location based services plugin. More...
| Import Statement: | import QtLocation 5.3 |
| Since: | Qt Location 5.5 |
Properties
- allowExperimental : bool
- availableServiceProviders : stringlist
- isAttached : bool
- locales : stringlist
- name : string
- parameters : list<PluginParameter>
- preferred : stringlist
- required : enumeration
Methods
- bool supportsGeocoding(GeocodingFeatures features)
- bool supportsMapping(MappingFeatures features)
- bool supportsPlaces(PlacesFeatures features)
- bool supportsRouting(RoutingFeatures features)
Detailed Description
The Plugin type is used to declaratively specify which available GeoServices plugin should be used for various tasks in the Location API. Plugins are used by Map, RouteModel, and GeocodeModel types, as well as a variety of others.
Plugins recognized by the system have a name property, a simple string normally indicating the name of the service that the Plugin retrieves data from. They also have a variety of features, which can be test for using the supportsRouting(), supportsGeocoding(), supportsMapping() and supportsPlaces() methods.
When a Plugin object is created, it is "detached" and not associated with any actual service plugin. Once it has received information via setting its name, preferred, or required properties, it will choose an appropriate service plugin to attach to. Plugin objects can only be attached once; to use multiple plugins, create multiple Plugin objects.
Example Usage
The following snippet shows a Plugin object being created with the required and preferred properties set. This Plugin will attach to the first found plugin that supports both mapping and geocoding, and will prefer plugins named "here" or "osm" to any others.
Plugin { id: plugin preferred: ["here", "osm"] required: Plugin.AnyMappingFeatures | Plugin.AnyGeocodingFeatures }
Property Documentation
This property indicates if the Plugin item is attached to a geoservice provider plugin.
This property contains an ordered list of preferred plugin locales. If the first locale cannot be accommodated, then the backend falls back to using the second, and so on. By default the locales property contains the system locale.
The locales are specified as strings which have the format "language[_script][_country]" or "C", where:
- language is a lowercase, two-letter, ISO 639 language code,
- script is a titlecase, four-letter, ISO 15924 script code,
- country is an uppercase, two- or three-letter, ISO 3166 country code (also "419" as defined by United Nations),
- the "C" locale is identical in behavior to English/UnitedStates as per QLocale
If the first specified locale cannot be accommodated, the Plugin falls back to the next and so forth. Some Plugin backends may not support a set of locales which are rigidly defined. An arbitrary example is that some Place's in France could have French and English localizations, while certain areas in America may only have the English localization available. In the above scenario, the set of supported locales is context dependent on the search location.
If the Plugin cannot accommodate any of the preferred locales, the manager falls back to using a supported language that is backend specific.
For Plugin's that do not support locales, the locales list is always empty.
The following code demonstrates how to set a single and multiple locales:
//single locale Plugin { locales: "en_US" } //multiple locales Plugin { locales: ["fr_FR","en_US"] }
This property holds the name of the plugin. Setting this property will cause the Plugin to only attach to a plugin with exactly this name. The value of required will be ignored.
[default] parameters : list<PluginParameter> |
This property holds the list of plugin parameters.
This property contains an ordered list of preferred plugin names, which will be checked for the required features set in required before any other available plugins are checked.
This property contains the set of features that will be required by the Plugin object when choosing which service plugin to attach to. If the name property is set, this has no effect.
Any of the following values or a bitwise combination of multiple values may be set:
- Plugin.NoFeatures
- Plugin.GeocodingFeature
- Plugin.ReverseGeocodingFeature
- Plugin.RoutingFeature
- Plugin.MappingFeature
- Plugin.AnyPlacesFeature
Method Documentation
This method returns a boolean indicating whether the specified set of features are supported by the geo service provider plugin. True is returned if all specified features are supported; otherwise false is returned.
The features parameter can be any flag combination of:
| Feature | Description |
|---|---|
| Plugin.NoGeocodingFeatures | No geocoding features are supported. |
| Plugin.OnlineGeocodingFeature | Online geocoding is supported. |
| Plugin.OfflineGeocodingFeature | Offline geocoding is supported. |
| Plugin.ReverseGeocodingFeature | Reverse geocoding is supported. |
| Plugin.LocalizedGeocodingFeature | Supports returning geocoding results with localized addresses. |
| Plugin.AnyGeocodingFeatures | Matches a geo service provider that provides any geocoding features. |
This method returns a boolean indicating whether the specified set of features are supported by the geo service provider plugin. True is returned if all specified features are supported; otherwise false is returned.
The features parameter can be any flag combination of:
| Feature | Description |
|---|---|
| Plugin.NoMappingFeatures | No mapping features are supported. |
| Plugin.OnlineMappingFeature | Online mapping is supported. |
| Plugin.OfflineMappingFeature | Offline mapping is supported. |
| Plugin.LocalizedMappingFeature | Supports returning localized map data. |
| Plugin.AnyMappingFeatures | Matches a geo service provider that provides any mapping features. |
This method returns a boolean indicating whether the specified set of features are supported by the geo service provider plugin. True is returned if all specified features are supported; otherwise false is returned.
The features parameter can be any flag combination of:
| Feature | Description |
|---|---|
| Plugin.NoPlacesFeatures | No places features are supported. |
| Plugin.OnlinePlacesFeature | Online places is supported. |
| Plugin.OfflinePlacesFeature | Offline places is supported. |
| Plugin.SavePlaceFeature | Saving categories is supported. |
| Plugin.RemovePlaceFeature | Removing or deleting places is supported. |
| Plugin.PlaceRecommendationsFeature | Searching for recommended places similar to another place is supported. |
| Plugin.SearchSuggestionsFeature | Search suggestions is supported. |
| Plugin.LocalizedPlacesFeature | Supports returning localized place data. |
| Plugin.NotificationsFeature | Notifications of place and category changes is supported. |
| Plugin.PlaceMatchingFeature | Supports matching places from two different geo service providers. |
| Plugin.AnyPlacesFeatures | Matches a geo service provider that provides any places features. |
This method returns a boolean indicating whether the specified set of features are supported by the geo service provider plugin. True is returned if all specified features are supported; otherwise false is returned.
The features parameter can be any flag combination of:
| Feature | Description |
|---|---|
| Plugin.NoRoutingFeatures | No routing features are supported. |
| Plugin.OnlineRoutingFeature | Online routing is supported. |
| Plugin.OfflineRoutingFeature | Offline routing is supported. |
| Plugin.LocalizedRoutingFeature | Supports returning routes with localized addresses and instructions. |
| Plugin.RouteUpdatesFeature | Updating an existing route based on the current position is supported. |
| Plugin.AlternativeRoutesFeature | Supports returning alternative routes. |
| Plugin.ExcludeAreasRoutingFeature | Supports specifying a areas which the returned route must not cross. |
| Plugin.AnyRoutingFeatures | Matches a geo service provider that provides any routing features. |


Event Sample1
description of the event
description of the event
description of the event
Event Sample2
description of the event
description of the event
description of the event
Event Sample2
description of the event
description of the event
description of the event


Scrum is an agile way to manage a project, usually software development. Agile software development with Scrum is often perceived as a methodology; but rather than viewing Scrum as methodology, think of it as a framework for managing a process.
What is Scrum?
In the agile Scrum world, instead of providing complete, detailed descriptions of how everything is to be done on a project, much of it is left up to the Scrum software development team. This is because the team will know best how to solve the problem they are presented.
This is why in Scrum development, for example, a sprint planning meeting is described in terms of the desired outcome (a commitment to a set of features to be developed in the next sprint) instead of a set of Entry criteria, Task definitions, Validation criteria, Exit criteria (ETVX) and so on, as would be provided in most methodologies.
Scrum relies on a self-organizing, cross-functional team. The scrum team is self-organizing in that there is no overall team leader who decides which person will do which task or how a problem will be solved. Those are issues that are decided by the team as a whole.
And in Scrum, a team is cross functional, meaning everyone is needed to take a feature from idea to implementation.
Within agile development, Scrum teams are supported by two specific roles. The first is a ScrumMaster, who can be thought of as a coach for the team, helping team members use the Scrum process to perform at the highest level.
The product owner (PO) is the other role, and in Scrum software development, represents the business, customers or users, and guides the team toward building the right product.
Scrum Development: What’s Involved?
The Scrum model suggests that projects progress via a series of sprints. In keeping with an agile methodology, sprints are timeboxed to no more than a month long, most commonly two weeks.
Scrum methodology advocates for a planning meeting at the start of the sprint, where team members figure out how many items they can commit to, and then create a sprint backlog – a list of the tasks to perform during the sprint.
During an agile Scrum sprint, the Scrum team takes a small set of features from idea to coded and tested functionality. At the end, these features are done, meaning coded, tested and integrated into the evolving product or system.
On each day of the sprint, all team members should attend a daily Scrum meeting, including the ScrumMaster and the product owner. This meeting is timeboxed to no more than 15 minutes. During that time, team members share what they worked on the prior day, will work on that day, and identify any impediments to progress.
The Scrum model sees daily scrums as a way to synchronize the work of team members as they discuss the work of the sprint.
At the end of a sprint, the team conducts a sprint review during which the team demonstrates the new functionality to the PO or any other stakeholder who wishes to provide feedback that could influence the next sprint.
This feedback loop within Scrum software development may result in changes to the freshly delivered functionality, but it may just as likely result in revising or adding items to the product backlog.
Another activity in Scrum project management is the sprint retrospective at the end of each sprint. The whole team participates in this meeting, including the ScrumMaster and PO. The meeting is an opportunity to reflect on the sprint that has ended, and identify opportunities to improve.
Scrum Process: The Main Artifacts
The primary artifact in Scrum development is, of course, the product itself. The Scrum model expects the team to bring the product or system to a potentially shippable state at the end of each Scrum sprint.
The product backlog is another artifact of Scrum. This is the complete list of the functionality that remains to be added to the product. The product owner prioritizes the backlog so the team always works on the most valuable features first.
The most popular and successful way to create a product backlog using Scrum methodology is to populate it with user stories, which are short descriptions of functionality described from the perspective of a user or customer.
In Scrum project management, on the first day of a sprint and during the planning meeting, team members create the sprint backlog. The sprint backlog can be thought of as the team's to-do list for the sprint, whereas a product backlog is a list of features to be built (written in the form of user stories).
The sprint backlog is the list of tasks the team needs to perform in order to deliver the functionality it committed to deliver during the sprint.
Additional artifacts resulting from the Scrum agile methodology is the sprint burndown chart and release burndown chart. Burndown charts show the amount of work remaining either in a sprint or a release, and are an effective tool in Scrum software development to determine whether a sprint or release is on schedule to have all planned work finished by the desired date.
The Agile Scrum Project: Main Roles
Even if you are new to Scrum, you may have heard of a role called the ScrumMaster. The ScrumMaster is the team's coach, and helps Scrum practitioners achieve their highest level of performance.
In the Scrum process, a ScrumMaster differs from a traditional project manager in many ways, including that this role does not provide day-to-day direction to the team and does not assign tasks to individuals.
A good ScrumMaster shelters the team from outside distractions, allowing team members to focus maniacally during the sprint on the goal they have selected.
While the ScrumMaster focuses on helping the team be the best that it can be, the product owner works to direct the team to the right goal. The product owner does this by creating a compelling vision of the product, and then conveying that vision to the team through the product backlog.
The product owner is responsible for prioritizing the backlog during Scrum development, to ensure it’s up to par as more is learned about the system being built, its users, the team and so on.
The third and final role in Scrum project management is the Scrum team itself. Although individuals may join the team with various job titles, in Scrum, those titles are insignificant. Scrum methodology states that each person contributes in whatever way they can to complete the work of each sprint.
This does not mean that a tester will be expected to re-architect the system; individuals will spend most (and sometimes all) of their time working in whatever discipline they worked before adopting the agile Scrum model. But with Scrum, individuals are expected to work beyond their preferred disciplines whenever doing so would be for the good of the team.
One way to think of the interlocking nature of these three roles in this agile methodology is as a racecar.
The Scrum team is the car itself, ready to speed along in whatever direction it is pointed. The product owner is the driver, making sure that the car is always going in the right direction. And the ScrumMaster is the chief mechanic, keeping the car well tuned and performing at its best.


HOME
RECIPES
Recipes
Flour & Grains
Rice
All Purpose Wheat Flour
MaizeFlour
Pasta
Beans
Lentils
Black Beans
Dairy
Milk
Eggs
Cheddar
Parmesan
Yogurt
Coconut Milk
Vegetables
Tomatoes
Cabbage
Brocolli
Cauliflower
Spinach
Sukuma
Onion
Garlic
Tangawizi
Meat
Chicken
Bacon
Beef
Fish
Pork
Submit
Your recipe will be here....


--- layout: post title: A Giveaway - Celebrating 100 Posts - CLOSED date: 2011-03-09 11:36:10.000000000 -05:00 categories: - Giveaways tags: - A Felt Farm by Rotraud Reinhard - Book giveaway - Giveaway - In My Dreams I Can Fly by Eveline Hasler & Kathi Bhend - Little Things To Sew by Oliver + S status: publish type: post published: true meta: aktt_notify_twitter: 'no' _edit_last: '1' dsq_thread_id: '1188586581' author: login: cint email<EMAIL_ADDRESS>display_name: Jacinta first_name: Jacinta last_name: '' ---
Well it seems I have hit a little over 100 posts here on my blog. I figure that is something worth celebrating with you lovely people. So I have for you, a book giveaway. There are three books to choose from. The winner will receive one new copy of one of the following books.
A Felt Farm by Rotraud Reinhard. This is a gorgeous book with lots of projects to do. It is well worth having if you are interested in felt craft or to just admire the beautiful pictures.
or Little Things to Sew, by Oliver + S. I have recently ordered myself a copy (I am still waiting for it's arrival), but I have only heard good things about it. Oliver + S patterns are so lovely, I figure it would be a book worth having.
or one for the children, In my dreams I can fly, by Eveline Hasler & Kathi Bhend. This books has gorgeous illustrations and a lovely storyline. We purchased Tenneille a copy of this book for her birthday and it is now one of our favourite bedtime stories.
Now all you have to do is leave a message in the comments section and tell me which book you would choose, if you won.
For a bonus entry, please mention this giveaway on your blog, with a link back here for others to see.
For a third bonus entry, mention the giveaway on Twitter with a link back to here.
Please leave a comment for each of your bonus entries here in the comments section too, so I can enter them into the giveaway.
The winner will be announced here on Wednesday 23rd March. This giveaway is open to both Australian and overseas entries. So come on, enter now! Thank you for visiting and goodluck. Jacinta x


Past Flourishes
2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2007
The statements, opinions, and ideas conveyed in this program do not necessarily
express the position of the University of Illinois at Chicago.
If you have a disability and need an accommodation in order to participate in this
event, please call<PHONE_NUMBER> at least one week prior to the event.


Description:

The Rich Text Editor (RTE) control is an easy to render in client side. Customer easy to edit the contents and get the HTML content for the displayed content. A rich text editor control provides users with a toolbar that helps them to apply rich text formats to the text entered in the text area.

Functional Specifications/Requirements:

Provide the tool bar support, it’s also customizable.

Options to get the HTML elements with styles.

Support to insert image from a defined path.

Footer elements and styles(tag / Element information , Action button (Upload, Cancel))

Re-size the editor support.

Provide efficient public methods and client side events.

Keyboard navigation support.

Selection culture
English
Deutsch
Español


---
layout: post
status: publish
published: true
title: Java 7 Code Coverage with Gradle and Jacoco
author:
display_name: Geoff Lane
login: admin
email<EMAIL_ADDRESS>url: http://www.zorched.net
author_login: admin
author_email<EMAIL_ADDRESS>author_url: http://www.zorched.net
date: '2012-09-13 13:18:03 -0400'
date_gmt: '2012-09-13 19:18:03 -0400'
tags:
- code
- java
- automation
- groovy
---
Thanks
Steven Dicks' post on Jacoco and Gradle is a great start to integrating Jacoco and Gradle, this is a small iteration on top of that work.
Java 7 Code Coverage
The state of Code Coverage took a serious turn for the worst when Java 7 came out. The byte-code changes in Java 7 effectively made Emma and Cobertura defunct. They will not work with Java 7 constructs. Fortunately there is a new player in town called JaCoCo (for Java Code Coverage). JaCoCo is the successor to Emma which is being built on the knowledge gained over the years by the Eclipse and Emma teams on how best to do code coverage. And works with Java 7 out-of-the-box.
The advantage of using established tools is that they generally are well supported across your toolchain. JaCoCo is fairly new and so support in Gradle isn't so smooth. Fortunately Steven's post got me started down the right path. The one thing that I wanted to improve right away was to use transitive dependency declarations as opposed to having local jar files in my source repository. JaCoCo is now available in the Maven repos so we can do that. One thing to note is that the default files build in the Maven repo are Eclipse plugins, so we need to reference the "runtime" classifier in our dependency
The Gradle Script
{% highlight groovy %}
configurations {
codeCoverage
codeCoverageAnt
}
dependencies {
codeCoverage 'org.jacoco:org.jacoco.agent:<IP_ADDRESS>208310627:runtime@jar'
codeCoverageAnt 'org.jacoco:org.jacoco.ant:<IP_ADDRESS>208310627'
}
test {
systemProperties = System.properties
jvmArgs "-javaagent:${configurations.codeCoverage.asPath}=destfile=${project.buildDir.path}/coverage-results/jacoco.exec,sessionid=HSServ,append=false",
'-Djacoco=true',
'-Xms128m',
'-Xmx512m',
'-XX:MaxPermSize=128m'
}
task generateCoverageReport << {
ant {
taskdef(name:'jacocoreport', classname: 'org.jacoco.ant.ReportTask', classpath: configurations.codeCoverageAnt.asPath)
mkdir dir: "build/reports/coverage"
jacocoreport {
executiondata {
fileset(dir: "build/coverage-results") {
include name: 'jacoco.exec'
}
}
structure(name: project.name) {
classfiles {
fileset(dir: "build/classes/main") {
exclude name: 'org/ifxforum/**/*'
exclude name: 'org/gca/euronet/generated**/*'
}
}
sourcefiles(encoding: 'CP1252') {
fileset dir: "src/main/java"
}
}
xml destfile: "build/reports/coverage/jacoco.xml"
html destdir: "build/reports/coverage"
}
}
}
{% endhighlight %}
A Few Details
The magic is in the jvmArgs of the test block. JaCoCo is run as a Java Agent which uses the runtime instrumentation feature added in Java 6 to be able to inspect the running code. Extra arguments can be added to JaCoCo there including things like excludes to exclude specific classes from coverage. The available parameters are the same as the maven JaCoCo parameters.
The generateCoverageReport task converts the jacoco.exec binary into html files for human consumption. If you're just integrating with a CI tool, like Jenkins, then you probably don't need this, but it's handy for local use and to dig into the details of what's covered.
Loose Ends
One problem that I ran into was referencing project paths like the project.buildDir from within an Ant task. Hopefully someone will come along and let me know how that's done.


Views
PSPS users Bootstrap 4 for its page templates. The views
package contains many facilities for supporting common HTML use-cases — most of them are detailed below.
Page Templates
bsBase.scala.html
-
Base template. Includes Bootstrap, the front-end Javascript router, CSRF token, and common Javascript files.
bsNavbar.scala.html
-
Extends
bsBase
with a nav-bar. frontEndBase.scala.html
-
Extends
bsNavBar
with the nav data for the public section. backEndBase.scala.html
-
Extends
bsNavBar
with the nav data for the back-office section. Also includes the back-end Javascript router.
| Bootstrap 4 uses JQuery, and thus PSPS views include it as well. This dependency will be removed when Bootstrap 5 is released. |
Type-safe Navigation Menus
Using pre-defined tree structure, developers can describe the site navigation structure. The UI templates will draw the structure, including drop-down items etc. The structure can be static or dynamic (e.g. composed based on user permissions).
For example, the following code:
object Structure {
val publicItems:Seq[TopSiteSection[PublicSections.Value]] = Seq(
PageSection("navbar.publicHome", PublicSections.Home, routes.HomeCtrl.index),
PageSection("navbar.login", PublicSections.Login, routes.UserCtrl.showLogin),
MultiPageSection("navbar.components", PublicSections.Components,
Seq(
PageSectionItem("pageTitleRow.title", routes.HomeCtrl.pageTitleRow()),
PageSectionItem("pager.title", routes.HomeCtrl.pager(1)),
PageSectionItem("informationals.title", routes.HomeCtrl.informationals),
PageSectionItem("styledInputs.title", routes.HomeCtrl.styledInputs),
JsSectionItem("jsSectionItem.title", Html("swal('This can be any JS code')"))
)
),
MultiPageSection("Other", PublicSections.Others,
Seq(
PageSectionItem("navbar.login", routes.UserCtrl.showLogin),
SeparatorSectionItem,
PageSectionItem("navbar.publicHome", routes.HomeCtrl.index)
)
)
)
...
}
Results in:
| File | contains the classes and structures of the public and back-office site areas.
Page Title Row
It is very common to have a title on a page, sometimes with a subtitle, sometimes with another component (e.g. page-wide action buttons), and sometimes with both. These titles involve some boilerplate, e.g.

TITLE GOES HERE</…
.
Because we’re not happy to repeat ourselves (we are NOT happy to repeat ourselves, I say!), we wrapped is in the component comps.pageTitleRow
. So this code:
@comps.pageTitleRow("My Fancy Page", "In this page, we enjoy subtitles as well as titles"){
<button class="btn btn-primary">Add</button>
<button class="btn btn-warning">Remove</button>
<button class="btn btn-info">More Info</button>
}
Results in this page header:
Informationals
This is an infrastructure for UI messages (typically success/fail/info), that can be generated both from a controller, or from Javascript code on the client side. When created from a controller, they are passed over the flash
scope, with the key FlashKeys.MESSAGE
(this covers the common POST
-then-REDIRECT
pattern).
Additional methods allow blocking the UI (e.g. for full page data load), showing background progress, and prompting the user with YES/NO questions.
| To view and experiment (live!) with the Informationals library, start PSPS and navigate to the | page.
Pagers
Having to page through a long list of data is a very common task. PSPS handles this with the views.PaginationInfo
case class, and the @comps.pager
partial template. For example, when pi
is a PaginationInfo
instance with currentPage
=3 and pageCount
=23, the following code:
@comps.pager(pi){i=>@routes.HomeCtrl.pager(i)}
-
will result in:


Toggle navigation
Search for Users
About Us
Compare Pictures
Leaderboards
Profile
Menu
All Users
FAQ
AGB's
Help
To upload new pictures please click on the "+" and select local files you want to upload.
To sign out please click on the button "Sign out" on the drop- down button "Menu".


Below is a list of public appliances that are ready for use.
Many groups around the world have made their applications publicly available to their organizations or community. Here you will find a list of such applications. Although these applications are publicly available, they may require registration or impose usage quotas.
If you would like to have your application added here, just let us know.


返回主页
日本朝日啤酒
Asahi Breweries, Ltd. TYO: 2502 is a leading brewery and soft drink company based in Tokyo, Japan. The company has a 40% share of the Japanese beer market.
The company's primary beer, from 1957 through the late 1980s, was Asahi Gold (overtaking Asahi Draft, its original formula, which remains in production). In 1987 it introduced Asahi Super Dry, which initiated the Japanese craze for dry beer; this led in turn to Asahi's dramatic turnaround in business performance, and its surpassing of former second ranker Sapporo Brewery in sales and profits.
Asahi was founded in Osaka in 1889. During the First World War German prisoners worked in the brewery.
In 1990, Asahi acquired a 19.9% stake in Australian brewery giant Elders IXL which has since become the Foster's Group.
In 2009, Asahi acquired the Australian beverages unit of Cadbury Schweppes.
On January 23, 2009, Asahi acquired 19.9% of Tsingtao Brewery from Anheuser-Busch InBev for $667 million. The sale will make Asahi Breweries, Ltd. the second largest shareholder in Tsingtao behind only the Tsingtao Brewery Group.
In July 2011, Asahi acquired New Zealand juice maker Charlie's and the water and juice divisions of Australian beverage company P&N Beverages.
In August 2011, Asahi acquired New Zealand's Independent Liquor, maker of Vodka Cruiser and other alcoholic beverages, for Y97.6 billion.
返回页首
返回主页
version:1.0<EMAIL_ADDRESS>© retter2012.com


Paul Sorey
9021 12th Avenue Southwest
Seattle, Washington 98106
<EMAIL_ADDRESS>(206) 767-4117
Public Art Project Experience:
Ballard Locks Smolt Salmon Migration Improvements, Salmon Waves. Percent for Art commission awarded jointly by Seattle Arts Commission and King County Arts Commission. Completed 2001.
Pacific Fountain. Percent for Art commission awarded by King County Arts Commission. Completed 2004.
City of Auburn Transit Station Artwork, Catch the Train!. Percent for Art commission awarded by Auburn Arts Commission. Completed 2004.
Sustainability Award Artwork. Public commission awarded by Seattle Arts Commission and Sustainable Seattle, to create an educational interactive sculptural display that teaches the public about sustainable practices and serves as an award to recognize the annual recipients of the Sustainability Award. Completed 2004.
Tree Bench at Pratt Park. Public commission awarded by Seattle Office of Arts and Cultural Affairs to create a public artwork in an urban park in Seattle. Project to be installed November 2005.
Coffee Pot Pergola in Lake City. Project artist for $20,000 commission through Seattle Office of Arts and Cultural affairs to create a public artwork in conjunction with street improvements taking place along Lake City Way. Seattle, Washington. Completed 2005.
Longfellow Creek Trail Gateway. Design and fabrication of 12 gateway posts, in collaboration with Pomegranate Center and Longfellow Creek Community Group in West Seattle. Seattle, Washington, 2001.
Regional Justice Center, Front Entry Artwork, Justice Garden Path. Two-artist collaboration with Lorna Jordan. King County 1% for Art, project budget $250,000. Kent, Washington. 1995-1997.
Pacific Science Center, The Tech Zone. Flock, a computer-controlled kinetic sculpture installation, on permanent display, 1995 - present.
Cedar River Watershed Interpretive Center, North Bend, Washington, Seattle Arts Commission 1% for Art project: technical design and fabrication of Water Drums, a computer-controlled musical instrument and environmental art piece, for artist Dan Corson. 2001.
Woodinville City Hall, Flowering Structure, Outdoor site sculpture, one year installation, Woodinville, Washington. 2001-2002.
Education:
University of Washington, School of Art, Seattle, Washington, Master of Fine Arts in Sculpture, 1993.
Utah State University, Department of Landscape Architecture and Environmental Planning, Bachelor of Landscape Architecture, 1984.
Bethany College, Lindsborg, Kansas. Bachelor of Arts in Art, emphasis in sculpture, 1980.
Recent Awards:
Artist Trust Fellowship 2000: $5,500 grant to be used for artistic development. 2000.
GAP Grant 2001: $1,400 Artist Trust grant for Oaxacan Tin Works project, a collaboration with tin workers in Mexico to make sculptural forms using 3-D design software I developed.
updated October 30, 2005


/**
Copyright (c) 2014, Nathan Carver
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions
and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse
or promote products derived from this software without specific prior written permission.

*
* Code is maintained at https://github.com/ncarver/TEETH-IotToothbrushHolder.
*
* All code and modules are defined in this file, main.js.
*
* @author Nathan Carver
* @copyright Nathan Carver 2014
* @version 0.0.1
*/
/*
* Required libraries
*
* You will need these libraries to interface with the services and hardware.
*/
var MRAA = require('mraa'); //require MRAA for communicating with hardware pins
var LCD = require('jsupm_i2clcd'); //require LCD libraries for signaling the LCD screen
var LED = require('jsupm_grove'); //require SEEED Grove library for photoresister
var BUZZ = require("jsupm_buzzer"); //require SEEED Grove library for buzzer
var MAILER = require('nodemailer'); //require for sending emails over SMTP
var NET = require('net'); //require for sending cloud data to Edison service on TCP
/**
* Change the constants properties to customize the operation of the TEETH Smart Toothbrush Timer
* @global
*/
var constants = {
'LOG_LEVEL': 3, //Change this value to limit loggin output: 0-none, 1-err, 2-warn, 3-info, 4-debug, 5-all
'USE_SOUND': true,
'PINS': { //Change these values to match the pins on your Edison build
'brushSwitch': [8, 4], //digital pins monitoring switches for toothbrushes
'buzzer': 3, //digital pin for signaling the buzzer
'roomLightSensor': 0, //analog pin for getting room light readings from photoresister on 10K external pullup
'roomLightThreshold' : 80 //value that indicates that room is dark
},
'MAIL': { //Change these values based on documentation at nodemailer to use your SMTP account
'service': 'Gmail', //Account service name, ex. "Gmail"
'user'<EMAIL_ADDRESS>//user name to login to your service
'pass': 'pass****', //password to login to your service
'from': 'TEETH<EMAIL_ADDRESS>//appears in the "From:" section of your emails
'brushTo'<EMAIL_ADDRESS><EMAIL_ADDRESS>//email value for each toothbrush
'subject': 'Great job on TEETH!', //appears as the subject of your emails
'body': 'You met the goal today. Way to go!' //the body text of your emails
},
'METRICS': { //Change these values to match the custom components of your Intel Cloud Analytics
'brushComponent': ['brush1', 'brush2'] //component value for each toothbrush
},
'SCREEN_MSG': { //Messages that appear on the LCD screen during the timer prep and countdown
'ready': '...get ready', //message to display during start of prep time
'set': '.....get set', //message to display last five seconds of prep time
'countdown': 'Countdown:', //message to display during countdown
'percent25': '...almost there!', //message to display 25% of the way through countdown
'percent50': 'good...halfway ', //message to display 50% of the way through countdown
'percent75': 'you\'re doing it ', //message to display 75% of the way through countdown
'finish': 'GREAT JOB!', //message to display at the end of countdown
'brushName': ['Nathan', 'Sarah'] //name to display during countdwon, one value for each toothbrush
},
'TIME': { //Time focused constants for timer, buzzer sounds
'brushPreptime': [10, 30], //seconds of prep time for each toothbrush
'brushGoaltime': [30, 120], //seconds of countdown time for each toothbrush
'buzzDuration': 20, //milliseconds of buzzer time for start and stop sounds
'buzzInterval': 150 //milliseconds between buzzer sounds for start and stop signals
},
'COLOR': { //Colors to use on the LCD screen
'off': [ 0, 0, 0], //black - use when LCD is off
'ready': [100, 100, 100], //light grey - use during prep time
'percent0': [255, 68, 29], //red - use at start of countdown
'percent25': [232, 114, 12], //brown - use when countdown is 25% finished
'percent50': [255, 179, 0], //orange - use when countdown is 50% finished
'percent75': [232, 211, 12], //yellow - use when countdown is 75% finished
'finish': [ 89, 132, 13], //green - use when countdown is finished
'colorFadeDuration': 1000, //milliseconds to fade to new color during countdown mode
'fadeSteps': 100 //number of fading steps to take during colorFadeDuration
}
};
/**
* These values hold the setTimeout and setInterval handles so they can be cleared as part of a timer interuption
* @global
*/
var timers = {
'fadeColor': null, //timer fading the color on the LCD screen
'buzzerPlay': null, //timer playing the buzzer sounds
'buzzerWait': null, //timer waiting in between buzzer sounds
'prepCountdown': null, //timer for the main prep time
'startCountdown': null, //timer for the last five seconds of prep time
'countdown': null, //timer for the main countdown
'lightsOut': null //timer lookin for "lights out" interuption
};
/**
* Creates a new Logger object and the helper methods used to send messages to console.
* Highest level of logging, ERR (1), only outputs errors during code execution.
* Lowest level DEBUG (4) outputs all logging messages.
*
* @see constants.LOG_LEVEL Use the constant constants.LOG_LEVEL to adjust the level of output.
* @class
*/
var Logger = function () {
this.ERR = 1;
this.WARN = 2;
this.INFO = 3;
this.DEBUG = 4;
/**
* @private
*/
var logLevels = ['', 'err', 'warn', 'info', 'debug'];
/**
* @param {string} msg message to send to logger
* @param {int} level log level for this message
* @public
*/
this.it = function (msg, level) {
if (constants.LOG_LEVEL >= level || level === undefined) {
console.log('%s - %s: %s', new Date(), logLevels[level], msg);
}
};
/*
* @param {string} msg message to send to logger at log level ERR (1)
* @public
*/
this.err = function (msg) {
this.it(msg, this.ERR);
};
/*
* @param {string} msg message to send to logger at log level WARN (2)
* @public
*/
this.warn = function (msg) {
this.it(msg, this.WARN);
};
/*
* @param {string} msg message to send to logger at log level INFO (3)
* @public
*/
this.info = function (msg) {
this.it(msg, this.INFO);
};
/*
* @param {string} msg message to send to logger at log level DEBUG (4)
* @public
*/
this.debug = function (msg) {
this.it(msg, this.DEBUG);
};
};
/**
* Creates a new Sensors object to monitor the hardware connected to the Edison
* @requires mraa:Gpio
* @requires mraa:Aio
* @param {Logger} log object for logging output
* @see constants.PINS Use the constant constants.PINS to identify the hardware connections
* @class
*/
var Sensors = function (log) {
log.info('instatiate Sensors');
/**
* @private
*/
var i;
/**
* the array of switches associated with each toothbrush are initialized
* as INPUT pins during instatiation.
* @public
*/
this.brushSwitch = [];
for (i = 0; i < constants.PINS.brushSwitch.length; i = i + 1) {
this.brushSwitch[i] = new MRAA.Gpio(constants.PINS.brushSwitch[i]);
this.brushSwitch[i].dir(MRAA.DIR_IN);
}
/**
* the analog pin for monitoring the phototransister is initialized
* during instatiation. Expected that this photo cell will have a 10K
* external pulldown resistor.
* @public
*/
// this.roomLightSensor = new MRAA.Aio(constants.PINS.roomLightSensor);
};
/**
* Creates a new Buzzer object to play a sound on the buzzer connected to the Edison
* @requires jsupm_buzzer:Buzzer
* @param {Logger} log object for logging output
* @see constants.TIME Use the properties of constants.TIME to adjust the buzzer sounds
* @class
*/
var Buzzer = function (log) {
log.info('instatiate Buzzer');
var buzzer = new BUZZ.Buzzer(constants.PINS.buzzer);
/**
* play calls the playSound method of the low-level buzzer class for a simple tone value
* @private
*/
function play(buzzingTime) {
log.debug('(buzzer.play for ' + buzzingTime + ')');
if (!constants.USE_SOUND) {
return;
}
buzzer.playSound(BUZZ.DO, 5000);
timers.buzzerPlay = setTimeout(function () {
buzzer.playSound(BUZZ.DO, 0);
}, buzzingTime);
}
/**
* plays the standard sound for the buzz duration, then waits, and plays again
* expected to be called at the beginning of the countdown
* @public
*/
this.playStartSound = function () {
log.info('buzzer.playStartSound');
play(constants.TIME.buzzDuration);
timers.buzzerWait = setTimeout(function () {
play(constants.TIME.buzzDuration);
}, constants.TIME.buzzInterval);
};
/**
* plays the standard sound for the buzz duration, then waits, and plays again
* expected to be called at the end of the countdown
* @public
*/
this.playStopSound = function () {
log.info('buzzer.playStopSound');
play(constants.TIME.buzzDuration);
timers.buzzerWait = setTimeout(function () {
play(constants.TIME.buzzDuration);
}, constants.TIME.buzzInterval);
};
};
/**
* Creates a new Screen object to display messages and colors RGB LCD connected to the Edison over I2C
* @requires jsupm_i2clcd:Jhd1313m1
* @param {Logger} log object for logging output
* @see constants.COLOR Use the properties of constants.COLOR to adjust the screen background colors
* @see constants.SCREEN_MSG Use the properties of constants.SCREEN_MSG to change the messages displayed on screen
* @class
*/
var Screen = function (log) {
log.info('instatiate Screen');
/**
* Instance variables to connect to LCD screen and manage the color fading
* @private
*/
var lcd = new LCD.Jhd1313m1(6, 0x3E, 0x62), //standard I2C bus
interval = constants.COLOR.colorFadeDuration / constants.COLOR.fadeSteps,
lastColor = constants.COLOR.off,
steps = constants.COLOR.fadeSteps;
/**
* getRemainingSteps identifies how many more steps are needed before fade is finished
* @returns {int} number of steps remaining
* @private
*/
function getRemainingSteps() {
log.debug('(screen.getRemainingSteps)');
return steps;
}
/**
* setRemainingSteps sets how many more steps are needed before fade is finished
* @params {int} remainingSteps new number of steps remaining
* @private
*/
function setRemainingSteps(remainingSteps) {
log.debug('(screen.setRemainingSteps: ' + remainingSteps + ')');
steps = remainingSteps;
}
/**
* setScreen color calls low-level methods to set RGB values of screen background
* also sets the instance variable "lastColor" to help with fade control
* @param {array} colorArray array of decimal color values in Red Green Blue order [r,g,b]
* @private
*/
function setScreenColor(colorArray) {
log.debug('(screen.setScreenColor to ' + colorArray + ')');
lcd.setColor(colorArray[0], colorArray[1], colorArray[2]);
lastColor = colorArray;
}
/**
* Inner method called by timeouts to fade background color from current color to the
* updated color passed RGB color array
* @private
*/
function _fadeColor(colorArray) {
log.debug('(screen._fadeColor: ' + colorArray + ')');
var step = getRemainingSteps();
if (step > 0) {
var diffRed = colorArray[0] - lastColor[0],
diffGrn = colorArray[1] - lastColor[1],
diffBlu = colorArray[2] - lastColor[2],
stepRed = parseInt(diffRed / step, 10),
stepGrn = parseInt(diffGrn / step, 10),
stepBlu = parseInt(diffBlu / step, 10),
nextRed = lastColor[0] + stepRed,
nextGrn = lastColor[1] + stepGrn,
nextBlu = lastColor[2] + stepBlu;
setScreenColor([nextRed, nextGrn, nextBlu]);
setRemainingSteps(step - 1);
timers.fadeColor = setTimeout(function () {
_fadeColor(colorArray);
}, interval);
}
}
/**
* Starts a timout sequence to slowy change the LCD RGB screen background from its current
* color to the one passed in the parameters. The speed and number of steps used for fading
* are controlled by the constants.
*
* @param {array} colorArray a 3-member array of decimal numbers describing the color to display
* on the screen background: [r,g,b]
* @public
*/
this.fadeColor = function (colorArray) {
log.info('screen.fadeColor: ' + colorArray);
setRemainingSteps(constants.COLOR.fadeSteps);
_fadeColor(colorArray);
};
/**
* Helper method combines clearing the screen of all text content and returning the cursor
* position back to the top left.
* @public
*/
this.reset = function () {
log.info('screen.reset');
lcd.clear();
lcd.setCursor(0, 0);
};
/**
* Helper method combines reseting the screen and returning the screen color to "off"
* @public
*/
this.resetAndTurnOff = function () {
log.info('screen.resetAndTurnOff');
this.reset();
setScreenColor(constants.COLOR.off);
};
/**
* Turns the screen on and displays the "ready" message defined in constants for the given toothbrush
*
* @param {int} componentIndex identifies the toothbrush by it's array index
* @public
*/
this.displayReady = function (componentIndex) {
log.info('screen.displayReady for ' + componentIndex);
lcd.clear();
setScreenColor(constants.COLOR.ready);
this.write(constants.SCREEN_MSG.brushName[componentIndex], 0, 0);
this.write(constants.SCREEN_MSG.ready, 1, 0);
};
/**
* Changes the "ready" message to the "set" message defined in constants for the given toothbrush
*
* @param {int} componentIndex identifies the toothbrush by it's array index
* @public
*/
this.displaySet = function (componentIndex) {
log.info('screen.displaySet for ' + componentIndex);
this.write(constants.SCREEN_MSG.set, 1, 0);
};
/**
* Helper message combines writing the given message to the screen at (optional) given coordinates
*
* @param {string} msg the string to ouput to the screen
* @param {int} col (optional) 0-indexed column number to set the cursor
* @param {int} row (optional) 0-indexed row number to set the cursor
* @public
*/
this.write = function (msg, col, row) {
//log.info('screen.write msg ' + msg);
var i;
if (!(col === undefined || row === undefined)) {
lcd.setCursor(col, row);
for (i = 0; i < 10000000; i = i + 1) {
//wait for slow LCD
}
}
lcd.write(msg);
};
//initialize the LCD screen during instatiation
this.resetAndTurnOff();
};
/**
* Creates a new Mailer object to send mail over SMTP
* @requires nodemailer
* @param {Logger} log object for logging output
* @see constants.MAIL Use the properties of constants.MAIL to configure your SMTP service
* @class
*/
var Mailer = function (log) {
log.info('instatiate Mailer');
/**
* Instance options taken from constants.MAIL are used by createTransport to authenticate SMTP
* @private
*/
var mailOptions = {
from: constants.MAIL.from, // sender address
to: constants.MAIL.brushTo[0], // list of receivers
subject: constants.MAIL.subject, // Subject line
text: constants.MAIL.body, // plaintext body
html: constants.MAIL.body // html body
},
transporter = MAILER.createTransport({
service: constants.MAIL.service,
auth: {
user: constants.MAIL.user,
pass: constants.MAIL.pass
}
});
/**
* Sends the message defined in constants.MAIL for the given toothbrush.
* Errors are sent to the log Logger object.
* @param {int} componentIndex identifies the toothbrush by it's array index in constants
* @public
*/
this.sendCongratsEmail = function (componentIndex) {
log.info('mailer.sendCongratsEmail for ' + componentIndex);
mailOptions.to = constants.MAIL.brushTo[componentIndex];
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
log.err('mail error ' + error + '.');
} else {
log.info('mail sent.');
}
});
};
};
/**
* Creates a new Metrics object to connect to Intel Cloud Analytics over TCP
* @requires net:socket
* @param {Logger} log object for logging output
* @class
*/
var Metrics = function (log) {
log.info('instatiate metrics');
/**
* instance objects and options to connect over TCP to Edison iot-agent
* @private
*/
var client = new NET.Socket(),
options = {
host : 'localhost', //use the Intel analytics client running locally on the Edison
port : 7070 //on default TCP port
};
/**
* sendObservation concatenates the string expected by cloud analytics based on the parameter values
* @param {string} name custom component name registered with Intel analytics
* @param {float} value data value to send to cloud
* @private
*/
function sendObservation(name, value) {
log.debug('(metrics.sendObservation for ' + name + ', ' + value + ')');
var msg = JSON.stringify({
n: name,
v: value
}),
sentMsg = msg.length + "#" + msg; //syntax for Intel analytics
client.write(sentMsg);
}
/**
* Method combines the activity of connecting to the Edision service and then sending data to the cloud
* @param {int} itemIndex array index of component names registered with Intel analytics
* @param {float} timeValue data value to send to cloud, expecting fractional number of seconds
* @public
*/
this.addDataToCloud = function (itemIndex, timeValue) {
log.info('metrics.addDataToCloud for ' + itemIndex + ', ' + timeValue);
client.on('error', function () {
log.err('Could not connect to cloud');
});
client.connect(options.port, options.host, function () {
sendObservation(constants.METRICS.brushComponent[itemIndex], timeValue);
});
};
};
/**
* Creates a new Teeth object to manage the countdown timer
* @param {Logger} log object for logging output
* @param {Sensors} sensor object for listening to hardware sensors
* @param {Buzzer} buzzer object for controlling the sounds
* @param {Screen} screen object for display on the RGB LCD screen
* @param {Mailer} mailer object for sending email
* @param {Metrics} metrics object for sending data to cloud
* @see constants.TIME Use the properties of constants.TIME to adjust the length of the countdown
* @class
*/
var Teeth = function (log, sensors, buzzer, lcdScreen, mailer, metrics) {
log.info('instatiate teeth');
/**
* flags to make sure fadeColor only called once for each color
* @private
*/
var fades = [],
currentComponent = -1,
timeSpent = 0;
/**
* clearAllTimers loops through all timers in constants to clear them and set to null
* @private
*/
function clearAllTimers() {
log.debug('(teeth.clearAllTimers)');
var key,
timer;
for (key in timers) {
if (timers.hasOwnProperty(key)) {
timer = timers[key];
if (timer !== null) {
clearTimeout(timer);
timer = null;
}
}
}
fades = [];
currentComponent = -1;
}
/**
* finishCountdown clears the screen, plays the stop sound, and starts the waiting process again
* @param {int} componentIndex array index number of the toothbrush that is finishing the countdown
* @private
*/
function finishCountdown(componentIndex) {
log.debug('(teeth.finishCountdown for ' + componentIndex + ')');
clearAllTimers();
lcdScreen.reset();
buzzer.playStopSound();
mailer.sendCongratsEmail(componentIndex);
lcdScreen.write(constants.SCREEN_MSG.finish);
metrics.addDataToCloud(componentIndex, constants.TIME.brushGoaltime[componentIndex]);
setTimeout(function () {
lcdScreen.resetAndTurnOff();
wait();
}, 1000);
}
/**
* countdown generates messages and colors to the screen during the countdown,
* called continuously until time is over
* @param {int} componentIndex array index number of the toothbrush that is in the middle of the countdown
* @param {int} timeRemaining the amount of time remaining in seconds before the countdown is over
* @private
*/
function countdown(componentIndex, timeRemaining) {
log.debug('(teeth.countdown for ' + componentIndex + ': ' + timeRemaining + ')');
var originalValue = constants.TIME.brushGoaltime[componentIndex];
timeSpent = originalValue - timeRemaining;
if (timeRemaining > 0) {
lcdScreen.write(constants.SCREEN_MSG.countdown + timeRemaining + ' ', 0, 0);
if (timeRemaining <= (0.25 * originalValue)) {
lcdScreen.write(constants.SCREEN_MSG.percent25, 1, 0);
if (fades[constants.COLOR.percent25] !== 1) {
lcdScreen.fadeColor(constants.COLOR.percent25);
fades[constants.COLOR.percent25] = 1;
}
} else if (timeRemaining <= (0.5 * originalValue)) {
lcdScreen.write(constants.SCREEN_MSG.percent50, 1, 0);
if (fades[constants.COLOR.percent50] !== 1) {
lcdScreen.fadeColor(constants.COLOR.percent50);
fades[constants.COLOR.percent50] = 1;
}
} else if (timeRemaining <= (0.75 * originalValue)) {
lcdScreen.write(constants.SCREEN_MSG.percent75, 1, 0);
if (fades[constants.COLOR.percent75] !== 1) {
lcdScreen.fadeColor(constants.COLOR.percent75);
fades[constants.COLOR.percent75] = 1;
}
}
timers.countdown = setTimeout(function () {
countdown(componentIndex, timeRemaining - 1);
}, 1000);
} else {
finishCountdown(componentIndex);
}
}
/**
* startCountdown plays the sound and begins the first call to countdown
* @param {int} componentIndex array index number of the toothbrush that is starting the countdown
* @private
*/
function startCountdown(componentIndex) {
log.info('(teeth.startCountdown for ' + componentIndex + ')');
buzzer.playStartSound();
lcdScreen.reset();
currentComponent = componentIndex;
countdown(componentIndex, constants.TIME.brushGoaltime[componentIndex]);
}
/**
* prepCountdown prepares the screen and waits for real countdown to begin, displays
* an additional warning with five seconds to go
* @param {int} componentIndex array index number of the toothbrush that is starting the countdown
* @private
*/
function prepCountdown(componentIndex) {
log.debug('(teeth.prepCountdown for ' + componentIndex + ')');
lcdScreen.displayReady(componentIndex);
var prepTime = constants.TIME.brushPreptime[componentIndex];
timers.prepCountdown = setTimeout(function () {
log.debug('setTimeout: prepCountdown');
lcdScreen.displaySet(componentIndex);
}, (prepTime - 5) * 1000);
timers.startCountdown = setTimeout(function () {
log.debug('setTimeout: startCountdown');
startCountdown(componentIndex);
}, prepTime * 1000);
}
/**
* watchForLightsOut polls the photoresistor to see if the room is dark, then stops all activity
* @private
*/
function watchForLightsOut() {
//do not log.debug >> called every 50ms
return;
var val = sensors.roomLightSensor.read();
if (val < constants.PINS.roomLightThreshold) {
log.info('Trigger for lights out: Stop Timer (early) then wait 5 seconds to start again');
if (currentComponent >= 0) {
metrics.addDataToCloud(currentComponent, timeSpent);
}
clearAllTimers();
lcdScreen.resetAndTurnOff();
setTimeout(wait, 5000);
}
}
/**
* wait is the main entry to this class, it polls the switches regularly to see if it should start the countdown
* @private
*/
function wait() {
//do not log.debug >> called every 100ms
var i,
val;
for (i = 0; i < sensors.brushSwitch.length; i = i + 1) {
val = sensors.brushSwitch[i].read();
if (val === 0) { //0 for NO (normally open switch), 1 for NC (normally closed)
log.info('Trigger for toothbrush ' + i + ': Start Timer');
prepCountdown(i);
timers.lightsOut = setInterval(watchForLightsOut, 50);
break;
}
}
if (i >= sensors.brushSwitch.length) {
setTimeout(wait, 100);
}
}
/**
* Entry point to Teeth, the start command initiates the Smart Toothbrush Holder and begins the process of waiting for a countdown
* @public
*/
this.start = function () {
log.info('Teeth.start waiting for toothbrush events');
wait();
};
};
/* Create instance objects of the classes needed by TEETH */
var log = new Logger(),
sensors = new Sensors(log),
buzzer = new Buzzer(log),
lcdScreen = new Screen(log),
mailer = new Mailer(log),
metrics = new Metrics(log),
teeth = new Teeth(log, sensors, buzzer, lcdScreen, mailer, metrics);
/* Get the code running by invoking the start method of the Teeth controller */
teeth.start();


---
layout: default
title: Do Not Disturb
---
Do Not Disturb
Technology Stack: Swift, iOS framework.
Do Not Disturb is an iOS application, built on top of the the native DND feature provided provided by iOS. The app extends the basic DND functionality to give the user flexbility and ease of exploiting DND at its best.
One can activate from numerous custom profiles with one tap anytime and let the app do the rest i.e. silence calls from contacts which are not allowed as per the profile.
The app can also be configured to tag a location to a profile. This geotagging feature then activates a DND profile according to the location.
Here is list of basic features the app provides-
Classify your contacts into groups for easier management. Create as many groups. Add a contact to multiple groups as needed.
Tag profiles to locations. And let the AUTO mode in the app activate or de-activate the profile to silence calls as you enter or exit the location.
The app comes with a widget, which lets you manage AUTO mode or activate / de-activate profiles conveniently.
Create profiles (such as work, home, vacation) to allow / disallow one or more groups. This is like creating multiple Do Not Disturb settings.
The user is notified through an interative notification. The user can activate the profile just with a tap.


&
&
&
Nine hundred attendees heard FFL President Serrin Foster present “The Feminist Case
Against Abortion” at the Washington State “Cornerstone” Catholic Conference–and
our booth was literally mobbed after Serrin spoke. People are hungry for our rich
pro-life feminist history and message of hope and focus on resources and support!
Here’s what the organizer, Pam Gunderson, had to say:
“Serrin had a compelling presence and a lot of credibility. The history and context
she provided were unique and eye-opening. Even long-time pro-lifers in the audience
were surprised at how much of this background they did not know; and those who are
more comfortable with feminism than with pro-life activity were captured.”
In advance of the conference, the Northwest Catholic featured an article entitled
“Women Deserve Better than Abortion” by Serrin, the creator of the “Women Deserve
Better than Abortion” campaign.
Read for yourself and please forward this essential article onto family and friends–those
who agree and those you’d like to invite to our movement.
http://www.nwcatholic.org/spirituality/catholic-voices/feminists-for-life [http://r20.rs6.net/tn.jsp?f=001k5MHTaCmXfCNpFq1QDBvQTE_CaRUz5WxQg7DrcVrR3v01GwuNDXqV-lcJNK2DLIOpB0QzioC7w4qqYxokL47oPPBKxHWswVD_O9l5QqNXcZ4Kk-8lNmCEA5HHOQ4P71c5XDTmEkKJ918BjnO7fN6mmb0jC31TPG3nku88iJCHP8zTiDTLks54tTW7cXo9qvwEG9NLmX1A7kYXkIn2njvr0PFQZLhdmA8T1nypcOerSTsSP5pjSCDug==&c=_yPsqsc-d9zGxHFVU9LZxgWBLE_5c4Vl-UjwZaHpNKtVACA6SD1oiQ==&ch=AruFGzp72ZxMLNxoXYdINj5T1wndBmoaY311nhVvMvUCIlK-ztbFng==]
Pro-life feminists recognize abortion as a symptom of, not a solution to, the continuing
struggles women face in the workplace, on campus, at home and in the world at large.
Like Susan B. Anthony and other early American suffragists, today’s pro-life feminists
envision a better world, where no woman is driven by desperation to abortion.
If you or someone you know is in Western Pennsylvania, please plan to attend Serrin’s
speech at Clarion University next Wednesday, Nov. 19 at 7 PM! [http://r20.rs6.net/tn.jsp?f=001k5MHTaCmXfCNpFq1QDBvQTE_CaRUz5WxQg7DrcVrR3v01GwuNDXqV-lcJNK2DLIO44guAF5YbYTtd4suJ0tGkqFd3iHuyDzFMkhugYHZza2IospZM69QJIEhhcr1b351R5hU6viqkSJ3LVp0WHfwiMHWBI4Q3hZ8x6KznHm71RHnPqHJuFDhwya6DxDj1FtsU1YM7GNr9uXT2HvC064nezyO6Tp98A-OenenzFEqH3bMMgtoShk1aQ==&c=_yPsqsc-d9zGxHFVU9LZxgWBLE_5c4Vl-UjwZaHpNKtVACA6SD1oiQ==&ch=AruFGzp72ZxMLNxoXYdINj5T1wndBmoaY311nhVvMvUCIlK-ztbFng==]
Contact<EMAIL_ADDRESS>to request a speaker for your next rally, banquet,
conference, or campus lecture–or to book an FFL moderator for an FFL Pregnancy
Resource Forum.
And don’t forget that the annual board-led YEAR END MATCH is ON! [http://r20.rs6.net/tn.jsp?f=001k5MHTaCmXfCNpFq1QDBvQTE_CaRUz5WxQg7DrcVrR3v01GwuNDXqV2JLYDh6iHALmywh-kjkx0bapOeSMEQrRVXpI_MlS3ebE-4MwmNZmf2IZQV7T83A1A5Rr0vgrzimvo4Qj54jkIf9g2yy_E1m9sqK-dhtUV6hfRzG1D6K2gTKIbTkJAeMlmD5f8cJvcEFWtMEPgjuXSI=&c=_yPsqsc-d9zGxHFVU9LZxgWBLE_5c4Vl-UjwZaHpNKtVACA6SD1oiQ==&ch=AruFGzp72ZxMLNxoXYdINj5T1wndBmoaY311nhVvMvUCIlK-ztbFng==]
*Double your single gift.
*Double it again if your employer has a corporate matching gift program!
*Double match your new or increased monthly gift. These donations provide year-round
core support for our work!
All donations are tax-deductible according to law.
Thank you to the visionaries who sacrifice to make life better for women and children
at highest risk of abortion.
Women Deserve Better is a registered trademark of Feminists for Life of America.
Feminists for Life of America is a 501©3 nonprofit. Membership contributions and
donations are tax-deductible to the extent allowed by law.
For more information, go to http://www.feministsforlife.org/support/
&
&
&
Forward this email
<EMAIL_ADDRESS>This email was sent to<EMAIL_ADDRESS>by<EMAIL_ADDRESS>Update Profile/Email Address
http://visitor.constantcontact.com/do?p=oo&m=001Q-HkL4DKCBRaE4RH1EOTXw%3D%3D&ch=5af11d60-4f61-11e3-9d28-d4ae5275dbea&ca=fbd73485-c67b-4eae-b6c5-567b7ff19119
Instant removal with SafeUnsubscribe™
http://visitor.constantcontact.com/do?p=un&m=001Q-HkL4DKCBRaE4RH1EOTXw%3D%3D&ch=5af11d60-4f61-11e3-9d28-d4ae5275dbea&ca=fbd73485-c67b-4eae-b6c5-567b7ff19119
Privacy Policy:
http://ui.constantcontact.com/roving/CCPrivacyPolicy.jsp
Online Marketing by
Constant Contact®
www.constantcontact.com
Feminists for Life | P.O. Box 320667 | Alexandria | VA | 22320


To ensure that incoming students are sufficiently prepared to undertake the academic and personal challenges of their first year of college at Notre Dame, with an emphasis on intellectual engagement, leadership, and service.
Why Apply?During this four-week residential experience, students complete a combination of academic coursework and personal development courses. They will be introduced to the vast resources Notre Dame offers, as well as the level of work that will be required in the fall.
More InformationEligibility for financial support for undergraduate research, internships, service opportunities, or international study after their first year.
Access to a large network of successful professionals who are alumni of the program
Balfour exclusive events aiming to build financial literacy, effective study habits, and other critical skills.
A close-knit community to support and guide you while at Notre Dame and beyond.
A second family.
Give us a call or send us an email and we will get back to you as soon as possible!
(574) 631-7363


public class ConnectionManagerImpl extends java.lang.Object implements ConnectionManager
ConnectionManager
.
| Constructor and Description |
|---|
ConnectionManagerImpl() |
public VirtualizationToolManager connectTo(PhysicalMachine physicalMachine)
ConnectionManager::connectTo(PhysicalMachine)
.
For each physical machine connection operation are used 3 attempts to establish the connection. Between each unsuccessful attempt is set up the waiting time to 2 seconds.
If there occurs any failure during the processing the connection operation, there can be thrown one of the following exceptions:
null
ConnectionFailureException
- thrown when there cannot be
the connection with the physical machine established and one of the following
problems occur:
IncompatibleVirtToolAPIVersionException
- thrown when the API
version of VirtualBox installation on the physical machine, which
there is being established the connection with, is incorrect (different
from the correct API version 4.3).
connectTo
in interface ConnectionManager
physicalMachine
- represents the physical machine which is going
to be connectedVirtualizationToolManager
for managing virtual machines on the newly
connected physical machinepublic VirtualizationToolManager connectTo(PhysicalMachine physicalMachine, long millis)
ConnectionManager::connectTo(PhysicalMachine, long)
.
This method use the same implementation as the method connectTo(PhysicalMachine)
.
With this method is possible specified the wating time between the unsuccessful connection establishment attempts. If the given time in milliseconds is negative, then it indicates that there will be performed just one attempt to establish the connection with a particular physical machine.
connectTo
in interface ConnectionManager
physicalMachine
- represents the physical machine which is going to
be connectedmillis
- time in milliseconds determining the waiting time between
each unsuccessful connection establishment attemptVirtualizationToolManager
for managing virtual machines on the newly
connected physical machinepublic void disconnectFrom(PhysicalMachine physicalMachine)
ConnectionManager::disconnectFrom(PhysicalMachine)
.
The given physical machine is just removed from the list of connected physical machines.
If there occurs any failure during the processing the disconnection operation, there can be thrown one of the following exceptions:
null
ConnectionFailureException
- thrown when the given physical
machine is not connected
disconnectFrom
in interface ConnectionManager
physicalMachine
- represents the physical machine which is going to
be disconnectedpublic void disconnectFrom(PhysicalMachine physicalMachine, ClosingActionType closingAction)
ConnectionManager::disconnectFrom(PhysicalMachine, ClosingActionType)
.
If there occurs any failure during the processing the disconnection operation, there can be thrown one of the following exceptions:
null
ConnectionFailureException
- thrown when the disconnection
operation cannot be performed correctly because of the following problems:
IncompatibleVirtToolAPIVersionException
- thrown when the API
version of VirtualBox installation on the physical machine, has been
changed to incorrect version (different from the correct API version 4.3).
disconnectFrom
in interface ConnectionManager
physicalMachine
- represents the physical machine which is going to
be disconnectedclosingAction
- specifies what action should be performed before
the physical machine is disconnectedpublic boolean isConnected(PhysicalMachine physicalMachine)
ConnectionManager::isConnected(PhysicalMachine)
.
If there occurs any error during the method is being processed,
the following exceptions can be thrown:
null
.
isConnected
in interface ConnectionManager
physicalMachine
- represents the queried physical machinetrue
if the physical machine is connected,
false
otherwisepublic java.util.List<PhysicalMachine> getConnectedPhysicalMachines()
ConnectionManager::getConnectedPhysicalMachines()
.
getConnectedPhysicalMachines
in interface ConnectionManager
public void close()
ConnectionManager::close()
.
disconnectFrom(PhysicalMachine, ClosingActionType)
with closing action set up to shut down all running virtual machines on
a particular physical machine.
close
in interface ConnectionManager


Developed AWS Web App using Flask Framework to predict FIFA18 Player Transfer Value. The web app was finally deployed on AWS Beanstalk environment.
Check it outDestiny II, a multiplayer first-person shooter, with data provided by the game’s developer Bungie Studio
Check it outConducted discrete time covariate analysis to predict customer repurchase likelihood on top 15 retailers from Shoprunner.
Check it outConverted raw Yelp review data (3.46G) to vector space for Natural Language Processing in Python (pandas, sklearn, tf-idf); Developed classification models (Logistic Regression, Naive Bayes, and Random Forest) based on restaurants’ ratings; Clustered users and identified the common users’ preferences by inspecting the cluster centroids
Check it outLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Check it out


{% extends "petitions/petition_form/petitionform_list.html" %} {% block replacedatefilter%} {%endblock%} {% block title %} Petition {% endblock title %} {% block petitiontableheading %}
This table shows who appealed against their conviction. You can search using any criteria
{% endblock %}


This document provides a comparison between Quokka and Maven for building a simple web application.
Throughout this
document, Quokka configuration will be shown in this style
, while
Maven configuration will be in this style
.
This document is divided into the following sections:
The web application in this example will consist of a static page, a simple servlet and a JSP page. It has a single dependency on log4j, just to provide a sample dependency. The source code structure of the application is as follows:
src/ main/ java/ sample/ HelloWorld.java HelloWorldHelper.java webapp/ index.html snoop.jsp WEB-INF/ web.xml test/ java/ sample/ HelloWorldHelperTest.java HelloWorldITest.java
Given the following quokka configuration file (build-quokka.xml
), building the .war file
is done by quokka package
:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE project PUBLIC "quokka.ws/dtd/project-0.2" "http://quokka.ws/dtd/project-0.2.dtd"> <project> <artifacts group="mycompany.myproject" version="0.1-ss"> <artifact type="war"/> </artifacts> <dependency-set> <dependency group="junit" version="3.8.2" paths="test-compile"/> <dependency group="sun.servlet.spec.dist" version="2.5" paths="compile"/> <dependency group="apache.log4j" version="1.2.15" paths="compile, war"/> <dependency-set group="quokka.depset.jar" version="0.3-ss"/> <plugin group="quokka.plugin.jee" version="0.1-ss" targets="war, war-exploded"/> </dependency-set> </project>
In comparision, here is the Maven configuration file (pom.xml
), that builds
a .war file using mvn package -Dmaven.test.failure.ignore=true
:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>mycompany</groupId> <artifactId>myproject</artifactId> <packaging>war</packaging> <version>0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.14</version> </dependency> <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-servlet_2.5_spec</artifactId> <version>1.2</version> <optional>true</optional> </dependency> </dependencies> </project>
Analysis:
Maven packaging versus Quokka dependency sets: Maven uses the concept of packaging to to introduce
plugin dependencies automatically (via the <packaging>war</packaging>
declaration). Unfortunately, this is basically voodoo as there is no easy way to determine
what dependencies are introduced via this mechanism. Maven packaging binds certain goals to
lifecycle phases (as shown here).
These goals then force the dynamic loading of plugins from certain well-known locations. In the past, this
took the latest available version making maven builds unreproducible, but I believe as of maven 2.0.9 it
locks down the versions of certain plugins.
In contrast, Quokka uses a nested dependency-set (via the <dependency-set group="quokka.depset.jar"
version="0.3-ss"/>
declaration). The nested dependency set is in exactly the same format as the project dependency set,
and is basically included, or merged into the project dependency set. The versions of any plugins are set
explicitly
in the nested set, so the build remains reproducible. Furthermore, it is easy to see what was included using the
quokka help system. Dependency sets are a core concept of Quokka replacing Maven packaging and project
inherientence
with a more general and flexible mechanism. It should also be noted that there is no hard-wired concept of
a lifecycle in Quokka. Instead there is a lifecycle plugin that defines abstract targets that other plugins
are free to implement.
Maven has hard-wired paths: Maven has hard-wired scopes for dependencies including compile, provided, test, runtime, system and import. It then applies a complicated set of rules as to what these paths mean in different contexts (see here for details).
In constrast, Quokka has no hard-wired paths in the core at all. Both plugins and the project can define
arbitrary paths. In the case above the lifecycle plugin defines the compile
path and jee plugin
defines the war
path. When a dependency is declared it can be assigned to one or more paths, with each assignment able to set
options as to whether transitive dependencies are included, if the dependency is optional and what
optional dependencies should be included. Finally, plugins can group (or merge) paths together. e.g. to run
unit tests the compile
and test
are merged (for exact details see the lifecycle
plugin).
Unlike Maven, Quokka does not store all of these paths in the repository. Instead, the project defines which paths are to the exported to the repository (which also supports arbitrary paths).
Essentially, Quokka has generalised all of Maven's special rules into a single mechanism. Furthermore, the same mechanism is available to projects and plugins making Quokka much more extensible. Although this simple example doesn't show it, path handling is a big differentiator between Quokka and Maven. See Paths and Path Specifications for more information.
Testing voodoo: Maven automatically discovers that there are jUnit tests and runs them, presumably via maven's testing plugin automatically detecting jUnit on the classpath. Quokka does not try to guess what you want - you need to include the jUnit plugin (see the next section) if you want your tests to be run with jUnit
Conciseness: The quokka definition is much more concise, thanks largely due the use of XML attributes instead of elements.
We'll now add a test report to both configurations. In Quokka, the following is added to the dependency set in build-quokka.xml
:
<plugin group="quokka.plugin.devreport" version="0.3-ss"/> <plugin group="quokka.plugin.junit" version="0.3-ss"/> <plugin group="quokka.plugin.junitreport" version="0.3-ss"/>
The jUnit report can now be generated using quokka junitreport
, or an index of all reports
can be generated via quokka reports
. (The devreport plugin is not required if you don't want an index).
For Maven, testing is done via the sure-fire plugin and is configured as follows in pom.xml
.
The report can be generated with mvn surefire-report:report
or all reports can be generated
using maven site
<reporting> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-report-plugin</artifactId> <version>2.4.3</version> </plugin> </plugins> </reporting>
Analysis:
Surefire verus native test frameworks: Testing in maven is forced go through the sure-fire plugin which supports a fixed set of frameworks. This has a number of important consequences in maven. Firstly, if you are the author of a testing framework, you have to lobby to get the surefire plugin patched to support your framework. Secondly, the surefire plugin produces the same reports regardless of the test framework used. This means you have to lobby to get features added to the surefire report that may only be appropriate for your testing framework. At present, the surefire report appears to support the lowest common demoninator and is inferior to any of the native reports from testing frameworks.
In constrast, any framework can perform testing in quokka - all it requires is a supporting plugin that
implements
the abstract test
target
from the lifecycle plugin. Test frameworks are free to generate whatever reports they wish and be configured
as is appropriate to the framework. The net result in this case is that the quokka junit test reports
are far better than the Maven equivalents.
Maven muddles developer reports with site generation: To get a summary of all reports in Maven,
you need to perform mvn site
. This generates a web site for your project that as a side effect
includes your developer reports. Web sites and developer reports have completely different audiences.
In fact, in most cases your developer reports should not be available on your web site. Furthermore,
to generate the summary, you have to suffer the overhead of generating site information that you are not
interested in.
In constrast, in Quokka there is a plugin dedicated to producing a useful summary of developer reports, namely the devreport plugin. Site generation is completely separate and unrelated.
Maven gives reports a special status: In Maven, there is a dedicated reports
section in the pom.xml
. In Quokka, reports are just plugins - there is nothing special
about them apart from them implementing devreport targets so that they appear in the reports index.
Plugin versions are optional in Maven: In the snippet above, the Maven plugin version has been set. However, this is optional in Maven and if not set it will find the latest available plugin by default. This is a big problem for Maven and is one of the reasons that Maven builds are not reproducible. i.e. If you forget to add the version and then check out your source a month later, it may not build as a different plugin version has been used.
In constrast, plugins (and all other dependencies) in Quokka must be explicitly versioned to avoid
this problem. Quokka also provides tools such as quokka list-plugins
to help you
find compatible plugin versions
Cobertura is an excellent open source test coverage framework. To enable it in Quokka, add the
following to build-quokka.xml
<plugin group="quokka.plugin.cobertura" version="0.3-ss">
The Cobertura report can now be generated using quokka cobertura
and as before it will be available via
quokka reports
.
For Maven, the cobertura plugin is added as a report and is configured as follows in pom.xml
.
The report can be generated with mvn cobertura:cobertura
or all reports can be generated
using maven site
<reporting> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <version>2.2</version> </plugin> </plugins> </reporting>
Analysis:
Maven forking runs tests twice: When generating the site for maven to view both the jUnit test results and the Cobertura reports, Maven runs all of the tests twice. This is due to Maven forking the build when it runs the Cobertura plugin. It does this so that it can use the instrumented classes instead of the normal classes.
In constrast, Quokka automatically detects when instrumentation is enabled and automatically adds the instrumented classes to the classpath during testing. This means the tests are only executed once.
To disable testing, Quokka relies on profiles. A profile can be used to modify the configuration of Quokka by enabling or disabling configuration elements. The following configuration shows how to disable testing using profiles in quokka:
<dependency-set> <profile id="skiptest" description="Skips tests, disabling all test-related targets"/> ... <plugin group="quokka.plugin.junit" version="0.3-ss" profiles="!skiptest"/> <plugin group="quokka.plugin.junitreport" version="0.3-ss" profiles="!skiptest"/> <plugin group="quokka.plugin.cobertura" version="0.3-ss" profiles="!skiptest"/> </dependency-set>
Testing can then be disabled by setting the profiles
property to skiptest
. This
is often done on the command line via quokka -Dprofiles=skiptest <target>
Maven supports profiles, but it is not possible to disable testing via it as the
test targets are included via the war packaging. The simplest way to disable the tests
in Maven is to set the maven.test.skip
property to true. This is often done on the command
line via mvn -Dmaven.test.skip=true <target>
Analysis:
Reduced overhead in Quokka: As the profiles eliminate all of the plugins at initialisation, there is no overhead associated with them. Maven too can achieve most of this via profiles, with the exception of the test goals themselves.
The following describes adding Jetty support to the project so that integration tests can be run against a running web container. The goal is to start a Jetty server instance prior to integration testing starting, run the tests against it and then shut down the instance when the tests complete.
In quokka, the following configuration achieves what we want:
<property name="q.jetty.defaults.stopKey" value="foo"/> <property name="q.jetty.defaults.stopPort" value="9999"/> <plugin group="quokka.plugin.jetty" version="0.1-ss" paths="(jsp-2.1)"> <target name="start-jetty" template="run-template" profiles="!skiptest" dependency-of="lifecycle:initialise-integration-test" depends="lifecycle:pre-initialise-integration-test"> <property name="daemon" value="true"/> <property name="scanIntervalSeconds" value="0"/> <property name="includeInstrumentedOuput" value="true"/> </target> <target name="stop-jetty" template="stop-template" profiles="!skiptest" dependency-of="lifecycle:finalise-integration-test" depends="lifecycle:pre-finalise-integration-test"/> </plugin>
There are several important things to note with the quokka configuration:
dependency-of
and depends
attributes of target
. This is how the Jetty server is started
and stopped before and after the integration tests.
plugin
declaration has a paths
entry that adds the optional jsp-2.1
dependency to the Jetty plugin's runtime path, effectively adding support for JSP 2.1. This is an example
of a Path Specification which gives great
control over what dependencies your project includes.
With the above configuration, Jetty will now be automatically started prior to integration testing.
By default, the jUnit plugin treats anything that ends in ITest.class as an integration test. So now,
the previously failing HelloWorldITest
will now succeed. As an added bonus, the includeInstrumentedOuput
property means that Cobertura classes are added to the Jetty container classpath along with the instrumented
classes, meaning that code coverage data will be collected for the integration tests running inside the Jetty server
instance.
For Maven, the following configuration needs to be added to the pom.xml
:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <excludes> <exclude>**/*ITest.java</exclude> </excludes> </configuration> <executions> <execution> <id>surefire-it</id> <phase>integration-test</phase> <goals> <goal>test</goal> </goals> <configuration> <excludes> <exclude>none</exclude> </excludes> <includes> <include>**/*ITest.java</include> </includes> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty-plugin</artifactId> <version>6.1.11</version> <configuration> <scanIntervalSeconds>10</scanIntervalSeconds> <stopKey>foo</stopKey> <stopPort>9999</stopPort> </configuration> <executions> <execution> <id>start-jetty</id> <phase>pre-integration-test</phase> <goals> <goal>run</goal> </goals> <configuration> <scanIntervalSeconds>0</scanIntervalSeconds> <daemon>true</daemon> </configuration> </execution> <execution> <id>stop-jetty</id> <phase>post-integration-test</phase> <goals> <goal>stop</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
Note that the surefire plugin configuration is required to make the test
phase
exclude integration tests (those ending with ITest.java
) and run them
in the integration-test
phase.
Analysis:
Maven configuration violates the schema: Maven target configuration allows arbitrary elements for configuration of targets. However, this violates the schema making tooling support irritating. It does however allow complex objects to be configured.
In constrast, Quokka target configuration is via property
elements and
therefore the quokka configuration file can be validated against a DTD. It also allows
configurations to be tweaked from other locations, such as command line properties,
user properties and project level properties. While properties are awkward for representing
complex types, Quokka supports build resources that allow arbitrary XML files to
be bundled with a dependency set. This is preferred mechanism for complex types in Quokka.
Maven configuration limitations: The Maven configuration has several issues. Firstly, only the test phase tests are included in the surefire report. It may be possible to configure the surefire-report plugin to fix this, but I can't find any documentation. Secondly, code coverage data is not collected for the integration tests.
Both Quokka and Maven plugins come with many configuration options. Furthermore, it can become difficult to track project paths due to transitive dependencies (dependencies of dependencies). It is therefore important to be able to get some help to work out what is going on.
Quokka provides the following help mechanisms:
-projecthelp
command line option that lists targets available in the
current project.
list-plugins
target that list plugins available in the repository, including
the versions that are compatible.
quokka help
to generate a detailed
HTML report that includes information
on all project paths, plugin paths, dependency sets, targets and properties. Help is also bundled
inside plugins. This allows you to get help on targets or plugins, using the target
and id
properties. e.g. quokka help -Dtarget=jetty:run
will bring up HTML help
for the version of the Jetty plugin you are using.
Maven provides the following help mechanisms:
dependency:tree
goal for listing project dependencies.
Analysis:
Maven plugins don't bundle help: The biggest issue with Maven is that help is not bundled with plugins and that the web site only shows help for the latest version of the plugin. Therefore, it is impossible to get help for a plugin version that isn't the latest.
In constrast, Quokka bundles the help with plugin, ensuring it is always avaiable.
Although not required for this particular project, it is common for projects to require ad hoc
targets for which no plugins exist. Quokka offers several options. Firstly, you can place
a standard Ant build.xml
file along side the build-quokka.xml
file
and it will be imported automatically. You are then free to use any Ant tasks you like. Some additional
tasks from AntContrib have also been bundled, such as if
, var
,
and for
to provide some limited imperative support.
If standard Ant targets are too limited or tedious, you can also use the script plugin. The script plugin allows you to write code in any scripting language support by Apache BSF, or JSR 223. The following is an example of using Javascript via script plugin:
<dependency group="apache.ant" name="commons-net" version="1.7.1" paths="ant-types"/> <plugin group="quokka.plugin.fop" version="?" templates="transform"/> <plugin group="quokka.plugin.script" version="?"> <target name="myscript" template="javascript"> <property name="script"><![CDATA[ // Example of built-in tasks (echo) for (i=1; i<=10; i++) { echo = project.createTask("echo"); echo.setMessage(i*i); echo.perform(); } // Example of optional Ant task (ftp) - dependencies are added to ant-types ftp = project.createTask("ftp") ftp.setUserid("ftp.crap.com") // Example of using a a quokka plugin (fop) - the plugin and template must be declared props = new java.util.Properties() props.put("in","src/fop/fo.xml") props.put("out","${q.project.targetDir}/fop/fo.pdf") quokka.execute("quokka.plugin.fop", "transform", props) ]]></property> </target> </plugin>
Maven's extensiblity appears to be limited to the antrun plugin. It allows arbitrary Ant tasks to be executed during any lifecycle phase.
Analysis:
Maven extensions are tied to lifecycle phases: From my understanding, Maven (via the antrun plugin) only allows tasks to piggy back on lifecycle phases. In constrast, Quokka allows arbitrary targets to be added. The may be stand-alone or participate in the lifecycle.
Quokka provides scripting support: As far as I'm aware there is no equivalent of the scripting capabilities of Quokka in Maven.
Quokka supports arbitrary paths: The ability to add arbitrary project paths coupled with the ability to add arbitrary targets is a powerful combination that lets an ad hoc target have similar functionality to a proper Quokka plugin.
Maven has no native concept of tasks: Maven isn't internally constructed of tasks that can be reused from ad hoc targets. This means it has to look elsewhere for a solution and ends up using Ant. It's a little ironic that having rewritten a lot of Ant tasks internally, it then relies on Ant for extensibility.
In constrast, Quokka is an extension to Ant and it's plugins are largely built on Ant tasks. It is there a natural mechanism for extensibility.
Quokka has a very strict policy regarding dependencies. Specifically, the core has no dependencies other than the Ant core. Furthermore, plugins never use any dependencies that are not required by the task itself. e.g. the Cobertura plugin will naturally have dependencies on Cobertura itself, but would never use Apache commons classes to ease development of the plugin itself.
The list below includes all dependencies required to build and run this project, including project and plugin dependencies. With this set the project can run offline:
apache.ant 1.7.1 junit jar apache.ant 1.7.1 trax jar apache.log4j 1.2.15 log4j jar apache.oro 2.0.8 oro jar eclipse.jdt.core 3.1.1 core jar junit 3.8.2 junit jar mortbay.jetty 6.1.11 jetty jar mortbay.jetty 6.1.11 jsp-2.1 jar mortbay.jetty 6.1.11 util jar ow2.asm 2.2.1 asm jar quokka.depset 0.3-ss jar depset quokka.plugin.cobertura 0.3-ss cobertura plugin quokka.plugin.devreport 0.3-ss devreport plugin quokka.plugin.help 0.3-ss help plugin quokka.plugin 0.3-ss jar plugin quokka.plugin.javac 0.3-ss javac plugin quokka.plugin.jee 0.1-ss jee plugin quokka.plugin.jetty 0.1-ss jetty plugin quokka.plugin.junit 0.3-ss junit plugin quokka.plugin.junitreport 0.3-ss junitreport plugin quokka.plugin.lifecycle 0.3-ss lifecycle plugin quokka.plugin.resources 0.3-ss resources plugin quokka.plugin.standard-lifecycle 0.3-ss standard-lifecycle plugin sf.cobertura 1.8 cobertura jar sun.jsp.spec.dist 2.1 dist jar sun.servlet.spec.dist 2.5 dist jar
This is pretty good, it basically contains the plugins we are using plus the project dependencies.
However, Quokka has another trick available to cut down dependencies even further, namely
Bundled Repositories. Bundled repositories basically a zipped up repository. Quokka publishes
one at present named quokka.bundle.extensions
that contains all of the quokka extensions (which
includes plugins, dependency sets and archetypes). It's primary design is to allow a project to keep
all of it's dependencies in version control along side the project, without clogging it up with
quokka related jars. e.g. The project can be configured to use a bundled repository by adding the following
properties to the project dependency set:
<property name="q.repo.project.url" value="delegate:core,bundle,local,shared"/> <property name="q.repo.bundle.url" value="bundle:quokka.bundle.extensions:2008.1;repository=shared"/> <property name="q.repo.local.url" value="file:${basedir}/lib;hierarchical=false;parents=shared"/>
Note: it is best to copy the bundled repository to the lib
directory and set the bundle repository's
repository
property to
local
after running with this configuration the first time to ensure the bundle itself is local.
Now when anlaysing the complete dependencies of our project we have:
apache.ant 1.7.1 junit apache.ant 1.7.1 trax apache.log4j 1.2.15 log4j apache.oro 2.0.8 oro eclipse.jdt.core 3.1.1 core junit 3.8.2 junit mortbay.jetty 6.1.11 jetty mortbay.jetty 6.1.11 jsp-2.1 mortbay.jetty 6.1.11 util ow2.asm 2.2.1 asm bundle:quokka.bundle.extensions 2008.1 extensions sf.cobertura 1.8 cobertura sun.jsp.spec.dist 2.1 dist sun.servlet.spec.dist 2.5 dist
This is essentially the miminum dependencies you can possibly have regardless of the build system you
use (well, maybe you could remove the Ant dependencies and of course the quokka bundle). Also, it is trivial to work
out where a
particular dependency is used - just use quokka help
and search the plugin paths for
the dependency you are interested in.
Maven on the other hand, seems to have no policy on dependencies and dependencies are included
freely.  In reality, Maven requires a lot of .pom dependencies as well to build.
Analysis:
Maven dependencies are excessive: To build the simple web application, Maven requires 116 .jar file dependencies (and many more .pom dependencies). Even worse, the bulk of the dependencies are not related to the project, they are there to support Maven developers.
In constrast, Quokka offers the same functionaly with a total of 26 or 14 dependencies (dependinging on whether you use bundled repositories or not).
Maven doesn't make use of optional dependencies: If you compare the Jetty dependencies between the Quokka and Maven list, you will see that the Maven version includes many more Jetty dependencies such as annotations, naming, management and plus. Quokka supports the notion of including optional dependencies. We have seen an example of this where the Jetty plugin included the jsp-2.1 dependency. This allows Quokka to exclude optional things by default, but allow them to be easily included by the user. Maven doesn't support this model and therefore included additional dependencies that the user has to exclude (if it is possible at all for plugin dependencies).
Maven's automatic conflict resolution introduces more dependencies: Maven traverses all dependencies in path and then applies conflict resolution rules on the resulting set. This means that even though a dependency won't end up being used, it is still required to build the project.
In contrast, quokka uses overrides to resolve conflicts. Overrides apply as a path is being traversed, so dependencies that are overridden are never traversed.
Maven exports it's project model to the repository: Although not listed above, there are many more dependencies required for the Maven build to work, namely Project Object Model (.pom) files.
In constrast, Quokka's repository layout is independent of the project model - projects export their artifacts and paths to the repository. So even if your project used a dozen nested dependency sets internally, that complexity is not exported to the repository.
Virtually no optimisation of Quokka has been done at present. However, in it's current form it
appears to significantly outperform maven for this project. Each of the following commands was run 3 times
with time
on an idle Core 2 Duo 2.4GHz processor under OS X 10.5.4, JDK 1.6.0_07 and the 3rd result was
taken (from real). It compares Quokka 0.3 against Maven 2.0.9
| Packaging, skipping tests | |
quokka clean package -Dprofiles=skiptest |
2.169s |
mvn -o clean package -Dmaven.test.skip=true |
4.479s |
| Quokka is 2.07x faster | |
| Installing, skipping tests | |
quokka clean install -Dprofiles=skiptest |
2.170s |
mvn -o clean install -Dmaven.test.skip=true |
8.338s *1 |
| Quokka is 3.84x faster | |
| Installing, including tests | |
quokka clean install |
7.162s |
mvn -o clean install -Dmaven.test.failure.ignore=true |
9.144s |
| Quokka is 1.28x faster | |
| Generating developer reports | |
quokka clean reports |
9.539s |
mvn -o clean site -Dmaven.test.failure.ignore=true |
14.790s |
| Quokka is 1.55x faster |
*1: This is unfair to maven as it includes the time to start and stop the Jetty server. This could be fixed by setting up a profile in Maven and disabling it for the test run. It is however extra configuration that most users would not perform.
Quokka and Maven are very similar in their functionality. Both are largely declarative build frameworks focusing on Java. Quokka has some clear advantages, in particular the number of dependencies it requires to operate, the fact that dependencies are always set to a specific version to make Quokka builds reproducible, extensibility and performance. There are numerous other advantages that this example doesn't cover, such as flexible multi-project builds, aggregated development reports, bootstrapping and support for multiple artifacts. See Quokka vs Maven for more information.


Ui for the run system action.
More...
|
| def | __init__ |
| | This is the constructor. More...
|
| |
| def | __init__ |
| | This is the constructor. More...
|
| |
|
def | save |
| | This function stores the path of the file in the action.
|
| |
|
def | load |
| | This function loads the data from the action to the pathWidget.
|
| |
| def | __init__ |
| | This is the constructor. More...
|
| |
|
def | remove_widget |
| | This functon removes this ui from te sessionUI instance.
|
| |
|
def | exectue |
| | This function execute the action it refers to.
|
| |
| def | move_action |
| | This function moves the the action up and down which then will be reflected from the ui once the data is updated. More...
|
| |
|
def | save |
| | This function needs to be reimplemented and defines how the data gets saved to the internal action.
|
| |
|
def | load |
| | This function needs to be reimplemented and defines how the data gets loaded from the internal action.
|
| |
|
|
| pathWidget |
| | Widget used to pick a path from disk.
|
| |
|
| session |
| | This attribute holds a pointer to the current session class.
|
| |
|
| internal_action |
| | This attribute holds a pointer to the action we are referring to.
|
| |
|
| session_ui |
| | This attribute holds a pointer to the sessionUi class that holds all the sub_ui.
|
| |
Ui for the run system action.
| def masterreload.master_ui.runSystemActionUi.RunSystemActionUi.__init__ |
( |
|
self, |
|
|
|
session_instance, |
|
|
|
internal_action, |
|
|
|
session_ui, |
|
|
|
parent = None |
|
) |
| |
This is the constructor.
- Parameters
-
| session_instance | this is the session we want to display the data of |
| internal_action | this is the acton class we want to work on with this UI |
| session_ui | this is is the instance of the sessionUi class |
| parent | this is the parent widget holding the sub_ui |
The documentation for this class was generated from the following file:
- master_ui/runSystemActionUi.py


Shows a with some hidden columns. As per this jira ticket moving some columns was not working. To confirm this example drag and drop the last few columns.