├── .gitignore ├── Dockerfile ├── Pipfile ├── Pipfile.lock ├── README.md ├── config.py ├── flaskr ├── __init__.py ├── api │ ├── __init__.py │ └── views.py ├── app.py ├── bojangles │ ├── __init__.py │ ├── templates │ │ └── home.html │ └── views.py ├── models.py ├── static │ └── vue.js └── templates │ └── base.html ├── gunicorn.py ├── run.py └── wsgi.py /.gitignore: -------------------------------------------------------------------------------- 1 | env/ 2 | *egg-info* 3 | *__pycache__* 4 | *.sw? 5 | *.pyc 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | 3 | WORKDIR /app 4 | 5 | RUN pip install pipenv 6 | 7 | # Install dependencies into their own layer so they are cached between code changes 8 | COPY Pipfile Pipfile.lock ./ 9 | RUN pipenv install --system --deploy 10 | 11 | COPY . . 12 | 13 | EXPOSE 8080 14 | 15 | CMD ["gunicorn", "-c", "gunicorn.py", "wsgi:app"] 16 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | 3 | url = "https://pypi.python.org/simple" 4 | verify_ssl = true 5 | 6 | 7 | [packages] 8 | 9 | flask = "==0.12.1" 10 | click = "==6.7" 11 | itsdangerous = "==0.24" 12 | "jinja2" = "==2.9.6" 13 | markupsafe = "==1.0" 14 | pyparsing = "==2.2.0" 15 | werkzeug = "==0.15.3" 16 | gunicorn = "*" 17 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "a9d44748944e11b03cad5a284ae3ea6a1daeb35873248852af121d9e9ec48bcf" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": {}, 8 | "sources": [ 9 | { 10 | "url": "https://pypi.python.org/simple", 11 | "verify_ssl": true 12 | } 13 | ] 14 | }, 15 | "default": { 16 | "click": { 17 | "hashes": [ 18 | "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", 19 | "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" 20 | ], 21 | "version": "==6.7" 22 | }, 23 | "flask": { 24 | "hashes": [ 25 | "sha256:6c3130c8927109a08225993e4e503de4ac4f2678678ae211b33b519c622a7242", 26 | "sha256:9dce4b6bfbb5b062181d3f7da8f727ff70c1156cbb4024351eafd426deb5fb88" 27 | ], 28 | "version": "==0.12.1" 29 | }, 30 | "gunicorn": { 31 | "hashes": [ 32 | "sha256:75af03c99389535f218cc596c7de74df4763803f7b63eb09d77e92b3956b36c6", 33 | "sha256:eee1169f0ca667be05db3351a0960765620dad53f53434262ff8901b68a1b622" 34 | ], 35 | "version": "==19.7.1" 36 | }, 37 | "itsdangerous": { 38 | "hashes": [ 39 | "sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519" 40 | ], 41 | "version": "==0.24" 42 | }, 43 | "jinja2": { 44 | "hashes": [ 45 | "sha256:2231bace0dfd8d2bf1e5d7e41239c06c9e0ded46e70cc1094a0aa64b0afeb054", 46 | "sha256:ddaa01a212cd6d641401cb01b605f4a4d9f37bfc93043d7f760ec70fb99ff9ff" 47 | ], 48 | "version": "==2.9.6" 49 | }, 50 | "markupsafe": { 51 | "hashes": [ 52 | "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665" 53 | ], 54 | "version": "==1.0" 55 | }, 56 | "pyparsing": { 57 | "hashes": [ 58 | "sha256:0832bcf47acd283788593e7a0f542407bd9550a55a8a8435214a1960e04bcb04", 59 | "sha256:fee43f17a9c4087e7ed1605bd6df994c6173c1e977d7ade7b651292fab2bd010" 60 | ], 61 | "version": "==2.2.0" 62 | }, 63 | "werkzeug": { 64 | "hashes": [ 65 | "sha256:97660b282aa7e29f94f3fe378e5c7162d7ab9d601a8dbb1cbb2ffc8f0e54607d", 66 | "sha256:cfd1281b1748288e59762c0e174d64d8bcb2b70e7c57bc4a1203c8825af24ac3" 67 | ], 68 | "version": "==0.15.3" 69 | } 70 | }, 71 | "develop": {} 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bojangles 2 | 3 | NOTE - this project is now dead in favour of [Notangles](https://github.com/csesoc/notangles) 4 | 5 | ## Getting your environment setup 6 | 7 | We will be using Docker eventually to automate this process, and to reduce environment inconsistencies, but to get this setup properly on an external system: 8 | 9 | Note: this assumes virtualenv has been enabled, and python3.6 (preferably) is available. 10 | 11 | ```sh 12 | pipenv python run.py 13 | ``` 14 | 15 | ### Installing a dependency 16 | 17 | ```sh 18 | pipenv install my-cool-dependency 19 | ``` 20 | 21 | ### Entering the virtual environment 22 | 23 | ```sh 24 | pipenv --shell 25 | ``` 26 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | class Config(object): 5 | DEBUG = False 6 | 7 | 8 | class Development(Config): 9 | DEBUG = True 10 | SECRET_KEY = 'development-only' 11 | CLASSUTIL_PATH = 'api/specialisation/db.json' 12 | 13 | 14 | class Production(Config): 15 | DEBUG = False 16 | 17 | def __init__(self): 18 | self.SECRET_KEY = os.environ['SECRET_KEY'] 19 | -------------------------------------------------------------------------------- /flaskr/__init__.py: -------------------------------------------------------------------------------- 1 | from flaskr.app import ( 2 | create_app 3 | ) 4 | -------------------------------------------------------------------------------- /flaskr/api/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | app = Blueprint( 4 | 'api', 5 | __name__, 6 | ) 7 | 8 | from . import views 9 | 10 | -------------------------------------------------------------------------------- /flaskr/api/views.py: -------------------------------------------------------------------------------- 1 | from flask import abort, jsonify, current_app 2 | import json 3 | from . import app 4 | 5 | 6 | """ 7 | Return json for all courses under specialisation 8 | in semester (X1|S1|S2) 9 | """ 10 | @app.route('/specialisation//') 11 | def specialisation(spec, sem): 12 | sem = sem.upper() # accept lower case request 13 | 14 | db_file = "%s/%s" % (current_app.root_path, current_app.config['CLASSUTIL_PATH']) 15 | db = json.load(open(db_file, 'r')) 16 | 17 | try: 18 | # todo: hesitant: Optimize this if scalability is required 19 | # As it stands there are around 354 entries in the list, which we are searching through the entirety of 20 | # as 354 is small, this is not really an issue, but we could easily optimize it by quitting as soon as 21 | # we find a match, or we could just use a binary search (as the list is sorted by specialisation 22 | # alphabetically) 23 | 24 | # We return next as the format guarantees there will only be one specialisation per session 25 | return jsonify(next( 26 | (x for x in db if x['specialisation'] == spec and x['session'] == sem) 27 | )) 28 | except StopIteration: 29 | # In the case of a empty return from filter, either the specialisation or session is invalid 30 | # (as there most be a object even if it has no courses) 31 | abort(400) 32 | -------------------------------------------------------------------------------- /flaskr/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from .bojangles import app as bojangles_bp 3 | from .api import app as api_bp 4 | 5 | 6 | def register_models(app): 7 | from . import models 8 | 9 | 10 | def register_blueprints(app): 11 | app.register_blueprint(bojangles_bp) 12 | app.register_blueprint(api_bp, url_prefix='/api') 13 | 14 | 15 | def create_app(config): 16 | app = Flask(__name__, static_url_path='/static') 17 | app.config.from_object(config) 18 | 19 | register_models(app) 20 | register_blueprints(app) 21 | 22 | return app 23 | -------------------------------------------------------------------------------- /flaskr/bojangles/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | app = Blueprint( 4 | 'bojangles', 5 | __name__, 6 | template_folder='templates', 7 | static_folder='static') 8 | 9 | from . import views 10 | 11 | -------------------------------------------------------------------------------- /flaskr/bojangles/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 | {% raw %} 5 | {{ message }} 6 | {% endraw %} 7 |
8 | {% endblock %} 9 | 10 | {% block scripts %} 11 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /flaskr/bojangles/views.py: -------------------------------------------------------------------------------- 1 | from flask import render_template_string, request, render_template 2 | import re 3 | from . import app 4 | 5 | @app.route('/') 6 | def home(): 7 | return render_template("home.html") 8 | -------------------------------------------------------------------------------- /flaskr/models.py: -------------------------------------------------------------------------------- 1 | # nothing 2 | -------------------------------------------------------------------------------- /flaskr/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Hello, world! 12 | 13 | 14 |
15 | {% block content %} 16 | {% endblock content %} 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {% block scripts %} 28 | {% endblock scripts %} 29 | 30 | 31 | -------------------------------------------------------------------------------- /gunicorn.py: -------------------------------------------------------------------------------- 1 | """ 2 | Gunicorn configuration used for production web server. 3 | """ 4 | 5 | import multiprocessing 6 | 7 | bind = "0.0.0.0:8080" 8 | workers = multiprocessing.cpu_count() * 2 + 1 9 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | from config import Development 2 | from flaskr import create_app 3 | app = create_app(config=Development()) 4 | 5 | if __name__ == "__main__": 6 | app.run(port=1337) 7 | -------------------------------------------------------------------------------- /wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | Expose the flask app for serving by a WSGI Server 3 | """ 4 | 5 | from flaskr import create_app 6 | from config import Production 7 | 8 | config = Production() 9 | app = create_app(config) 10 | --------------------------------------------------------------------------------