diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d0a113f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.pyc
+.DS_Store
\ No newline at end of file
diff --git a/source/Dockerfile b/source/Dockerfile
new file mode 100644
index 0000000..6bccc09
--- /dev/null
+++ b/source/Dockerfile
@@ -0,0 +1,7 @@
+ FROM python:2.7
+ ENV PYTHONUNBUFFERED 1
+ RUN mkdir /code
+ WORKDIR /code
+ ADD requirements.txt /code/
+ RUN pip install -r requirements.txt
+ ADD . /code/
\ No newline at end of file
diff --git a/source/README.md b/source/README.md
new file mode 100644
index 0000000..59a404b
--- /dev/null
+++ b/source/README.md
@@ -0,0 +1,6 @@
+## What needs to be done to run the project:
+
+1. docker-compose build
+2. docker-compose run web ./manage.py migrate
+3. docker-compose run web ./manage.py createsuperuser
+4. docker-compose up
diff --git a/source/commits/__init__.py b/source/commits/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/source/commits/admin.py b/source/commits/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/source/commits/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/source/commits/apps.py b/source/commits/apps.py
new file mode 100644
index 0000000..6076d3b
--- /dev/null
+++ b/source/commits/apps.py
@@ -0,0 +1,7 @@
+from __future__ import unicode_literals
+
+from django.apps import AppConfig
+
+
+class ApigithubConfig(AppConfig):
+ name = 'apigithub'
diff --git a/source/commits/migrations/0001_initial.py b/source/commits/migrations/0001_initial.py
new file mode 100644
index 0000000..ad07e04
--- /dev/null
+++ b/source/commits/migrations/0001_initial.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.6 on 2017-03-15 09:40
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Commits',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('sha', models.CharField(default='', max_length=200)),
+ ('author', models.CharField(blank=True, default='', max_length=200)),
+ ('pub_date', models.DateTimeField()),
+ ('text', models.TextField()),
+ ('read_status', models.BooleanField(default=False)),
+ ],
+ options={
+ 'db_table': 'commits',
+ },
+ ),
+ ]
diff --git a/source/commits/migrations/__init__.py b/source/commits/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/source/commits/models.py b/source/commits/models.py
new file mode 100644
index 0000000..e624f69
--- /dev/null
+++ b/source/commits/models.py
@@ -0,0 +1,16 @@
+from __future__ import unicode_literals
+
+from django.db import models
+
+# Create your models here.
+class Commits(models.Model):
+ class Meta():
+ db_table = 'commits'
+
+
+ sha = models.CharField(max_length=200, default='')
+ author = models.CharField(max_length=200, blank=True, default='')
+ pub_date = models.DateTimeField()
+ text = models.TextField()
+ read_status = models.BooleanField(default=False)
+
diff --git a/source/commits/tasks.py b/source/commits/tasks.py
new file mode 100644
index 0000000..1327838
--- /dev/null
+++ b/source/commits/tasks.py
@@ -0,0 +1,37 @@
+from celery.task.schedules import crontab
+from celery.decorators import periodic_task
+from celery.utils.log import get_task_logger
+
+from django.utils import timezone
+
+from .models import Commits
+from urllib2 import urlopen
+from dateutil.parser import parse
+
+import json
+
+logger = get_task_logger(__name__)
+
+
+@periodic_task(ignore_result=True, run_every=(crontab(hour="*", minute=timezone.now().minute+1, day_of_week="*")))
+def get_latest_commit(owner='nodejs', repo='node'):
+ logger.info("Start task")
+ url = 'https://api.github.com/repos/{owner}/{repo}/commits'.format(owner=owner, repo=repo)
+ response = urlopen(url).read()
+ data = json.loads(response.decode('UTF-8'))
+ list = data[:25]
+ for el in list:
+ sha = el['sha']
+ name = el['commit']['author']['name']
+ msg = el['commit']['message'].encode('ascii','ignore')
+ date = parse(el['commit']['author']['date'])
+ try:
+ if not Commits.objects.filter(sha=sha):
+ comment = Commits(author=name, text=msg, pub_date=date, read_status=False, sha=sha)
+ comment.save()
+ logger.info("Get!!!")
+ except Exception as ex:
+ print('ex is: {}'.format(ex))
+ logger.info("End task success!!!")
+ return list
+
\ No newline at end of file
diff --git a/source/commits/templates/commits.html b/source/commits/templates/commits.html
new file mode 100644
index 0000000..bed034d
--- /dev/null
+++ b/source/commits/templates/commits.html
@@ -0,0 +1,34 @@
+{% extends 'index.html' %}
+
+{% block commits %}
+
+
Commits!
+ {% for commit in commits %}
+
{{ commit.author }} - {{ commit.pub_date }}
+
{{ commit.text }}
+ Read -
{{ commit.read_status }}
+
+ {% endfor %}
+
+
+ {% if commits.has_previous %}
+
<
+ {% else %}
+
<
+ {% endif %}
+ {% for page in commits.paginator.page_range %}
+ {% if page == commits.number %}
+
{{ page }}
+ {% else %}
+
{{ page }}
+ {% endif %}
+ {% endfor %}
+ {% if commits.has_next %}
+
>
+ {% else %}
+
>
+ {% endif %}
+
+
+{% endblock %}
+
\ No newline at end of file
diff --git a/source/commits/tests.py b/source/commits/tests.py
new file mode 100644
index 0000000..5440075
--- /dev/null
+++ b/source/commits/tests.py
@@ -0,0 +1,25 @@
+from urllib2 import urlopen
+import json
+
+from django.test import TestCase
+
+from .tasks import get_latest_commit
+from .models import Commits
+
+
+class TasksMethodTests(TestCase):
+
+ def test_get_latest_commit(self):
+ """
+ test_get_latest_commit - checking get_latest_commit saved only new commits
+ """
+ error = ''
+ list = get_latest_commit()
+ for el in list:
+ sha = el['sha']
+ try:
+ if not Commits.objects.get(sha=sha):
+ error = 'object not exist'
+ except Exception as ex:
+ error = 'there are duplicates'
+ self.assertEqual(error, '')
\ No newline at end of file
diff --git a/source/commits/urls.py b/source/commits/urls.py
new file mode 100644
index 0000000..d394397
--- /dev/null
+++ b/source/commits/urls.py
@@ -0,0 +1,24 @@
+"""composeexample URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/1.10/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.conf.urls import url, include
+ 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
+"""
+from django.conf.urls import url
+from views import commits, readed
+
+
+urlpatterns = [
+ url(r'^page/(\d+)/$', commits),
+ url(r'^readed/(?P\d+)/(?P\d+)/$', readed),
+ url(r'^$', commits),
+]
diff --git a/source/commits/views.py b/source/commits/views.py
new file mode 100644
index 0000000..9ac38ac
--- /dev/null
+++ b/source/commits/views.py
@@ -0,0 +1,30 @@
+from django.shortcuts import render_to_response, redirect
+from django.http.response import Http404
+from django.core.paginator import Paginator
+from django.core.exceptions import ObjectDoesNotExist
+from django.utils import timezone
+
+from .models import Commits
+
+# Create your views here.
+
+def commits(request, page_number=1):
+
+ all_commits = Commits.objects.order_by('-pub_date')
+ current_page = Paginator(all_commits, 5)
+ print(timezone.now().minute)
+ return render_to_response('commits.html', {'commits': current_page.page(page_number)})
+
+def readed(request, commit_id, page_number):
+ try:
+ commits = Commits.objects.get(id=commit_id)
+ if commits.read_status:
+ commits.read_status = False
+ else:
+ commits.read_status = True
+ commits.save()
+ response = redirect('/page/'+page_number+"/")
+ return response
+ except ObjectDoesNotExist:
+ raise Http404
+ return redirect('/page/'+page_number+'/')
diff --git a/source/db.sqlite3 b/source/db.sqlite3
new file mode 100644
index 0000000..13fb7cc
Binary files /dev/null and b/source/db.sqlite3 differ
diff --git a/source/docker-compose.yml b/source/docker-compose.yml
new file mode 100644
index 0000000..8ac732b
--- /dev/null
+++ b/source/docker-compose.yml
@@ -0,0 +1,31 @@
+ version: '2'
+ services:
+ db:
+ image: postgres
+ web:
+ build: .
+ command: python manage.py runserver 0.0.0.0:8000
+ volumes:
+ - .:/code
+ ports:
+ - "8000:8000"
+ depends_on:
+ - db
+ # RabbitMQ
+ rabbit:
+ hostname: rabbit
+ image: rabbitmq:3.6.0
+ environment:
+ - RABBITMQ_DEFAULT_USER=admin
+ - RABBITMQ_DEFAULT_PASS=mypass
+ ports:
+ - "5672:5672" # we forward this port because it's useful for debugging
+ - "15672:15672" # here, we can access rabbitmq management plugin
+ celery:
+ build: .
+ environment:
+ - C_FORCE_ROOT=true
+ command: python manage.py celeryd -l INFO -B
+ volumes:
+ - .:/code
+
diff --git a/source/github/__init__.py b/source/github/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/source/github/settings.py b/source/github/settings.py
new file mode 100644
index 0000000..f3844cd
--- /dev/null
+++ b/source/github/settings.py
@@ -0,0 +1,156 @@
+"""
+Django settings for github project.
+
+Generated by 'django-admin startproject' using Django 1.10.6.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/1.10/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/1.10/ref/settings/
+"""
+
+import os
+import djcelery
+
+djcelery.setup_loader()
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = '#d&h$dv!$zm_of%e9abqc(ib_(gie8_b_(!8eokb3y@*e+#m*%'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+CELERYBEAT_SCHEDULER = "djcelery.schedulers.DatabaseScheduler"
+CELERY_ACCEPT_CONTENT = ['pickle', 'json', 'msgpack', 'yaml']
+# Application definition
+
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'commits',
+ "djcelery",
+]
+RABBIT_HOSTNAME = os.environ.get('RABBIT_PORT_5672_TCP', 'rabbit')
+
+if RABBIT_HOSTNAME.startswith('tcp://'):
+ RABBIT_HOSTNAME = RABBIT_HOSTNAME.split('//')[1]
+
+BROKER_URL = os.environ.get('BROKER_URL',
+ '')
+if not BROKER_URL:
+ BROKER_URL = 'amqp://{user}:{password}@{hostname}/{vhost}/'.format(
+ user=os.environ.get('RABBIT_ENV_USER', 'admin'),
+ password=os.environ.get('RABBIT_ENV_RABBITMQ_PASS', 'mypass'),
+ hostname=RABBIT_HOSTNAME,
+ vhost=os.environ.get('RABBIT_ENV_VHOST', ''))
+
+# We don't want to have dead connections stored on rabbitmq, so we have to negotiate using heartbeats
+BROKER_HEARTBEAT = '?heartbeat=30'
+if not BROKER_URL.endswith(BROKER_HEARTBEAT):
+ BROKER_URL += BROKER_HEARTBEAT
+
+BROKER_POOL_LIMIT = 1
+BROKER_CONNECTION_TIMEOUT = 10
+
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'github.urls'
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [os.path.join(BASE_DIR, 'templates'),
+ os.path.join(BASE_DIR, 'commits/templates'),
+ ],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = 'github.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
+
+DATABASES = {
+ # 'default': {
+ # 'ENGINE': 'django.db.backends.sqlite3',
+ # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+ # }
+ 'default': {
+ 'ENGINE': 'django.db.backends.postgresql',
+ 'NAME': 'postgres',
+ 'USER': 'postgres',
+ 'HOST': 'db',
+ 'PORT': 5432,
+ }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/1.10/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/1.10/howto/static-files/
+
+STATIC_URL = '/static/'
diff --git a/source/github/urls.py b/source/github/urls.py
new file mode 100644
index 0000000..2a82b9d
--- /dev/null
+++ b/source/github/urls.py
@@ -0,0 +1,23 @@
+"""composeexample URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/1.10/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.conf.urls import url, include
+ 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
+"""
+from django.conf.urls import url, include
+from django.contrib import admin
+
+urlpatterns = [
+
+ url(r'^admin/', admin.site.urls),
+ url(r'^', include('commits.urls')),
+]
diff --git a/source/github/wsgi.py b/source/github/wsgi.py
new file mode 100644
index 0000000..e7d3874
--- /dev/null
+++ b/source/github/wsgi.py
@@ -0,0 +1,17 @@
+"""
+WSGI config for composeexample project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "composeexample.settings")
+os.environ["CELERY_LOADER"] = "django"
+
+application = get_wsgi_application()
diff --git a/source/manage.py b/source/manage.py
new file mode 100755
index 0000000..e838fd4
--- /dev/null
+++ b/source/manage.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == "__main__":
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "github.settings")
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError:
+ # The above import may fail for some other reason. Ensure that the
+ # issue is really that Django is missing to avoid masking other
+ # exceptions on Python 2.
+ try:
+ import django
+ except ImportError:
+ raise ImportError(
+ "Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?"
+ )
+ raise
+ execute_from_command_line(sys.argv)
diff --git a/source/requirements.txt b/source/requirements.txt
new file mode 100644
index 0000000..d079016
--- /dev/null
+++ b/source/requirements.txt
@@ -0,0 +1,6 @@
+ Django
+ psycopg2
+ python-dateutil
+ django-celery
+ python-dateutil
+
\ No newline at end of file
diff --git a/source/templates/index.html b/source/templates/index.html
new file mode 100644
index 0000000..dc4653a
--- /dev/null
+++ b/source/templates/index.html
@@ -0,0 +1,9 @@
+
+
+
+ Django
+
+
+ {% block commits %}{% endblock %}
+
+
\ No newline at end of file