From c8536289fd59b97f50ee3d953af57ff65f3ee994 Mon Sep 17 00:00:00 2001 From: Aatish Neupane Date: Wed, 17 Oct 2018 13:20:54 +0545 Subject: [PATCH] create initial models for tag, job and application --- .gitignore | 91 +++++++++++++++++++++++ ipv6work/__init__.py | 0 ipv6work/settings.py | 124 ++++++++++++++++++++++++++++++++ ipv6work/urls.py | 21 ++++++ ipv6work/wsgi.py | 16 +++++ jobs/__init__.py | 0 jobs/admin.py | 3 + jobs/apps.py | 5 ++ jobs/date_utils.py | 5 ++ jobs/migrations/0001_initial.py | 61 ++++++++++++++++ jobs/migrations/__init__.py | 0 jobs/models.py | 63 ++++++++++++++++ jobs/tests.py | 3 + jobs/views.py | 3 + manage.py | 15 ++++ requirements.txt | 1 + 16 files changed, 411 insertions(+) create mode 100644 .gitignore create mode 100644 ipv6work/__init__.py create mode 100644 ipv6work/settings.py create mode 100644 ipv6work/urls.py create mode 100644 ipv6work/wsgi.py create mode 100644 jobs/__init__.py create mode 100644 jobs/admin.py create mode 100644 jobs/apps.py create mode 100644 jobs/date_utils.py create mode 100644 jobs/migrations/0001_initial.py create mode 100644 jobs/migrations/__init__.py create mode 100644 jobs/models.py create mode 100644 jobs/tests.py create mode 100644 jobs/views.py create mode 100755 manage.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a7ac3c --- /dev/null +++ b/.gitignore @@ -0,0 +1,91 @@ +# Created by https://www.gitignore.io + +### OSX ### +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo +*.pot + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + + +### Django ### +*.log +*.pot +*.pyc +__pycache__/ +local_settings.py + +venv/ +.vscode/ +.env +db.sqlite3 diff --git a/ipv6work/__init__.py b/ipv6work/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ipv6work/settings.py b/ipv6work/settings.py new file mode 100644 index 0000000..c8260df --- /dev/null +++ b/ipv6work/settings.py @@ -0,0 +1,124 @@ +""" +Django settings for ipv6work project. + +Generated by 'django-admin startproject' using Django 2.1.2. + +For more information on this file, see +https://docs.djangoproject.com/en/2.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/2.1/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/2.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '+0t^onasc-b+_ry$!6@hpf4o79rw6m%q7dow5#ia+@nla&2@0-' + +# 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', +] + +INSTALLED_APPS += [ + 'jobs', +] + +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 = 'ipv6work.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 = 'ipv6work.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/2.1/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/2.1/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/2.1/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/2.1/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/ipv6work/urls.py b/ipv6work/urls.py new file mode 100644 index 0000000..034e7bf --- /dev/null +++ b/ipv6work/urls.py @@ -0,0 +1,21 @@ +"""ipv6work URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/2.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path + +urlpatterns = [ + path('admin/', admin.site.urls), +] diff --git a/ipv6work/wsgi.py b/ipv6work/wsgi.py new file mode 100644 index 0000000..6aa7d32 --- /dev/null +++ b/ipv6work/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for ipv6work 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/2.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ipv6work.settings') + +application = get_wsgi_application() diff --git a/jobs/__init__.py b/jobs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jobs/admin.py b/jobs/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/jobs/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/jobs/apps.py b/jobs/apps.py new file mode 100644 index 0000000..14c323a --- /dev/null +++ b/jobs/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class JobsConfig(AppConfig): + name = 'jobs' diff --git a/jobs/date_utils.py b/jobs/date_utils.py new file mode 100644 index 0000000..8983446 --- /dev/null +++ b/jobs/date_utils.py @@ -0,0 +1,5 @@ +from datetime import datetime, timedelta + + +def after_30_days(): + return datetime.now() + timedelta(days=30) diff --git a/jobs/migrations/0001_initial.py b/jobs/migrations/0001_initial.py new file mode 100644 index 0000000..5fbb505 --- /dev/null +++ b/jobs/migrations/0001_initial.py @@ -0,0 +1,61 @@ +# Generated by Django 2.1.2 on 2018-10-17 07:35 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import jobs.date_utils + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Application', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('applicant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='applications', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Job', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=2055)), + ('description', models.TextField()), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('expires', models.DateTimeField(default=jobs.date_utils.after_30_days)), + ('closed', models.BooleanField(default=False)), + ('close_reason', models.CharField(blank=True, choices=[('filled', 'Job filled'), ('cancelled', 'Job Cancelled'), ('not-found', 'Suitable candidate not found')], max_length=2055)), + ('posted_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='jobs', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Tag', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(db_index=True, max_length=255, unique=True)), + ], + ), + migrations.AddField( + model_name='job', + name='tags', + field=models.ManyToManyField(related_name='jobs', to='jobs.Tag'), + ), + migrations.AddField( + model_name='application', + name='job', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='applications', to='jobs.Job'), + ), + migrations.AlterUniqueTogether( + name='application', + unique_together={('applicant', 'job')}, + ), + ] diff --git a/jobs/migrations/__init__.py b/jobs/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jobs/models.py b/jobs/models.py new file mode 100644 index 0000000..83702ef --- /dev/null +++ b/jobs/models.py @@ -0,0 +1,63 @@ +from django.db import models +from django.contrib.auth import get_user_model + +from .date_utils import after_30_days + +User = get_user_model() + + +class Tag(models.Model): + name = models.CharField(max_length=255, unique=True, db_index=True) + + def __str__(self): + return self.name + + +class Job(models.Model): + CLOSED_REASON_FILLED = 'filled' + CLOSED_REASON_NOT_FOUND = 'not-found' + CLOSED_REASON_CANCELLED = 'cancelled' + CLOSED_REASON_CHOICES = ( + (CLOSED_REASON_FILLED, "Job filled"), + (CLOSED_REASON_CANCELLED, "Job Cancelled"), + (CLOSED_REASON_NOT_FOUND, "Suitable candidate not found") + ) + + title = models.CharField(max_length=2055) + description = models.TextField() + tags = models.ManyToManyField(Tag, related_name="jobs") + + created = models.DateTimeField(auto_now_add=True) + updated = models.DateTimeField(auto_now=True) + expires = models.DateTimeField(default=after_30_days) + + closed = models.BooleanField(default=False) + close_reason = models.CharField( + max_length=2055, choices=CLOSED_REASON_CHOICES, blank=True) + + posted_by = models.ForeignKey( + User, related_name="jobs", on_delete=models.CASCADE) + + def renew(self): + self.expires = after_30_days() + self.save() + + def __str__(self): + return self.title + + +class Application(models.Model): + ''' A model representing applications to job ''' + + applicant = models.ForeignKey( + User, related_name="applications", on_delete=models.CASCADE) + job = models.ForeignKey( + Job, related_name="applications", on_delete=models.CASCADE) + + created = models.DateTimeField(auto_now_add=True) + + class Meta: + unique_together = ('applicant', 'job') + + def __str__(self): + return "{0} - Job: {1}".format(self.applicant, self.job) diff --git a/jobs/tests.py b/jobs/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/jobs/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/jobs/views.py b/jobs/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/jobs/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..c1e66bb --- /dev/null +++ b/manage.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == '__main__': + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ipv6work.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + 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?" + ) from exc + execute_from_command_line(sys.argv) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..81e6b2d --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Django==2.1.2 \ No newline at end of file