Getting started
Add ScalaWebTest to your project
Before you get to use all the nice features, you have to introduce ScalaWebTest to your project. All you have to do, is add the core module to your test or integration test dependencies.
Add ScalaWebTest to your SBT project
You can add ScalaWebTest to your integration testing dependencies in the build.sbt/scala of your sbt project as follows.
{% highlight scala %} libraryDependencies += "org.scalawebtest" %% "scalawebtest-core" % "{{page.scalaWebTestVersion}}" % "it" {% endhighlight %}If you want to manage the most important transitive dependencies of ScalaWebTest, you might configure it as follows. Especially selenium-java and selenium-htmlunit-driver should align.
{% highlight scala %} libraryDependencies ++= Seq( "org.scalawebtest" %% "scalawebtest-core" % "{{page.scalaWebTestVersion}}" % "it" "org.scalatest" %% "scalatest" % "3.0.0" % "it", "org.seleniumhq.selenium" % "selenium-java" % "2.53.1" % "it", "org.seleniumhq.selenium" % "selenium-htmlunit-driver" % "2.52.0" % "it", ) {% endhighlight %}Add ScalaWebTest to your maven project
You have to add the following dependency to your maven project to use ScalaWebTest.
{% highlight xml %}ScalaWebTest is also available for Scala 2.10 and 2.11, change the version after the underscore, to get a ScalaWebTest version which is binary compatible with the Scala version which you use.
{% highlight xml %}In case you want to manage the most important transitive dependencies of ScalaWebTest, you can add our bill-of-materials to your project. You may to so by adding the following dependency
{% highlight xml %}Write your first test
To write your first test you have to create a scala file in src/it/scala or src/test/scala, depending on your test setup. You then create a class named TestSubjectSpec which extends IntegrationFlatSpec. Other styles are available, if you prefer. Lets assume we want to verify our homepage and call the test HomepageSpec {% highlight scala %} import org.scalawebtest.core.IntegrationFlatSpec class HomepageSpec extends IntegrationFlatSpec { override val host = "http://localhost:8080" path = "/index.html" "Our homepage" should "contain a header with the company name" in { webDriver.findElementByTagName("h1").getText shouldEqual "Unic AG" } } {% endhighlight%} Thats all you need. ScalaWebTest will automatically call http://localhost:8080/index.html before executing a test, call "Our homepage" - "should" - "contain a header with the company name". With the test, we can then access elements within the webDriver. The webDriver emulates a web browser.Selecting a style
ScalaTest provides a wide variety of testing styles. ScalaWebTest supports all available styles. As the ScalaWebTest creators have a JUnit background, most of the examples use FlatSpec style. There is no technical reason behind it, it is simply a matter of taste. In your BaseTrait extend one of the Integration*Spec/Suite classes from Styles.scala
Build your base trait
Configurations, style choice and possible custom extension should be shared across your project. To do so, we recommend to create a BaseTrait. When creating a new Test you then simply extend your BaseTrait. You can find such an example in ScalaWebTest's own integration tests. {% highlight scala %} import org.scalatest.AppendedClues import org.scalatest.concurrent.PatienceConfiguration.Timeout import org.scalatest.time.SpanSugar._ import org.scalawebtest.core.gauge.HtmlGauge import org.scalawebtest.core.{FormBasedLogin, IntegrationFlatSpec} import scala.language.postfixOps trait ScalaWebTestBaseSpec extends IntegrationFlatSpec with AemTweaks with FormBasedLogin with HtmlGauge { override val host = "http://localhost:8080" override val loginPath = "/fakeLogin.jsp" override val projectRoot = "" override def loginTimeout = Timeout(5 seconds) } {% endhighlight %} This BaseTrait uses FlatSpec style, adds the tweaks from the AEM module, and uses FormBasedLogin.Configure your tests
A reasonable default configuration is provided, which can easily get adapted. If you want to change a configuration for all your tests, we recommend to change it in your base trait, otherwise best practice is to change it in the constructor of your TestSpec. Two config objects exist, one which is applied during login and one which is applied during test execution.
In the following TestSpec JavaScript errors are swallowed during Login phase, no matter if JavaScript execution is enabled or disable. During test execution JavaScript is enabled and JavaScript errors let the test fail.
{% highlight scala %} import org.scalawebtest.core.IntegrationFlatSpec class HomepageSpec extends IntegrationFlatSpec { override val host = "http://localhost:8080" path = "/index.html" loginConfig.swallowJavaScriptErrors config.enableJavaScript(throwOnError = true) "Our homepage" should "contain a header with the company name" in { webDriver.findElementByTagName("h1").getText shouldEqual "Unic AG" } } {% endhighlight%}By default JavaScript is not executed and JavaScript errors don't throw. Also CSS is not interpreted. This applies to both loginConfig and config.
Writing gauges
Using gauges to write your integration tests, is the core idea of ScalaWebTest. We think using org.scalatest.selenium.WebBrowser.Query, i.e CssSelectorQuery, to select elements in a HTML document and then verifying it is cumbersome and the resulting test is hard to read. Instead we copy a concept from the manufacturing industry. They build gauges, which they then lay their workpiece into. If the workpiece fits in the gauge, it satisfies the requirements. Gauges are defined in HTML (technically speaking it is XML, using scala.xml.NodeSeq) Let's have a look at a simple example to get an idea how those gauges work.
{% highlight scala %} package org.scalawebtest.integration.gauge import org.scalawebtest.integration.ScalaWebTestBaseSpec class SimpleGaugeSpec extends ScalaWebTestBaseSpec { path = "/navigation.jsp" "The navigation" should "contain our navigation links in correct structure, order and with the expected text" in { fit( ) } } {% endhighlight %}This test would be successful, when run against, when navigation.jsp contains the following HTML
{% highlight html %} {% endhighlight %}We can already, see that fits() ignored html, head and body, elements before the navigation. But following the idea from manufacturing, we should strip our gauge, from everything which we don't need. Let's assume we only want to make sure our navigation contains the correct links. We don't care about classes added for design or text contained in the links. Therefore we reduce our test to.
{% highlight scala %} "The navigation" should "contain our navigation links in correct structure and order" in { fit( ) } {% endhighlight %}If we don't care about the unnumbered list, we can reduce our test even further.
{% highlight scala %} "The navigation" should "contain our navigation links in correct order" in { fit( ) } {% endhighlight %}The document then fits our gauge as follows. Matching parts are green, parts in grey have no influence whether the document fits or not.
<!DOCTYPE html>

first navigation element

second navigation element

third navigation element

If the gauge doesn't fit, we get an error. Lets change the test to the following.
{% highlight scala %} "The navigation" should "contain our navigation links in correct order" in { fit( ) } {% endhighlight %}This will of course not match, because the text within the first link is wrong and belongs to the second link. See the different highlighted in the following html block.
<!DOCTYPE html>

second navigation element first navigation element

second navigation element

third navigation element

This results in the following error message from ScalaWebTest
{% highlight text %} 23:06:37.482 [ScalaTest-run-running-SimpleGaugeSpec] INFO IntegrationSpec - Going to http://localhost:8080/navigation.jsp Misfitting Text: The text [first navigation element] within [HtmlAnchor[]] didn't equal [second navigation element] Current document does not match provided gauge: ScalaTestFailure {% endhighlight %}Test for classes on elements
The class attribute is special, because it is basically an unsorted set of class attributes. Usually we don't bother, if additional classes are present in our HTML and for sure we never care about the order. ScalaWebTest therefore handles the class attribute different from the rest. Per default the attribute content, has to match exactly the one you provided in your gauge, but for classes, it only asserts, that the classes which an element contains within your gauge definition, are all present on that element in the HTML document. The following gauge therefore matches all the elements shown below.
{% highlight scala %} fit( ) {% endhighlight %} {% highlight html %} {% endhighlight %} {% highlight html %} {% endhighlight %} {% highlight html %} {% endhighlight %}Test single elements
Often trying to fit the complete document into the defined gauge isn't the most natural and efficient thing to do. Especially when a website contains multiple elements of the same kind, such as content cards, gallery images or items of a product list, finding all those elements first, and then trying to fit each element with the gauge, is better.
{% highlight scala %} import org.scalatest.exceptions.TestFailedException import org.scalawebtest.integration.ScalaWebTestBaseSpec import org.scalawebtest.core.gauge.HtmlElementGauge class ElementGaugeSpec extends ScalaWebTestBaseSpec with HtmlElementGauge { path = "/galleryOverview.jsp" def images = findAll(CssSelectorQuery("ul div.image_columns")) val imageGauge = "The gallery" should "contain the expected HTML for every image" in { images.size should be > 5 withClue " - gallery didn't contain the expected amount of images" for (image <- images) { image fits imageGauge } } {% endhighlight %}When using findAll with a for-comprehension, one should always verify, that the expected amount of elements was found.
When using fits or doesntFit, the same features are available, no matter if the whole document, or a single element is checked. Therefore the following chapters apply to both usecases. There are two restrictions when using gauges for single elements (built using ElementGaugeBuilder.GaugeFromElement)
- the gauge may only contain one top level element
- the top level element of the gauge has to be the same, as the one which is checked (not one of its children)
Test for containment
It is common that you don't want to verify the complete attribute value, or text of an element. You may use @contains to trigger the ContainsMatcher instead of the DefaultMatcher. Thanks to this matcher, this gauge matches the following HTML element
{% highlight scala %} fit() {% endhighlight %} {% highlight html %} {% endhighlight %}The containsMatcher is also available, when matching text.
{% highlight scala %} fit(@contains available) {% endhighlight %} {% highlight html %} ScalaWebTest is available on github {% endhighlight %}Hint: always add a space after the matcher annotation. We enforce this space to improve readability of tests.
Test for regex matches
When we want to enforce textual rules on our content, we need a more powerful Matcher. The RegexMatcher is our friend. It is trigger using @regex
{% highlight scala %} fit() {% endhighlight %}This gauge will fit
{% highlight html %} {% endhighlight %}but it won't fit
{% highlight html %} {% endhighlight %}Negating your tests
Sometimes you want to make sure that a certain content is not shown. For example you want to make sure, that no login form is shown, after logging in. Or you want to make sure that no private information is shown, when an error is thrown. To do so you may use doesnt fit() or not fit(). They are synonyms. Choose whatever reads better in your current context.
{% highlight scala %} package org.scalawebtest.integration.gauge import org.scalawebtest.integration.ScalaWebTestBaseSpec class LoggedInSpec extends ScalaWebTestBaseSpec{ path = "/protectedContent.jsp?username=admin&password=secret" "When logged in the protectedContent page" should "not show the login form" in { not fit } } {% endhighlight %}In case the login form is mistakenly rendered, the following error will appear.
{% highlight text %} Current document matches the provided gauge, although expected not to! Fitting nodeTesting a complete process
Todays webapplication are of course not that static. Usually it is more interesting to test a complete process. This is easily possible with Selenium. You can use it to press buttons and even trigger JavaScript functions. As the gauges are based on Selenium, they can be combined with actions as well. As soon as an action is executed, the currentPage is updated and our next call to fit() or doesnt fit() will be executed against the updated page.
Lets try this out using a page with protected content. On first load a form will be shown, after logging in the form disappears, instead the text sensitive information appears.
{% highlight scala%} package org.scalawebtest.integration.gauge import org.scalawebtest.integration.ScalaWebTestBaseSpec class LoginSpec extends ScalaWebTestBaseSpec { path = "/protectedContent.jsp" "When accessing protectedContent it" should "show the login form" in { fits( ) } it should "hide the protected content, when not logged in" in { not fitsensitive information
} it should "show the protected content, after logging in" in { textField("username").value = "admin" pwdField("password").value = "secret" submit() fits(sensitive information
) } } {% endhighlight %}This example test was split into three smaller tests. This is technically not necessary, but has the following advantage. When only part of the tests fails, you have a better idea what went wrong. For example, if the login fails, but the login form is correctly shown, the issue has to be your login process. But if the login form isn't shown, the problem has a very different cause. In addition the verbose output from the gauge should help you finding the root cause.
Executing JavaScript
Thanks to Selenium executing JavaScript is not an issue. Per default JavaScript is not executed with ScalaWebTest, because tests will be faster without. To execute JavaScript, you have to enable it.
{% highlight scala %} config.enableJavaScript(throwOnError = true) {% endhighlight %}In addition, you have to decide whether a JavaScript error should cause your test to fail or not. Usually failing on JavaScript errors is good, but sometimes you have to write tests for a web application with major issues in JavaScript. This is one of the rare moments, where you have to disable throwOnError to be able to test the web application.
The other thing which is very important with JavaScript, is to remember, that after receiving the document, your browser needs some time to execute JavaScript. The same is true for the Selenium webdriver. We have to wait for it execute the provided JavaScript. ScalaTest provides eventually, which does exactly what we need. It repeats a given test until it succeeds or the given timeout has been surpassed.
{% highlight scala %} eventually(timeout(3 seconds)) { fits(Modules
Every web application framework has its own approaches/concepts, which are relevant for testing. ScalaWebTest provides this framework specific logic via modules. We encourage other, to create additional modules for their framework, or use an existing one, if it is already available.
What should be shared via modules?
We think even simple things such as default login method, ports and urls might be worth sharing. Functions to create content, update accounts, or requesting a page in debug mode, could be even better candidates for a module.
AEM module
As the creators of ScalaWebTest work a lot with Adobe Experience Manager, they decided to create the first module for this CMS
To use this module extend AemTweaks from the aem module in your BaseTrait. This will cause the following.
- FormBasedLogin is activated
- loginPath is set to the default AEM 6 login path
- pages are requested with wcmmode cookie set to DISABLED
PageProperties
Disclaimer: This trait is currently only available in scalawebtest-aem_2.11, due to dependencies on play-json which wasn't yet released for 2.12
The PageProperties trait populates the pageProperties, and if applicable componentProperties and suffixProperties fields with a JsValue representing the properties of the currentPage, component and suffix respectively. It does so by retrieving the JSON representation of the currentPage. This works by default on all CQ/AEM author instances. In addition it provides convenience methods to access the pageProperties content.
- pageProperties(name: String) - retrieve a page property by name
- jcrContent(name: String) - retrieve a property from jcr:content by name
- findByResourceType(value: String) - search through a parsys field in the pageProperties and find all component with given resourceType
- findByProperty(name: String)(value: String) - search through a parsys field in the pageProperties and find all component with given property name and value
It populates the pageProperties field with a play.api.libs.json.JsValue, which represents the properties of the currentPage. In case the url/path points to something below jcr:content, the componentProperties will be populated with the properties of the component, and the pageProperties with those of the containing page. In case the url/path contains a suffix, the suffixProperties will be populated with the properties of the page referenced in the suffix. It does so by manipulating the url field, to request the JSON representation of the currentPage from CQ/AEM. This feature is available on CQ/AEM author instances by default. The enable.json property of the org.apache.sling.servlets.get.DefaultGetServlet of your CQ/AEM instance has to be set to true. Only extend this trait in tests which need the feature, as it otherwise unnecessarily slows down your tests, due to additional requests for page properties.
JSON module
JSON is currently the most important data exchange format in the web. We think writing integration tests for web-services, which return JSON, should be as easy as for websites. Therefore we created the JsonGauge, which provides functionality comparable to the HTML gauge. All its magic is exposed via the according builder, which provides implicit conversions for JsValue, JsLookup and functions retrieving the currentPage from the webDriver. We choose to use play-json to parse the JSON response. To keep the core module clean from additional dependencies, a separate module was introduced.
Only verifying a few values
To verify the value or type of a few values, we consider standard play-json sufficient. Just use Json.parse to parse the response and then use \ and \\ to navigate the json structure.
{% highlight scala %} import org.scalawebtest.integration.ScalaWebTestBaseSpec import play.api.libs.json.Json class ReducedJsonSpec extends ScalaWebTestBaseSpec { path = "/dijkstra.json" def json = Json.parse(webDriver.getPageSource) "Dijkstra" should "have the correct firstname" in { def firstName = (json \ "firstName").as[String] firstName should equal("Edsger") } } {% endhighlight %}All JSON tests will work with the following JSON response
Of course this way of verifying JSON responses reaches its limitations very soon. Therefore we created JsonGauge
JsonGauge - the ScalaWebTest way
Same as when verifying HTML, we think the easiest and most natural way to formulate your expectation is using the same language, as the tested response itself. With the JSON gauge you can use JSON to specify the gauge, into which the JSON response has to fit.
{% highlight scala %} import org.scalawebtest.integration.ScalaWebTestBaseSpec import org.scalawebtest.json.JsonGauge class DocumentFitsValuesSpec extends ScalaWebTestBaseSpec extends JsonGauge { path = "/jsonResponse.json.jsp" "FitsValues" should "report success, when the json gauge contains the same values as the response it is tested against" in { fitsValues( """{ "name": "Dijkstra", "firstName": "Edsger", "yearOfBirth": 1930, "isTuringAwardWinner": true, "theories": [ "shortest path", "graph theory" ] }""" ) } } {% endhighlight %}Often verifying the complete response isn't ideal. We recommend to parse and traverse the document using play-json and then verify if the JsLookup or JsValue fits the defined gauge.
{% highlight scala %} import org.scalatest.exceptions.TestFailedException import org.scalawebtest.integration.ScalaWebTestBaseSpec import org.scalawebtest.json.JsonGauge import play.api.libs.json.{JsValue, Json} class DijkstraJsonGaugeSpec extends ScalaWebTestBaseSpec extends JsonGauge { path = "/dijkstra.json" def dijkstra = Json.parse(webDriver.getPageSource) "The response for Dijkstra" should "contain the expected values" in { dijkstra fits values of """{ "firstName": "Edsger", "name": "Dijkstra", "yearOfBirth": 1930, "theories": [ "shortest path", "graph theory" ] } """ } it should "contain the correct universities" in { val universities = dijkstra \ "universities" universities fit values of """ [ { "name": "Universität Leiden","begin": 1948, "end": 1956 }, { "name": "Mathematisch Centrum Amsterdam", "begin": 1951, "end": 1959 }, { "name": "Technische Universiteit Eindhoven", "begin": 1962, "end": 1984 }, { "name": "University of Texas at Austin", "begin": 1984, "end": 1999 } ] """ } {% endhighlight %}As with the HTML gauge, controlled variance is allowed. The original response contains additional key/value pairs (isTuringAwardWinner and universities). Also the order of the name and firstName is the other way around. Nevertheless this test would report success, when run. It would report an error, if the response would:
- contain a different value for one of the keys >
- miss a key, which is contained in the gauge
- contain a different hierarchy of the key/value pairs
Behaviors are a good way to share tests. In this case, verifying values is too specific. Therefore we provide fits types of in addition to fits values of.
{% highlight scala %} import org.scalatest.exceptions.TestFailedException import org.scalawebtest.integration.ScalaWebTestBaseSpec import org.scalawebtest.json.JsonGauge import play.api.libs.json.{JsValue, Json} class DijkstraJsonGaugeFitsTypesSpec extends ScalaWebTestBaseSpec extends JsonGauge { path = "/dijkstra.json" def dijkstra = Json.parse(webDriver.getPageSource) "The response for Dijkstra" should "contain the expected types" in { dijkstra fits types of """{ "firstName": "", "name": "", "yearOfBirth": 0, "theories": [ "" ] } """ } {% endhighlight %}Now the values no longer matter. They are only used to determine the expected type. Therefore we recommend to use values, which indicate that only the type matters. Use empty strings and 0. This test would report success. It would fail, when the response would:
- contain a value with a type different from the one expected for the given key
- miss a key, which is contained in the gauge
- contain a different hierarchy of the key/value pairs
- when an array value would contain an element with a mismatching type
Lets have a closer look at arrays. Usually we expect all array elements to match certain expectations. When using fits types of, you only have to define those expectations once. All array elements then have to fulfill them. Lets have a look at an example. Lets assume the test of the previous example would be replaced with the following
{% highlight scala %} "The response for Dijkstra" should "contain the expected types" in { dijkstra fits types of """{ "firstName": "", "name": "", "yearOfBirth": 0, "theories": [ "" ] "universities": [{ "name": "", "begin": 0, "end": 0 }] } """ } {% endhighlight %}This would enforce all elements of the universities array to contain a key name with a value of type string and the keys begin and end with a value of type number. The elements might contain additional keys, such as department, but no contradictions. Sometimes this is not the behavior one is looking for. Two more options exist to verify arrays. One common scenario is, that the size of the array matters, i.e. coordinates in a GeoJson document. To test this use fits typesAndArraySizes of. In this case every array element is considered unique, you have to provide specific expectations for every element, but those expectations are allowed to differ. For example, we expect Martin to have studied and worked at three universities. For the first two an end is known. This isn't the case for the last one.
{% highlight scala %} "The response for Odersky" should "contain the expected types and array sizes" in { odersky fits typesAndArraySizes of """{ "firstName": "", "name": "", "universities": [{ "name": "", "begin": 0, "end": 0 },{ "name": "", "begin": 0, "end": 0 },{ "name": "", "begin": 0 }] } """ } {% endhighlight %}One more common situation exists with arrays. Often the array elements are not sorted. Although we expect the array to contain a specific element, we don't know it's position. For this situation containsElementFitting values/types/typesAndArraySizes of exists.
{% highlight scala %} import org.scalawebtest.integration.ScalaWebTestBaseSpec import org.scalawebtest.json.JsonGauge import play.api.libs.json.{JsLookupResult, JsValue, Json} class ContainsElementFittingSpec extends ScalaWebTestBaseSpec extends JsonGauge { path = "/jsonResponse.json.jsp" def dijkstra: JsValue = Json.parse(webDriver.getPageSource) def universities: JsLookupResult = { dijkstra \ "universities" } "The universities array" should "contain an element with the expected types" in { universities containsElementFitting types of """{ | "name": "", | "begin": 0, | "end": 0 | } """.stripMargin } it should "contain an element with the expected values" in { universities containsElementFitting values of """{ | "name": "Technische Universiteit Eindhoven", | "begin": 1962, | "end": 1984 | }""".stripMargin } } {% endhighlight %}Because of its simplicity we prefer play-json over Argonaut and Circe. A good alternative to our JSON module is cornichon. Cornichon requires your tests to be a bit more specific then the ScalaWebTest ones, but it provides nice ways to do so. As cornichon uses Akka Http instead of HtmlUnit to retrieve the JSON response, it is only partially compatible with ScalaWebTest. They might coexist in the same codebase and both are based on ScalaTest, but cornichon can't make much use of the automatisation provided by ScalaWebTests (login, navigation, ...).
Extending ScalaWebTest
One of the goals for ScalaWebTest is to make it as as easy as possible, to extend it. Therefore the foreseen extension points are a part of the documentation.
Create a custom matcher
One of the core extension points for gauge testing, are the matchers. ScalaWebTest uses two types of matchers, one for text and one for attributes. You may create your own TextMatcher or AttributeMatcher by extending those traits. You can then add the your own matcher, by prepending to the list of textMatchers or attributesMatchers in the Matchers object.
Here is a full example, of how to implement your own Matcher
Create a custom login
If the available login mechanisms don't work with your web application. No worries, simply create your own implementation of the Login interface and extend it in your base trait. Ideally you then contribute it to the project via pull request.
The FormBasedLogin provides a good example on how to implement your custom login
Create your own module
If you implement multiple web applications with the same framework, you might want to create a framework specific module. This allows to share framework specific testing code among multiple projects. You can start using it within your company/project and share it later with the rest of the community by creating a pull request. If you provide your own module to ScalaWebTest, we ask you to help maintaining it.
IntegrationSpec
The IntegrationSpec is the base trait, which all testing style specific traits extend. When writing a spec you should not extend IntegrationSpec, but one of the testing style specific traits, such as IntegrationFlatSpec
The base trait extends the following traits
- Webbrowser - Selenium DSL for ScalaTest
- Suite - encapsulates a conceptual suite of tests
- BeforeAndAfterEach - provides beforeEach and afterEach test hooks
- BeforeAndAfterAll - provides beforeAll and AfterAll tests hooks
- WebClientExposingHtmlUnit - wrapper for HtmlUnitDriver, which exposes the WebClient
- IntegrationSettings - a set of fields to configure IntegrationSpec (moving this to Configuration is currently under debate)
- Eventually - provides eventually construct
Integration_Spec
There is a complete set of test style specific base traits. Choose a testing style and then extend the according style specific Integration_Spec trait to create your test.
This traits extend the following traits
- _Spec i.e. FlatSpec - provides the testing style specific DSL
- IntegrationSpec - integrates ScalaTest and Selenium with additional features from ScalaWebTest
- Matchers - provides DSL for assertions using the word should
- Inspectors - provides nestable inspector methods that enable assertions to be made about collections
_SpecBehavior
One way to share assertions between testing suites, is to use behavior. A behavior contains a set of assertions. You can then use X behaves like SomeBehavior within the suite. Behavior and Suite have to use the same testing style. Therefore ScalaWebTest builds its style specific base traits on the style specific base traits. The following traits, which are part of the Integration_Spec, are in fact inherite from the behavior
- _Spec i.e. FlatSpec
- Matchers
- Inspectors
Gauge
The Gauge provides the methods fit, fits, doesnt fit and not fit. All of them are used to verify if the current webpage matches the given gauge specification. To do so, the gauge, searches for elements, as defined by the specification, in the current webpage. Elements are found by element name and containing classes. All elements which fulfill the search criteria, are considered candidates. Candidates are then verified for correct attributes and textcontent, using Matchers, next they are verified by checking whether their children match. As soon as something doesn't match a Misfit is reported. If no candidate matches all criteria, the gauge doesn't fit. While verifying the candidates, a list of Misfits was acquired, the gauge will only report the most specific of all Misfits.
Misfit
A Misfit is a container for an error message, when something didn't fit a given Matcher. It contains an error message and a relevance. The deeper the current check in the gauge specification is, the higher its relevance. In the end only the Misfits with the highest relevance will be part of the error message.
Matchers
When working with gauges for testing, Matchers are used to test attributes and text content of elements. By default the following Matchers are available.
- Default Matcher: tests whether an attribute or text exactly matches a given String
- Contains Matcher: tests whether an attribute or text contains a given String
- Regex Matcher: tests whether an attribute or text matches the given Regex
All Matchers, which are available by default, can be used for attributes and text. When creating a new Matcher, one is not forced to provide both functionalities. For each of the funcionalities a specific trait exists. Extend AttributeMatcher or TextMatcher or both, when creating your own Matcher
When creating a custom Matcher the most important thing is to provide a detailed Some(Misfit) in case the Matcher doesn't match. In case of a match, the Matcher simply returns None.
Login
The Login trait is very simple. It only defines what the username and password field should be called. It is used to mark specific implementations as Login and to assert that username and password are consistent over all implementations. Depending on the authentication process for which a Login trait was implemented, it might make sense to overwrite the login function from IntegrationSpec.
WebDriver
ScalaWebTest uses a custom web driver, more precisely a wrapper for the HtmlUnitDriver. Thanks to this wrapper, we can expose the WebClient to enable more control over the WebClient. We also rely on the HtmlUnitDriver, because it has the option to compare Html Elements their order within the document. This is not available with all WebDriver implementations, but crucial for the implementation of Gauge.
WebDriverConfigFixtures
The WebDriverConfigFixtures provide the option to execute a closure with a specific configuration. After the closure is executed, the WebDriver configuration will be reverted to what it was before the call of the fixture.


ZORK
Welcome to ZORK.
(Test implementation by Dillon Lomnitzer. A Reiteration of Josh Stevens.)
Original game © 1981, 1982, 1983 Infocom Inc. All rights reserved.
Welcome to ZORK.
(Test implementation by Dillon Lomnitzer. A Reiteration of Josh Stevens.)
Original game © 1981, 1982, 1983 Infocom Inc. All rights reserved.
>


modeltools: Tools and Classes for Statistical Models
A collection of tools to deal with statistical models.
The functionality is experimental and the user interface is likely to
change in the future. The documentation is rather terse, but packages ‘coin’
and ‘party’ have some working examples. However, if you find the
implemented ideas interesting we would be very interested in a discussion
of this proposal. Contributions are more than welcome!
| Version: |
0.2-22 |
| Depends: |
stats, stats4 |
| Imports: |
methods |
| Published: |
2018-07-16 |
| Author: |
Torsten Hothorn, Friedrich Leisch, Achim Zeileis |
| Maintainer: |
Torsten Hothorn <Torsten.Hothorn at R-project.org> |
| License: |
GPL-2 |
| NeedsCompilation: |
no |
| Materials: |
NEWS |
| CRAN checks: |
modeltools results |
Downloads:
Reverse dependencies:
| Reverse depends: |
archetypes, flexclust, party |
| Reverse imports: |
betareg, bujar, coin, eaf, flexmix, mobForest, ockc, psychomix, revtools, topicmodels, TSDT |
| Reverse suggests: |
mlr |
| Reverse enhances: |
clue |
Linking:
Please use the canonical form
https://CRAN.R-project.org/package=modeltools
to link to this page.


Font can be changed incrementally or to specific sizes.
Items marked in red are DEFAULTS which can be adjusted easily.
- For incremental changes:
- Any element with class name increaseClickItem will increase the font size.
- Any element with class name decreaseClickItem will decrease the font size.
- The Default increment value is 2. See below for example of ways to change the options.
When the minFont or maxFont values are reached, class DISABLED will be passed to increaseClickItem / decreaseClickItem classes, respectively. The same is true if the fontResizer cookie returns a value that matches minFont or maxFont.
- For specific changes:
- Any element with class name setFontSize will set the font size.
- The size to be set is determined via the title attribute which needs to be passed when the class is set.
- Example: <a class="setFontSize" title="20"> will set font size to 20.
When an element with class setFontSize is clicked, class ON will be passed to it. When another element with class setFontSize is clicked, ON will be removed from the previous element. This also occurs when increaseClickItem / decreaseClickItem elements are clicked and reach a value that matches a setFontSize elements value. The same is true if the fontResizer cookie returns a value that matches a setFontSize value.


HTML LISTS
Navigation Menu
In this example, we use CSS to style the list horizontally, to create a navigation menu:
Different types of Unordered List
Different types of ordered List
- coffee
- Tea
- milk
- coffee
- Tea
- milk
- coffee
- Tea
- milk
- coffee
- Tea
- milk
- coffee
- Tea
- milk
This is an Description List
- coffee
- Hot drink
- Tea
- Hot drink
- Milk
- Cold drink
This is an Nested List
This is an Ordered list with control list controlling
- Coffee
- Tea
- Milk


Functions supporting the common needs of packages 'ChemoSpec' and 'ChemoSpec2D'.
| Version: | 0.2.163 |
| Depends: | R (≥ 3.1) |
| Imports: | plyr, robustbase, RColorBrewer, amap |
| Suggests: | ChemoSpec (≥ 5.0), ChemoSpec2D, testthat |
| Published: | 2018-11-28 |
| Author: | Bryan A. Hanson [aut, cre] (ORCID 0000-0003-3536-8246) |
| Maintainer: | Bryan A. Hanson <hanson at depauw.edu> |
| BugReports: | https://github.com/bryanhanson/ChemoSpecUtils/issues |
| License: | GPL-3 |
| URL: | https://github.com/bryanhanson/ChemoSpecUtils |
| NeedsCompilation: | no |
| Materials: | NEWS |
| CRAN checks: | ChemoSpecUtils results |
| Reference manual: | ChemoSpecUtils.pdf |
| Package source: | ChemoSpecUtils_0.2.163.tar.gz |
| Windows binaries: | r-devel: ChemoSpecUtils_0.2.163.zip, r-release: ChemoSpecUtils_0.2.163.zip, r-oldrel: ChemoSpecUtils_0.2.163.zip |
| OS X binaries: | r-release: ChemoSpecUtils_0.2.163.tgz, r-oldrel: ChemoSpecUtils_0.2.163.tgz |
| Old sources: | ChemoSpecUtils archive |
| Reverse depends: | ChemoSpec, ChemoSpec2D |
Please use the canonical form https://CRAN.R-project.org/package=ChemoSpecUtils to link to this page.


Next
Auto Review
Random
Certain
Uncertain
Relevant
Irrelevant
Undetermined
Plot
Auto Plot
Export
Restart


haematuria
-
rapid and relatively sensitive (>80%)
-
haemaglobin
-
myoglobin: rush, rhabdomyolysis, myositis
-
therefore spec range 65-99%
-
trace = negative
macroscopic (visible)
-
associated with urological conditions
-
consider:
-
haemoglobinuria
- haemolytic anaemia
-
myoglobinuria
- rhabdomyolysis
-
beeturia (after eating beetroot)
-
prophyria
-
medicines
-
rifampicin
-
chlorpromazine
-
-
-
if UTI ruled out
- refer urology and cystoscopy or nephrology with no risk factors
microscopic (non visible)
-
transient microscopic = common
-
reported in 39% of people
-
mixture of urological and glomerular causes
-
persistent microscopic haematuria = urine positive on 2/3 consdectuive dipsticks (1-2 week)
-
once benign causes ruled out:
-
UTI
-
exercise
-
menstruation
-
-
imaging
-
if >40 urological assessment or risk factors
-
assess prostate
-
investigatiosn
-
BP
-
renal function
-
eGFR
-
ACR
-
urine microscopy for urinary casts and dysmorphic red cells
-
-
referral to renal:
eGFR \<30
eFGR declining by >5mL in previous year or >10mL/min over last 5 years
uncontrolled hypertension ≥ 140/90
Unexplained visible heamaturia following urological assessment where no cause found
Assessment
-
symptomatic/asymptomatic
-
dysuria
-
frequence
-
urgency
-
hesitancy
-
-
anti-coagulant/anti-platelet medicines more likely to exacerbate compared to cause haematuria
-
urine cytology probably not useful
-
definatley not as part of routine investigation
-
low sensitiveity
-
red flags
history of recurrent macroscopic haematuria
age >40
current smoker or recent histoyr of smoking
history of recurrent UTI or other urological disorders
occupational exposure to chemicals/dyes
Previous pelvic irradiation
history of excessive analgeisa
cyclophosphamide
bladder cancer
family histoyr
smoking
male
occupational
Imaging
-
CTU = gold standard in investigation of haematuria
-
3 phases:
-
non contrast - stones
-
contrast - structural, vascular, infectious abnormal of parenchyma
-
delayed excretory phase
-
= triphasic CT
-
sens for renal stones = 94-98%
-
superior to uss and ivu for detecting renal masses
-
-
Proteinuria
-
urine dipstick - highly specific but lacks sensitivity
-
ACR more sensitive (better than PCR)
-
spot accurately reflects 24hr albuminuria and proteinuria
-
false positive
-
alkaline urine (pH >7)
-
gross haematuria
-
mucus
-
semen or leukocytes
-
-
transient
-
exercise
-
standing for long periods
-
pregnnacy
-
uti
-
acute febrile illness
-
CHF
-
-
persistent
- do ACR (or PCR)
-
manage in primary care if:
-
ACR \< 70 (or PCR \<100)
-
haematuria absent
-
eGFR ≥ 30mL/min
-
-
BP, urinalysis, renal function every 6-12 mo
-
HTN \<130/80
-
-
routine referral to nephrology:
-
ACR >70 or PCR >100
-
haematuria present and ACR >30 or PCR >50
-
eGFR \< 30
-
-
urgent
- ACR ≥ 250 or PCR ≥ 300


New Zealand volcano: fatal eruption on White Island – video report
New Zealand’s Whakaari/White Island volcano erupted on Monday at 2.11pm as 50 people were visiting the country's most active volcano. At least five people were killed and authorities confirmed some people were still waiting to be rescued hours after the eruption on the island, as experts explained it was unsafe for emergency services to return • New Zealand volcano: five confirmed dead after eruption on White Island – live • White Island volcano eruption: dozens missing after fatal incident in New Zealand


In this project, you will use the tools you learned about in the lesson to identify lane lines on the road. You can develop your pipeline on a series of individual images, and later apply the result to a video stream (really just a series of images). Check out the video clip "raw-lines-example.mp4" (also contained in this repository) to see what the output should look like after using the helper functions below.
Once you have a result that looks roughly like "raw-lines-example.mp4", you'll need to get creative and try to average and/or extrapolate the line segments you've detected to map out the full extent of the lane lines. You can see an example of the result you're going for in the video "P1_example.mp4". Ultimately, you would like to draw just one line for the left side of the lane, and one for the right.
In addition to implementing code, there is a brief writeup to complete. The writeup should be completed in a separate file, which can be either a markdown file or a pdf document. There is a write up template that can be used to guide the writing process. Completing both the code in the Ipython notebook and the writeup template will cover all of the rubric points for this project.
Let's have a look at our first image called 'test_images/solidWhiteRight.jpg'. Run the 2 cells below (hit Shift-Enter or the "play" button above) to display the image.
Note: If, at any point, you encounter frozen display windows or other confounding issues, you can always start again with a clean slate by going to the "Kernel" menu above and selecting "Restart & Clear Output".
The tools you have are color selection, region of interest selection, grayscaling, Gaussian smoothing, Canny Edge Detection and Hough Tranform line detection. You are also free to explore and try other techniques that were not presented in the lesson. Your goal is piece together a pipeline to detect the line segments in the image, then average/extrapolate them and draw them onto the image for display (as below). Once you have a working pipeline, try it out on the video stream below.
Run the cell below to import some packages. If you get an import error
for a package you've already installed, try changing your kernel (select the Kernel menu above --> Change Kernel). Still have problems? Try relaunching Jupyter Notebook from the terminal prompt. Also, consult the forums for more troubleshooting tips.
#importing some useful packages
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
import cv2
%matplotlib inline
#reading in an image
image = mpimg.imread('test_images/solidWhiteRight.jpg')
#printing out some stats and plotting
print('This image is:', type(image), 'with dimensions:', image.shape)
plt.imshow(image) # if you wanted to show a single color channel image called 'gray', for example, call as plt.imshow(gray, cmap='gray')
Some OpenCV functions (beyond those introduced in the lesson) that might be useful for this project are:
cv2.inRange()
for color selection
cv2.fillPoly()
for regions selection
cv2.line()
to draw lines on an image given endpoints
cv2.addWeighted()
to coadd / overlay two images
cv2.cvtColor()
to grayscale or change color
cv2.imwrite()
to output images to file
cv2.bitwise_and()
to apply a mask to an image
Check out the OpenCV documentation to learn about these and discover even more awesome functionality!
Below are some helper functions to help get you started. They should look familiar from the lesson!
import math
def grayscale(img):
"""Applies the Grayscale transform
This will return an image with only one color channel
but NOTE: to see the returned image as grayscale
(assuming your grayscaled image is called 'gray')
you should call plt.imshow(gray, cmap='gray')"""
return cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# Or use BGR2GRAY if you read an image with cv2.imread()
# return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
def canny(img, low_threshold, high_threshold):
"""Applies the Canny transform"""
return cv2.Canny(img, low_threshold, high_threshold)
def gaussian_blur(img, kernel_size):
"""Applies a Gaussian Noise kernel"""
return cv2.GaussianBlur(img, (kernel_size, kernel_size), 0)
def region_of_interest(img, vertices):
"""
Applies an image mask.
Only keeps the region of the image defined by the polygon
formed from `vertices`. The rest of the image is set to black.
`vertices` should be a numpy array of integer points.
"""
#defining a blank mask to start with
mask = np.zeros_like(img)
#defining a 3 channel or 1 channel color to fill the mask with depending on the input image
if len(img.shape) > 2:
channel_count = img.shape[2] # i.e. 3 or 4 depending on your image
ignore_mask_color = (255,) * channel_count
else:
ignore_mask_color = 255
#filling pixels inside the polygon defined by "vertices" with the fill color
cv2.fillPoly(mask, vertices, ignore_mask_color)
#returning the image only where mask pixels are nonzero
masked_image = cv2.bitwise_and(img, mask)
return masked_image
def calc_line_x(y, slope, intercept):
"""Calculate x value given y, slope, intercept"""
return int((y - intercept)/slope)
def merge_lane_lines(lines, image_height, slope_thd=0.5):
"""
1. Separate lines to left, right lanes based on slope.
2. Calculated avg. slope, intercept weighted by line length
3. Calculate left, right lane end points based on above slopes, image height
"""
left_y_top = right_y_top = image_height
left_slope = right_slope = 0
left_intercept = right_intercept = 0
left_weight = right_weight = 0
for line in lines:
for x1,y1,x2,y2 in line:
if x2 == x1:
continue
m = (y2-y1)/(x2-x1)
c = y1 - m * x1
d = ((y2-y1)**2 + (x2-x1)**2)**0.5
# Update left, right lane weighted slopes, intercepts
if m > slope_thd:
# Left Lane Lines
left_slope = (left_slope * left_weight + m * d)/(left_weight + d)
left_intercept = (left_intercept * left_weight + c * d)/(left_weight + d)
left_weight += d
left_y_top = min(left_y_top, y1, y2)
elif m < -slope_thd:
# Right Lane Lines
right_slope = (right_slope * right_weight + m * d)/(right_weight + d)
right_intercept = (right_intercept * right_weight + c * d)/(right_weight + d)
right_weight += d
right_y_top = min(right_y_top, y1, y2)
# Use same value for left, right y top (lines are parallel!)
left_y_top = right_y_top = min(left_y_top, right_y_top)
# Calculate left, right lane end points
left_y_bottom = right_y_bottom = image_height
left_x_top = calc_line_x(left_y_top, slope=left_slope, intercept=left_intercept)
right_x_top = calc_line_x(right_y_top, slope=right_slope, intercept=right_intercept)
left_x_bottom = calc_line_x(left_y_bottom, slope=left_slope, intercept=left_intercept)
right_x_bottom = calc_line_x(right_y_bottom, slope=right_slope, intercept=right_intercept)
left_line_merged = np.array([[left_x_top, left_y_top, left_x_bottom, left_y_bottom]])
right_line_merged = np.array([[right_x_top, right_y_top, right_x_bottom, right_y_bottom]])
lines_merged = np.array([left_line_merged, right_line_merged])
return lines_merged
def draw_lines(img, lines, slope_thd=0.5, color=[255, 0, 0], thickness=2):
"""
NOTE: this is the function you might want to use as a starting point once you want to
average/extrapolate the line segments you detect to map out the full
extent of the lane (going from the result shown in raw-lines-example.mp4
to that shown in P1_example.mp4).
Think about things like separating line segments by their
slope ((y2-y1)/(x2-x1)) to decide which segments are part of the left
line vs. the right line. Then, you can average the position of each of
the lines and extrapolate to the top and bottom of the lane.
This function draws `lines` with `color` and `thickness`.
Lines are drawn on the image inplace (mutates the image).
If you want to make the lines semi-transparent, think about combining
this function with the weighted_img() function below
"""
try:
merged_lines = merge_lane_lines(lines, image_height=img.shape[0], slope_thd=slope_thd)
except (ZeroDivisionError, OverflowError) as e:
print("Error merging lines. Using raw lines instead!")
merged_lines = lines
for line in merged_lines:
for x1,y1,x2,y2 in line:
cv2.line(img, (x1, y1), (x2, y2), color, thickness)
def hough_lines(img, rho, theta, threshold, min_line_len, max_line_gap, slope_thd):
"""
`img` should be the output of a Canny transform.
Returns an image with hough lines drawn.
"""
lines = cv2.HoughLinesP(img, rho, theta, threshold, np.array([]), minLineLength=min_line_len, maxLineGap=max_line_gap)
line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
draw_lines(line_img, lines, slope_thd=slope_thd, thickness=10)
return line_img, lines
# Python 3 has support for cool math symbols.
def weighted_img(img, initial_img, α=0.8, β=1., γ=0.):
"""
`img` is the output of the hough_lines(), An image with lines drawn on it.
Should be a blank image (all black) with lines drawn on it.
`initial_img` should be the image before any processing.
The result image is computed as follows:
initial_img * α + img * β + γ
NOTE: initial_img and img must be the same shape!
"""
return cv2.addWeighted(initial_img, α, img, β, γ)
Build your pipeline to work on the images in the directory "test_images"
You should make sure your pipeline works well on these images before you try the videos.
import glob
test_images_paths = glob.glob('test_images/*.jpg')
print(test_images_paths)
Build the pipeline and run your solution on all test_images. Make copies into the test_images_output
directory, and you can use the images in your writeup report.
Try tuning the various parameters, especially the low and high Canny thresholds as well as the Hough lines parameters.
import time
# Tuning Parameters
gauss_kernel_size = 5
canny_low_thd = 50
canny_high_thd = 180
v_x0 = 0.1
v_x1a = 0.472
v_x1b = 0.528
v_y1 = 0.597
v_x2 = 0.94
hough_rho = 1
hough_theta = 2.8/180.
hough_thd = 15
hough_min_len = 30
hough_max_gap = 30
slope_thd = 0.45
for image_fpath in test_images_paths:
image = cv2.imread(image_fpath)
# Get Grayscale Image
img_gray = grayscale(img=image)
# Replace Yellow pixels with white in gray scale image
mask_yellow = cv2.inRange(image, (128, 128, 0), (255, 255, 90))
img_gray = cv2.bitwise_or(img_gray, mask_yellow)
# Use Gaussian Blur to filter image and remove noise
blur_gray = gaussian_blur(img=img_gray, kernel_size=gauss_kernel_size)
# Get Edges using "CANNY EDGE DETECTION"
edges = canny(img=blur_gray, low_threshold=canny_low_thd, high_threshold=canny_high_thd)
# Mask Image Using Polygon
h = edges.shape[0]
w = edges.shape[1]
vertices = np.array([[(v_x0*w, h), (v_x1a*w, v_y1*h),
(v_x1b*w, v_y1*h), (v_x2*w, h)]],
dtype=np.int32)
masked_edges = region_of_interest(img=edges, vertices=vertices)
# Get Raw Lane Lines using "HOUGH LINES"
lines_image, lines = hough_lines(img=masked_edges, rho=hough_rho, theta=hough_theta, threshold=hough_thd,
min_line_len=hough_min_len, max_line_gap=hough_max_gap, slope_thd=slope_thd)
# Overlay Images
combined_img = weighted_img(img=lines_image, initial_img=image, α=1.0, β=0.9)
plt.figure()
plt.imshow(combined_img)
You know what's cooler than drawing lanes over images? Drawing lanes over video!
We can test our solution on two provided videos:
solidWhiteRight.mp4
solidYellowLeft.mp4
Note: if you get an import error when you run the next cell, try changing your kernel (select the Kernel menu above --> Change Kernel). Still have problems? Try relaunching Jupyter Notebook from the terminal prompt. Also, consult the forums for more troubleshooting tips.
If you get an error that looks like this:
NeedDownloadError: Need ffmpeg exe.
You can download it by calling:
imageio.plugins.ffmpeg.download()
Follow the instructions in the error message and check out this forum post for more troubleshooting tips across operating systems.
# Import everything needed to edit/save/watch video clips
from moviepy.editor import VideoFileClip
from IPython.display import HTML
def process_image(image):
# Tuning Parameters
gauss_kernel_size = 5
canny_low_thd = 50
canny_high_thd = 180
v_x0 = 0.1
v_x1a = 0.472
v_x1b = 0.528
v_y1 = 0.597
v_x2 = 0.94
hough_rho = 1
hough_theta = 2.8/180.
hough_thd = 15
hough_min_len = 30
hough_max_gap = 30
slope_thd = 0.45
# Get Grayscale Image
img_gray = grayscale(img=image)
# Replace Yellow pixels with white in gray scale image
mask_yellow = cv2.inRange(image, (128, 128, 0), (255, 255, 90))
img_gray = cv2.bitwise_or(img_gray, mask_yellow)
# Use Gaussian Blur to filter image and remove noise
blur_gray = gaussian_blur(img=img_gray, kernel_size=gauss_kernel_size)
# Get Edges using "CANNY EDGE DETECTION"
edges = canny(img=blur_gray, low_threshold=canny_low_thd, high_threshold=canny_high_thd)
# Mask Image Using Polygon
h = edges.shape[0]
w = edges.shape[1]
vertices = np.array([[(v_x0*w, h), (v_x1a*w, v_y1*h),
(v_x1b*w, v_y1*h), (v_x2*w, h)]],
dtype=np.int32)
masked_edges = region_of_interest(img=edges, vertices=vertices)
# Get Raw Lane Lines using "HOUGH LINES"
lines_image, lines = hough_lines(img=masked_edges, rho=hough_rho, theta=hough_theta, threshold=hough_thd,
min_line_len=hough_min_len, max_line_gap=hough_max_gap, slope_thd=slope_thd)
# Overlay Images
combined_img = weighted_img(img=lines_image, initial_img=image, α=1.0, β=0.9)
return combined_img
Let's try the one with the solid white lane on the right first ...
white_output = 'test_videos_output/solidWhiteRight.mp4'
## To speed up the testing process you may want to try your pipeline on a shorter subclip of the video
## To do so add .subclip(start_second,end_second) to the end of the line below
## Where start_second and end_second are integer values representing the start and end of the subclip
## You may also uncomment the following line for a subclip of the first 5 seconds
##clip1 = VideoFileClip("test_videos/solidWhiteRight.mp4").subclip(0,5)
clip1 = VideoFileClip("test_videos/solidWhiteRight.mp4")
white_clip = clip1.fl_image(process_image) #NOTE: this function expects color images!!
%time white_clip.write_videofile(white_output, audio=False)
Play the video inline, or if you prefer find the video in your filesystem (should be in the same directory) and play it in your video player of choice.
HTML("""
<video width="960" height="540" controls>
<source src="{0}">
</video>
""".format(white_output))
At this point, if you were successful with making the pipeline and tuning parameters, you probably have the Hough line segments drawn onto the road, but what about identifying the full extent of the lane and marking it clearly as in the example video (P1_example.mp4)? Think about defining a line to run the full length of the visible lane based on the line segments you identified with the Hough Transform. As mentioned previously, try to average and/or extrapolate the line segments you've detected to map out the full extent of the lane lines. You can see an example of the result you're going for in the video "P1_example.mp4".
 The new output should draw a single, solid line over the left lane line and a single, solid line over the right lane line. The lines should start from the bottom of the image and extend out to the top of the region of interest.
Now for the one with the solid yellow lane on the left. This one's more tricky!
yellow_output = 'test_videos_output/solidYellowLeft.mp4'
## To speed up the testing process you may want to try your pipeline on a shorter subclip of the video
## To do so add .subclip(start_second,end_second) to the end of the line below
## Where start_second and end_second are integer values representing the start and end of the subclip
## You may also uncomment the following line for a subclip of the first 5 seconds
##clip2 = VideoFileClip('test_videos/solidYellowLeft.mp4').subclip(0,5)
clip2 = VideoFileClip('test_videos/solidYellowLeft.mp4')
yellow_clip = clip2.fl_image(process_image)
%time yellow_clip.write_videofile(yellow_output, audio=False)
HTML("""
<video width="960" height="540" controls>
<source src="{0}">
</video>
""".format(yellow_output))
Try your lane finding pipeline on the video below. Does it still work? Can you figure out a way to make it more robust? If you're up for the challenge, modify your pipeline so it works with this video and submit it along with the rest of your project!
challenge_output = 'test_videos_output/challenge.mp4'
## To speed up the testing process you may want to try your pipeline on a shorter subclip of the video
## To do so add .subclip(start_second,end_second) to the end of the line below
## Where start_second and end_second are integer values representing the start and end of the subclip
## You may also uncomment the following line for a subclip of the first 5 seconds
##clip3 = VideoFileClip('test_videos/challenge.mp4').subclip(0,5)
clip3 = VideoFileClip('test_videos/challenge.mp4')
challenge_clip = clip3.fl_image(process_image)
%time challenge_clip.write_videofile(challenge_output, audio=False)
HTML("""
<video width="960" height="540" controls>
<source src="{0}">
</video>
""".format(challenge_output))
# image = clip3.get_frame(115./25.)
# # # image = clip2.get_frame(42./25.)
# plt.imshow(process_image(image))
# # Tuning Parameters
# gauss_kernel_size = 5
# canny_low_thd = 50
# canny_high_thd = 150
# v_x0 = 0.1
# v_x1a = 0.472
# v_x1b = 0.528
# v_y1 = 0.597
# v_x2 = 0.94
# hough_rho = 1
# hough_theta = 2.8/180.
# hough_thd = 15
# hough_min_len = 30
# hough_max_gap = 30
# slope_thd = 0.45
# # Get Grayscale Image
# img_gray = grayscale(img=image)
# plt.imshow(img_gray, cmap='gray')
# mask_yellow = cv2.inRange(image, (128, 128, 0), (255, 255, 90))
# img_gray = cv2.bitwise_or(img_gray, mask_yellow)
# plt.figure()
# plt.imshow(img_gray, cmap='gray')
# # Use Gaussian Blur to filter image and remove noise
# blur_gray = gaussian_blur(img=img_gray, kernel_size=gauss_kernel_size)
# # Get Edges using "CANNY EDGE DETECTION"
# edges = canny(img=blur_gray, low_threshold=canny_low_thd, high_threshold=canny_high_thd)
# plt.figure()
# plt.imshow(edges, cmap='gray')
# # Mask Image Using Polygon
# h = edges.shape[0]
# w = edges.shape[1]
# vertices = np.array([[(v_x0*w, h), (v_x1a*w, v_y1*h),
# (v_x1b*w, v_y1*h), (v_x2*w, h)]],
# dtype=np.int32)
# masked_edges = region_of_interest(img=edges, vertices=vertices)
# plt.figure()
# plt.imshow(masked_edges, cmap='gray')
# # Get Raw Lane Lines using "HOUGH LINES"
# lines_image, lines = hough_lines(img=masked_edges, rho=hough_rho, theta=hough_theta, threshold=hough_thd,
# min_line_len=hough_min_len, max_line_gap=hough_max_gap, slope_thd=slope_thd)
# # Overlay Images
# combined_img = weighted_img(img=lines_image, initial_img=image, α=1.0, β=0.9)
# plt.figure()
# plt.imshow(combined_img)


8 April, 2018
Edward GarganGift-giving between foreign diplomats and the U.S. president and white house staff is a surprisingly common event. The gifts that are 'formally' received are officially documented by the U.S. Department of State's 'Gift Protocol Unit', whose records currently span 2002 to 2016. From the state department's website,
"The Gift Office within the Office of the Chief of Protocol receives all diplomatic gifts on behalf of White House and Department of State officials and maintains records pertaining to diplomatic gifts. This includes all diplomatic gifts received by the President, Vice President, and Secretary of State and their spouses"
Above is my attempt at visualising the data that the Gift Office have recorded over the years. The visualisation is a sort-of Sankey diagram, whose connections represent how generous other countries have been in their visits to the US. The bottom row of the diagram represents the countries -- click on one to see a detailed timeline of that country's gift-liness!


MIGHTY JOE YOUNG
Reviewed by Harvey Karten, Ph.D. Disney Pictures Director: Ron Underwood Writer: Mark Rosenthal & Lawrence Konner Cast: Bill Paxton, Charlize Theron, Regina King, David Paymer, Rade Serbedzija, Naveen Andrews
The title character of "Mighty Joe Young," who could serve as a model for a Rogaine commercial, is handsomer than the Frankenstein monster and is anthropomorphically a more sympathetic individual than Godzilla. With his deep set, expressive brown eyes and tendency to communicate his feelings with them in much way a human being can, Joe is a gorilla that the small fry can care about. It should not be at all surprising that the kids in the audience would scream when poachers threaten to put down the 2,000 pound simian or when the L.A.P.D., in the touchstone standoff with creatures far larger than they, take careful aim with their rifles awaiting the order from the chief to blast off. An update of Ernest B. Schoedsack's 1949 movie , which starred Terry Moore, Ben Johnson and Robert Armstrong--which in turn updated the "King Kong" theme and had comparable special effects--"Mighty Joe Young" suffers from the same agonizing dialogue and repetitive actions. Each time Joe is about to bounce off walls, crush cars, and down the Ferris wheel, he is brought to heel by the soothing voice of the woman who reared him from the time she was 12, after both her mom and Joe's were killed by poachers. Since these counseling sessions occur quite often in the story--in a Los Angeles amusement part, in a metal cage zooming down the California freeways, inside a California conservatory and in the Botswana bush--the recapitulation can become annoying. But since Joe's stepmom, Jill (Charlize Theron) is the most beautiful actress that Hollywood now boasts--even better looking than the stepmom who has eyes for Ed Harris--you don't mind the redundancies too much.
A thoroughly predictable fable that uses convincing computer graphics and other special effects to portray the title figure, "Mighty Joe Young" opens in Botswana where Dr. Ruth Young (Linda Purl), a Dian Fossey type who is studying gorilla protocol while caring for her 12-year-old daughter Jill (Mika Boorem), is killed while trying to protect her hirsute buddies from the unlawful activities of poachers. Having evoked a promise from her daughter to carry on the work, she leaves Jill to tend to the clan, and, twelve years later, the 24-year-old woman (Charlize Theron) realizes that she can no longer protect her large monkeys from the illegal activities of the hunters. Convinced by eccentric researcher Greg O'Hara (Bill Paxton) to transport Joe to a California conservatory, she allows Joe to be captured and travels with him and Greg to the states. While in the conservatory, Joe is confronted by poachers Strasser (Rade Sherbedgia) and his accomplice Garth (Peter Firth), the former seeking revenge for losing his thumb to Joe's attack twelve years earlier. Provoked by his old enemy, Joe Young escapes and creates havoc in Hollywood, alternately hiding from helicopters and seeking out a child to save from a collapsing Ferris Wheel.
Bill Paxton is just boyish enough to appeal to the young ones in the audience and while he does not get a chance to act maturely as he did so well in "A Simple Plan," he is just right for the part of a guy who plants the first human kiss on Jill's lips. We even root for him win Jill's affections in the triangular romance plot but at the same time hope that Joe does not get overly jealous that his role is about to be taken over by a human. Director Ron Underwood may have forgone the wit that allowed him to make "Tremors" more than a silly story of worm-like predators in the desert out for Kevin Bacon's hide. But while there are many reasons to be depressed during the holiday season, "Mighty Joe Young" is not one of them.
Rated PG. Running Time: 115 minutes. (C) 1998 Harvey Karten
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Activities
Learn more about our different activities here. Once you are ready, navigate to Create My Day and add the activities in your own calendar.
All categories
View
Edit


FoodFrenzy
Hello, {{customer.name}}
Your Profile
Order History
Order Cart
Log out
About
{{restaurant.name}}
{{restaurant.address}}, {{restaurant.city}}
{{restaurant.phone}}
10:00 - 22:00
{{restaurant.cuisine}}
Distance from your address (km): {{distance}}
{% csrf_token %}
{% if fooditems %} {% for x,y in fooditems.items %}
Cuisine: {{x}}
Item Name
Price
Add Item
{%for z in y %}
{% with z.pk|stringformat:"s" as zstr %} {% with "food"|add:zstr as foodid %} {% with "numfood"|add:zstr as foodipid %}
{{z.name}}
{{z.price}}
0
{% endwith %} {% endwith %} {% endwith %}
{%endfor%}
{%endfor%} {%endif%}
Cart
Total : ₹
0
Add to cart
{% csrf_token %}
Give Rating & Review
★
★
★
★
★
★
★
★
★
★
★
★
★
★
★
Write your Review here
Submit Rating


#Utrum Web Portal Teaser. Coming soon. Stay Tuned.. Introducing first of it's kind community business model where we share platform revenues with our members through content rewards. 70% to Contributors & 30% to voters. $KMD $OOT #OOT @KomodoPlatform @Chainzillaio @marmar...


@(
data: io.flow.dependency.www.lib.UiData,
organization: io.flow.dependency.v0.models.Organization,
projects: io.flow.play.util.PaginatedCollection[io.flow.dependency.v0.models.Project]
)(implicit flash: Flash, messages: Messages)
@main(data.copy(title = Some(organization.key))) {
Members
@if(io.flow.dependency.www.lib.Permissions.Organization.edit(organization, data.user)) {
|
Edit
}
@if(io.flow.dependency.www.lib.Permissions.Organization.delete(organization, data.user)) {
|
Delete
}
Projects
@if(projects.isEmpty) {
This organization does not have any projects
} else {
@views.html.projects.table(projects.items)
@if(projects.hasPrevious || projects.hasNext) {
}
}
}


European Central Bank reserve maintenance dates.
More...
#include <ql/time/ecb.hpp>
European Central Bank reserve maintenance dates.
Definition at line 34 of file ecb.hpp.
◆ knownDates()
| const std::set< Date > & knownDates |
( |
| ) |
|
|
static |
◆ addDate()
| void addDate |
( |
const Date & |
d | ) |
|
|
static |
◆ removeDate()
| void removeDate |
( |
const Date & |
d | ) |
|
|
static |
◆ date() [1/2]
maintenance period start date in the given month/year
Definition at line 41 of file ecb.hpp.
◆ date() [2/2]
| Date date |
( |
const std::string & |
ecbCode, |
|
|
const Date & |
referenceDate = Date() |
|
) |
| |
|
static |
returns the ECB date for the given ECB code (e.g. March xxth, 2013 for MAR10).
- Warning:
- It raises an exception if the input string is not an ECB code
Definition at line 77 of file ecb.cpp.
◆ code()
| string code |
( |
const Date & |
ecbDate | ) |
|
|
static |
returns the ECB code for the given date (e.g. MAR10 for March xxth, 2010).
- Warning:
- It raises an exception if the input date is not an ECB date
Definition at line 112 of file ecb.cpp.
◆ nextDate() [1/2]
next maintenance period start date following the given date
Definition at line 173 of file ecb.cpp.
◆ nextDate() [2/2]
| static Date nextDate |
( |
const std::string & |
ecbCode, |
|
|
const Date & |
referenceDate = Date() |
|
) |
| |
|
static |
next maintenance period start date following the given ECB code
Definition at line 65 of file ecb.hpp.
◆ nextDates() [1/2]
next maintenance period start dates following the given date
Definition at line 185 of file ecb.cpp.
◆ nextDates() [2/2]
| static std::vector< Date > nextDates |
( |
const std::string & |
ecbCode, |
|
|
const Date & |
referenceDate = Date() |
|
) |
| |
|
static |
next maintenance period start dates following the given code
Definition at line 74 of file ecb.hpp.
◆ isECBdate()
returns whether or not the given date is a maintenance period start date
Definition at line 81 of file ecb.hpp.
◆ isECBcode()
| bool isECBcode |
( |
const std::string & |
in | ) |
|
|
static |
returns whether or not the given string is an ECB code
Definition at line 198 of file ecb.cpp.
◆ nextCode() [1/2]
| static std::string nextCode |
( |
const Date & |
d = Date() | ) |
|
|
static |
next ECB code following the given date
Definition at line 90 of file ecb.hpp.
◆ nextCode() [2/2]
| string nextCode |
( |
const std::string & |
ecbCode | ) |
|
|
static |
next ECB code following the given code
Definition at line 229 of file ecb.cpp.


|
Xerces-C++
3.1.4
|
This class defines that core information that defines an XML entity, no matter what validator is used. More...
Public Member Functions | |
| void | setId (const XMLSize_t newId) |
Constructors | |
| XMLEntityDecl (MemoryManager *const manager=XMLPlatformUtils::fgMemoryManager) | |
| Default Constructor. More... | |
| XMLEntityDecl (const XMLCh *const entName, MemoryManager *const manager=XMLPlatformUtils::fgMemoryManager) | |
| Constructor with a const entity name. More... | |
| XMLEntityDecl (const XMLCh *const entName, const XMLCh *const value, MemoryManager *const manager=XMLPlatformUtils::fgMemoryManager) | |
| Constructor with a const entity name and value. More... | |
| XMLEntityDecl (const XMLCh *const entName, const XMLCh value, MemoryManager *const manager=XMLPlatformUtils::fgMemoryManager) | |
| Constructor with a const entity name and single XMLCh value. More... | |
Destructor | |
| virtual | ~XMLEntityDecl () |
| Default destructor. More... | |
The pure virtual methods in this interface. | |
| virtual bool | getDeclaredInIntSubset () const =0 |
| Get the 'declared in internal subset' flag. More... | |
| virtual bool | getIsParameter () const =0 |
| Get the 'is parameter entity' flag. More... | |
| virtual bool | getIsSpecialChar () const =0 |
| Get the 'is special char entity' flag. More... | |
Getter methods | |
| XMLSize_t | getId () const |
| Gets the pool id of this entity. More... | |
| const XMLCh * | getName () const |
| Returns a const pointer to the name of this entity decl. More... | |
| const XMLCh * | getNotationName () const |
| Gets the notation name, if any, declared for this entity. More... | |
| const XMLCh * | getPublicId () const |
| Gets the public id declared for this entity. More... | |
| const XMLCh * | getSystemId () const |
| Gets the system id declared for this entity. More... | |
| const XMLCh * | getBaseURI () const |
| Gets the base URI for this entity. More... | |
| const XMLCh * | getValue () const |
| This method returns the value of an internal entity. More... | |
| XMLSize_t | getValueLen () const |
| This method returns the number of characters in the value returned by getValue(). More... | |
| bool | isExternal () const |
| Indicates that this entity is an external entity. More... | |
| bool | isUnparsed () const |
| Indicates whether this entity is unparsed. More... | |
| MemoryManager * | getMemoryManager () const |
| Get the plugged-in memory manager. More... | |
Setter methods | |
| void | setName (const XMLCh *const entName) |
| This method will set the entity name. More... | |
| void | setIsExternal (bool value) |
| This method will mark whether the entity is external. More... | |
| void | setNotationName (const XMLCh *const newName) |
| This method will set the notation name for this entity. More... | |
| void | setPublicId (const XMLCh *const newId) |
| This method will set a new public id on this entity. More... | |
| void | setSystemId (const XMLCh *const newId) |
| This method will set a new sysetm id on this entity. More... | |
| void | setBaseURI (const XMLCh *const newId) |
| This method will set a new baseURI on this entity. More... | |
| void | setValue (const XMLCh *const newValue) |
| This method will set a new value for this entity. More... | |
| const XMLCh * | getKey () const |
| This method allows objects of this class to be used within a standard keyed collection used commonly within the parser system. More... | |
| Public Member Functions inherited from XMemory | |
| void * | operator new (size_t size) |
| This method overrides operator new. More... | |
| void * | operator new (size_t size, MemoryManager *memMgr) |
| This method defines a custom operator new, that will use the provided memory manager to perform the allocation. More... | |
| void * | operator new (size_t size, void *ptr) |
| This method overrides placement operator new. More... | |
| void | operator delete (void *p) |
| This method overrides operator delete. More... | |
| void | operator delete (void *p, MemoryManager *memMgr) |
| This method provides a matching delete for the custom operator new. More... | |
| void | operator delete (void *p, void *ptr) |
| This method provides a matching delete for the placement new. More... | |
Additional Inherited Members | |
| Protected Member Functions inherited from XMemory | |
| XMemory () | |
| Protected default constructor. More... | |
This class defines that core information that defines an XML entity, no matter what validator is used.
Each validator will create a derivative of this class which adds any extra information it requires.
This class supports keyed collection semantics via the getKey() method which extracts the key field, the entity name in this case. The name will have whatever form is deemed appropriate for the type of validator in use.
When setting the fields of this class, you must make sure that you do not set conflicting values. For instance, an internal entity cannot have a notation name. And an external entity cannot have a value string. These rules are defined by the XML specification. In most cases, these objects are created by validator objects as they parse a DTD or Schema or whatever, at which time they confirm the correctness of the data before creating the entity decl object.
| XMLEntityDecl::XMLEntityDecl | ( | MemoryManager *const | manager = XMLPlatformUtils::fgMemoryManager | ) |
Default Constructor.
| XMLEntityDecl::XMLEntityDecl | ( | const XMLCh *const | entName, |
| MemoryManager *const | manager = XMLPlatformUtils::fgMemoryManager |
||
| ) |
Constructor with a const entity name.
| entName | The new name to give to this entity. |
| manager | Pointer to the memory manager to be used to allocate objects. |
| XMLEntityDecl::XMLEntityDecl | ( | const XMLCh *const | entName, |
| const XMLCh *const | value, | ||
| MemoryManager *const | manager = XMLPlatformUtils::fgMemoryManager |
||
| ) |
Constructor with a const entity name and value.
| entName | The new name to give to this entity. |
| value | The new value to give to this entity name. |
| manager | Pointer to the memory manager to be used to allocate objects. |
| XMLEntityDecl::XMLEntityDecl | ( | const XMLCh *const | entName, |
| const XMLCh | value, | ||
| MemoryManager *const | manager = XMLPlatformUtils::fgMemoryManager |
||
| ) |
Constructor with a const entity name and single XMLCh value.
| entName | The new name to give to this entity. |
| value | The new value to give to this entity name. |
| manager | Pointer to the memory manager to be used to allocate objects. |
|
virtual |
Default destructor.
| const XMLCh * XMLEntityDecl::getBaseURI | ( | ) | const |
Gets the base URI for this entity.
|
pure virtual |
Get the 'declared in internal subset' flag.
Gets the state of the flag which indicates whether the entity was declared in the internal or external subset. Some structural description languages might not have an internal subset concept, in which case this will always return false.
| XMLSize_t XMLEntityDecl::getId | ( | ) | const |
Gets the pool id of this entity.
Validators maintain all decls in pools, from which they can be quickly extracted via id.
|
pure virtual |
Get the 'is parameter entity' flag.
Gets the state of the flag which indicates whether this entity is a parameter entity. If not, then its a general entity.
|
pure virtual |
Get the 'is special char entity' flag.
Gets the state of the flag that indicates whether this entity is one of the special, intrinsically supported character entities.
| const XMLCh * XMLEntityDecl::getKey | ( | ) | const |
This method allows objects of this class to be used within a standard keyed collection used commonly within the parser system.
The collection calls this method to get the key (usually to hash it) by which the object is to be stored.
| MemoryManager * XMLEntityDecl::getMemoryManager | ( | ) | const |
Get the plugged-in memory manager.
This method returns the plugged-in memory manager user for dynamic memory allocation/deallocation.
| const XMLCh * XMLEntityDecl::getName | ( | ) | const |
Returns a const pointer to the name of this entity decl.
This name will be in whatever format is appropriate for the type of validator in use.
| const XMLCh * XMLEntityDecl::getNotationName | ( | ) | const |
Gets the notation name, if any, declared for this entity.
If this entity is not a notation type entity, it will be a null pointer.
| const XMLCh * XMLEntityDecl::getPublicId | ( | ) | const |
Gets the public id declared for this entity.
Public ids are optional so it can be a null pointer.
| const XMLCh * XMLEntityDecl::getSystemId | ( | ) | const |
Gets the system id declared for this entity.
The system id is required so this method should never return a null pointers.
| const XMLCh * XMLEntityDecl::getValue | ( | ) | const |
This method returns the value of an internal entity.
If this is not an internal entity (i.e. its external), then this will be a null pointer.
| XMLSize_t XMLEntityDecl::getValueLen | ( | ) | const |
This method returns the number of characters in the value returned by getValue().
If this entity is external, this will be zero since an external entity has no internal value.
| bool XMLEntityDecl::isExternal | ( | ) | const |
Indicates that this entity is an external entity.
If not, then it is assumed to be an internal entity, surprise.
| bool XMLEntityDecl::isUnparsed | ( | ) | const |
Indicates whether this entity is unparsed.
This is meaningless for internal entities. Some external entities are unparsed in that they refer to something other than XML source.
| void XMLEntityDecl::setBaseURI | ( | const XMLCh *const | newId | ) |
This method will set a new baseURI on this entity.
This will then control the URI used to resolve the relative system Id.
| newId | The new base URI to give to the entity. |
References MemoryManager::deallocate(), and XMLString::replicate().
| void XMLEntityDecl::setId | ( | const XMLSize_t | newId | ) |
| void XMLEntityDecl::setIsExternal | ( | bool | value | ) |
This method will mark whether the entity is external.
| value | The new value for the 'is external' flag. |
| void XMLEntityDecl::setName | ( | const XMLCh *const | entName | ) |
This method will set the entity name.
The format of this name is defined by the particular validator in use, since it will be the one who creates entity definitions as it parses the DTD, Schema, ect...
| entName | The new name to give to this entity. |
| void XMLEntityDecl::setNotationName | ( | const XMLCh *const | newName | ) |
This method will set the notation name for this entity.
By setting this, you are indicating that this is an unparsed external entity.
| newName | The new notation name to give to this entity. |
References MemoryManager::deallocate(), and XMLString::replicate().
| void XMLEntityDecl::setPublicId | ( | const XMLCh *const | newId | ) |
This method will set a new public id on this entity.
The public id has no particular form and is purely for client consumption.
| newId | The new public id to give to this entity. |
References MemoryManager::deallocate(), and XMLString::replicate().
| void XMLEntityDecl::setSystemId | ( | const XMLCh *const | newId | ) |
This method will set a new sysetm id on this entity.
This will then control where the source for this entity lives. If it is an internal entity, then the system id is only for bookkeeping purposes, and to allow any external entities referenced from within the entity to be correctly resolved.
| newId | The new system id to give to the entity. |
References MemoryManager::deallocate(), and XMLString::replicate().
| void XMLEntityDecl::setValue | ( | const XMLCh *const | newValue | ) |
This method will set a new value for this entity.
This is only valid if the entity is to be an internal entity. By setting this field, you are indicating that the entity is internal.
| newValue | The new value to give to this entity. |
References MemoryManager::deallocate(), XMLString::replicate(), and XMLString::stringLen().


Regional Center Partnership business meetings are open to the public and are held in the County Engineering Conference Room located on the second floor of the County Administration Building at 20 Grove Street in Somerville, unless noted otherwise. Free parking is available in the parking deck off East High Street behind the County Administration Building. All meetings begin at 5:00 P.M. on the third Wednesday of the month, unless noted otherwise and are held every other month beginning in January. Changes to meeting dates, times, or locations will be posted on this website.
Meetings will be held bi-monthly. Additional meetings may be scheduled as warranted. When needed, these additional meetings will be scheduled on the third Wednesday of the month at 5:00 PM unless noted otherwise. Meeting reminders will be sent out in which any changes or special meetings will be identified.
The 11th Annual Kite Flying Festival
County Administration Building, 20 Grove Street, 08876
Sunset Hiking Trail Marathon
County Administration Building, 20 Grove Street, 08876
The Annual Summer Hill Climb ‘19
County Administration Building, 20 Grove Street, 08876