Ver código fonte

Began re-writing project in scala. Got UI for loading food data from ndb.

Tom Flucke 8 anos atrás
commit
95f0974865

+ 9 - 0
.gitignore

@@ -0,0 +1,9 @@
+logs
+target
+/.idea
+/.idea_modules
+/.classpath
+/.project
+/.settings
+/RUNNING_PID
+**/#*#

+ 12 - 0
app/controllers/FoodController.scala

@@ -0,0 +1,12 @@
+package controllers
+
+import models.Food
+import play.api.mvc._
+
+object FoodController extends Controller {
+  def put(id: Int): Result = null
+  def update(): Result = null
+  def get(id: Int): Result = null
+  def query(query: String = ""): Result = null
+  def delete(query: String = ""): Result = null
+}

+ 24 - 0
app/controllers/HomeController.scala

@@ -0,0 +1,24 @@
+package controllers
+
+import javax.inject._
+import play.api._
+import play.api.mvc._
+
+/**
+ * This controller creates an `Action` to handle HTTP requests to the
+ * application's home page.
+ */
+@Singleton
+class HomeController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {
+
+  /**
+   * Create an Action to render an HTML page.
+   *
+   * The configuration in the `routes` file means that this method
+   * will be called when the application receives a `GET` request with
+   * a path of `/`.
+   */
+  def index() = Action { implicit request: Request[AnyContent] =>
+    Ok(views.html.index())
+  }
+}

+ 24 - 0
app/controllers/IngredientController.scala

@@ -0,0 +1,24 @@
+package controllers
+
+import javax.inject._
+import play.api._
+import play.api.mvc._
+
+/**
+ * This controller creates an `Action` to handle HTTP requests to the
+ * application's home page.
+ */
+@Singleton
+class IngredientController @Inject() (cc: ControllerComponents) extends AbstractController(cc) {
+
+  /**
+   * Create an Action to render an HTML page.
+   *
+   * The configuration in the `routes` file means that this method
+   * will be called when the application receives a `GET` request with
+   * a path of `/`.
+   */
+  def editorPage() = Action { implicit request: Request[AnyContent] =>
+    Ok(views.html.ingredients())
+  }
+}

+ 17 - 0
app/models/Food.scala

@@ -0,0 +1,17 @@
+package models
+
+class Food(
+  name: String,
+  glutenFree: Boolean = false,
+  vegitarian: Boolean = false,
+  vegan: Boolean = false,
+  nutrients: Map[Nutrient, Float],
+  source: String,
+  catagory: Seq[String],
+  primaryMeasure: Measure,
+  density: Float,
+  mass_p_u: Float,
+  price: Int,
+  alternatives: Seq[Food] = Nil
+  //picture: Image
+)

+ 22 - 0
app/models/Measure.scala

@@ -0,0 +1,22 @@
+package models
+
+sealed trait Measure {
+  def productPrefix: String
+  def name: String = this.productPrefix.split("\\.").last
+  def primaryUnit: Unit
+}
+
+case object Mass extends Measure
+{
+  val primaryUnit = Gram
+}
+
+case object Volume extends Measure
+{
+  val primaryUnit = Liter
+}
+
+case object Number extends Measure
+{
+  val primaryUnit = Count
+}

+ 3 - 0
app/models/Nutrient.scala

@@ -0,0 +1,3 @@
+package models
+
+class Nutrient(id: Int, name: String, unit: Unit, description: String)

+ 97 - 0
app/models/Unit.scala

@@ -0,0 +1,97 @@
+package models
+
+class ConversionException(from: Unit, to: Unit) extends
+    RuntimeException(s"Cannot convert from $from to $to.", null)
+
+sealed trait Unit {
+  def productPrefix: String
+  protected def conversion: Float
+  def name: String = this.productPrefix.split("\\.").last
+  protected def abreviation: String
+  def measure: Measure
+  def convertToPrimary(v: Float): Float = v/conversion
+  def convertFromPrimary(v: Float): Float = v*conversion
+  def convertTo(v: Float, other: Unit) = {
+    if (measure == other.measure)
+      other.convertFromPrimary(convertToPrimary(v))
+    else
+      throw new ConversionException(this, other)
+  }
+}
+
+case object Gram extends Unit
+{
+  protected val conversion: Float = 1f
+  val measure: Measure = Mass
+  val abreviation: String = "g"
+}
+
+case object Kilogram extends Unit
+{
+  protected val conversion: Float = 0.001f
+  val measure: Measure = Mass
+  val abreviation: String = "kg"
+}
+
+case object Ounce extends Unit
+{
+  protected val conversion: Float = 0.03527396f
+  val measure: Measure = Mass
+  val abreviation: String = "oz"
+}
+
+case object Pound extends Unit
+{
+  protected val conversion: Float = 0.00220462249993f
+  val measure: Measure = Mass
+  val abreviation: String = "lb"
+}
+
+case object Mililiter extends Unit
+{
+  protected val conversion: Float = 1000f
+  val measure: Measure = Volume
+  val abreviation: String = "mL"
+}
+
+case object Liter extends Unit
+{
+  protected val conversion: Float = 1f
+  val measure: Measure = Volume
+  val abreviation: String = "L"
+}
+
+case object Teaspoon extends Unit
+{
+  protected val conversion: Float = 202.8842218965f
+  val measure: Measure = Volume
+  val abreviation: String = "tsp"
+}
+
+case object Tablespoon extends Unit
+{
+  protected val conversion: Float = 67.627891024f
+  val measure: Measure = Volume
+  val abreviation: String = "Tbsp"
+}
+
+case object Cup extends Unit
+{
+  protected val conversion: Float = 4.166666f
+  val measure: Measure = Volume
+  val abreviation: String = "cups"
+}
+
+case object Gallon extends Unit
+{
+  protected val conversion: Float = 0.26417290088f
+  val measure: Measure = Volume
+  val abreviation: String = "gallons"
+}
+
+case object Count extends Unit
+{
+  protected val conversion: Float = 1f
+  val measure: Measure = Number
+  val abreviation: String = ""
+}

+ 5 - 0
app/views/index.scala.html

@@ -0,0 +1,5 @@
+@()
+
+@main("Welcome to Play") {
+  <h1>Welcome to Play!</h1>
+}

+ 99 - 0
app/views/ingredients.scala.html

@@ -0,0 +1,99 @@
+@*
+ * This template is called from the `index` template. This template
+ * handles the rendering of the page header and body tags. It takes
+ * two arguments, a `String` for the title of the page and an `Html`
+ * object to insert into the body of the page.
+ *@
+@()
+
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <title>Ingredients</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <link rel="stylesheet" type="text/css" href="@routes.Assets.versioned("stylesheets/main.css")" />
+    <link rel="stylesheet" type="text/css" href="@routes.Assets.versioned("lib/bootstrap/css/bootstrap.min.css")" />
+    <link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")" />
+    <script type="text/javascript" src="@routes.Assets.versioned("lib/angularjs/angular.min.js")"></script>
+    <script type="text/javascript" src="@routes.Assets.versioned("lib/angularjs/angular-resource.min.js")"></script>
+    <script type="text/javascript"
+            src="@routes.Assets.versioned("lib/angular-ui-bootstrap/ui-bootstrap-tpls.min.js")"></script>
+    <script type="text/javascript" src="@routes.Assets.versioned("javascripts/ndbDatabase.js")"></script>
+    <script type="text/javascript">
+      var app = angular.module('ingredients', ['ndbDatabase', 'ui.bootstrap']);
+      app.controller('SearchController', ['$scope', '$timeout', 'NDBSearch', function($scope, $timeout, NDBSearch) {
+          $scope.searchTerm = "";
+          $scope.searchOffset = 1;
+          $scope.searchSize = "10";
+          $scope.searchResults = [];
+          
+          var searchTimeout = false;
+          $scope.$watchGroup(['searchTerm', 'searchOffset'], function(newValues, oldValues, scope) {
+              if (searchTimeout)
+              {
+                  $timeout.cancel(searchTimeout);
+              }
+              if ($scope.searchTerm.length >= 3)
+              {
+                  searchTimeout = $timeout(function() {
+                      NDBSearch.get({key: "CfiHcUnSf0RX0jBuqiWjDK2d2ziOmoZG15CTdhQn",
+                                     "query": $scope.searchTerm
+                                    }, function(data) {
+                                        console.log(data);
+                                        $scope.searchResults = data;
+                                    }, function (err) {
+                                        console.error(err);
+                                    });
+                  }, 100);
+              }
+          });
+          
+          $scope.focusItem = function(ndbno) {
+              
+          };
+      }]);
+    </script>
+  </head>
+  <body data-ng-app="ingredients">
+    <div class="section container" data-ng-controller="SearchController">
+      <h2>Ingredient Querier</h2>
+      <div class="form-group">
+        <label for="search">Search: </label>
+        <input type="text" class="form-control input-sm" id="search" data-ng-model="searchTerm" />
+      </div>
+      <table class="table table-striped table-hover table-responsive">
+        <tr>
+          <th class="col-md-1"> </th>
+          <th class="col-md-1">NDB #</th>
+          <th class="col-md-6">Name</th>
+          <th class="col-md-3">Group</th>
+          <th class="col-md-3">Manufacturer</th>
+        </tr>
+        <tr data-ng-repeat="item in searchResults | limitTo:searchSize:searchSize*(searchOffset-1)"
+            data-ng-click="item.checked = !item.checked">
+          <th><input type="checkbox" class="checkbox-inline" data-ng-model="item.checked"></th>
+          <td>{{ ::item.ndbno }}</td>
+          <td>{{ ::item.name }}</td>
+          <td>{{ ::item.group }}</td>
+          <td>{{ ::item.manu }}</td>
+        </tr>
+      </table>
+      <div class="form-group col-xs-5">
+        <ul class="pagination-sm"
+            style="margin: 0;"
+            data-uib-pagination=""
+            data-total-items="searchResults.length"
+            data-ng-model="searchOffset"
+            data-items-per-page="searchSize"></ul>
+      </div>
+      <div class="form-group form-group-sm col-xs-2 pull-right">
+        <select id="sizeSelector" class="form-control input-sm" data-ng-model="searchSize">
+          <option>10</option>
+          <option>20</option>
+          <option>30</option>
+          <option>40</option>
+        </select>
+      </div>
+    </div>
+  </body>
+</html>

+ 25 - 0
app/views/main.scala.html

@@ -0,0 +1,25 @@
+@*
+ * This template is called from the `index` template. This template
+ * handles the rendering of the page header and body tags. It takes
+ * two arguments, a `String` for the title of the page and an `Html`
+ * object to insert into the body of the page.
+ *@
+@(title: String)(content: Html)
+
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        @* Here's where we render the page title `String`. *@
+        <title>@title</title>
+        <link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
+        <link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")">
+
+    </head>
+    <body>
+        @* And here's where we render the `Html` object containing
+         * the page content. *@
+        @content
+
+      <script src="@routes.Assets.versioned("javascripts/main.js")" type="text/javascript"></script>
+    </body>
+</html>

+ 25 - 0
build.sbt

@@ -0,0 +1,25 @@
+name := """recipes"""
+organization := "com.example"
+
+version := "1.0-SNAPSHOT"
+
+lazy val root = (project in file(".")).enablePlugins(PlayScala)
+
+scalaVersion := "2.12.4"
+
+libraryDependencies += guice
+libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.1.2" % Test
+
+// https://mvnrepository.com/artifact/org.webjars/angularjs
+libraryDependencies += "org.webjars" % "angularjs" % "1.6.10"
+// https://mvnrepository.com/artifact/org.webjars.bower/angular-resource
+libraryDependencies += "org.webjars.bower" % "angular-resource" % "1.6.9"
+// https://mvnrepository.com/artifact/org.webjars/angular-ui-bootstrap
+libraryDependencies += "org.webjars" % "angular-ui-bootstrap" % "2.5.0"
+
+
+// Adds additional packages into Twirl
+//TwirlKeys.templateImports += "com.example.controllers._"
+
+// Adds additional packages into conf/routes
+// play.sbt.routes.RoutesKeys.routesImport += "com.example.binders._"

+ 356 - 0
conf/application.conf

@@ -0,0 +1,356 @@
+# https://www.playframework.com/documentation/latest/Configuration
+
+# This is the main configuration file for the application.
+# https://www.playframework.com/documentation/latest/ConfigFile
+# ~~~~~
+# Play uses HOCON as its configuration file format.  HOCON has a number
+# of advantages over other config formats, but there are two things that
+# can be used when modifying settings.
+#
+# You can include other configuration files in this main application.conf file:
+#include "extra-config.conf"
+#
+# You can declare variables and substitute for them:
+#mykey = ${some.value}
+#
+# And if an environment variable exists when there is no other subsitution, then
+# HOCON will fall back to substituting environment variable:
+#mykey = ${JAVA_HOME}
+
+## Akka
+# https://www.playframework.com/documentation/latest/ScalaAkka#Configuration
+# https://www.playframework.com/documentation/latest/JavaAkka#Configuration
+# ~~~~~
+# Play uses Akka internally and exposes Akka Streams and actors in Websockets and
+# other streaming HTTP responses.
+akka {
+  # "akka.log-config-on-start" is extraordinarly useful because it log the complete
+  # configuration at INFO level, including defaults and overrides, so it s worth
+  # putting at the very top.
+  #
+  # Put the following in your conf/logback.xml file:
+  #
+  # <logger name="akka.actor" level="INFO" />
+  #
+  # And then uncomment this line to debug the configuration.
+  #
+  #log-config-on-start = true
+}
+
+## Secret key
+# http://www.playframework.com/documentation/latest/ApplicationSecret
+# ~~~~~
+# The secret key is used to sign Play's session cookie.
+# This must be changed for production, but we don't recommend you change it in this file.
+play.http.secret.key = "changeme"
+
+## Modules
+# https://www.playframework.com/documentation/latest/Modules
+# ~~~~~
+# Control which modules are loaded when Play starts.  Note that modules are
+# the replacement for "GlobalSettings", which are deprecated in 2.5.x.
+# Please see https://www.playframework.com/documentation/latest/GlobalSettings
+# for more information.
+#
+# You can also extend Play functionality by using one of the publically available
+# Play modules: https://playframework.com/documentation/latest/ModuleDirectory
+play.modules {
+  # By default, Play will load any class called Module that is defined
+  # in the root package (the "app" directory), or you can define them
+  # explicitly below.
+  # If there are any built-in modules that you want to disable, you can list them here.
+  #enabled += my.application.Module
+
+  # If there are any built-in modules that you want to disable, you can list them here.
+  #disabled += ""
+}
+
+## IDE
+# https://www.playframework.com/documentation/latest/IDE
+# ~~~~~
+# Depending on your IDE, you can add a hyperlink for errors that will jump you
+# directly to the code location in the IDE in dev mode. The following line makes 
+# use of the IntelliJ IDEA REST interface: 
+#play.editor="http://localhost:63342/api/file/?file=%s&line=%s"
+
+## Internationalisation
+# https://www.playframework.com/documentation/latest/JavaI18N
+# https://www.playframework.com/documentation/latest/ScalaI18N
+# ~~~~~
+# Play comes with its own i18n settings, which allow the user's preferred language
+# to map through to internal messages, or allow the language to be stored in a cookie.
+play.i18n {
+  # The application languages
+  langs = [ "en" ]
+
+  # Whether the language cookie should be secure or not
+  #langCookieSecure = true
+
+  # Whether the HTTP only attribute of the cookie should be set to true
+  #langCookieHttpOnly = true
+}
+
+## Play HTTP settings
+# ~~~~~
+play.http {
+  ## Router
+  # https://www.playframework.com/documentation/latest/JavaRouting
+  # https://www.playframework.com/documentation/latest/ScalaRouting
+  # ~~~~~
+  # Define the Router object to use for this application.
+  # This router will be looked up first when the application is starting up,
+  # so make sure this is the entry point.
+  # Furthermore, it's assumed your route file is named properly.
+  # So for an application router like `my.application.Router`,
+  # you may need to define a router file `conf/my.application.routes`.
+  # Default to Routes in the root package (aka "apps" folder) (and conf/routes)
+  #router = my.application.Router
+
+  ## Action Creator
+  # https://www.playframework.com/documentation/latest/JavaActionCreator
+  # ~~~~~
+  #actionCreator = null
+
+  ## ErrorHandler
+  # https://www.playframework.com/documentation/latest/JavaRouting
+  # https://www.playframework.com/documentation/latest/ScalaRouting
+  # ~~~~~
+  # If null, will attempt to load a class called ErrorHandler in the root package,
+  #errorHandler = null
+
+  ## Filters
+  # https://www.playframework.com/documentation/latest/ScalaHttpFilters
+  # https://www.playframework.com/documentation/latest/JavaHttpFilters
+  # ~~~~~
+  # Filters run code on every request. They can be used to perform
+  # common logic for all your actions, e.g. adding common headers.
+  # Defaults to "Filters" in the root package (aka "apps" folder)
+  # Alternatively you can explicitly register a class here.
+  #filters = my.application.Filters
+
+  ## Session & Flash
+  # https://www.playframework.com/documentation/latest/JavaSessionFlash
+  # https://www.playframework.com/documentation/latest/ScalaSessionFlash
+  # ~~~~~
+  session {
+    # Sets the cookie to be sent only over HTTPS.
+    #secure = true
+
+    # Sets the cookie to be accessed only by the server.
+    #httpOnly = true
+
+    # Sets the max-age field of the cookie to 5 minutes.
+    # NOTE: this only sets when the browser will discard the cookie. Play will consider any
+    # cookie value with a valid signature to be a valid session forever. To implement a server side session timeout,
+    # you need to put a timestamp in the session and check it at regular intervals to possibly expire it.
+    #maxAge = 300
+
+    # Sets the domain on the session cookie.
+    #domain = "example.com"
+  }
+
+  flash {
+    # Sets the cookie to be sent only over HTTPS.
+    #secure = true
+
+    # Sets the cookie to be accessed only by the server.
+    #httpOnly = true
+  }
+}
+
+## Netty Provider
+# https://www.playframework.com/documentation/latest/SettingsNetty
+# ~~~~~
+play.server.netty {
+  # Whether the Netty wire should be logged
+  #log.wire = true
+
+  # If you run Play on Linux, you can use Netty's native socket transport
+  # for higher performance with less garbage.
+  #transport = "native"
+}
+
+## WS (HTTP Client)
+# https://www.playframework.com/documentation/latest/ScalaWS#Configuring-WS
+# ~~~~~
+# The HTTP client primarily used for REST APIs.  The default client can be
+# configured directly, but you can also create different client instances
+# with customized settings. You must enable this by adding to build.sbt:
+#
+# libraryDependencies += ws // or javaWs if using java
+#
+play.ws {
+  # Sets HTTP requests not to follow 302 requests
+  #followRedirects = false
+
+  # Sets the maximum number of open HTTP connections for the client.
+  #ahc.maxConnectionsTotal = 50
+
+  ## WS SSL
+  # https://www.playframework.com/documentation/latest/WsSSL
+  # ~~~~~
+  ssl {
+    # Configuring HTTPS with Play WS does not require programming.  You can
+    # set up both trustManager and keyManager for mutual authentication, and
+    # turn on JSSE debugging in development with a reload.
+    #debug.handshake = true
+    #trustManager = {
+    #  stores = [
+    #    { type = "JKS", path = "exampletrust.jks" }
+    #  ]
+    #}
+  }
+}
+
+## Cache
+# https://www.playframework.com/documentation/latest/JavaCache
+# https://www.playframework.com/documentation/latest/ScalaCache
+# ~~~~~
+# Play comes with an integrated cache API that can reduce the operational
+# overhead of repeated requests. You must enable this by adding to build.sbt:
+#
+# libraryDependencies += cache
+#
+play.cache {
+  # If you want to bind several caches, you can bind the individually
+  #bindCaches = ["db-cache", "user-cache", "session-cache"]
+}
+
+## Filters
+# https://www.playframework.com/documentation/latest/Filters
+# ~~~~~
+# There are a number of built-in filters that can be enabled and configured
+# to give Play greater security.  You must enable this by adding to build.sbt:
+#
+# libraryDependencies += filters
+#
+play.filters {
+  ## CORS filter configuration
+  # https://www.playframework.com/documentation/latest/CorsFilter
+  # ~~~~~
+  # CORS is a protocol that allows web applications to make requests from the browser
+  # across different domains.
+  # NOTE: You MUST apply the CORS configuration before the CSRF filter, as CSRF has
+  # dependencies on CORS settings.
+  cors {
+    # Filter paths by a whitelist of path prefixes
+    #pathPrefixes = ["/"]
+
+    # The allowed origins. If null, all origins are allowed.
+    allowedOrigins = null #["https://api.nal.usda.gov"]
+
+    # The allowed HTTP methods. If null, all methods are allowed
+    #allowedHttpMethods = null #["GET", "POST"]
+  }
+
+  ## CSRF Filter
+  # https://www.playframework.com/documentation/latest/ScalaCsrf#Applying-a-global-CSRF-filter
+  # https://www.playframework.com/documentation/latest/JavaCsrf#Applying-a-global-CSRF-filter
+  # ~~~~~
+  # Play supports multiple methods for verifying that a request is not a CSRF request.
+  # The primary mechanism is a CSRF token. This token gets placed either in the query string
+  # or body of every form submitted, and also gets placed in the users session.
+  # Play then verifies that both tokens are present and match.
+  csrf {
+    # Sets the cookie to be sent only over HTTPS
+    #cookie.secure = true
+
+    # Defaults to CSRFErrorHandler in the root package.
+    #errorHandler = MyCSRFErrorHandler
+  }
+
+  ## Security headers filter configuration
+  # https://www.playframework.com/documentation/latest/SecurityHeaders
+  # ~~~~~
+  # Defines security headers that prevent XSS attacks.
+  # If enabled, then all options are set to the below configuration by default:
+  headers {
+    # The X-Frame-Options header. If null, the header is not set.
+    #frameOptions = "DENY"
+
+    # The X-XSS-Protection header. If null, the header is not set.
+    #xssProtection = "1; mode=block"
+
+    # The X-Content-Type-Options header. If null, the header is not set.
+    #contentTypeOptions = "nosniff"
+
+    # The X-Permitted-Cross-Domain-Policies header. If null, the header is not set.
+    #permittedCrossDomainPolicies = "master-only"
+
+    # The Content-Security-Policy header. If null, the header is not set.
+    contentSecurityPolicy = "default-src 'self' api.nal.usda.gov; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"# img-src 'self'
+  }
+
+  ## Allowed hosts filter configuration
+  # https://www.playframework.com/documentation/latest/AllowedHostsFilter
+  # ~~~~~
+  # Play provides a filter that lets you configure which hosts can access your application.
+  # This is useful to prevent cache poisoning attacks.
+  hosts {
+    # Allow requests to example.com, its subdomains, and localhost:9000.
+    #allowed = [".example.com", "localhost:9000"]
+  }
+}
+
+## Evolutions
+# https://www.playframework.com/documentation/latest/Evolutions
+# ~~~~~
+# Evolutions allows database scripts to be automatically run on startup in dev mode
+# for database migrations. You must enable this by adding to build.sbt:
+#
+# libraryDependencies += evolutions
+#
+play.evolutions {
+  # You can disable evolutions for a specific datasource if necessary
+  #db.default.enabled = false
+}
+
+## Database Connection Pool
+# https://www.playframework.com/documentation/latest/SettingsJDBC
+# ~~~~~
+# Play doesn't require a JDBC database to run, but you can easily enable one.
+#
+# libraryDependencies += jdbc
+#
+play.db {
+  # The combination of these two settings results in "db.default" as the
+  # default JDBC pool:
+  #config = "db"
+  #default = "default"
+
+  # Play uses HikariCP as the default connection pool.  You can override
+  # settings by changing the prototype:
+  prototype {
+    # Sets a fixed JDBC connection pool size of 50
+    #hikaricp.minimumIdle = 50
+    #hikaricp.maximumPoolSize = 50
+  }
+}
+
+## JDBC Datasource
+# https://www.playframework.com/documentation/latest/JavaDatabase
+# https://www.playframework.com/documentation/latest/ScalaDatabase
+# ~~~~~
+# Once JDBC datasource is set up, you can work with several different
+# database options:
+#
+# Slick (Scala preferred option): https://www.playframework.com/documentation/latest/PlaySlick
+# JPA (Java preferred option): https://playframework.com/documentation/latest/JavaJPA
+# EBean: https://playframework.com/documentation/latest/JavaEbean
+# Anorm: https://www.playframework.com/documentation/latest/ScalaAnorm
+#
+db {
+  # You can declare as many datasources as you want.
+  # By convention, the default datasource is named `default`
+
+  # https://www.playframework.com/documentation/latest/Developing-with-the-H2-Database
+  #default.driver = org.h2.Driver
+  #default.url = "jdbc:h2:mem:play"
+  #default.jndiName=DefaultDS
+
+  # You can turn on SQL logging for any datasource
+  # https://www.playframework.com/documentation/latest/Highlights25#Logging-SQL-statements
+  #default.logSql=true
+}
+
+jpa.default=defaultPersistenceUnit

+ 41 - 0
conf/logback.xml

@@ -0,0 +1,41 @@
+<!-- https://www.playframework.com/documentation/latest/SettingsLogger -->
+<configuration>
+
+  <conversionRule conversionWord="coloredLevel" converterClass="play.api.libs.logback.ColoredLevel" />
+
+  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+    <file>${application.home:-.}/logs/application.log</file>
+    <encoder>
+      <pattern>%date [%level] from %logger in %thread - %message%n%xException</pattern>
+    </encoder>
+  </appender>
+
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>%coloredLevel %logger{15} - %message%n%xException{10}</pattern>
+    </encoder>
+  </appender>
+
+  <appender name="ASYNCFILE" class="ch.qos.logback.classic.AsyncAppender">
+    <appender-ref ref="FILE" />
+  </appender>
+
+  <appender name="ASYNCSTDOUT" class="ch.qos.logback.classic.AsyncAppender">
+    <appender-ref ref="STDOUT" />
+  </appender>
+
+  <logger name="play" level="INFO" />
+  <logger name="application" level="DEBUG" />
+
+  <!-- Off these ones as they are annoying, and anyway we manage configuration ourselves -->
+  <logger name="com.avaje.ebean.config.PropertyMapLoader" level="OFF" />
+  <logger name="com.avaje.ebeaninternal.server.core.XmlConfigLoader" level="OFF" />
+  <logger name="com.avaje.ebeaninternal.server.lib.BackgroundThread" level="OFF" />
+  <logger name="com.gargoylesoftware.htmlunit.javascript" level="OFF" />
+
+  <root level="WARN">
+    <!--<appender-ref ref="ASYNCFILE" />-->
+    <appender-ref ref="ASYNCSTDOUT" />
+  </root>
+
+</configuration>

+ 1 - 0
conf/messages

@@ -0,0 +1 @@
+# https://www.playframework.com/documentation/latest/ScalaI18N

+ 12 - 0
conf/routes

@@ -0,0 +1,12 @@
+# Routes
+# This file defines all application routes (Higher priority routes first)
+# https://www.playframework.com/documentation/latest/ScalaRouting
+# ~~~~
+
+# An example controller showing a sample home page
+GET     /                           controllers.HomeController.index
+
+GET     /ingredients                controllers.IngredientController.editorPage
+
+# Map static resources from the /public folder to the /assets URL path
+GET     /assets/*file               controllers.Assets.versioned(path="/public", file: Asset)

+ 1 - 0
project/build.properties

@@ -0,0 +1 @@
+sbt.version=1.1.2

+ 2 - 0
project/plugins.sbt

@@ -0,0 +1,2 @@
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.13")
+addSbtPlugin("org.ensime" % "sbt-ensime" % "2.1.0")

+ 5 - 0
project/scaffold.sbt

@@ -0,0 +1,5 @@
+// Defines scaffolding (found under .g8 folder)
+// http://www.foundweekends.org/giter8/scaffolding.html
+// sbt "g8Scaffold form"
+// not working on sbt 1.0
+//addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.10.0")

BIN
public/images/favicon.png


+ 0 - 0
public/javascripts/main.js


+ 53 - 0
public/javascripts/ndbDatabase.js

@@ -0,0 +1,53 @@
+angular.module('ndbDatabase', ['ngResource'])
+    .factory('NDBSearch', ['$resource', '$q', function($resource, $q) {
+        return $resource('https://api.nal.usda.gov/ndb/search?'+
+                         'q=:query&ds=Standard+Reference&api_key=:key&fg=:groups&max=:max&offset=:offset', {}, {
+                             get: {method: "GET",
+                                   isArray: true,
+                                   transformRequest: function(data, headers) {
+                                       return angular.extend({}, headers, {'Content-Type': 'application/json'});
+                                   },
+                                   headers: {
+                                       'Content-Type': 'application/json'
+                                   },
+                                   transformResponse: function(data, headersGetter, status) {
+                                       var json = angular.fromJson(data);
+                                       return status < 400 && json.list? json.list.item : json.errors.error;
+                                   }
+                                  }
+                         });
+    }]).factory('NDBList', ['$resource', '$q', function($resource, $q) {
+        return $resource('https://api.nal.usda.gov/ndb/list?'+
+                         'api_key=:key&lt=:type&nutrients=:nutrients&fg=:groups&max=:max&offset=:offset', {}, {
+                             get: {method: "GET",
+                                   isArray: true,
+                                   transformRequest: function(data, headers) {
+                                       return angular.extend({}, headers, {'Content-Type': 'application/json'});
+                                   },
+                                   headers: {
+                                       'Content-Type': 'application/json'
+                                   },
+                                   transformResponse: function(data, headersGetter, status) {
+                                       var json = angular.fromJson(data);
+                                       return status < 400 && json.list? json.list.item : json.errors.error;
+                                   }
+                                  }
+                         });
+    }]).factory('NDBNutrients', ['$resource', '$q', function($resource, $q) {
+        return $resource('https://api.nal.usda.gov/ndb/nutrients?'+
+                         'api_key=:key&nbno=:nums&nutrients=:nutrients&fg=:groups&max=:max&offset=:offset', {}, {
+                             get: {method: "GET",
+                                   isArray: true,
+                                   transformRequest: function(data, headers) {
+                                       return angular.extend({}, headers, {'Content-Type': 'application/json'});
+                                   },
+                                   headers: {
+                                       'Content-Type': 'application/json'
+                                   },
+                                   transformResponse: function(data, headersGetter, status) {
+                                       var json = angular.fromJson(data);
+                                       return status < 400 && json.list? json.list.item : json.errors.error;
+                                   }
+                                  }
+                         });
+    }]);

+ 0 - 0
public/stylesheets/main.css


+ 45 - 0
test/controllers/HomeControllerSpec.scala

@@ -0,0 +1,45 @@
+package controllers
+
+import org.scalatestplus.play._
+import org.scalatestplus.play.guice._
+import play.api.test._
+import play.api.test.Helpers._
+
+/**
+ * Add your spec here.
+ * You can mock out a whole application including requests, plugins etc.
+ *
+ * For more information, see https://www.playframework.com/documentation/latest/ScalaTestingWithScalaTest
+ */
+class HomeControllerSpec extends PlaySpec with GuiceOneAppPerTest with Injecting {
+
+  "HomeController GET" should {
+
+    "render the index page from a new instance of controller" in {
+      val controller = new HomeController(stubControllerComponents())
+      val home = controller.index().apply(FakeRequest(GET, "/"))
+
+      status(home) mustBe OK
+      contentType(home) mustBe Some("text/html")
+      contentAsString(home) must include ("Welcome to Play")
+    }
+
+    "render the index page from the application" in {
+      val controller = inject[HomeController]
+      val home = controller.index().apply(FakeRequest(GET, "/"))
+
+      status(home) mustBe OK
+      contentType(home) mustBe Some("text/html")
+      contentAsString(home) must include ("Welcome to Play")
+    }
+
+    "render the index page from the router" in {
+      val request = FakeRequest(GET, "/")
+      val home = route(app, request).get
+
+      status(home) mustBe OK
+      contentType(home) mustBe Some("text/html")
+      contentAsString(home) must include ("Welcome to Play")
+    }
+  }
+}