├── pi_server ├── pi_server │ ├── __init__.py │ ├── views │ │ ├── __init__.py │ │ └── hello.py │ ├── wsgi.py │ ├── urls.py │ └── settings.py ├── requirements.txt ├── .env.sample ├── Pipfile ├── manage.py ├── Pipfile.lock └── .gitignore ├── web_server ├── web_server │ ├── __init__.py │ ├── tests │ │ ├── __init__.py │ │ └── test_model_owner.py │ ├── models │ │ ├── __init__.py │ │ ├── Owner.py │ │ └── Doggo.py │ ├── views │ │ ├── gh_hash.py │ │ ├── __init__.py │ │ └── handle_github_event.py │ ├── urls.py │ ├── wsgi.py │ └── settings.py ├── .env.sample ├── Pipfile ├── manage.py ├── .gitignore ├── README.md └── Pipfile.lock ├── .gitignore ├── glitch.json ├── LICENSE.md ├── .circleci └── config.yml ├── README.md └── CODE_OF_CONDUCT.md /pi_server/pi_server/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web_server/web_server/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web_server/web_server/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | run-build-locally.sh 3 | -------------------------------------------------------------------------------- /pi_server/pi_server/views/__init__.py: -------------------------------------------------------------------------------- 1 | from pi_server.views.hello import hello 2 | -------------------------------------------------------------------------------- /pi_server/requirements.txt: -------------------------------------------------------------------------------- 1 | django==2.1.7 2 | python-dotenv==0.10.1 3 | pytz==2018.9 4 | -------------------------------------------------------------------------------- /web_server/web_server/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .Doggo import Doggo 2 | from .Owner import Owner 3 | -------------------------------------------------------------------------------- /web_server/web_server/models/Owner.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class Owner(models.Model): 4 | name = models.CharField(max_length=30) 5 | -------------------------------------------------------------------------------- /pi_server/.env.sample: -------------------------------------------------------------------------------- 1 | DJANGO_SECURITY_KEY=longstringofrandomletters 2 | GITHUB_PERSONAL_ACCESS_TOKEN=get_this_from_github 3 | GITHUB_WEBHOOK_SECRET=longstringofrandomletters 4 | -------------------------------------------------------------------------------- /web_server/.env.sample: -------------------------------------------------------------------------------- 1 | DJANGO_SECURITY_KEY=longstringofrandomletters 2 | GITHUB_PERSONAL_ACCESS_TOKEN=get_this_from_github 3 | GITHUB_WEBHOOK_SECRET=longstringofrandomletters 4 | -------------------------------------------------------------------------------- /pi_server/pi_server/views/hello.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse, HttpResponseBadRequest 2 | 3 | def hello(request): 4 | return HttpResponse('Good job, your pi server is working!') 5 | -------------------------------------------------------------------------------- /pi_server/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | django = "*" 10 | python-dotenv = "*" 11 | -------------------------------------------------------------------------------- /web_server/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | django = "*" 10 | python-dotenv = "*" 11 | pygithub = "*" 12 | 13 | [requires] 14 | -------------------------------------------------------------------------------- /web_server/web_server/views/gh_hash.py: -------------------------------------------------------------------------------- 1 | import hmac 2 | from hashlib import sha1 3 | from django.utils.encoding import force_bytes 4 | 5 | def hash(wh, request): 6 | mac = hmac.new(force_bytes(wh), msg=force_bytes(request.body), digestmod=sha1) 7 | return 'sha1={}'.format(mac.hexdigest()) 8 | -------------------------------------------------------------------------------- /web_server/web_server/models/Doggo.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from web_server.models.Owner import Owner 3 | 4 | class Doggo(models.Model): 5 | name = models.CharField(max_length=30) 6 | is_good = models.BooleanField(default=True) 7 | owner = models.ForeignKey(Owner, on_delete=models.CASCADE) 8 | -------------------------------------------------------------------------------- /web_server/web_server/urls.py: -------------------------------------------------------------------------------- 1 | from web_server import views 2 | from django.contrib import admin 3 | from django.urls import path 4 | 5 | urlpatterns = [ 6 | path('', views.root, name='root'), 7 | path('github_webhook' ,views.github_webhook, name='github_webhook'), 8 | path('admin/', admin.site.urls), 9 | ] 10 | -------------------------------------------------------------------------------- /glitch.json: -------------------------------------------------------------------------------- 1 | { 2 | "install": "pip install --user pipenv && cd web_server && pipenv install", 3 | "start": "cd web_server && pipenv run python manage.py runserver", 4 | "watch": { 5 | "ignore": [ 6 | "\\.pyc$" 7 | ], 8 | "restart": { 9 | "include": [ 10 | "\\.py$", 11 | "^start\\.sh" 12 | ] 13 | }, 14 | "throttle": 1000 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pi_server/pi_server/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for pi_server project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pi_server.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /web_server/web_server/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for web_server project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'web_server.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /web_server/web_server/tests/test_model_owner.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from web_server.models import Owner 3 | 4 | class OwnerTest(TestCase): 5 | 6 | def create_owner(self, name='Al Gore'): 7 | return Owner.objects.create(name=name) 8 | 9 | def test_owner_creation(self): 10 | o1 = self.create_owner() 11 | o2 = self.create_owner(name='London Tipton') 12 | 13 | self.assertEqual('Al Gore', o1.name) 14 | self.assertEqual('London Tipton', o2.name) 15 | -------------------------------------------------------------------------------- /web_server/web_server/views/__init__.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse, HttpResponseBadRequest 2 | from django.views.decorators.csrf import csrf_exempt 3 | 4 | from web_server.views.handle_github_event import handle_github_event 5 | 6 | def root(request): 7 | return HttpResponse('Good job, your server is working!') 8 | 9 | @csrf_exempt 10 | def github_webhook(request): 11 | 12 | if request.method != 'POST': 13 | return HttpResponseBadRequest('needs to be post \U0001F61E') 14 | 15 | return handle_github_event(request) 16 | -------------------------------------------------------------------------------- /pi_server/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == '__main__': 6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pi_server.settings') 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /web_server/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == '__main__': 6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'web_server.settings') 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /pi_server/pi_server/urls.py: -------------------------------------------------------------------------------- 1 | """pi_server URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path 18 | from pi_server.views.hello import hello 19 | 20 | urlpatterns = [ 21 | path('admin/', admin.site.urls), 22 | path('hello/', hello), 23 | ] 24 | -------------------------------------------------------------------------------- /web_server/web_server/views/handle_github_event.py: -------------------------------------------------------------------------------- 1 | from web_server.views.gh_hash import hash 2 | from django.http import HttpResponse, HttpResponseBadRequest 3 | import json 4 | import os 5 | 6 | webhook_secret_base = os.getenv('GITHUB_WEBHOOK_SECRET') # TODO remove 7 | 8 | def is_legit(request): 9 | webhook_secret = hash(webhook_secret_base, request) 10 | given_secret = request.META.get('HTTP_X_HUB_SIGNATURE') 11 | # TODO: check if given_secret and webhook_secret is same 12 | return True 13 | 14 | def handle_github_event(request): 15 | if not is_legit(request): 16 | return HttpResponseBadRequest('bad secret, {}!'.format(given_secret)) 17 | 18 | payload = request.POST 19 | print('received this payload:') 20 | print(payload) 21 | 22 | # GOAL 23 | # When an issue is opened, comment on issue, say they got 50 tokens 24 | # when an issue is closed, comment on issue, say thanks, 50 tokens 25 | 26 | return HttpResponse('\U0001F44D') 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 UCSD ECE Undergraduate Student Council 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pi_server/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "8601b3a6d7b9c76abba665436069a2b0ad121fef230ade8345ea3a6f7fbd2a08" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": {}, 8 | "sources": [ 9 | { 10 | "name": "pypi", 11 | "url": "https://pypi.org/simple", 12 | "verify_ssl": true 13 | } 14 | ] 15 | }, 16 | "default": { 17 | "django": { 18 | "hashes": [ 19 | "sha256:275bec66fd2588dd517ada59b8bfb23d4a9abc5a362349139ddda3c7ff6f5ade", 20 | "sha256:939652e9d34d7d53d74d5d8ef82a19e5f8bb2de75618f7e5360691b6e9667963" 21 | ], 22 | "index": "pypi", 23 | "version": "==2.1.7" 24 | }, 25 | "python-dotenv": { 26 | "hashes": [ 27 | "sha256:a84569d0e00d178bc5b957f7ff208bf49287cbf61857c31c258c4a91f571527b", 28 | "sha256:c9b1ddd3cdbe75c7d462cb84674d87130f4b948f090f02c7d7144779afb99ae0" 29 | ], 30 | "index": "pypi", 31 | "version": "==0.10.1" 32 | }, 33 | "pytz": { 34 | "hashes": [ 35 | "sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9", 36 | "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c" 37 | ], 38 | "version": "==2018.9" 39 | } 40 | }, 41 | "develop": {} 42 | } 43 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Python CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-python/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | # use `-browsers` prefix for selenium tests, e.g. `3.6.1-browsers` 11 | - image: circleci/python:3.6.1 12 | 13 | # Specify service dependencies here if necessary 14 | # CircleCI maintains a library of pre-built images 15 | # documented at https://circleci.com/docs/2.0/circleci-images/ 16 | # - image: circleci/postgres:9.4 17 | 18 | working_directory: ~/repo/web_server 19 | 20 | steps: 21 | - checkout 22 | 23 | # Download and cache dependencies 24 | - restore_cache: 25 | keys: 26 | - v1-dependencies-{{ checksum "web_server/Pipfile.lock" }} 27 | # fallback to using the latest cache if no exact match is found 28 | - v1-dependencies- 29 | 30 | - run: 31 | name: install dependencies 32 | command: | 33 | cd web_server 34 | sudo pip install pipenv 35 | pipenv install 36 | 37 | - save_cache: 38 | paths: 39 | - ./venv 40 | key: v1-dependencies-{{ checksum "web_server/Pipfile.lock" }} 41 | 42 | # run tests! 43 | - run: 44 | name: run tests 45 | command: | 46 | cd web_server 47 | pipenv run python manage.py test 48 | 49 | - store_artifacts: 50 | path: test-reports 51 | destination: test-reports 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project Vending Machine [![CircleCI](https://circleci.com/gh/eceusc/project-vending-machine.svg?style=svg)](https://circleci.com/gh/eceusc/project-vending-machine) ![](https://img.shields.io/badge/slack-%23project--vending--machine-%234d394b.svg?style=flat&logo=slack&link=https%3A%2F%2Feceopensource.slack.com%2Fmessages%2FCF90BHRAM) 2 | 3 | A vending machine powered by open source contributions! :atm: 4 | 5 | ## About 6 | 7 | We plan to create an actual, phyical vending machine where you can pay for items with open source contributions on our [@eceusc](https://www.github.com/eceusc) GitHub organization! 8 | 9 | The "open source contributions" will be powered by a Django app that listen to the GitHub API for issues and pull requests created by users. The vending machine will made with a Rasperry Pi that controls an Arduino to dispense items. We will build a webapp that users can use to login and "buy" items. 10 | 11 | ## Structure 12 | 13 | This repo is empty as of now (bc the project hasn't started yet), but this repo will be divided into several parts. For example, there will be directories called `/arduino`, `/pi-server`, `/web-app`, etc. Each directory will have their own contribution guides. 14 | 15 | ## Contributing 16 | 17 | If you would like to contribute, follow these steps: 18 | 19 | 1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository 20 | 2. On your forked repository, create a branch for your specific feature/fix (`git checkout -b my-contribution`) 21 | 3. Commit your changes, push it to your repo (`git commit -m "message describing your changes"`, `git push origin master`) 22 | 4. On GitHub, send a pull request to this repository! 23 | 24 | Also, take a look at the Issues tab above to see what needs help. Also keep in mind our Code of Conduct while you make your contributions :) 25 | -------------------------------------------------------------------------------- /pi_server/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # IPython 78 | profile_default/ 79 | ipython_config.py 80 | 81 | # pyenv 82 | .python-version 83 | 84 | # celery beat schedule file 85 | celerybeat-schedule 86 | 87 | # SageMath parsed files 88 | *.sage.py 89 | 90 | # Environments 91 | .env 92 | .venv 93 | env/ 94 | venv/ 95 | ENV/ 96 | env.bak/ 97 | venv.bak/ 98 | 99 | # Spyder project settings 100 | .spyderproject 101 | .spyproject 102 | 103 | # Rope project settings 104 | .ropeproject 105 | 106 | # mkdocs documentation 107 | /site 108 | 109 | # mypy 110 | .mypy_cache/ 111 | .dmypy.json 112 | dmypy.json 113 | 114 | # Pyre type checker 115 | .pyre/ 116 | -------------------------------------------------------------------------------- /web_server/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # IPython 78 | profile_default/ 79 | ipython_config.py 80 | 81 | # pyenv 82 | .python-version 83 | 84 | # celery beat schedule file 85 | celerybeat-schedule 86 | 87 | # SageMath parsed files 88 | *.sage.py 89 | 90 | # Environments 91 | .env 92 | .venv 93 | env/ 94 | venv/ 95 | ENV/ 96 | env.bak/ 97 | venv.bak/ 98 | 99 | # Spyder project settings 100 | .spyderproject 101 | .spyproject 102 | 103 | # Rope project settings 104 | .ropeproject 105 | 106 | # mkdocs documentation 107 | /site 108 | 109 | # mypy 110 | .mypy_cache/ 111 | .dmypy.json 112 | dmypy.json 113 | 114 | # Pyre type checker 115 | .pyre/ 116 | -------------------------------------------------------------------------------- /web_server/README.md: -------------------------------------------------------------------------------- 1 | # project-vending-machine: Web Server 2 | 3 | This directory contains a [Django](https://www.djangoproject.com/) app, written in python, that controls the database, web app, and other key parts for our vending machine. 4 | 5 | ## Getting Started 6 | 7 | ### Quick Demo 8 | 9 | `Remix with Glitch` & `Deploy with Heroku` buttons coming soon! 10 | 11 | ### Prerequisites 12 | 13 | Make sure you have the following installed: 14 | 15 | - [`python3`](https://www.python.org/downloads/) 16 | - [`pip`](https://pip.pypa.io/en/stable/installing/) 17 | - [`pipenv`](https://pipenv.readthedocs.io/en/latest/install/#installing-pipenv) - install with `pip install pipenv --user` 18 | 19 | ### Installing 20 | 21 | Make sure you have the `project-vending-machine` repo cloned already, then: 22 | 23 | ``` bash 24 | cd web_server 25 | pipenv install 26 | pipenv shell 27 | python manage.py runserver 28 | ``` 29 | 30 | And your server is running! 31 | 32 | When you do `python manage.py runserver`, you might see warning message like: 33 | 34 | ``` 35 | python manage.py runserver 36 | You have 15 unapplied migration(s). Your project may not work properly until you... 37 | ``` 38 | 39 | This has to do with how Django takes care of databases - which means you'll occasionally need to "migrate" database changes, like so: 40 | 41 | ```bash 42 | python manage.py migrate 43 | ``` 44 | 45 | Now, head to http://localhost:8000 to see your webserver running! 46 | 47 | ### Configuration 48 | 49 | This Django app uses a `.env` file to safely store secrets like passwords or API keys - so that way you don't accidentally commit your secrets to GitHub! 50 | 51 | To configure your secrets, copy and paste the [`.env.sample`](https://github.com/asg017/project-vending-machine/blob/master/.env.sample) file into a file called `.env`, and add your secrets ONLY to that new `.env` file. 52 | 53 | Here are all the key/values that should be in your .env file: 54 | 55 | |Key|Description|Example 56 | |-|-|- 57 | |`DJANGO_SECURITY_KEY`|A secure password that Django uses for encryption/security|`DJANGO_SECURITY_KEY=QxcT0L%11E*!pbKScP$nRz` 58 | |`GITHUB_PERSONAL_ACCESS_TOKEN`|A secret token generator by GitHub used to automatically comment on new issues/PR's - generate [here](https://github.com/settings/tokens) |`GITHUB_PERSONAL_ACCESS_TOKEN=abc978de86bcd8e7850` 59 | |`GITHUB_WEBHOOK_SECRET`|A secure password to verify that an incoming GitHub webook message is indeed from GitHub|`GITHUB_WEBHOOK_SECRET=r!S@!PEr2UM@z$Bomi2@XQd` 60 | 61 | #### DJANGO_SECURITY_KEY error 62 | 63 | If you tried running `python manage.py runserver` but got a warning about including a secret in a `.env` file, make sure you have a `.env` file in the parent directory. There should be a `.env.sample` file in that folder, you can copy/paste or run `cp .env.sample .env` to make your own. Make sure to replace those values with actual secure passwords! ([password generator](https://1password.com/password-generator/)) 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## UC San Diego Principles of Community 4 | 5 | As a student-ran organization within UC San Diego, all work done in the name of 6 | an Open Source project should be done with the 7 | [UC San Diego Principles of Community](https://ucsd.edu/about/principles.html) 8 | in mind. Failure to do so will result in the enforcement of this code described below. 9 | 10 | ## Our Pledge 11 | 12 | In the interest of fostering an open and welcoming environment, we as 13 | contributors and maintainers pledge to making participation in our project and 14 | our community a harassment-free experience for everyone, regardless of age, body 15 | size, disability, ethnicity, sex characteristics, gender identity and expression, 16 | level of experience, education, socio-economic status, nationality, personal 17 | appearance, race, religion, or sexual identity and orientation. 18 | 19 | ## Our Standards 20 | 21 | Examples of behavior that contributes to creating a positive environment 22 | include: 23 | 24 | * Using welcoming and inclusive language 25 | * Being respectful of differing viewpoints and experiences 26 | * Gracefully accepting constructive criticism 27 | * Focusing on what is best for the community 28 | * Showing empathy towards other community members 29 | 30 | Examples of unacceptable behavior by participants include: 31 | 32 | * The use of sexualized language or imagery and unwelcome sexual attention or 33 | advances 34 | * Trolling, insulting/derogatory comments, and personal or political attacks 35 | * Public or private harassment 36 | * Publishing others' private information, such as a physical or electronic 37 | address, without explicit permission 38 | * Other conduct which could reasonably be considered inappropriate in a 39 | professional setting 40 | 41 | ## Our Responsibilities 42 | 43 | Project maintainers are responsible for clarifying the standards of acceptable 44 | behavior and are expected to take appropriate and fair corrective action in 45 | response to any instances of unacceptable behavior. 46 | 47 | Project maintainers have the right and responsibility to remove, edit, or 48 | reject comments, commits, code, wiki edits, issues, and other contributions 49 | that are not aligned to this Code of Conduct, or to ban temporarily or 50 | permanently any contributor for other behaviors that they deem inappropriate, 51 | threatening, offensive, or harmful. 52 | 53 | ## Scope 54 | 55 | This Code of Conduct applies both within project spaces and in public spaces 56 | when an individual is representing the project or its community. Examples of 57 | representing a project or community include using an official project e-mail 58 | address, posting via an official social media account, or acting as an appointed 59 | representative at an online or offline event. Representation of a project may be 60 | further defined and clarified by project maintainers. 61 | 62 | ## Enforcement 63 | 64 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 65 | reported by contacting the project team at eceusc+opensource@eng.ucsd.edu. All 66 | complaints will be reviewed and investigated and will result in a response that 67 | is deemed necessary and appropriate to the circumstances. The project team is 68 | obligated to maintain confidentiality with regard to the reporter of an incident. 69 | Further details of specific enforcement policies may be posted separately. 70 | 71 | Project maintainers who do not follow or enforce the Code of Conduct in good 72 | faith may face temporary or permanent repercussions as determined by other 73 | members of the project's leadership. 74 | 75 | ## Attribution 76 | 77 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 78 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 79 | 80 | [homepage]: https://www.contributor-covenant.org 81 | -------------------------------------------------------------------------------- /web_server/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "47c6eee846a7988a67c2ba03c6fb9d7310e2d488a45e8af47746ac6c2c38ac0f" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": {}, 8 | "sources": [ 9 | { 10 | "name": "pypi", 11 | "url": "https://pypi.org/simple", 12 | "verify_ssl": true 13 | } 14 | ] 15 | }, 16 | "default": { 17 | "certifi": { 18 | "hashes": [ 19 | "sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7", 20 | "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033" 21 | ], 22 | "version": "==2018.11.29" 23 | }, 24 | "chardet": { 25 | "hashes": [ 26 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 27 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 28 | ], 29 | "version": "==3.0.4" 30 | }, 31 | "deprecated": { 32 | "hashes": [ 33 | "sha256:8bfeba6e630abf42b5d111b68a05f7fe3d6de7004391b3cd614947594f87a4ff", 34 | "sha256:b784e0ca85a8c1e694d77e545c10827bd99772392e79d5f5442e761515a1246e" 35 | ], 36 | "version": "==1.2.4" 37 | }, 38 | "django": { 39 | "hashes": [ 40 | "sha256:275bec66fd2588dd517ada59b8bfb23d4a9abc5a362349139ddda3c7ff6f5ade", 41 | "sha256:939652e9d34d7d53d74d5d8ef82a19e5f8bb2de75618f7e5360691b6e9667963" 42 | ], 43 | "index": "pypi", 44 | "version": "==2.1.7" 45 | }, 46 | "idna": { 47 | "hashes": [ 48 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 49 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 50 | ], 51 | "version": "==2.8" 52 | }, 53 | "pygithub": { 54 | "hashes": [ 55 | "sha256:263102b43a83e2943900c1313109db7a00b3b78aeeae2c9137ba694982864872" 56 | ], 57 | "index": "pypi", 58 | "version": "==1.43.5" 59 | }, 60 | "pyjwt": { 61 | "hashes": [ 62 | "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e", 63 | "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96" 64 | ], 65 | "version": "==1.7.1" 66 | }, 67 | "python-dotenv": { 68 | "hashes": [ 69 | "sha256:a84569d0e00d178bc5b957f7ff208bf49287cbf61857c31c258c4a91f571527b", 70 | "sha256:c9b1ddd3cdbe75c7d462cb84674d87130f4b948f090f02c7d7144779afb99ae0" 71 | ], 72 | "index": "pypi", 73 | "version": "==0.10.1" 74 | }, 75 | "pytz": { 76 | "hashes": [ 77 | "sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9", 78 | "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c" 79 | ], 80 | "version": "==2018.9" 81 | }, 82 | "requests": { 83 | "hashes": [ 84 | "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", 85 | "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" 86 | ], 87 | "version": "==2.21.0" 88 | }, 89 | "urllib3": { 90 | "hashes": [ 91 | "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", 92 | "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" 93 | ], 94 | "version": "==1.24.1" 95 | }, 96 | "wrapt": { 97 | "hashes": [ 98 | "sha256:4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533" 99 | ], 100 | "version": "==1.11.1" 101 | } 102 | }, 103 | "develop": {} 104 | } 105 | -------------------------------------------------------------------------------- /pi_server/pi_server/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for pi_server project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.1.7. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.1/ref/settings/ 11 | """ 12 | 13 | import os 14 | import logging 15 | 16 | from dotenv import load_dotenv 17 | from pathlib import Path 18 | env_path = Path('..') / '.env' 19 | load_dotenv(dotenv_path=str(env_path)) 20 | 21 | logger = logging.getLogger(__name__) 22 | 23 | 24 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 25 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 26 | 27 | 28 | # Quick-start development settings - unsuitable for production 29 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ 30 | 31 | # SECURITY WARNING: keep the secret key used in production secret! 32 | security_key = os.getenv('DJANGO_SECURITY_KEY') 33 | 34 | # We don't completely kill the server, in order to make this more friendly as a whole 35 | if not security_key: 36 | logger.warning(""" 37 | WARNING: You need to include a "DJANGO_SECURITY_KEY in the .env file. 38 | Please go here to learn more about the .env file and how to properly fill it: 39 | 40 | https://github.com/eceusc/project-vending-machine/web_server/README.md#configuration 41 | 42 | """) 43 | SECRET_KEY = 'WARNING_CHANGE_ME' 44 | else: 45 | SECRET_KEY = security_key 46 | 47 | 48 | # SECURITY WARNING: don't run with debug turned on in production! 49 | DEBUG = True 50 | 51 | ALLOWED_HOSTS = [] 52 | 53 | 54 | # Application definition 55 | 56 | INSTALLED_APPS = [ 57 | 'django.contrib.admin', 58 | 'django.contrib.auth', 59 | 'django.contrib.contenttypes', 60 | 'django.contrib.sessions', 61 | 'django.contrib.messages', 62 | 'django.contrib.staticfiles', 63 | ] 64 | 65 | MIDDLEWARE = [ 66 | 'django.middleware.security.SecurityMiddleware', 67 | 'django.contrib.sessions.middleware.SessionMiddleware', 68 | 'django.middleware.common.CommonMiddleware', 69 | 'django.middleware.csrf.CsrfViewMiddleware', 70 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 71 | 'django.contrib.messages.middleware.MessageMiddleware', 72 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 73 | ] 74 | 75 | ROOT_URLCONF = 'pi_server.urls' 76 | 77 | TEMPLATES = [ 78 | { 79 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 80 | 'DIRS': [], 81 | 'APP_DIRS': True, 82 | 'OPTIONS': { 83 | 'context_processors': [ 84 | 'django.template.context_processors.debug', 85 | 'django.template.context_processors.request', 86 | 'django.contrib.auth.context_processors.auth', 87 | 'django.contrib.messages.context_processors.messages', 88 | ], 89 | }, 90 | }, 91 | ] 92 | 93 | WSGI_APPLICATION = 'pi_server.wsgi.application' 94 | 95 | 96 | # Database 97 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases 98 | 99 | DATABASES = { 100 | 'default': { 101 | 'ENGINE': 'django.db.backends.sqlite3', 102 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 103 | } 104 | } 105 | 106 | 107 | # Password validation 108 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators 109 | 110 | AUTH_PASSWORD_VALIDATORS = [ 111 | { 112 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 113 | }, 114 | { 115 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 116 | }, 117 | { 118 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 119 | }, 120 | { 121 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 122 | }, 123 | ] 124 | 125 | 126 | # Internationalization 127 | # https://docs.djangoproject.com/en/2.1/topics/i18n/ 128 | 129 | LANGUAGE_CODE = 'en-us' 130 | 131 | TIME_ZONE = 'UTC' 132 | 133 | USE_I18N = True 134 | 135 | USE_L10N = True 136 | 137 | USE_TZ = True 138 | 139 | 140 | # Static files (CSS, JavaScript, Images) 141 | # https://docs.djangoproject.com/en/2.1/howto/static-files/ 142 | 143 | STATIC_URL = '/static/' 144 | -------------------------------------------------------------------------------- /web_server/web_server/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for web_server project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.1.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.1/ref/settings/ 11 | """ 12 | 13 | import logging 14 | import os 15 | 16 | from dotenv import load_dotenv 17 | from pathlib import Path 18 | env_path = Path('..') / '.env' 19 | load_dotenv(dotenv_path=str(env_path)) 20 | 21 | logger = logging.getLogger(__name__) 22 | 23 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 24 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 25 | 26 | 27 | # Quick-start development settings - unsuitable for production 28 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ 29 | 30 | security_key = os.getenv('DJANGO_SECURITY_KEY') 31 | 32 | # We don't completely kill the server, in order to make this more friendly as a whole 33 | if not security_key: 34 | logger.warning(""" 35 | WARNING: You need to include a "DJANGO_SECURITY_KEY in the .env file. 36 | Please go here to learn more about the .env file and how to properly fill it: 37 | 38 | https://github.com/eceusc/project-vending-machine/web_server/README.md#configuration 39 | 40 | """) 41 | SECRET_KEY = 'WARNING_CHANGE_ME' 42 | else: 43 | SECRET_KEY = security_key 44 | 45 | # SECURITY WARNING: don't run with debug turned on in production! 46 | DEBUG = True 47 | 48 | ALLOWED_HOSTS = [ 49 | 'localhost', 50 | '.ngrok.io', 51 | '.glitch.me', 52 | 'localhost:8000', 53 | '127.0.0.1', 54 | ] 55 | 56 | APPEND_SLASH=False 57 | 58 | # Application definition 59 | 60 | INSTALLED_APPS = [ 61 | 'django.contrib.admin', 62 | 'django.contrib.auth', 63 | 'django.contrib.contenttypes', 64 | 'django.contrib.sessions', 65 | 'django.contrib.messages', 66 | 'django.contrib.staticfiles', 67 | 'web_server', 68 | ] 69 | 70 | MIDDLEWARE = [ 71 | 'django.middleware.security.SecurityMiddleware', 72 | 'django.contrib.sessions.middleware.SessionMiddleware', 73 | 'django.middleware.common.CommonMiddleware', 74 | 'django.middleware.csrf.CsrfViewMiddleware', 75 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 76 | 'django.contrib.messages.middleware.MessageMiddleware', 77 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 78 | ] 79 | 80 | ROOT_URLCONF = 'web_server.urls' 81 | 82 | TEMPLATES = [ 83 | { 84 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 85 | 'DIRS': [], 86 | 'APP_DIRS': True, 87 | 'OPTIONS': { 88 | 'context_processors': [ 89 | 'django.template.context_processors.debug', 90 | 'django.template.context_processors.request', 91 | 'django.contrib.auth.context_processors.auth', 92 | 'django.contrib.messages.context_processors.messages', 93 | ], 94 | }, 95 | }, 96 | ] 97 | 98 | WSGI_APPLICATION = 'web_server.wsgi.application' 99 | 100 | 101 | # Database 102 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases 103 | 104 | DATABASES = { 105 | 'default': { 106 | 'ENGINE': 'django.db.backends.sqlite3', 107 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 108 | } 109 | } 110 | 111 | 112 | # Password validation 113 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators 114 | 115 | AUTH_PASSWORD_VALIDATORS = [ 116 | { 117 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 118 | }, 119 | { 120 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 121 | }, 122 | { 123 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 124 | }, 125 | { 126 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 127 | }, 128 | ] 129 | 130 | 131 | # Internationalization 132 | # https://docs.djangoproject.com/en/2.1/topics/i18n/ 133 | 134 | LANGUAGE_CODE = 'en-us' 135 | 136 | TIME_ZONE = 'UTC' 137 | 138 | USE_I18N = True 139 | 140 | USE_L10N = True 141 | 142 | USE_TZ = True 143 | 144 | 145 | # Static files (CSS, JavaScript, Images) 146 | # https://docs.djangoproject.com/en/2.1/howto/static-files/ 147 | 148 | STATIC_URL = '/static/' 149 | --------------------------------------------------------------------------------