Commit 1a0849c5 authored by Jay Wilson's avatar Jay Wilson 🎤

Merge branch '18-port-to-js' into 'master'

Resolve "Port to JS"

Closes #18

See merge request !1
parents b02cfb9a f5ef664e
# Created by
# Edit at
### Node ###
# Logs
# Runtime data
# Directory for instrumented libs generated by jscoverage/JSCover
# Coverage directory used by tools like istanbul
# nyc test coverage
# Grunt intermediate storage (
# Bower dependency directory (
# node-waf configuration
# Compiled binary addons (
# Dependency directories
# TypeScript v1 declaration files
# Optional npm cache directory
# Optional eslint cache
# Optional REPL history
# Output of 'npm pack'
# Yarn Integrity file
# dotenv environment variables file
# parcel-bundler cache (
# next.js build output
# nuxt.js build output
# vuepress build output
# Serverless directories
# FuseBox cache
# End of
"recommendations": [
// Use IntelliSense to learn about possible Node.js debug attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit:
"version": "0.2.0",
"configurations": [
"type": "node",
"request": "attach",
"name": "Attach by Process ID",
"processId": "${command:PickProcess}",
"protocol": "inspector"
"type": "node",
"request": "launch",
"name": "Launch in Docker",
"preLaunchTask": "tsc-watch",
"runtimeExecutable": "npm",
"runtimeArgs": [
"port": 9222,
"restart": true,
"timeout": 60000,
"localRoot": "${workspaceFolder}",
"remoteRoot": "/server",
"outFiles": [
"skipFiles": [
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
// Place your settings in this file to overwrite default and user settings.
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/dist": true,
"**/coverge": true
"typescript.referencesCodeLens.enabled": true,
"tslint.ignoreDefinitionFiles": false,
"tslint.autoFixOnSave": true,
"tslint.exclude": "**/node_modules/**/*",
"cSpell.words": [
"version": "2.0.0",
"tasks": [
"type": "npm",
"script": "build",
"group": {
"kind": "build",
"isDefault": true
"version": "0.1.0",
"tasks": [
"taskName": "tsc-watch",
"command": "npm",
"args": [
"isShellCommand": true,
"isBackground": true,
"isBuildCommand": true,
"problemMatcher": "$tsc-watch",
"showOutput": "always"
FROM node:10-slim
WORKDIR /server
COPY . /server
RUN npm install
RUN npm run build
CMD [ "npm", "start" ]
This diff is collapsed.
......@@ -5,3 +5,14 @@ Map out points of interest at Mason, like water fountains, bathrooms, and study
Ideas/Planning: [](
For updates, join #where on Slack.
# Running
First run `npm install` or `yarn`. Then run `npm run build` to build then project, and then `npm run start`. You should then be able to view the site [in your browser](http://localhost:3000/)
Alternatively, you can use Docker Compose by running `docker-compose up`. You can build and run the Docker container on it's own by executing the following
docker build -t where .
docker run -p 3000:3000 where
import * as shell from "shelljs";
shell.cp("-R", "src/public/js/lib", "dist/public/js/");
shell.cp("-R", "src/public/fonts", "dist/public/");
shell.cp("-R", "src/public/images", "dist/public/");
version: "2"
build: .
command: npm run debug
- ./dist:/server/dist
- "3000:3000"
- "9222:9222"
module.exports = {
globals: {
'ts-jest': {
tsConfigFile: 'tsconfig.json'
moduleFileExtensions: [
transform: {
'^.+\\.(ts|tsx)$': './node_modules/ts-jest/preprocessor.js'
testMatch: [
testEnvironment: 'node'
\ No newline at end of file
recursive-include map_mason/templates *
recursive-include map_mason/static *
all: run
rm -rf venv && rm -rf *.egg-info && rm -rf dist && rm -rf *.log*
virtualenv --python=python3 venv && venv/bin/python develop
run: venv
FLASK_APP=map_mason MAP_MASON_SETTINGS=../settings.cfg venv/bin/flask run
test: venv
MAP_MASON_SETTINGS=../settings.cfg venv/bin/python -m unittest discover -s tests
sdist: venv test
venv/bin/python sdist
# MapMason
MapMason description
## Quick Start
Run the application:
make run
And open it in the browser at [](
## Prerequisites
This is built to be used with Python 3. Update `Makefile` to switch to Python 2 if needed.
Some Flask dependencies are compiled during installation, so `gcc` and Python header files need to be present.
For example, on Ubuntu:
apt install build-essential python3-dev
## Development environment and release process
- create virtualenv with Flask and MapMason installed into it (latter is installed in
[develop mode]( which allows
modifying source code directly without a need to re-install the app): `make venv`
- run development server in debug mode: `make run`; Flask will restart if source code is modified
- run tests: `make test` (see also: [Testing Flask Applications](
- create source distribution: `make sdist` (will run tests first)
- to remove virtualenv and built distributions: `make clean`
- to add more python dependencies: add to `install_requires` in ``
- to modify configuration in development environment: edit file `settings.cfg`; this is a local configuration file
and it is *ignored* by Git - make sure to put a proper configuration file to a production environment when
## Deployment
If you are interested in an out-of-the-box deployment automation, check out accompanying
Or, check out [Deploying with Fabric]( on one of the
possible ways to automate the deployment.
In either case, generally the idea is to build a package (`make sdist`), deliver it to a server (`scp ...`),
install it (`pip install map_mason.tar.gz`), ensure that configuration file exists and
`MAP_MASON_SETTINGS` environment variable points to it, ensure that user has access to the
working directory to create and write log files in it, and finally run a
[WSGI container]( with the application.
And, most likely, it will also run behind a
[reverse proxy](
import os
from flask import Flask
app = Flask(__name__)
if not app.debug:
import logging
from logging.handlers import TimedRotatingFileHandler
file_handler = TimedRotatingFileHandler(os.path.join(app.config['LOG_DIR'], 'map_mason.log'), 'midnight')
file_handler.setFormatter(logging.Formatter('<%(asctime)s> <%(levelname)s> %(message)s'))
import map_mason.views
DEBUG = False # make sure DEBUG is off unless enabled explicitly otherwise
LOG_DIR = '.' # create log files in current working directory
This diff is collapsed.
<!doctype html>
<html lang="en">
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link href="" rel="stylesheet">
{% block extra_head %}
{% endblock %}
<title>{% block title %}{% endblock %} - MapMason</title>
{% block content %}{% endblock %}
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src=""></script>
{% block extra_js %}{% endblock %}
\ No newline at end of file
{% extends 'base.html' %}
{% block extra_head %}
<link rel="stylesheet" href=""
<link rel="stylesheet" href="{{ url_for('static', filename='titatoggle-dist-min.css') }}">
#map {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
html, body {
height: 100%;
width: 100%;
#page-content {
height: 100%;
#map-container {
padding: 0;
margin: 0;
{% endblock %}
{% block extra_js %}
<script src=""
<script type="text/javascript">
// GMU Decimal Latitude and Decimal Longitude
const gmuLatitude = 38.8322871;
const gmuLongitude = -77.3080912;
const defaultZoom = 16;
var map ='map', {
minZoom: 0,
maxZoom: 20,
zoomSnap: 0,
zoomDelta: 0.25
var cartodbAttribution = '&copy; <a href="">OpenStreetMap</a> contributors, &copy; <a href="">CartoDB</a>';
var tileUrl = 'http://{s}{z}/{x}/{y}.png';
// 'http://{s}{z}/{x}/{y}.png'
var positron = L.tileLayer(tileUrl, {
attribution: cartodbAttribution
map.setView([gmuLatitude, gmuLongitude], defaultZoom);
{% endblock %}
{% block content %}
<div class="container-fluid" id="page-content">
<div class="row" style="height: 100%;">
<nav class="col-sm-3 col-md-2 d-none d-sm-block navbar-inverse navbar-dark bg-dark sidebar">
<h1 class="navbar-brand navbar-dark bg-dark">MapMason</h1>
<h3 class="text-primary">Search</h3>
<form class="form-inline mt-2 mt-md-0">
<small class="form-text text-light">Search via Name or Keyword</small>
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Name or Keyword">
<h3 class="text-primary">Filter Locations</h3>
<div class="nav flex-column text-light">
<div class="form-check checkbox-slider--b-flat">
<input type="checkbox"><span>Parking</span>
<div class="form-check checkbox-slider--b-flat">
<input type="checkbox"><span>Residence Halls</span>
<div class="form-check checkbox-slider--b-flat">
<input type="checkbox"><span>Dining Halls</span>
<p class="text-light">Having problems? <a href="">Submit feedback</a></p>
<p class="text-muted">A service of <a href="">Mason SRCT</a>. Licensed under the <a href="">Apache License, Version 2.0</a>.</p>
<main role="main" class="col-sm-9 ml-sm-auto col-md-10" id="map-container">
<div id="map"></div>
{% endblock %}
from flask import render_template
from map_mason import app
def index():
# app.logger.warning('Hit index')
return render_template('index.html')
from setuptools import setup, find_packages
import unittest
import map_mason
class Map_masonTestCase(unittest.TestCase):
def setUp(self): =
def test_index(self):
rv ='/')
self.assertIn('Welcome to MapMason',
if __name__ == '__main__':
"name": "map_mason",
"version": "0.1.0",
"description": "Map mason",
"repository": {
"type": "git",
"url": ""
"author": "Kunal Sarkhel",
"license": "Apache-2.0",
"scripts": {
"start": "npm run serve",
"build": "npm run build-sass && npm run build-ts && npm run tslint && npm run copy-static-assets",
"serve": "node dist/server.js",
"watch-node": "nodemon dist/server.js",
"watch": "concurrently -k -p \"[{name}]\" -n \"Sass,TypeScript,Node\" -c \"yellow.bold,cyan.bold,green.bold\" \"npm run watch-sass\" \"npm run watch-ts\" \"npm run watch-node\"",
"test": "jest --forceExit --coverage --verbose",
"watch-test": "npm run test -- --watchAll",
"build-ts": "tsc",
"watch-ts": "tsc -w",
"build-sass": "node-sass src/public/css/main.scss dist/public/css/main.css",
"watch-sass": "node-sass -w src/public/css/main.scss dist/public/css/main.css",
"tslint": "tslint -c tslint.json -p tsconfig.json",
"copy-static-assets": "ts-node copyStaticAssets.ts",
"debug": "npm run build && npm run watch-debug",
"serve-debug": "nodemon --inspect dist/server.js",
"watch-debug": "concurrently -k -p \"[{name}]\" -n \"Sass,TypeScript,Node\" -c \"yellow.bold,cyan.bold,green.bold\" \"npm run watch-sass\" \"npm run watch-ts\" \"npm run serve-debug\""
"dependencies": {
"async": "^2.6.0",
"bcrypt-nodejs": "^0.0.3",
"bluebird": "^3.5.1",
"body-parser": "^1.18.2",
"compression": "^1.7.1",
"dotenv": "^4.0.0",
"errorhandler": "^1.5.0",
"express": "^4.16.2",
"express-validator": "^4.3.0",
"fbgraph": "^1.4.1",
"lodash": "^4.17.5",
"lusca": "^1.5.2",
"nodemailer": "^4.4.1",
"pug": "^2.0.0-rc.4",
"request": "^2.83.0",
"request-promise": "^4.2.2",
"titatoggle": "^2.1.2",
"winston": "^2.4.0"
"devDependencies": {
"@types/async": "^2.0.45",
"@types/bcrypt-nodejs": "^0.0.30",
"@types/bluebird": "^3.5.20",
"@types/body-parser": "^1.16.8",
"@types/compression": "^0.0.35",
"@types/dotenv": "^4.0.3",
"@types/errorhandler": "^0.0.32",
"@types/express": "^4.11.1",
"@types/jest": "^22.1.3",
"@types/jquery": "^3.2.17",
"@types/lodash": "^4.14.91",
"@types/lusca": "^1.5.0",
"@types/morgan": "^1.7.35",
"@types/node": "^9.4.6",
"@types/nodemailer": "^4.3.4",
"@types/request": "^2.47.0",
"@types/shelljs": "^0.7.8",
"@types/supertest": "^2.0.4",
"chai": "^4.1.2",
"concurrently": "^3.5.1",
"jest": "^22.0.4",
"node-sass": "^4.7.2",
"nodemon": "^1.13.0",
"shelljs": "^0.8.1",
"supertest": "^3.0.0",
"ts-jest": "^22.0.4",
"ts-node": "^5.0.0",
"tslint": "^5.9.1",
"typescript": "^2.7.2"
import express from "express";
import compression from "compression"; // compresses requests
import bodyParser from "body-parser";
import logger from "./util/logger";
import lusca from "lusca";
import dotenv from "dotenv";
import path from "path";
import expressValidator from "express-validator";
import bluebird from "bluebird";
// Load environment variables from .env file, where API keys and passwords are configured
dotenv.config({path: ".env.example"});
// Controllers (route handlers)
import * as homeController from "./controllers/home";
import * as apiController from "./controllers/api";
// Create Express server
const app = express();
// Express configuration
app.set("port", process.env.PORT || 3000);
app.set("views", path.join(__dirname, "../views"));
app.set("view engine", "pug");
app.use(bodyParser.urlencoded({extended: true}));
express.static(path.join(__dirname, "public"), {maxAge: 31557600000})
* Primary app routes.
app.get("/", homeController.index);
* API examples routes.
app.get("/api", apiController.getApi);
export default app;
"use strict";
import async from "async";
import request from "request";
import { Response, Request, NextFunction } from "express";
* GET /api
* List of API examples.
export let getApi = (req: Request, res: Response) => {
res.render("api/index", {
title: "API Examples"
import { Request, Response } from "express";
* GET /
* Home page.
export let index = (req: Request, res: Response) => {
res.render("home", {
title: "Home"
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.