diff --git a/back/Pipfile b/back/Pipfile index 876b3dc..0c24bf0 100644 --- a/back/Pipfile +++ b/back/Pipfile @@ -6,7 +6,9 @@ verify_ssl = true [dev-packages] [packages] -django = "==2.1.4" +django = "==2.1.5" +django-cors-headers = "==2.4.0" + gunicorn = "==19.6.0" [requires] diff --git a/back/Pipfile.lock b/back/Pipfile.lock index fe77e7d..5aef303 100644 --- a/back/Pipfile.lock +++ b/back/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "6fedc9216775508baa2496b59b7cd7e991827db84bd68be0cada8c472f1adabf" + "sha256": "5f6bc22a581526fd5e7cba376397b8d499b15a0c071bdcc5cc799c32859f88ed" }, "pipfile-spec": 6, "requires": { @@ -18,18 +18,34 @@ "default": { "django": { "hashes": [ - "sha256:068d51054083d06ceb32ce02b7203f1854256047a0d58682677dd4f81bceabd7", - "sha256:55409a056b27e6d1246f19ede41c6c610e4cab549c005b62cbeefabc6433356b" + "sha256:a32c22af23634e1d11425574dce756098e015a165be02e4690179889b207c7a8", + "sha256:d6393918da830530a9516bbbcbf7f1214c3d733738779f06b0f649f49cc698c3" ], "index": "pypi", - "version": "==2.1.4" + "version": "==2.1.5" + }, + "django-cors-headers": { + "hashes": [ + "sha256:5545009c9b233ea7e70da7dbab7cb1c12afa01279895086f98ec243d7eab46fa", + "sha256:c4c2ee97139d18541a1be7d96fe337d1694623816d83f53cb7c00da9b94acae1" + ], + "index": "pypi", + "version": "==2.4.0" + }, + "gunicorn": { + "hashes": [ + "sha256:723234ea1fa8dff370ab69830ba8bc37469a7cba13fd66055faeef24085e6530", + "sha256:813f6916d18a4c8e90efde72f419308b357692f81333cb1125f80013d22fb618" + ], + "index": "pypi", + "version": "==19.6.0" }, "pytz": { "hashes": [ - "sha256:31cb35c89bd7d333cd32c5f278fca91b523b0834369e757f4c5641ea252236ca", - "sha256:8e0f8568c118d3077b46be7d654cc8167fa916092e28320cde048e54bfc9f1e6" + "sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9", + "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c" ], - "version": "==2018.7" + "version": "==2018.9" } }, "develop": {} diff --git a/back/backend/admin.py b/back/backend/admin.py index 8c38f3f..82c2833 100644 --- a/back/backend/admin.py +++ b/back/backend/admin.py @@ -1,3 +1,12 @@ from django.contrib import admin +from .models import * -# Register your models here. +admin.site.register(Report) +admin.site.register(Section) +admin.site.register(Field) +admin.site.register(DataBool) +admin.site.register(DataDecimal) +admin.site.register(DataDate) +admin.site.register(DataFile) +admin.site.register(DataString) +admin.site.register(DataInteger) diff --git a/back/backend/migrations/0001_initial.py b/back/backend/migrations/0001_initial.py new file mode 100644 index 0000000..1176375 --- /dev/null +++ b/back/backend/migrations/0001_initial.py @@ -0,0 +1,88 @@ +# Generated by Django 2.1.5 on 2019-01-23 00:38 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='DataBool', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='DataDate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data', models.DateField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='DataDecimal', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data', models.DecimalField(blank=True, decimal_places=2, max_digits=9, null=True)), + ], + ), + migrations.CreateModel( + name='DataFile', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data', models.FileField(blank=True, null=True, upload_to='')), + ], + ), + migrations.CreateModel( + name='DataInteger', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data', models.IntegerField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='DataString', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data', models.TextField(default='')), + ], + ), + migrations.CreateModel( + name='Field', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('label', models.CharField(max_length=256)), + ('number', models.IntegerField()), + ('type', models.CharField(max_length=128)), + ('completed', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='Report', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=128)), + ('date_created', models.DateTimeField(verbose_name='date created')), + ('date_submitted', models.DateTimeField(blank=True, null=True, verbose_name='date submitted')), + ('submitted', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='Section', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('completed', models.BooleanField()), + ('title', models.CharField(max_length=256)), + ('html_description', models.TextField()), + ('number', models.IntegerField()), + ('report_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='backend.Report')), + ], + ), + ] diff --git a/back/backend/migrations/0002_auto_20190123_0038.py b/back/backend/migrations/0002_auto_20190123_0038.py new file mode 100644 index 0000000..c29c19d --- /dev/null +++ b/back/backend/migrations/0002_auto_20190123_0038.py @@ -0,0 +1,58 @@ +# Generated by Django 2.1.5 on 2019-01-23 00:38 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('backend', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='report', + name='user_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='field', + name='section_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='backend.Section'), + ), + migrations.AddField( + model_name='datastring', + name='field_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='backend.Field'), + ), + migrations.AddField( + model_name='datainteger', + name='field_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='backend.Field'), + ), + migrations.AddField( + model_name='datafile', + name='field_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='backend.Field'), + ), + migrations.AddField( + model_name='datadecimal', + name='field_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='backend.Field'), + ), + migrations.AddField( + model_name='datadate', + name='field_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='backend.Field'), + ), + migrations.AddField( + model_name='databool', + name='field_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='backend.Field'), + ), + ] diff --git a/back/backend/migrations/__init__.py b/back/backend/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/backend/models.py b/back/backend/models.py index b096caa..afff981 100644 --- a/back/backend/models.py +++ b/back/backend/models.py @@ -1,4 +1,47 @@ from django.db import models +from django.conf import settings -# Create your models here. +class Report(models.Model): + user_id = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + title = models.CharField(max_length=128) + date_created = models.DateTimeField('date created') + date_submitted = models.DateTimeField('date submitted', null=True, blank=True) + submitted = models.BooleanField(default=False) +class Section(models.Model): + report_id = models.ForeignKey(Report, on_delete=models.CASCADE) + completed = models.BooleanField() + title = models.CharField(max_length=256) + html_description = models.TextField() + number = models.IntegerField() + +class Field(models.Model): + section_id = models.ForeignKey(Section, on_delete=models.CASCADE) + label = models.CharField(max_length=256) + number = models.IntegerField() + type = models.CharField(max_length=128) + completed = models.BooleanField(default=False) + +class DataBool(models.Model): + field_id = models.ForeignKey(Field, on_delete=models.CASCADE) + data = models.BooleanField(default=False) + +class DataDecimal(models.Model): + field_id = models.ForeignKey(Field, on_delete=models.CASCADE) + data = models.DecimalField(max_digits=9,decimal_places=2, null=True, blank=True) + +class DataDate(models.Model): + field_id = models.ForeignKey(Field, on_delete=models.CASCADE) + data = models.DateField(null=True, blank=True) + +class DataFile(models.Model): + field_id = models.ForeignKey(Field, on_delete=models.CASCADE) + data = models.FileField(null=True, blank=True) + +class DataString(models.Model): + field_id = models.ForeignKey(Field, on_delete=models.CASCADE) + data = models.TextField(default='') + +class DataInteger(models.Model): + field_id = models.ForeignKey(Field, on_delete=models.CASCADE) + data = models.IntegerField(null=True, blank=True) diff --git a/back/db.sqlite3 b/back/db.sqlite3 index f5449da..9f3a8ef 100644 Binary files a/back/db.sqlite3 and b/back/db.sqlite3 differ diff --git a/back/manage.py b/back/manage.py index 689d9f6..25af262 100755 --- a/back/manage.py +++ b/back/manage.py @@ -11,5 +11,5 @@ if __name__ == '__main__': "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/back/reimbursinator/settings.py b/back/reimbursinator/settings.py index e127de1..323a058 100644 --- a/back/reimbursinator/settings.py +++ b/back/reimbursinator/settings.py @@ -40,6 +40,8 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'backend', 'rest_framework', + 'users', + 'corsheaders', ] REST_FRAMEWORK = { @@ -49,6 +51,7 @@ REST_FRAMEWORK = { } MIDDLEWARE = [ + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', @@ -58,6 +61,12 @@ MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] +CORS_ORIGIN_WHITELIST = ( + 'localhost:8443', + '192.168.99.100:8443', + '127.0.0.1:8443', +) + ROOT_URLCONF = 'reimbursinator.urls' TEMPLATES = [ @@ -89,6 +98,9 @@ DATABASES = { } } +# Authentication + +AUTH_USER_MODEL = 'users.CustomUser' # Password validation # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators diff --git a/back/users/__init__.py b/back/users/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/users/admin.py b/back/users/admin.py new file mode 100644 index 0000000..f82158a --- /dev/null +++ b/back/users/admin.py @@ -0,0 +1,13 @@ +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin + +from .forms import CustomUserCreationForm, CustomUserChangeForm +from .models import CustomUser + +class CustomUserAdmin(UserAdmin): + add_form = CustomUserCreationForm + form = CustomUserChangeForm + model = CustomUser + list_display = ['username', 'email', 'first_name', 'last_name'] + +admin.site.register(CustomUser, CustomUserAdmin) diff --git a/back/users/apps.py b/back/users/apps.py new file mode 100644 index 0000000..4ce1fab --- /dev/null +++ b/back/users/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class UsersConfig(AppConfig): + name = 'users' diff --git a/back/users/forms.py b/back/users/forms.py new file mode 100644 index 0000000..ad18d01 --- /dev/null +++ b/back/users/forms.py @@ -0,0 +1,15 @@ +from django import forms +from django.contrib.auth.forms import UserCreationForm, UserChangeForm + +from .models import CustomUser + +class CustomUserCreationForm(UserCreationForm): + class Meta(UserCreationForm): + model = CustomUser + fields = UserCreationForm.Meta.fields + ('age',) + +class CustomUserChangeForm(UserChangeForm): + + class Meta: + model = CustomUser + fields = UserChangeForm.Meta.fields diff --git a/back/users/migrations/0001_initial.py b/back/users/migrations/0001_initial.py new file mode 100644 index 0000000..89ddacf --- /dev/null +++ b/back/users/migrations/0001_initial.py @@ -0,0 +1,45 @@ +# Generated by Django 2.1.5 on 2019-01-23 00:38 + +import django.contrib.auth.models +import django.contrib.auth.validators +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0009_alter_user_last_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='CustomUser', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('age', models.PositiveIntegerField(blank=True, null=True)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + ] diff --git a/back/users/migrations/__init__.py b/back/users/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/users/models.py b/back/users/models.py new file mode 100644 index 0000000..e51d043 --- /dev/null +++ b/back/users/models.py @@ -0,0 +1,5 @@ +from django.contrib.auth.models import AbstractUser +from django.db import models + +class CustomUser(AbstractUser): + age = models.PositiveIntegerField(null=True, blank=True) diff --git a/back/users/tests.py b/back/users/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/back/users/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/back/users/views.py b/back/users/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/back/users/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/docker-compose.yml b/docker-compose.yml index bd21cbc..44f8df0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,6 @@ services: web: build: ./front image: reimbursinator_front - image: nginx:1.10.3 ports: - "8443:443" environment: diff --git a/front/static/css/signup.css b/front/static/css/signup.css new file mode 100644 index 0000000..e887d49 --- /dev/null +++ b/front/static/css/signup.css @@ -0,0 +1,13 @@ +.signup +{ + height: 200px; + width: 400px; + position: fixed; + left: 50%; + margin-left: -150px; + width:60%; +} +.border +{ + border-color:black; +} \ No newline at end of file diff --git a/front/static/dashboard.html b/front/static/dashboard.html index a8b588e..8cc163b 100644 --- a/front/static/dashboard.html +++ b/front/static/dashboard.html @@ -7,7 +7,7 @@ - +