├── .gitignore ├── LICENSE.md ├── README.md ├── Vagrantfile ├── ansible └── flask-mongo-playbook.yml ├── app ├── app.py └── templates │ ├── 404.html │ ├── base.html │ ├── city_by_zip.html │ └── home.html ├── bootstrap.sh └── flask-mongo-app-screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | 38 | # Vagrant stuff 39 | boxes/* 40 | .vagrant 41 | 42 | # vim stuff 43 | *.swp 44 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Free as in Free Beer 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | flask-mongo-app 2 | =============== 3 | 4 | A Python Flask Web App Development Skeleton using mongoDB featuring javascript. 5 | 6 | I wanted to learn some neat tools so I created this project to tie a few interesting things together. After you start up the virtual machine, you will be able to query the database which dynamically generates a templated javascript map to view GPS coordinates and information based on zipcodes. 7 | 8 | Screenshot 9 | ---------- 10 | ![Screen Shot](./flask-mongo-app-screenshot.png?raw=true) 11 | 12 | 13 | Try it out! 14 | ----------- 15 | *Bring up the virtualmachine and provision the server* 16 | 17 | $ vagrant up 18 | 19 | _Point your browser at_ 20 | 21 | http://localhost:5000/ 22 | 23 | _Cleaning up_ 24 | 25 | $ vagrant destroy 26 | 27 | _If you accidently delete the dir before doing destroy_ 28 | 29 | $ VBoxManage list vms 30 | $ VBoxManage unregistervm flask-app-server –delete 31 | 32 | ####Requires: 33 | * [Vagrant](http://www.vagrantup.com/) -- `sudo apt-get install vagrant` 1.2.2 34 | * [VirtualBox](https://www.virtualbox.org/wiki/Downloads/) -- `sudo apt-get install virtualbox-4.2` 35 | 36 | ####Built On: 37 | * [ansible](https://github.com/ansible/ansible/) -- provisioner loaded up first 38 | * [Flask](http://flask.pocoo.org) -- Python microframework web app engine 39 | * [mongoDB](http://mongodb.org) -- noSQL database 40 | * [Flask-PyMongo](http://flask-pymongo.readthedocs.org/en/latest/) -- bridges Flask and PyMongo 41 | * [leafletjs] (http://leafletjs.com/) -- javascript mapping 42 | * [bootstrap] (http://twitter.github.io/bootstrap/) -- pretty css 43 | 44 | ####TODO: 45 | * Try out some Javascript Charts & Graphs: 46 | * https://github.com/mbostock/d3/wiki/Gallery 47 | * http://square.github.io/crossfilter/ 48 | * http://bl.ocks.org/tjdecke/5558084 49 | * http://www.chartjs.org/ 50 | * http://www.flotcharts.org/flot/examples/ 51 | * Various Mapping Styles: 52 | * http://maps.stamen.com/#toner/12/37.7706/-122.3782 53 | * Play with adding an [Nginx](http://wiki.nginx.org/Main) layer on top of Flask 54 | 55 | 56 | ####Issues: 57 | * sometimes have to vagrant ssh && sudo supervisorctl start flask-mongo-app 58 | 59 | ####Notes: 60 | * bootstrap.sh does a one-time install of ansible to get the ball rolling 61 | * mongodb stores its data files in /var/lib/mongodb/ 62 | * mongodb stores its log files in /var/log/mongodb/ 63 | 64 | ####My workflow: 65 | * git config --global user.name 'Your Name' 66 | * git config --global user.email your@email 67 | * git pull 68 | * edit some code 69 | * git add 70 | * git commit -am 'fixed some bugs' 71 | * git push -u origin master 72 | 73 | ####Thanks to: 74 | * http://www.pixelmonkey.org/2013/03/13/rapid-web-prototyping-with-lightweight-tools 75 | * http://ryaneshea.com/lightweight-python-apps-with-flask-twitter-bootstrap-and-heroku 76 | 77 | Contributing 78 | ------------ 79 | 80 | 1. Fork it. 81 | 2. Create a branch (`git checkout -b my_new_feature`) 82 | 3. Commit your changes (`git commit -am "Added Cool Thing"`) 83 | 4. Push to the branch (`git push origin my_new_feature`) 84 | 5. Open a [Pull Request][1] 85 | 6. Enjoy a few plumphelmets while you wait cuz I've never done this before. 86 | 87 | [1]: http://github.com/ubergarm/flask-mongo-app/pulls 88 | 89 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure("2") do |config| 5 | 6 | config.vm.define :web do |web_config| 7 | web_config.vm.box = "precise32" 8 | config.vm.box_url = "http://files.vagrantup.com/precise32.box" 9 | web_config.vm.network :forwarded_port, host: 5000, guest: 5000 10 | web_config.vm.network :private_network, ip: "192.168.33.3" 11 | #web_config.vm.synced_folder "./data", "/vagrant_data" 12 | 13 | #the bootstrap script installs ansible in the VM itself 14 | web_config.vm.provision :shell, :path => "bootstrap.sh" 15 | 16 | #don't do the next section, as ansible is installed in the VM itself 17 | #web_config.vm.provision :ansible do |ansible| 18 | # ansible.playbook = "ansible/flask-mongo-playbook.yml" 19 | # ansible.hosts = "servers" 20 | #end 21 | 22 | web_config.vm.provider :virtualbox do |vb| 23 | #vb.gui = true 24 | vb.customize [ 25 | "modifyvm", :id, 26 | "--name", "flask-app-server", 27 | "--memory", "1024" 28 | ] 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /ansible/flask-mongo-playbook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | #this ansible playbook will provision the flask-mongo-app 3 | - name: Set up the flask-mongo-app system 4 | hosts: servers 5 | user: vagrant 6 | sudo: yes 7 | tasks: 8 | 9 | #FIXME this guy seemed to hang... 10 | # - name: have vagrant keep itself up to date 11 | # action: apt pkg=vagrant state=latest 12 | 13 | - name: install python-flask 14 | action: apt pkg=python-flask state=latest 15 | 16 | #TODO clean this up with apt-key call 17 | - name: fetch mongodb apt repo key 18 | #action: apt-key url=https://keyserver.ubuntu.com id=7F0CEB10 state=present 19 | #action: shell apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10 20 | #at work there is likely a firewall on port 11371 so force port 80 below if you get timeout errors 21 | action: shell apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 22 | 23 | #TODO clean this up with apt_repository call 24 | - name: install mongodb apt sources.list 25 | #action: apt_repository repo='deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' 26 | action: shell echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' > /etc/apt/sources.list.d/10gen.list creates=/etc/apt/sources.list.d/10gen.list 27 | 28 | - name: install mongodb 29 | action: apt pkg=mongodb-10gen state=latest update_cache=yes 30 | 31 | - name: make sure mongodb is running 32 | action: service name=mongodb state=started 33 | 34 | # TODO add this at some point 35 | # http://www.ansibleworks.com/docs/modules.html 36 | #- name: setup mongodb_user 37 | # action: mongodb_user 38 | 39 | - name: download zip code data set 40 | #action: command wget -O /tmp/zips.json http://media.mongodb.org/zips.json 41 | get_url: url=http://media.mongodb.org/zips.json dest=/tmp/zips.json 42 | 43 | #TODO make this next guy faster at being idempotent... 44 | - name: import zip code data set into mongodb, goes to 45 | action: command mongoimport --db app_db --collection zips --file /tmp/zips.json 46 | 47 | - name: install python pip (package installer) 48 | action: apt pkg=python-pip state=latest 49 | 50 | - name: install pymongo 51 | action: apt pkg=python-pymongo state=latest 52 | 53 | - name: pip install Flask-PyMongo 54 | action: pip name=Flask-PyMongo 55 | 56 | - name: install supervisor daemon 57 | action: apt pkg=supervisor state=latest 58 | 59 | #TODO should probably use some sort of template for these next three 60 | - name: update supervisord.conf file to watch our app 61 | action: lineinfile dest=/etc/supervisor/supervisord.conf regexp='^\[program:flask-mongo-app\]' line='[program:flask-mongo-app]' state=present 62 | 63 | - name: update supervisord.conf file to watch our app 64 | action: lineinfile dest=/etc/supervisor/supervisord.conf regexp='^command=\/vagrant\/app\/app.py' line='command=/vagrant/app/app.py' state=present 65 | 66 | # - name: update supervisord.conf file to watch our app 67 | # action: lineinfile dest=/etc/supervisor/supervisord.conf regexp='^autorestart=true' line='autorestart=true' state=present 68 | 69 | - name: make sure supervisor is running and picks up latest .conf 70 | action: service name=supervisor state=restarted 71 | 72 | #FIXME still having some trouble need to spend time fixing up supervisord stuff 73 | # - name: keep flask app up and running 74 | # #action: command python /vagrant/app/app.py & 75 | # action: supervisorctl name=flask-mongo-app state=started 76 | 77 | - name: give it a try! 78 | action: debug msg="Point your browser to http://localhost:5000/" 79 | -------------------------------------------------------------------------------- /app/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from flask import Flask, render_template, url_for 4 | from flask import Response 5 | from flask.ext.pymongo import PyMongo 6 | import json, os, datetime 7 | 8 | app = Flask(__name__) 9 | 10 | ##alternate db config settings 11 | app.config['MONGO_DBNAME'] = 'app_db' 12 | #app.config['MONGO_HOST'] = 'localhost' 13 | #app.config['MONGO_PORT'] = 27017 14 | #app.config['MONGO_USERNAME'] = 'flask' 15 | #app.config['MONGO_PASSWORD'] = 'flask123' 16 | mongo = PyMongo(app, config_prefix='MONGO') 17 | 18 | @app.route("/") 19 | def home(): 20 | links = [] 21 | links.append({'title':'Show city data on a js map', 'url':url_for('city_by_zip',zipcode='47586')}) 22 | links.append({'title':'Insert values into db and print collection.', 23 | 'url':url_for('db_insert', val=42)}) 24 | #return Response(str(url_for(city_by_zip)) 25 | return render_template('home.html', links=links) 26 | 27 | #example javascript interaction with mongodb 28 | @app.route("/city_by_zip/") 29 | def city_by_zip(zipcode): 30 | #cursor = mongo.db.zips.find({ '_id' : '47586' }) 31 | #cursor = mongo.db.zips.find({ 'state' : 'WY' }) 32 | cursor = mongo.db.zips.find({ '_id' : zipcode }) 33 | #records = [record for record in cursor] 34 | records = list(cursor) 35 | return render_template('city_by_zip.html', records=records) 36 | 37 | @app.route("/db_insert/") 38 | def db_insert(val): 39 | mongo.db.collection.save({'datetime': datetime.datetime.utcnow(), \ 40 | 'value':val}) 41 | items = list(mongo.db.collection.find()) 42 | result = 'Quick and dirty example of a RESTful db insertion.\r\n' 43 | result += 'Try adding your favorite integer in the URL.\r\n' 44 | result += 'Use back button to return to main menu when done.\r\n\r\n' 45 | result += 'Found {0} items in {1}.collection:\r\n'.format(len(items), mongo.db.name) 46 | for item in items: 47 | result += json.dumps(str(item))+'\r\n' 48 | return Response(result, mimetype='application/json') 49 | 50 | #handle 404 51 | @app.errorhandler(404) 52 | def page_not_found(error): 53 | return render_template('404.html'), 404 54 | 55 | if __name__ == "__main__": 56 | app.run(host='0.0.0.0', debug=True) 57 | -------------------------------------------------------------------------------- /app/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %} - Page Not Found{% endblock %} 3 | {% block content %} 4 |

Page Not Found

5 |

home

6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block head %} 5 | flask-mongo-app{% block title %}{% endblock %} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | {% endblock %} 25 | 26 | 27 | 28 | 29 | {% block navbar %} 30 | 58 | {% endblock %} 59 | 60 |
61 |
62 | {% block content %} 63 | {% endblock %} 64 |
65 | 66 |
67 | 68 | {% block footer %} 69 |
70 |

© flask-mongo-app

71 |
72 | {% endblock %} 73 | 74 |
75 | {% block js_footer %} 76 | 77 | {% endblock %} 78 | 79 | 80 | -------------------------------------------------------------------------------- /app/templates/city_by_zip.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %} - city by zip{% endblock %} 3 | {% block content %} 4 | 5 | 6 | 7 |
8 |

Based on leafletjs

9 |

Try your favorite zip code in url to see if it is in the mongodb.

10 | 11 |
12 | 13 | 42 | 43 | {# 44 |

45 | {% if records %} 46 | {{records}} 47 |

    48 | {% for record in records %} 49 |
  • {{ record.city }}
  • 50 |
  • {{ [ record.loc[1], record.loc[0] ]}}
  • 51 | {% endfor %} 52 |
53 |

54 | {% endif %} 55 | #} 56 | 57 | {% endblock %} 58 | -------------------------------------------------------------------------------- /app/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %} - Home{% endblock %} 3 | {% block content %} 4 | 5 | {# 6 | {% if not session.logged_in %} 7 |

You are not logged in.

8 | {% else %} 9 |

You are logged in.

10 | {% endif %} 11 | #} 12 | 13 |

Welcome, please try a couple examples:

14 | 15 |
    16 | {% for link in links %} 17 |
  • {{ link.title }}
  • 18 | {% else %} 19 |
  • No links found!
  • 20 | {% endfor %} 21 |
22 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # This vagrant bootstrap script is run as root at boot time 3 | # It must be idempotent! My favorite $5 word these days. 4 | 5 | # First portion will only run one time on each fresh box. 6 | if [ ! -f /home/vagrant/boot_strapped ]; then 7 | 8 | #update default locale to UTF8 9 | sed -i.bak 's/en_US\"/en_US.UTF-8\"/g' /etc/default/locale 10 | sed -i.bak 's/en_US$/en_US.UTF-8/g' /etc/default/locale 11 | 12 | #update apt sources 13 | apt-get update 14 | 15 | #install ansible 16 | apt-get install -y ansible 17 | 18 | #use ansible to finish provisioning system 19 | #setup /etc/ansible/hosts 20 | echo "[servers]" > /etc/ansible/hosts 21 | echo localhost >> /etc/ansible/hosts 22 | 23 | #setup ssh agent authorized keys 24 | #this might not be the best/safest, but it makes ansible happy... 25 | if [ ! -d /root/.ssh ]; then 26 | ssh-keygen -t dsa -N '' -C 'root@precise32' -f ~/.ssh/id_dsa 27 | cat /root/.ssh/id_dsa.pub > /home/vagrant/.ssh/authorized_keys2 28 | chown vagrant:vagrant /home/vagrant/.ssh/authorized_keys2 29 | fi 30 | #test ansible setup 31 | ansible servers -m ping -u vagrant --sudo 32 | 33 | #if ansible is ready to go, then dont run this stuff again 34 | if [ $? -eq 0 ]; then 35 | touch /home/vagrant/boot_strapped 36 | fi 37 | fi 38 | 39 | #finish up using the ansible playbook 40 | ansible-playbook /vagrant/ansible/flask-mongo-playbook.yml 41 | 42 | -------------------------------------------------------------------------------- /flask-mongo-app-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubergarm/flask-mongo-app/5c5cf798e6cf6d58d1c2b736e342a0d773929a37/flask-mongo-app-screenshot.png --------------------------------------------------------------------------------