Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea
22 changes: 22 additions & 0 deletions source/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM python:3.5.2
RUN apt-get update && apt-get -y install rsyslog mc
ENV PYTHONUNBUFFERED 1
ENV DJANGO_SETTINGS_MODULE gitradar.settings

RUN mkdir /django-app
ADD /django-app /django-app
WORKDIR /django-app

RUN pip install -r requirements.txt
CMD python manage.py makemigrations
CMD python manage.py migrate
CMD python manage.py test

ADD /django-app/cronjob /etc/cron.d/
RUN chmod +x /django-app/cron.sh
RUN chmod 0644 /etc/cron.d/cronjob
RUN touch /var/log/cron.log
RUN service cron reload

CMD python /django-app/manage.py syncdata
CMD python manage.py runserver 0.0.0.0:8080
27 changes: 27 additions & 0 deletions source/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
```bash
cd source
docker build --tag=gitradar .
docker run -it --rm --publish=127.0.0.1:8080:8080 gitradar
```

#API

## List of authors
```
GET /api/authors/
```

## Commits from an author

```
GET /api/commits/author/{{AUTHOR_ID}}
```

## Mark commit as read or unread
```
GET /api/commit/{{COMMIT_ID}}/{{STATUS}}/
```

STATUS is status of commit:
'R' - read
'U' - unread
2 changes: 2 additions & 0 deletions source/django-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
db.sqlite3
4 changes: 4 additions & 0 deletions source/django-app/cron.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
# cron.sh

python /django-app/manage.py syncdata
1 change: 1 addition & 0 deletions source/django-app/cronjob
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* * * * * root /django-app/cron.sh >> /var/log/cron.log 2>&1
Empty file.
115 changes: 115 additions & 0 deletions source/django-app/gitradar/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"""
Django settings for gitradar 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

# 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 = 'ri%+7o1lp@ltxo+j(jgf=t*=49k2)x66ri&+w3+l$uob=5yb)%'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'sync_machine'
]

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 = 'gitradar.urls'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'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 = 'gitradar.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'),
}
}

# 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/'
22 changes: 22 additions & 0 deletions source/django-app/gitradar/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""gitradar 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'^api/', include('sync_machine.urls'))
]
16 changes: 16 additions & 0 deletions source/django-app/gitradar/wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
WSGI config for gitradar 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", "gitradar.settings")

application = get_wsgi_application()
22 changes: 22 additions & 0 deletions source/django-app/manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gitradar.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)
5 changes: 5 additions & 0 deletions source/django-app/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
appdirs==1.4.2
Django==1.10.6
packaging==16.8
pyparsing==2.1.10
six==1.10.0
Empty file.
3 changes: 3 additions & 0 deletions source/django-app/sync_machine/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
5 changes: 5 additions & 0 deletions source/django-app/sync_machine/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class SyncMachineConfig(AppConfig):
name = 'sync_machine'
Empty file.
Empty file.
11 changes: 11 additions & 0 deletions source/django-app/sync_machine/management/commands/syncdata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.core.management import BaseCommand

from sync_machine.models import Commit


class Command(BaseCommand):
help = 'Closes the specified poll for voting'

def handle(self, *args, **options):
Commit.data_loader.run()
print('Task Complete.')
3 changes: 3 additions & 0 deletions source/django-app/sync_machine/migrations/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*
!.gitignore
!__init__.py
Empty file.
50 changes: 50 additions & 0 deletions source/django-app/sync_machine/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from django.db import models
from urllib import request
import json


class Contributor(models.Model):
name = models.CharField(blank=False, max_length=100)
email = models.EmailField()

def __str__(self):
return self.name


class CommitDataLoader(models.Manager):
def run(self):
path = 'https://api.github.com/repos/nodejs/node/commits'
f = request.urlopen(path)
response = f.read().decode('utf-8')
commits = json.loads(response)
for commit in commits:
sha = commit.get('sha')
message = commit['commit']['message']

author_email = commit['commit']['author']['email']
author_name = commit['commit']['author']['name']
author, created = Contributor.objects.get_or_create(
email=author_email,
name=author_name)

Commit.objects.get_or_create(sha=sha,
defaults={
'author': author,
'message': message
})


class Commit(models.Model):
STATUS_TYPE = (
('R', 'read'),
('U', 'unread')
)

sha = models.CharField(blank=False, max_length=40)
message = models.TextField()
author = models.ForeignKey(Contributor)
status = models.CharField(blank=False, max_length=1, default='U', choices=STATUS_TYPE)
created_at = models.DateTimeField(auto_now_add=True)

data_loader = CommitDataLoader()
objects = models.Manager()
59 changes: 59 additions & 0 deletions source/django-app/sync_machine/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from django.test import Client
from django.test import TestCase
import json

from sync_machine.models import Contributor, Commit


class AuthTest(TestCase):
def setUp(self):
Commit.data_loader.run()

def test_api_authors(self):
"""
Should test API route: /api/authors
:return:
"""
count_authors = Contributor.objects.all().count()

response = Client().get('/api/authors/')
content = json.loads(response.content.decode("utf-8"))
result = content.get('result')

self.assertEqual(response.status_code, 200)
self.assertIsNotNone(result)
self.assertEqual(count_authors, len(result))

def test_api_commits_author(self):
"""
Should test API route: /api/commits/author/(?P<author_id>[0-9]+)
:return:
"""
author = Contributor.objects.all().first()
commit_author_count = Commit.objects.filter(author_id=author.id).count()

response = Client().get('/api/commits/author/' + str(author.id) + '/')
content = json.loads(response.content.decode("utf-8"))
result = content.get('result')

self.assertIsNotNone(author)
self.assertGreater(commit_author_count, 0)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(result), commit_author_count)

def test_api_commit_status(self):
"""
Should test API route: /api/commit/(?P<commit_id>[0-9]+)/(?P<status>(r|u)+)
:return:
"""
author = Contributor.objects.all().first()
commit = Commit.objects.filter(author_id=author.id).first()

self.assertIsNotNone(commit)
self.assertEqual(commit.status, 'U')

response = Client().get('/api/commit/' + str(commit.id) + '/' + 'R' + '/')
self.assertEqual(response.status_code, 200)

commit.refresh_from_db()
self.assertEqual(commit.status, 'R')
Loading