Skip to content
This repository has been archived by the owner on Sep 11, 2020. It is now read-only.

uwasystemhealth/uwa-sites-ks

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

UWA Sites Index

This is an interface of a database that details the Infrastructure around campus, their datails, and the possible sensors that can be used on them.

Contents

  1. Technologies Used
  2. Installation
  3. Styling
  4. Templating
  5. Routing
  6. Database

Technologies Used

  • MongoDB - Document based (NoSQL) database, easily scalable and allows for dynamic manipulation of the database.
  • KeystoneJS - A JavaScript framework that allows quick and easy access to the database through a dynamically generated admin interface.
    • Express - Web routing framework for Node.js, allows for easy definition of the routes used for displaying the data.
    • Mongoose - A DB model abstraction that allows the definition of schemas to stucture the data accepted by specific documents.
    • Handlebars - A powerful HTML templating language used by the backend to dynamically inject data into the frontend html.
    • Cloudinary - Hosts image files automagically.
  • UIKit A simple HTML/CSS frontend for quickly generating pretty frontend views for the data.
  • Google Maps - Using the embeded method in iframes to display locations on a map.

Installation

  1. clone this repo into a folder of your choosing, enter the folder
  2. run npm i to install the dependancies
  3. setup the .env file detailed below
  4. run npm start to start the server

.env file

NB: The vaules used in the SHL site can be found in the google drive in UWA SHL/Projects/Living Lab/sites-index-env.txt

The .env file contains the enviroment variables and can contain the following: (* are required)

MONGO_URI=<*mongodb-uri>
CLOUDINARY_URL=<*coudinary-url>
COOKIE_SECRET=<*cookie-secret>
NODE_ENV=<development/production>
PORT=<port>

The values mean the following:

  • mongodb-uri: The connection URI to the mongo database, a uri for a local database would look like monogodb://localhost:27017/uwa-sites-index
  • cloudinary: The URI that contains the API credentials for a cloudinary account. You can get this by registering for free.
  • cookie-secret: A long random string that is used to cryptographically secure session cookies, the longer, the better.
  • development/production: Defaults to development, define this as production when deploying.
  • port: The port to run the server on, defaults to 3000.

Styling

Most of the styling for this site is done by the default UIKit framework.

Custom styling is done in stylus located in the public/styles/site/_layout.styl file. After making changes, run npm run compile to transpile stylus into css.

Templating

The data displayed is built from handlebars templates. This is broken up for easy maintenance.

This briefly covers the basics of handlebars, for a more in-depth explinations please consult the Documentation.

Header, Navbar, and Footer

These are defined as a "layout" in the /templates/views/layoutes/ folder. The default layout is approprately named default.hbs.

The main body of a page is loaded into the section specified with a {{{body}}} field.

To use a cutom layout on a page, add {{!< ~layout~}} to the top of the page. (replacing ~layout~ with the name of the layout)

Pages

The specific page template is defined in the /templates/views/ folder.

The website runs off of three main views, find.hbs, get.hbs, and index.hbs:

  • find.hbs: Displays any collection(table) of data.
  • get.hbs: Displays a single document(row) from any collection.
  • index.hbs: Dispays the homepage.

Why is there no specific pages for specific collections?

Good question. As all of the collections contain similar types of data between one another, we make sure that similar types have a consistant field name in the document. This allows the templates to blindly display the data based on the names of the fields present.

These field names used are, but not limited to:

  • name: the name of the document
  • desc: the description of the document
  • qty: the quantity of an item
  • age: the date of the start of something (like a DOB)
  • examples: a list of examples (example problem/solution satements), these are stored as a single string, deliminated by "|"
  • images: an array of cloudinary images, if present, will render a slideshow at the top of a get page
  • coords: a tuple of geo coordinates, stored as [long, lat], if present will render a map at the top of the get page. Will also display as a link that, when clicked on, will open up the map in a dismissable view
  • classes: an array of classifier documents associated with the current document
  • infra: an array of infrastructure documents associated with the current document
  • sensors: an array of sensor documents associated with the current document
  • faults: an array of fault documents associated with the current document
  • location: a single location document associated with the current document. Will display as a link that, when clicked on, will open up the map in a dismissable view

Partials

Partials are repaetable components used in the templates, and are located in /templates/views/partials and can be called in the layouts/pages by using {{>~partial~}} (replacing ~partial~ with the partials name)

Helpers

Helpers are javascript functions that can be called on data from within the templates, an example of this would be {{date age format="MM YYYY"}} which takes a Date object (age) and returns a pretty and formatted string using a moment.js template (format="MM YYYY").

These are defined in /templates/views/helpers/index.js

Routing

The backend uses express to route incoming connections to certain views/data, further reading can be done at Express's documentation.

Routes

The routes available are defined in /routes/views/index.js, an sample is bellow:

app.get('/', routes.views.index);
  
app.get('/infrastructure/', middleware.requireUser, routes.views.find);
app.get('/infrastructure/:class', middleware.requireUser, routes.views.find);
app.get('/piece/:slug', middleware.requireUser, routes.views.get);

You can use app.get, app.post, app.patch, app.delete, app.put, and app.use (catch-all) to handle the actions to take for different HTTP Methods they use the following argument pattern:

  • first arg - tells what path to use for the view, segments with a :parameter make the defined parameter available in req.params.
  • middle-args - define middleware to use before we call the final view, as seen above, middleware.requireUser will block a request if a user is not logged in.
  • last-arg - the final view that will call res.render(), the data is usually loading from the database in this function.

Rendering

The view functions used as the last arg in a route are defined in the folder /routes/views/. Inside these, Mongoose Queries are used to load the data from the database, and store the data in the res.locals field.

Once a view has finished loading the data, it will call res.render('page') which will pass the res.locals field to the template as the rendering context, and then send the finished render to the client.

Navbar

The definition that is used to build the navbar can be found in /routes/middleware.js. It should look like:

res.locals.navLinks = [
		{ label: 'Home', key: 'home', href: '/' },
		...(!req.user ? [] : [ //only display the following if logged in
			{ label: 'Infrastructure', key: 'infrastructure', href: '/infrastructure' },
			{ label: 'Sensors', key: 'sensors', href: '/sensors' },
			{ label: 'Classifiers', key: 'classifiers', href: '/classifiers' },
			{ label: 'Faults', key: 'faults', href: '/faults' },
			{ label: 'Locations', key: 'locations', href: '/locations' },
		]),
	];

Database

The Model/Schema definitions for the Mongo collections(tables) are defined in the /models/ directory. For further information, please read the Model and Schema documentation.

Schema

The schema defines what fields the backend should allow, their type, defaults, whether they are required, and other information about the structure of the data.

Keystone provides a comprehensive list of data types/options it supports. Along with the default Mongoose options.

An example schema definition:

Sensors.add({
  name: {
    type: String, // just a plain string
    required: true, // must be defined
    initial: true,  // show in the create pop-up
  },
	desc: {
    type: Types.Markdown, // is text formatted as markdown,
      // saved in the document as {html: '<b>bold</b>', md: '**bold**'}, 
      // will show in a wysiwyg editor in the admin interface.
    initial: true
  },
	classes: {
    type: Types.Relationship, // is a relationship with another collection
    ref: 'Classifiers', // the collection in relation
    many: true, // one-to-many/many-to-many relationship 
    initial: true,
  },
	images: {
    type: Types.CloudinaryImages // and array of image information that is stored in cloudinary
  },
	examples: {
		type: Types.TextArray, // an array of text, stored as a string delimited by " | "
		label: 'Example Solution Statements', // custom label for the field to show in the admin interface
		initial: true,
	},
});

Models/Lists

A Model defines the ways in which the data can/is read and written to the database.

Keystone provides it's own wrapper around Models called Lists, which add extra options.

An example of initialising a Model/List:

var Sensors = new keystone.List('Sensors', {
	autokey: { path: 'slug', from: 'name', unique: true }, //automagically creates the slug based on the name of the document
	track: true, // adds createdAt, updatedAt, createdBy and updatedBy
	drilldown: 'classes', // tells admin interface to populate the `classes` dropdown menu with the names of the available classses.
});

The following things can also be defined with a Model/List:

  • Virtuals: data that is calculated and then added to a document when being read from the database, but the data is not stored in the database.
  • Models: functions that are attached to a document, and operate in the context of the document.
  • Statics: static functions attached to the model itself, and operated in the context of the model. Usually used to abstract complex search queries.
  • Query Helpers: functions that are attached to a query, and operated in the context of that query.

Queries

Querying in mongo can be a little confusing but very powerful.

Explaining mongo queries is a little out of the scope of this document, but here are some resources:

An example of a mongoose query:

keystone.list('Classifiers').model // get the Classifiers Mongoose model
  .findOne({ slug: locals.filters.class }) // get one doc with the matching slug
  .exec(function (err, result) { // execute the query and return this function when done
    locals.data.class = result;
    next(err);
  });

Another way of writing this is:

keystone.list('Classifiers').model
  .findOne()
  .where('slug')
  .equals(locals.filters.class)
  .exec(function (err, result) {
    locals.data.class = result;
    next(err);
  });

Or even .where('slug', locals.filters.class)

About

No description or website provided.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published