├── .gitignore ├── .idea ├── Panzerbeere.iml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md ├── architecture.png ├── docker-compose.yml ├── ocr ├── Dockerfile ├── Procfile ├── __init__.py ├── db.sqlite3 ├── manage.py ├── ocr │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── requirements.txt └── worker │ ├── OcrService.py │ ├── OcrWorker.py │ ├── __init__.py │ └── apps.py ├── variables.env └── webapp ├── Dockerfile ├── Procfile ├── __init__.py ├── manage.py ├── media └── tmp │ ├── somename.png │ ├── somename_1.png │ ├── somename_1_Ax4clCq.png │ ├── somename_1_P6VMEfv.png │ ├── somename_1_PJp9ToK.png │ ├── somename_1_SFGjDJq.png │ ├── somename_1_jQJGDVl.png │ ├── somename_1_pYQCvs5.png │ ├── somename_1_pdYjRrp.png │ └── test_1.png ├── requirements.txt ├── tasks ├── TaskProcessingService.py ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20170928_2000.py │ ├── 0003_auto_20170928_2003.py │ ├── 0004_auto_20171001_0024.py │ ├── 0005_auto_20171001_0034.py │ ├── 0006_auto_20171001_2311.py │ ├── 0007_auto_20171001_2313.py │ ├── 0008_auto_20171001_2318.py │ ├── 0009_auto_20171008_1617.py │ ├── 0010_auto_20171008_1656.py │ ├── 0011_auto_20171008_2138.py │ └── __init__.py ├── models.py ├── serializers.py ├── signals.py ├── tests.py └── views.py ├── test.txt └── webapp ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py /.gitignore: -------------------------------------------------------------------------------- 1 | # ipfs 2 | ipfs/ 3 | 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 5 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 6 | 7 | # User-specific stuff: 8 | .idea/**/workspace.xml 9 | .idea/**/tasks.xml 10 | .idea/dictionaries 11 | 12 | # Sensitive or high-churn files: 13 | .idea/**/dataSources/ 14 | .idea/**/dataSources.ids 15 | .idea/**/dataSources.xml 16 | .idea/**/dataSources.local.xml 17 | .idea/**/sqlDataSources.xml 18 | .idea/**/dynamic.xml 19 | .idea/**/uiDesigner.xml 20 | 21 | # Gradle: 22 | .idea/**/gradle.xml 23 | .idea/**/libraries 24 | 25 | # CMake 26 | cmake-build-debug/ 27 | 28 | # Mongo Explorer plugin: 29 | .idea/**/mongoSettings.xml 30 | 31 | ## File-based project format: 32 | *.iws 33 | 34 | ## Plugin-specific files: 35 | 36 | # IntelliJ 37 | out/ 38 | 39 | # mpeltonen/sbt-idea plugin 40 | .idea_modules/ 41 | 42 | # JIRA plugin 43 | atlassian-ide-plugin.xml 44 | 45 | # Cursive Clojure plugin 46 | .idea/replstate.xml 47 | 48 | # Crashlytics plugin (for Android Studio and IntelliJ) 49 | com_crashlytics_export_strings.xml 50 | crashlytics.properties 51 | crashlytics-build.properties 52 | fabric.properties 53 | 54 | # Django 55 | *.egg-info 56 | *.pot 57 | *.py[co] 58 | .tox/ 59 | __pycache__ 60 | MANIFEST 61 | dist/ 62 | docs/_build/ 63 | docs/locale/ 64 | node_modules/ 65 | tests/coverage_html/ 66 | tests/.coverage 67 | build/ 68 | tests/report/ 69 | -------------------------------------------------------------------------------- /.idea/Panzerbeere.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RabbitMQ-with-Django 2 | Example for using Microservices with RabbitMQ in a Django Web-Application 3 | 4 | ![Architecture-Diagram](https://github.com/anjakammer/RabbitMQ-with-Django/blob/master/architecture.png) 5 | -------------------------------------------------------------------------------- /architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/architecture.png -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | db: 4 | image: postgres 5 | ports: 6 | - '5432:5432' 7 | - '15672:15672' 8 | networks: 9 | - 'default' 10 | webapp: 11 | build: 12 | context: webapp 13 | image: webapp 14 | env_file: 15 | - variables.env 16 | volumes: 17 | - './webapp:/webappDev' 18 | - './webapp/data/ipfs:/data/ipfs' 19 | working_dir: "/webappDev" 20 | ports: 21 | - "8000:8000" 22 | networks: 23 | - 'default' 24 | depends_on: 25 | - db 26 | - rabbitmq 27 | rabbitmq: 28 | image: rabbitmq:3.6.12-management 29 | hostname: rabbitmq 30 | ports: 31 | - "8080:15672" # management port (guest:guest) 32 | - "5672:5672" # amqp port 33 | networks: 34 | - 'default' 35 | ocr: 36 | build: 37 | context: ocr 38 | image: ocr 39 | env_file: 40 | - variables.env 41 | volumes: 42 | - './ocr:/ocrDev' 43 | - './ocr/data/ipfs:/data/ipfs' 44 | working_dir: "/ocrDev" 45 | depends_on: 46 | - rabbitmq 47 | networks: 48 | - 'default' 49 | 50 | networks: 51 | default: 52 | -------------------------------------------------------------------------------- /ocr/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM dbmobilelife/docker-python-opencv-tesseract 2 | ENV PYTHONUNBUFFERED 1 3 | 4 | RUN locale-gen en_US.UTF-8 5 | ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8' 6 | 7 | RUN apt-get update && apt-get install -y \ 8 | software-properties-common \ 9 | wget \ 10 | git \ 11 | swig \ 12 | python3-dev \ 13 | python3-setuptools \ 14 | python3-pip \ 15 | tesseract-ocr-deu \ 16 | tesseract-ocr-eng 17 | 18 | RUN wget https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz 19 | RUN tar -xvf go1.8.3.linux-amd64.tar.gz 20 | RUN mv go /usr/local 21 | 22 | RUN mkdir /ocr 23 | 24 | # Golang config 25 | ENV GOROOT /usr/local/go 26 | ENV GOPATH /webapp 27 | ENV PATH $GOPATH/bin:$GOROOT/bin:$PATH 28 | 29 | # ipfs config 30 | ENV IPFS_PATH /data/ipfs 31 | RUN go get -u github.com/ipfs/ipfs-update 32 | RUN ipfs-update install latest 33 | VOLUME $IPFS_PATH 34 | 35 | WORKDIR /ocr 36 | ADD requirements.txt /ocr/ 37 | RUN pip3 install -r requirements.txt 38 | ADD . /ocr/ 39 | 40 | # honcho - starts ipdf daemon and django server 41 | RUN pip3 install honcho 42 | ADD Procfile /Procfile 43 | WORKDIR / 44 | CMD honcho start 45 | -------------------------------------------------------------------------------- /ocr/Procfile: -------------------------------------------------------------------------------- 1 | ipfs: ipfs daemon 2 | django: python3 manage.py runserver 0.0.0.0:8000 -------------------------------------------------------------------------------- /ocr/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/ocr/__init__.py -------------------------------------------------------------------------------- /ocr/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/ocr/db.sqlite3 -------------------------------------------------------------------------------- /ocr/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", "ocr.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /ocr/ocr/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/ocr/ocr/__init__.py -------------------------------------------------------------------------------- /ocr/ocr/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for ocr project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.11.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.11/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'h$0!&$(#w!c$)x=#4+_^!vp28mqq%-3j0mfo!jm9+keb)qj=-j' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'worker' 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | 'django.middleware.security.SecurityMiddleware', 45 | 'django.contrib.sessions.middleware.SessionMiddleware', 46 | 'django.middleware.common.CommonMiddleware', 47 | 'django.middleware.csrf.CsrfViewMiddleware', 48 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | ] 52 | 53 | ROOT_URLCONF = 'ocr.urls' 54 | 55 | TEMPLATES = [ 56 | { 57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 58 | 'DIRS': [], 59 | 'APP_DIRS': True, 60 | 'OPTIONS': { 61 | 'context_processors': [ 62 | 'django.template.context_processors.debug', 63 | 'django.template.context_processors.request', 64 | 'django.contrib.auth.context_processors.auth', 65 | 'django.contrib.messages.context_processors.messages', 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = 'ocr.wsgi.application' 72 | 73 | 74 | # Database 75 | # https://docs.djangoproject.com/en/1.11/ref/settings/#databases 76 | 77 | DATABASES = { 78 | 'default': { 79 | 'ENGINE': 'django.db.backends.sqlite3', 80 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 81 | } 82 | } 83 | 84 | 85 | # Password validation 86 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 87 | 88 | AUTH_PASSWORD_VALIDATORS = [ 89 | { 90 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 91 | }, 92 | { 93 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 94 | }, 95 | { 96 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 97 | }, 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 100 | }, 101 | ] 102 | 103 | 104 | # Internationalization 105 | # https://docs.djangoproject.com/en/1.11/topics/i18n/ 106 | 107 | LANGUAGE_CODE = 'en-us' 108 | 109 | TIME_ZONE = 'UTC' 110 | 111 | USE_I18N = True 112 | 113 | USE_L10N = True 114 | 115 | USE_TZ = True 116 | 117 | 118 | # Static files (CSS, JavaScript, Images) 119 | # https://docs.djangoproject.com/en/1.11/howto/static-files/ 120 | 121 | STATIC_URL = '/static/' 122 | -------------------------------------------------------------------------------- /ocr/ocr/urls.py: -------------------------------------------------------------------------------- 1 | """ocr URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.11/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: url(r'^$', 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: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import url 17 | from django.contrib import admin 18 | 19 | urlpatterns = [ 20 | url(r'^admin/', admin.site.urls), 21 | ] 22 | -------------------------------------------------------------------------------- /ocr/ocr/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for ocr 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/1.11/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", "ocr.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /ocr/requirements.txt: -------------------------------------------------------------------------------- 1 | Django>=1.8,<2.0 2 | pika 3 | Pillow==3.3.0 4 | gunicorn==19.6.0 5 | opencv-python 6 | pytesseract 7 | ipfsapi -------------------------------------------------------------------------------- /ocr/worker/OcrService.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | import pytesseract, cv2, os, re 3 | 4 | class OcrService(): 5 | TESSDATA_DIR_CONFIG = '--tessdata-dir "/usr/share/tesseract-ocr"' 6 | CHARS_TO_REMOVE = '[^\w\d_äÄöÖüÜß+\-(){}|\,\.\\n!?<>]+' 7 | 8 | def extract_text_from_image(self, image_path): 9 | normalized_image = self.__pre_processing(image_path) 10 | 11 | text = pytesseract.image_to_string(Image.open(normalized_image), lang='deu', config=self.TESSDATA_DIR_CONFIG) 12 | 13 | self.__remove_resources(image_path, normalized_image) 14 | 15 | decoded_string = re.sub(self.CHARS_TO_REMOVE, ' ', text).strip().replace('\n', '\\n') 16 | 17 | return decoded_string 18 | 19 | def __remove_resources(self, *args): 20 | for resource in args: 21 | os.remove(resource) 22 | 23 | def __pre_processing(self, source_image): 24 | image = cv2.imread(source_image, 0) 25 | 26 | gray = cv2.threshold(image, 0, 255, 27 | cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] 28 | 29 | output = "{}.png".format(os.getpid()) 30 | cv2.imwrite(output, gray) 31 | 32 | return output -------------------------------------------------------------------------------- /ocr/worker/OcrWorker.py: -------------------------------------------------------------------------------- 1 | import pika, time, json, os, ipfsapi, logging, sys 2 | from .OcrService import OcrService 3 | 4 | logging.basicConfig(stream=sys.stdout, level=logging.INFO) 5 | 6 | 7 | class OcrWorker(): 8 | QUEUE_BROKER = os.getenv('QUEUE_BROKER') 9 | QUEUE_NAME = 'ocr_' + os.getenv('QUEUE_NAME') 10 | IPFS_HOST = '127.0.0.1' 11 | IPFS_API_PORT = 5001 12 | 13 | KEY_PROCESSING_ERROR = 'processing error' 14 | 15 | def __init__(self): 16 | server_down = True 17 | while server_down: 18 | try: 19 | self.__connect() 20 | server_down = False 21 | logging.info('%s is up!', self.QUEUE_BROKER) 22 | except: 23 | server_down = True 24 | logging.warning('Cannot connect to %s try again in 2 Sec.', self.QUEUE_BROKER) 25 | time.sleep(2) 26 | 27 | def __connect(self): 28 | connection = pika.BlockingConnection(pika.ConnectionParameters(host=self.QUEUE_BROKER)) 29 | channel = connection.channel() 30 | 31 | channel.queue_declare(queue=self.QUEUE_NAME) 32 | 33 | channel.basic_qos(prefetch_count=1) 34 | channel.basic_consume(self.__on_request, queue=self.QUEUE_NAME) 35 | 36 | logging.info('[x] Awaiting OCR requests') 37 | 38 | try: 39 | channel.start_consuming() 40 | except: 41 | logging.error('%s restarts. Error occurred while listening to %s', OcrWorker.__name__, self.QUEUE_BROKER) 42 | 43 | def __on_request(self, ch, method, props, body): 44 | request_string = body.decode('unicode_escape') 45 | logging.info('[.] processing: %s' % request_string) 46 | 47 | request_object = json.loads(request_string) 48 | response = self.__process(request_object) 49 | 50 | ch.basic_publish(exchange='', 51 | routing_key=props.reply_to, 52 | properties=pika.BasicProperties(correlation_id= \ 53 | props.correlation_id), 54 | body=json.dumps(response)) 55 | ch.basic_ack(delivery_tag=method.delivery_tag) 56 | 57 | def __process(self, request): 58 | file_hash = request.get('file_hash') 59 | self.__load_image(file_hash) 60 | 61 | image_path = self.__load_image(file_hash) 62 | extracted_text = self.KEY_PROCESSING_ERROR 63 | try: 64 | ocr_service = OcrService() 65 | extracted_text = ocr_service.extract_text_from_image(image_path) 66 | except BaseException as e: 67 | logging.error('Failed to use %s with error: %s', OcrService.__name__, str(e)) 68 | 69 | return { 70 | "id": request.get('id'), 71 | "result": extracted_text 72 | } 73 | 74 | def __load_image(self, file_hash): 75 | file_storage = ipfsapi.connect(self.IPFS_HOST, self.IPFS_API_PORT) 76 | file_storage.get(file_hash) 77 | 78 | return file_hash 79 | -------------------------------------------------------------------------------- /ocr/worker/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'worker.apps.WorkerConfig' -------------------------------------------------------------------------------- /ocr/worker/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from .OcrWorker import OcrWorker 3 | 4 | class WorkerConfig(AppConfig): 5 | name = 'worker' 6 | 7 | def ready(self): 8 | OcrWorker() -------------------------------------------------------------------------------- /variables.env: -------------------------------------------------------------------------------- 1 | QUEUE_BROKER=rabbitmq 2 | QUEUE_NAME=task_queue 3 | DEFAULT_INDEX_TABLESPACE=tables -------------------------------------------------------------------------------- /webapp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | ENV PYTHONUNBUFFERED 1 3 | 4 | RUN apt-get update && apt-get install -y wget 5 | 6 | RUN wget https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz 7 | RUN tar -xvf go1.8.3.linux-amd64.tar.gz 8 | RUN mv go /usr/local 9 | 10 | RUN mkdir /webapp 11 | 12 | # Golang config 13 | ENV GOROOT /usr/local/go 14 | ENV GOPATH /webapp 15 | ENV PATH $GOPATH/bin:$GOROOT/bin:$PATH 16 | 17 | # ipfs config 18 | ENV IPFS_PATH /data/ipfs 19 | RUN go get -u github.com/ipfs/ipfs-update 20 | RUN ipfs-update install latest 21 | VOLUME $IPFS_PATH 22 | 23 | WORKDIR /webapp 24 | ADD requirements.txt /webapp/ 25 | RUN pip install -r requirements.txt 26 | ADD . /webapp/ 27 | 28 | EXPOSE 80 29 | 30 | # honcho - starts ipdf daemon and django server 31 | RUN pip install honcho 32 | ADD Procfile /Procfile 33 | WORKDIR / 34 | CMD honcho start 35 | -------------------------------------------------------------------------------- /webapp/Procfile: -------------------------------------------------------------------------------- 1 | ipfs: ipfs daemon 2 | django: python3 manage.py runserver 0.0.0.0:8000 -------------------------------------------------------------------------------- /webapp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/__init__.py -------------------------------------------------------------------------------- /webapp/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", "webapp.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /webapp/media/tmp/somename.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/media/tmp/somename.png -------------------------------------------------------------------------------- /webapp/media/tmp/somename_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/media/tmp/somename_1.png -------------------------------------------------------------------------------- /webapp/media/tmp/somename_1_Ax4clCq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/media/tmp/somename_1_Ax4clCq.png -------------------------------------------------------------------------------- /webapp/media/tmp/somename_1_P6VMEfv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/media/tmp/somename_1_P6VMEfv.png -------------------------------------------------------------------------------- /webapp/media/tmp/somename_1_PJp9ToK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/media/tmp/somename_1_PJp9ToK.png -------------------------------------------------------------------------------- /webapp/media/tmp/somename_1_SFGjDJq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/media/tmp/somename_1_SFGjDJq.png -------------------------------------------------------------------------------- /webapp/media/tmp/somename_1_jQJGDVl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/media/tmp/somename_1_jQJGDVl.png -------------------------------------------------------------------------------- /webapp/media/tmp/somename_1_pYQCvs5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/media/tmp/somename_1_pYQCvs5.png -------------------------------------------------------------------------------- /webapp/media/tmp/somename_1_pdYjRrp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/media/tmp/somename_1_pdYjRrp.png -------------------------------------------------------------------------------- /webapp/media/tmp/test_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/media/tmp/test_1.png -------------------------------------------------------------------------------- /webapp/requirements.txt: -------------------------------------------------------------------------------- 1 | Django>=1.8,<2.0 2 | psycopg2 3 | Pillow 4 | djangorestframework 5 | markdown 6 | django-filter 7 | pika 8 | ipfsapi -------------------------------------------------------------------------------- /webapp/tasks/TaskProcessingService.py: -------------------------------------------------------------------------------- 1 | import pika, uuid, time, json, os, logging, sys 2 | from django.dispatch import Signal 3 | 4 | logging.basicConfig(stream=sys.stdout, level=logging.INFO) 5 | 6 | class TaskProcessingService(): 7 | QUEUE_BROKER = os.getenv('QUEUE_BROKER') 8 | QUEUE_NAME = os.getenv('QUEUE_NAME') 9 | 10 | task_finished = Signal(providing_args=["response"]) 11 | task_processing = Signal(providing_args=["id"]) 12 | 13 | def __init__(self): 14 | server_down = True 15 | while server_down: 16 | try: 17 | self.__connect() 18 | server_down = False 19 | logging.info('%s is up!', self.QUEUE_BROKER) 20 | except: 21 | server_down = True 22 | logging.warning('Cannot connect to %s try again in 2 Sec.', self.QUEUE_BROKER) 23 | time.sleep(2) 24 | 25 | def __connect(self): 26 | self.connection = pika.BlockingConnection(pika.ConnectionParameters(host=self.QUEUE_BROKER)) 27 | self.channel = self.connection.channel() 28 | 29 | result = self.channel.queue_declare(exclusive=True) 30 | self.callback_queue = result.method.queue 31 | 32 | self.channel.basic_consume(self.__on_response, no_ack=False, 33 | queue=self.callback_queue) 34 | self.channel.confirm_delivery() 35 | 36 | def __on_response(self, ch, method, props, body): 37 | if self.corr_id == props.correlation_id: 38 | self.response = body 39 | 40 | response_string = body.decode('unicode_escape') 41 | response_object = json.loads(response_string) 42 | 43 | logging.debug('received %s', response_object) 44 | self.task_finished.send(sender=self.__class__, response=response_object) 45 | 46 | def send(self, request, task_id, task_type): 47 | self.response = None 48 | self.corr_id = str(uuid.uuid4()) 49 | logging.info('[x] Requesting %s for %s', task_type, request) 50 | request_confirmed = self.channel.basic_publish(exchange='', 51 | routing_key=task_type + '_' + self.QUEUE_NAME, 52 | properties=pika.BasicProperties( 53 | reply_to=self.callback_queue, 54 | correlation_id=self.corr_id, 55 | delivery_mode=2, # make message persistent 56 | content_type='application/json', 57 | ), 58 | body=json.dumps(request)) 59 | 60 | if request_confirmed: 61 | self.task_processing.send(sender=self.__class__, id=task_id) 62 | while self.response is None: 63 | self.connection.process_data_events(10) 64 | else: 65 | logging.warning('%s Request with id %d could not be confirmed', task_type, task_id) 66 | -------------------------------------------------------------------------------- /webapp/tasks/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'tasks.apps.TasksConfig' -------------------------------------------------------------------------------- /webapp/tasks/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /webapp/tasks/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | class TasksConfig(AppConfig): 4 | name = 'tasks' 5 | 6 | def ready(self): 7 | import tasks.signals 8 | -------------------------------------------------------------------------------- /webapp/tasks/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-09-28 19:52 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Task', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('status', models.CharField(choices=[('0', 'Requested'), ('1', 'Progressing'), ('2', 'Done')], max_length=1)), 21 | ('type', models.CharField(max_length=100)), 22 | ('resource', models.URLField()), 23 | ('result', models.TextField()), 24 | ('created_at', models.DateTimeField(auto_now=True)), 25 | ('resolved_at', models.DateTimeField()), 26 | ], 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /webapp/tasks/migrations/0002_auto_20170928_2000.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-09-28 20:00 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('tasks', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='task', 17 | name='result', 18 | field=models.TextField(blank=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /webapp/tasks/migrations/0003_auto_20170928_2003.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-09-28 20:03 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('tasks', '0002_auto_20170928_2000'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='task', 17 | name='resolved_at', 18 | field=models.DateTimeField(blank=True, null=True), 19 | ), 20 | migrations.AlterField( 21 | model_name='task', 22 | name='result', 23 | field=models.TextField(blank=True, null=True), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /webapp/tasks/migrations/0004_auto_20171001_0024.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-01 00:24 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('tasks', '0003_auto_20170928_2003'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='task', 17 | name='type', 18 | field=models.CharField(default='ocr', max_length=100), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /webapp/tasks/migrations/0005_auto_20171001_0034.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-01 00:34 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('tasks', '0004_auto_20171001_0024'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='task', 17 | name='result', 18 | field=models.TextField(blank=True, default=''), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /webapp/tasks/migrations/0006_auto_20171001_2311.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-01 23:11 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('tasks', '0005_auto_20171001_0034'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='task', 17 | name='status', 18 | field=models.CharField(choices=[('requested', 'Requested'), ('progressing', 'Progressing'), ('done', 'Done')], max_length=1), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /webapp/tasks/migrations/0007_auto_20171001_2313.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-01 23:13 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('tasks', '0006_auto_20171001_2311'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='task', 17 | name='status', 18 | field=models.CharField(choices=[('requested', 'Requested'), ('progressing', 'Progressing'), ('done', 'Done')], max_length=32), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /webapp/tasks/migrations/0008_auto_20171001_2318.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-01 23:18 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('tasks', '0007_auto_20171001_2313'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='task', 17 | name='status', 18 | field=models.CharField(choices=[('requested', 'Requested'), ('progressing', 'Progressing'), ('done', 'Done')], default='requested', max_length=32), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /webapp/tasks/migrations/0009_auto_20171008_1617.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.6 on 2017-10-08 16:17 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('tasks', '0008_auto_20171001_2318'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='task', 17 | name='resource', 18 | field=models.TextField(blank=True, default=''), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /webapp/tasks/migrations/0010_auto_20171008_1656.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.6 on 2017-10-08 16:56 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('tasks', '0009_auto_20171008_1617'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='task', 17 | name='resource', 18 | field=models.CharField(default='ocr', max_length=250), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /webapp/tasks/migrations/0011_auto_20171008_2138.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.6 on 2017-10-08 21:38 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('tasks', '0010_auto_20171008_1656'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='task', 17 | name='resource', 18 | field=models.CharField(max_length=250), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /webapp/tasks/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/tasks/migrations/__init__.py -------------------------------------------------------------------------------- /webapp/tasks/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class Task(models.Model): 4 | STATUS_REQUESTED = 'requested' 5 | STATUS_PENDING = 'pending' 6 | STATUS_DONE = 'done' 7 | STATUS = ( 8 | (STATUS_REQUESTED, 'Requested'), 9 | (STATUS_PENDING, 'Pending'), 10 | (STATUS_DONE, 'Done'), 11 | ) 12 | TYPE_OCR = 'ocr' 13 | KEY_ID = 'id' 14 | KEY_TYPE = 'type' 15 | KEY_RESOURCE = 'resource' 16 | 17 | status = models.CharField(max_length=32, choices=STATUS, default=STATUS_REQUESTED) 18 | type = models.CharField(max_length=100, default=TYPE_OCR) 19 | resource = models.CharField(max_length=250) 20 | result = models.TextField(blank=True, default='') 21 | created_at = models.DateTimeField(auto_now=True) 22 | resolved_at = models.DateTimeField(null=True, blank=True) 23 | -------------------------------------------------------------------------------- /webapp/tasks/serializers.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | from rest_framework import serializers 3 | from tasks.models import Task 4 | 5 | class TaskSerializerGet(serializers.Serializer): 6 | id = serializers.IntegerField(read_only=True) 7 | type = serializers.CharField(read_only=True) 8 | status = serializers.CharField(read_only=True) 9 | created_at = serializers.DateTimeField(read_only=True) 10 | resolved_at = serializers.DateTimeField(read_only=True) 11 | result = serializers.CharField(read_only=True) 12 | resource = serializers.CharField(read_only=True) 13 | 14 | class TaskSerializer(serializers.Serializer): 15 | type = serializers.ChoiceField((Task.TYPE_OCR, 'None')) 16 | 17 | resource = serializers.ImageField(max_length=None, allow_empty_file=False, use_url=True) 18 | 19 | def create(self, validated_data): 20 | validated_data[Task.KEY_RESOURCE] = str(validated_data[Task.KEY_RESOURCE]) 21 | return Task.objects.create(**validated_data) 22 | 23 | def to_representation(self, instance): 24 | ret = OrderedDict() 25 | fields = self._readable_fields 26 | for field in fields: 27 | try: 28 | attribute = field.get_attribute(instance) 29 | except: 30 | continue 31 | if field.field_name == Task.KEY_RESOURCE: 32 | resource = self.fields[Task.KEY_RESOURCE] 33 | ret[field.field_name] = resource.get_attribute(instance) 34 | else: 35 | ret[field.field_name] = field.to_representation(attribute) 36 | 37 | return ret -------------------------------------------------------------------------------- /webapp/tasks/signals.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from threading import Thread 3 | from django.utils.timezone import now 4 | from django.dispatch import receiver 5 | from django.db.models.signals import post_save 6 | from .TaskProcessingService import TaskProcessingService 7 | from .models import Task 8 | from .views import TaskListView 9 | 10 | def start_worker(loop): 11 | asyncio.set_event_loop(loop) 12 | loop.run_forever() 13 | 14 | @receiver(TaskProcessingService.task_processing, sender=TaskProcessingService) 15 | def task_request_received(sender, **kwargs): 16 | id = kwargs.get('id') 17 | task = Task.objects.get(pk=id) 18 | task.status = Task.STATUS_PENDING 19 | task.save() 20 | 21 | @receiver(TaskProcessingService.task_finished, sender=TaskProcessingService) 22 | def write_result(sender, **kwargs): 23 | response_object = kwargs.get('response') 24 | id = response_object.get('id') 25 | result = response_object.get('result') 26 | 27 | task = Task.objects.get(pk=id) 28 | task.result = result 29 | task.status = Task.STATUS_DONE 30 | task.resolved_at = now() 31 | task.save() 32 | 33 | @receiver(TaskListView.request_processing, sender=TaskListView) 34 | def request_processing(sender, **kwargs): 35 | task_id = kwargs.get(Task.KEY_ID) 36 | task_type = kwargs.get(Task.KEY_TYPE) 37 | file_hash = kwargs.get('file_hash') 38 | 39 | task_processor = TaskProcessingService() 40 | 41 | request = { 42 | "id": task_id, 43 | "file_hash": file_hash 44 | } 45 | 46 | worker_loop.call_soon_threadsafe(task_processor.send, request, task_id, task_type) 47 | 48 | worker_loop = asyncio.new_event_loop() 49 | worker = Thread(target=start_worker, args=(worker_loop,)) 50 | worker.start() 51 | 52 | -------------------------------------------------------------------------------- /webapp/tasks/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /webapp/tasks/views.py: -------------------------------------------------------------------------------- 1 | from django.core.files.storage import default_storage 2 | from django.conf import settings 3 | from django.dispatch import Signal 4 | from rest_framework import status, generics 5 | from rest_framework.response import Response 6 | from .models import Task 7 | from .serializers import TaskSerializer, TaskSerializerGet 8 | import ipfsapi, os 9 | 10 | class TaskListView(generics.ListCreateAPIView): 11 | 12 | TMP_FILE_PATH = 'tmp/tmp_file' 13 | IPFS_HOST = '127.0.0.1' 14 | IPFS_API_PORT = 5001 15 | 16 | model = Task 17 | queryset = Task.objects.all() 18 | serializer_class = TaskSerializer 19 | request_processing = Signal(providing_args=["id", "type", "file_hash"]) 20 | 21 | def get_serializer_class(self): 22 | if self.request.method == 'GET': 23 | return TaskSerializerGet 24 | return TaskSerializer 25 | 26 | def get(self, request, *args, **kwargs): 27 | return self.list(request, *args, **kwargs) 28 | 29 | def post(self, request, *args, **kwargs): 30 | serializer = TaskSerializer(data=request.data) 31 | is_valid = serializer.is_valid() 32 | task_type = serializer.initial_data.get(Task.KEY_TYPE) 33 | 34 | error_message = self.__validate_request_type(task_type) 35 | 36 | if is_valid and (len(error_message) == 0): 37 | task = serializer.save() 38 | response = Response(serializer.data, status=status.HTTP_201_CREATED) 39 | self.__process_by_type(request, task.id, task.type) 40 | return response 41 | else: 42 | return Response([serializer.errors, error_message], status=status.HTTP_400_BAD_REQUEST) 43 | 44 | def __get_file_hash_from_stream(self, data): 45 | with default_storage.open(self.TMP_FILE_PATH, 'wb+') as destination: 46 | for chunk in data.chunks(): 47 | destination.write(chunk) 48 | tmp_file = os.path.join(settings.MEDIA_ROOT, self.TMP_FILE_PATH) 49 | 50 | file_storage = ipfsapi.connect(self.IPFS_HOST, self.IPFS_API_PORT) 51 | res = file_storage.add(tmp_file) 52 | os.remove(tmp_file) 53 | 54 | return res.get('Hash') 55 | 56 | def __update_task_resource(self, id, file_hash): 57 | task = Task.objects.get(id=id) 58 | task.resource = file_hash 59 | task.save() 60 | 61 | def __process_by_type(self, request, task_id, task_type): 62 | if task_type == Task.TYPE_OCR: 63 | data = request.FILES.get(Task.KEY_RESOURCE) 64 | file_hash = self.__get_file_hash_from_stream(data) 65 | 66 | self.__update_task_resource(task_id, file_hash) 67 | self.request_processing.send( 68 | sender=self.__class__, 69 | id=task_id, 70 | type=task_type, 71 | file_hash=file_hash 72 | ) 73 | 74 | def __validate_request_type(self, task_type): 75 | error_message = {} 76 | 77 | if task_type == None or task_type == 'None': 78 | error_message[Task.KEY_TYPE] = ['Please choose a valid request type'] 79 | 80 | return error_message -------------------------------------------------------------------------------- /webapp/test.txt: -------------------------------------------------------------------------------- 1 | test inhalt -------------------------------------------------------------------------------- /webapp/webapp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anjakammer/RabbitMQ-with-Django/6e93809b050ebe4ffaf8d8b9f912643a36860c1e/webapp/webapp/__init__.py -------------------------------------------------------------------------------- /webapp/webapp/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for webapp project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.11.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.11/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '22x70k@4s8fqyu(btp0&ub^2o^55ml7d)**8hm^g05t&%!#(u#' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 31 | MEDIA_URL = '/media/' 32 | 33 | # Application definition 34 | 35 | INSTALLED_APPS = [ 36 | 'django.contrib.admin', 37 | 'django.contrib.auth', 38 | 'django.contrib.contenttypes', 39 | 'django.contrib.sessions', 40 | 'django.contrib.messages', 41 | 'django.contrib.staticfiles', 42 | 'rest_framework', 43 | 'tasks' 44 | ] 45 | 46 | MIDDLEWARE = [ 47 | 'django.middleware.security.SecurityMiddleware', 48 | 'django.contrib.sessions.middleware.SessionMiddleware', 49 | 'django.middleware.common.CommonMiddleware', 50 | 'django.middleware.csrf.CsrfViewMiddleware', 51 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 52 | 'django.contrib.messages.middleware.MessageMiddleware', 53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 54 | ] 55 | 56 | ROOT_URLCONF = 'webapp.urls' 57 | 58 | TEMPLATES = [ 59 | { 60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 61 | 'DIRS': [], 62 | 'APP_DIRS': True, 63 | 'OPTIONS': { 64 | 'context_processors': [ 65 | 'django.template.context_processors.debug', 66 | 'django.template.context_processors.request', 67 | 'django.contrib.auth.context_processors.auth', 68 | 'django.contrib.messages.context_processors.messages', 69 | ], 70 | }, 71 | }, 72 | ] 73 | 74 | WSGI_APPLICATION = 'webapp.wsgi.application' 75 | 76 | 77 | # Database 78 | # https://docs.djangoproject.com/en/1.11/ref/settings/#databases 79 | 80 | DATABASES = { 81 | 'default': { 82 | 'ENGINE': 'django.db.backends.postgresql', 83 | 'NAME': 'postgres', 84 | 'USER': 'postgres', 85 | 'HOST': 'db', 86 | 'PORT': 5432, 87 | } 88 | } 89 | 90 | 91 | # Password validation 92 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 93 | 94 | AUTH_PASSWORD_VALIDATORS = [ 95 | { 96 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 97 | }, 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 100 | }, 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 103 | }, 104 | { 105 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 106 | }, 107 | ] 108 | 109 | 110 | # Internationalization 111 | # https://docs.djangoproject.com/en/1.11/topics/i18n/ 112 | 113 | LANGUAGE_CODE = 'en-us' 114 | 115 | TIME_ZONE = 'UTC' 116 | 117 | USE_I18N = True 118 | 119 | USE_L10N = True 120 | 121 | USE_TZ = True 122 | 123 | 124 | # Static files (CSS, JavaScript, Images) 125 | # https://docs.djangoproject.com/en/1.11/howto/static-files/ 126 | 127 | STATIC_URL = '/static/' 128 | 129 | REST_FRAMEWORK = { 130 | # Use Django's standard `django.contrib.auth` permissions, 131 | # or allow read-only access for unauthenticated users. 132 | 'DEFAULT_PERMISSION_CLASSES': [ 133 | 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly' 134 | ] 135 | } 136 | -------------------------------------------------------------------------------- /webapp/webapp/urls.py: -------------------------------------------------------------------------------- 1 | """webapp URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.11/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: url(r'^$', 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: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls.static import static 17 | from django.conf import settings 18 | from django.contrib import admin 19 | from django.conf.urls import url, include 20 | from tasks.models import Task 21 | from tasks.serializers import TaskSerializer 22 | from tasks.views import TaskListView 23 | 24 | urlpatterns = [ 25 | url(r'^api/tasks/', TaskListView.as_view(queryset=Task.objects.all(), serializer_class=TaskSerializer), name='user-list'), 26 | url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), 27 | url(r'^admin/', admin.site.urls), 28 | ] 29 | 30 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) -------------------------------------------------------------------------------- /webapp/webapp/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for webapp 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/1.11/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", "webapp.settings") 15 | 16 | application = get_wsgi_application() 17 | --------------------------------------------------------------------------------