├── .gitignore ├── README.md ├── apps ├── __init__.py ├── __pycache__ │ └── __init__.cpython-36.pyc ├── lists │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20200605_2313.py │ │ ├── 0003_listitem.py │ │ ├── __init__.py │ │ └── __pycache__ │ │ │ ├── 0001_initial.cpython-36.pyc │ │ │ ├── 0002_auto_20200605_2313.cpython-36.pyc │ │ │ └── __init__.cpython-36.pyc │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── projects │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── tasks │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20200605_2313.py │ │ ├── 0003_auto_20200607_1555.py │ │ ├── 0004_auto_20200612_2259.py │ │ ├── 0005_auto_20200701_1416.py │ │ ├── 0006_task_project.py │ │ ├── 0007_tag.py │ │ ├── __init__.py │ │ └── __pycache__ │ │ │ ├── 0001_initial.cpython-36.pyc │ │ │ ├── 0002_auto_20200605_2313.cpython-36.pyc │ │ │ └── __init__.cpython-36.pyc │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── users │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ ├── admin.cpython-36.pyc │ │ ├── apps.cpython-36.pyc │ │ └── models.cpython-36.pyc │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_user_is_staff.py │ │ ├── 0003_userprofile.py │ │ ├── __init__.py │ │ └── __pycache__ │ │ │ ├── 0001_initial.cpython-36.pyc │ │ │ ├── 0002_user_is_staff.cpython-36.pyc │ │ │ ├── 0003_userprofile.cpython-36.pyc │ │ │ └── __init__.cpython-36.pyc │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py └── utils │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── admin.cpython-36.pyc │ └── models.cpython-36.pyc │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── tests.py │ └── views.py ├── config ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── manage.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/django,python 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=django,python 3 | 4 | ### Django ### 5 | *.log 6 | *.pot 7 | *.pyc 8 | __pycache__/ 9 | local_settings.py 10 | db.sqlite3 11 | media 12 | 13 | # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ 14 | # in your Git repository. Update and uncomment the following line accordingly. 15 | # /staticfiles/ 16 | 17 | ### Django.Python Stack ### 18 | # Byte-compiled / optimized / DLL files 19 | *.py[cod] 20 | *$py.class 21 | 22 | # C extensions 23 | *.so 24 | 25 | # Distribution / packaging 26 | .Python 27 | build/ 28 | develop-eggs/ 29 | dist/ 30 | downloads/ 31 | eggs/ 32 | .eggs/ 33 | lib/ 34 | lib64/ 35 | parts/ 36 | sdist/ 37 | var/ 38 | wheels/ 39 | pip-wheel-metadata/ 40 | share/python-wheels/ 41 | *.egg-info/ 42 | .installed.cfg 43 | *.egg 44 | MANIFEST 45 | 46 | # PyInstaller 47 | # Usually these files are written by a python script from a template 48 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 49 | *.manifest 50 | *.spec 51 | 52 | # Installer logs 53 | pip-log.txt 54 | pip-delete-this-directory.txt 55 | 56 | # Unit test / coverage reports 57 | htmlcov/ 58 | .tox/ 59 | .nox/ 60 | .coverage 61 | .coverage.* 62 | .cache 63 | nosetests.xml 64 | coverage.xml 65 | *.cover 66 | .hypothesis/ 67 | .pytest_cache/ 68 | 69 | # Translations 70 | *.mo 71 | 72 | # Django stuff: 73 | 74 | # Flask stuff: 75 | instance/ 76 | .webassets-cache 77 | 78 | # Scrapy stuff: 79 | .scrapy 80 | 81 | # Sphinx documentation 82 | docs/_build/ 83 | 84 | # PyBuilder 85 | target/ 86 | 87 | # Jupyter Notebook 88 | .ipynb_checkpoints 89 | 90 | # IPython 91 | profile_default/ 92 | ipython_config.py 93 | 94 | # pyenv 95 | .python-version 96 | 97 | # pipenv 98 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 99 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 100 | # having no cross-platform support, pipenv may install dependencies that don’t work, or not 101 | # install all needed dependencies. 102 | #Pipfile.lock 103 | 104 | # celery beat schedule file 105 | celerybeat-schedule 106 | 107 | # SageMath parsed files 108 | *.sage.py 109 | 110 | # Environments 111 | .env 112 | .venv 113 | env/ 114 | venv/ 115 | ENV/ 116 | env.bak/ 117 | venv.bak/ 118 | 119 | # Spyder project settings 120 | .spyderproject 121 | .spyproject 122 | 123 | # Rope project settings 124 | .ropeproject 125 | 126 | # mkdocs documentation 127 | /site 128 | 129 | # mypy 130 | .mypy_cache/ 131 | .dmypy.json 132 | dmypy.json 133 | 134 | # Pyre type checker 135 | .pyre/ 136 | 137 | ### Python ### 138 | # Byte-compiled / optimized / DLL files 139 | 140 | # C extensions 141 | 142 | # Distribution / packaging 143 | 144 | # PyInstaller 145 | # Usually these files are written by a python script from a template 146 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 147 | 148 | # Installer logs 149 | 150 | # Unit test / coverage reports 151 | 152 | # Translations 153 | 154 | # Django stuff: 155 | 156 | # Flask stuff: 157 | 158 | # Scrapy stuff: 159 | 160 | # Sphinx documentation 161 | 162 | # PyBuilder 163 | 164 | # Jupyter Notebook 165 | 166 | # IPython 167 | 168 | # pyenv 169 | 170 | # pipenv 171 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 172 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 173 | # having no cross-platform support, pipenv may install dependencies that don’t work, or not 174 | # install all needed dependencies. 175 | 176 | # celery beat schedule file 177 | 178 | # SageMath parsed files 179 | 180 | # Environments 181 | 182 | # Spyder project settings 183 | 184 | # Rope project settings 185 | 186 | # mkdocs documentation 187 | 188 | # mypy 189 | 190 | # Pyre type checker 191 | 192 | # End of https://www.toptal.com/developers/gitignore/api/django,python 193 | 194 | .vscode/ 195 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reasonable Productivity Back-end 2 | 3 | This is an api for the Reasonable Productivity system built with the Django REST Framework. 4 | 5 | ## Running Locally 6 | 7 | 1. Clone this repo 8 | 1. cd into this repo 9 | 1. Create a python virtual environment: `python -m venv venv` 10 | 1. Activate virutal environment: `source venv/bin/activate` 11 | 1. `pip install -r requirements.txt` 12 | 1. `python manage.py runserver` 13 | 14 | ## Schema 15 | 16 | **Task** 17 | 18 | * user_id: ForeinKey 19 | * title: required 20 | * description: default='' 21 | * completed: boolean 22 | * due_date: nullable 23 | * created_at 24 | * updated_at 25 | 26 | **Tag** 27 | 28 | * text: CharField(max_length=25) 29 | * color: CharField(max_length=6) 30 | * *many to many relationship with Task* 31 | 32 | **List** 33 | 34 | * user_id 35 | * name 36 | * created_at 37 | * updated_at 38 | 39 | **ListItem** 40 | 41 | * list_id 42 | * text 43 | * created_at 44 | * updated_at 45 | 46 | **User** 47 | 48 | * email (unique, used for login) 49 | * password 50 | 51 | **UserProfile** 52 | 53 | * user_id 54 | * first_name 55 | * last_name 56 | * image 57 | * fb_profile 58 | * twitter_profile 59 | * linkedin_profile 60 | * website 61 | 62 | **Project** 63 | 64 | * user_id 65 | * title 66 | * description 67 | * created_at 68 | * updated_at 69 | * due_date (for version 2) 70 | 71 | ## API 72 | 73 | **/users** 74 | 75 | * GET 76 | * POST 77 | 78 | **/users/:id** 79 | 80 | * GET 81 | * PATCH 82 | * DELETE 83 | 84 | **/users/:id/tasks** 85 | 86 | * GET 87 | * POST 88 | 89 | **/users/:id/tasks/:task_id** 90 | 91 | * GET 92 | * PATCH 93 | * DELETE 94 | 95 | **/users/:id/lists** 96 | 97 | * GET 98 | * POST 99 | 100 | **/users/:id/lists/:list_id** 101 | 102 | * GET 103 | * PATCH 104 | * DELETE 105 | 106 | **/users/:id/lists/:list_id/list-items** 107 | 108 | * POST 109 | 110 | **/users/:id/list-items/:item_id** 111 | 112 | * PATCH 113 | * DELETE 114 | 115 | **/tasks** 116 | 117 | * GET 118 | 119 | **/tasks/:id** 120 | 121 | * GET 122 | 123 | ## Roadmap 124 | 125 | ### Version 1.0 126 | 127 | * Auth endpoints 128 | * Login 129 | * Update password, forgot password 130 | * Add searching, sorting, filtering for tasks and lists 131 | 132 | ### Version 1.1 133 | 134 | * Recurring Tasks 135 | * recurring: Boolean, default=False 136 | * recurring_time: nullable 137 | * recurring_frequency: daily, weekly, monthly, yearly 138 | * recurring_custom: (based off of recurring frequency option, user can choose specific days/weeks, etc.) 139 | * Reminders 140 | 141 | ### Version 1.2 142 | 143 | * Nested & Dependant Tasks 144 | * Back-end: tasks should be able to be marked as dependant and sub-tasks 145 | * Front-end: Users can drag tasks to be nested under other tasks, they can also 146 | 147 | ### Version 1.3 148 | 149 | * Calendar View 150 | 151 | ### Version 1.4 152 | 153 | * Contact integration for tasks: Users can sync with contacts 154 | 155 | ### Version 1.5 156 | 157 | * Markdown enabled in task descriptions 158 | 159 | ### Version 1.6 160 | 161 | * Calendar Integrations 162 | -------------------------------------------------------------------------------- /apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/__init__.py -------------------------------------------------------------------------------- /apps/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apps/lists/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/lists/__init__.py -------------------------------------------------------------------------------- /apps/lists/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /apps/lists/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ListsConfig(AppConfig): 5 | name = 'lists' 6 | -------------------------------------------------------------------------------- /apps/lists/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-06-05 23:11 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='List', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('created_at', models.DateField(auto_now_add=True)), 22 | ('updated_at', models.DateField(auto_now=True)), 23 | ('name', models.CharField(max_length=255)), 24 | ('description', models.TextField(null=True)), 25 | ('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 26 | ], 27 | options={ 28 | 'abstract': False, 29 | }, 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /apps/lists/migrations/0002_auto_20200605_2313.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-06-05 23:13 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('lists', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField( 14 | model_name='list', 15 | old_name='user_id', 16 | new_name='user', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/lists/migrations/0003_listitem.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-06-07 15:55 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('lists', '0002_auto_20200605_2313'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='ListItem', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('created_at', models.DateField(auto_now_add=True)), 19 | ('updated_at', models.DateField(auto_now=True)), 20 | ('text', models.CharField(max_length=255)), 21 | ('parent_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='lists.List')), 22 | ], 23 | options={ 24 | 'abstract': False, 25 | }, 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /apps/lists/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/lists/migrations/__init__.py -------------------------------------------------------------------------------- /apps/lists/migrations/__pycache__/0001_initial.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/lists/migrations/__pycache__/0001_initial.cpython-36.pyc -------------------------------------------------------------------------------- /apps/lists/migrations/__pycache__/0002_auto_20200605_2313.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/lists/migrations/__pycache__/0002_auto_20200605_2313.cpython-36.pyc -------------------------------------------------------------------------------- /apps/lists/migrations/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/lists/migrations/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apps/lists/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth import get_user_model 3 | 4 | from apps.utils.models import Timestamps 5 | 6 | 7 | class List(Timestamps, models.Model): 8 | user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) 9 | name = models.CharField(max_length=255) 10 | description = models.TextField(null=True) 11 | 12 | def __str__(self): 13 | return self.name 14 | 15 | 16 | class ListItem(Timestamps, models.Model): 17 | parent_list = models.ForeignKey(List, on_delete=models.CASCADE) 18 | text = models.CharField(max_length=255) 19 | 20 | def __str__(self): 21 | return self.text 22 | -------------------------------------------------------------------------------- /apps/lists/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/lists/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | from rest_framework_nested import routers 3 | from .views import ListViewSet, ListItemViewSet 4 | 5 | router = routers.SimpleRouter() 6 | router.register(r'', ListViewSet) 7 | 8 | lists_router = routers.NestedSimpleRouter(router, r'', lookup='list') 9 | lists_router.register(r'list-items', ListItemViewSet) 10 | 11 | urlpatterns = [ 12 | path(r'', include(router.urls)), 13 | path(r'', include(lists_router.urls)), 14 | ] 15 | -------------------------------------------------------------------------------- /apps/lists/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers, viewsets 2 | from django.contrib.auth import get_user_model 3 | 4 | from .models import List, ListItem 5 | 6 | 7 | class ListSerializer(serializers.HyperlinkedModelSerializer): 8 | user = serializers.PrimaryKeyRelatedField( 9 | queryset=get_user_model().objects.all()) 10 | 11 | class Meta: 12 | model = List 13 | fields = ['id', 'name', 'description', 'user', 14 | 'created_at', 'updated_at'] 15 | 16 | 17 | class ListItemSerializer(serializers.HyperlinkedModelSerializer): 18 | parent_list = serializers.PrimaryKeyRelatedField( 19 | queryset=List.objects.all()) 20 | 21 | class Meta: 22 | model = ListItem 23 | fields = ['id', 'text', 'parent_list', 24 | 'created_at', 'updated_at'] 25 | 26 | 27 | class ListViewSet(viewsets.ModelViewSet): 28 | queryset = List.objects.all() 29 | serializer_class = ListSerializer 30 | 31 | 32 | class ListItemViewSet(viewsets.ModelViewSet): 33 | queryset = ListItem.objects.all() 34 | serializer_class = ListItemSerializer 35 | -------------------------------------------------------------------------------- /apps/projects/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/projects/__init__.py -------------------------------------------------------------------------------- /apps/projects/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Project 3 | 4 | 5 | class ProjectAdmin(admin.ModelAdmin): 6 | fields = ('user', 'title', 'description') 7 | list_display = ('user', 'title', 'created_at', 'updated_at') 8 | 9 | 10 | admin.site.register(Project, ProjectAdmin) 11 | -------------------------------------------------------------------------------- /apps/projects/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ProjectsConfig(AppConfig): 5 | name = 'projects' 6 | -------------------------------------------------------------------------------- /apps/projects/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-07-08 14:14 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Project', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('created_at', models.DateField(auto_now_add=True)), 22 | ('updated_at', models.DateField(auto_now=True)), 23 | ('title', models.CharField(max_length=100)), 24 | ('description', models.TextField(blank=True, default='')), 25 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 26 | ], 27 | options={ 28 | 'abstract': False, 29 | }, 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /apps/projects/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/projects/migrations/__init__.py -------------------------------------------------------------------------------- /apps/projects/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth import get_user_model 3 | 4 | from apps.utils.models import Timestamps 5 | 6 | 7 | class Project(Timestamps, models.Model): 8 | user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) 9 | title = models.CharField(max_length=100) 10 | description = models.TextField(default='', blank=True) 11 | 12 | def __str__(self): 13 | return self.title 14 | -------------------------------------------------------------------------------- /apps/projects/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/projects/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | from rest_framework_nested import routers 3 | from .views import ProjectViewSet, ProjectTaskViewSet 4 | 5 | router = routers.SimpleRouter() 6 | router.register(r'', ProjectViewSet) 7 | 8 | project_router = routers.NestedSimpleRouter(router, r'', lookup='list') 9 | project_router.register('tasks', ProjectTaskViewSet, basename='project') 10 | 11 | urlpatterns = [ 12 | path('', include(router.urls)), 13 | path('', include(project_router.urls)), 14 | ] 15 | -------------------------------------------------------------------------------- /apps/projects/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers, viewsets 2 | from django.contrib.auth import get_user_model 3 | from rest_framework.decorators import action 4 | from rest_framework.response import Response 5 | 6 | from apps.tasks.views import TaskSerializer 7 | from apps.tasks.models import Task 8 | from .models import Project 9 | 10 | 11 | class ProjectSerializer(serializers.HyperlinkedModelSerializer): 12 | user = serializers.PrimaryKeyRelatedField( 13 | queryset=get_user_model().objects.all()) 14 | 15 | class Meta: 16 | model = Project 17 | fields = ['id', 'title', 'description', 'user', 18 | 'created_at', 'updated_at'] 19 | 20 | 21 | class ProjectViewSet(viewsets.ModelViewSet): 22 | queryset = Project.objects.all() 23 | serializer_class = ProjectSerializer 24 | 25 | @action(detail=False) 26 | def get_project_tasks(self, request, pk=None): 27 | project_tasks = Task.objects.filter(project=pk) 28 | serializer = self.get_serializer(project_tasks) 29 | return Response(serializer.data) 30 | 31 | 32 | class ProjectTaskViewSet(viewsets.ViewSet): 33 | def list(self, request, *args, **kwargs): 34 | queryset = Task.objects.filter(project=kwargs['list_pk']) 35 | serializer = TaskSerializer(queryset, many=True) 36 | return Response(serializer.data) 37 | -------------------------------------------------------------------------------- /apps/tasks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/tasks/__init__.py -------------------------------------------------------------------------------- /apps/tasks/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Task, Tag 3 | 4 | 5 | class TaskAdmin(admin.ModelAdmin): 6 | fields = ('user', 'project', 'title', 'description') 7 | list_display = ('user', 'title', 'project', 'created_at', 'updated_at') 8 | # filter_horizontal = ('tags',) 9 | 10 | 11 | class TagAdmin(admin.ModelAdmin): 12 | fields = ('text', 'color') 13 | list_display = ('text', 'color') 14 | 15 | 16 | admin.site.register(Task, TaskAdmin) 17 | admin.site.register(Tag, TagAdmin) 18 | -------------------------------------------------------------------------------- /apps/tasks/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class TaskConfig(AppConfig): 5 | name = 'tasks' 6 | -------------------------------------------------------------------------------- /apps/tasks/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-06-05 23:11 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Task', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('created_at', models.DateField(auto_now_add=True)), 22 | ('updated_at', models.DateField(auto_now=True)), 23 | ('title', models.CharField(max_length=100)), 24 | ('description', models.CharField(max_length=1000, null=True)), 25 | ('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 26 | ], 27 | options={ 28 | 'abstract': False, 29 | }, 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /apps/tasks/migrations/0002_auto_20200605_2313.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-06-05 23:13 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('tasks', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField( 14 | model_name='task', 15 | old_name='user_id', 16 | new_name='user', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/tasks/migrations/0003_auto_20200607_1555.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-06-07 15:55 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('tasks', '0002_auto_20200605_2313'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='task', 15 | name='description', 16 | field=models.TextField(null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/tasks/migrations/0004_auto_20200612_2259.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-06-12 22:59 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('tasks', '0003_auto_20200607_1555'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='task', 15 | name='completed', 16 | field=models.BooleanField(default=False), 17 | ), 18 | migrations.AlterField( 19 | model_name='task', 20 | name='description', 21 | field=models.TextField(blank=True, default=''), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/tasks/migrations/0005_auto_20200701_1416.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-07-01 14:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('tasks', '0004_auto_20200612_2259'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='task', 15 | name='category', 16 | field=models.IntegerField(choices=[(1, 'inbox'), (2, 'next'), (3, 'maybe'), (4, 'project')], default=1), 17 | ), 18 | migrations.AddField( 19 | model_name='task', 20 | name='date', 21 | field=models.DateField(blank=True, null=True), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/tasks/migrations/0006_task_project.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-07-08 14:16 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('projects', '0001_initial'), 11 | ('tasks', '0005_auto_20200701_1416'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='task', 17 | name='project', 18 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='projects.Project'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/tasks/migrations/0007_tag.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-10-04 16:07 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('tasks', '0006_task_project'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='Tag', 15 | fields=[ 16 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 17 | ('text', models.CharField(max_length=25)), 18 | ('color', models.CharField(default='424244', max_length=6)), 19 | ('tasks', models.ManyToManyField(blank=True, to='tasks.Task')), 20 | ], 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/tasks/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/tasks/migrations/__init__.py -------------------------------------------------------------------------------- /apps/tasks/migrations/__pycache__/0001_initial.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/tasks/migrations/__pycache__/0001_initial.cpython-36.pyc -------------------------------------------------------------------------------- /apps/tasks/migrations/__pycache__/0002_auto_20200605_2313.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/tasks/migrations/__pycache__/0002_auto_20200605_2313.cpython-36.pyc -------------------------------------------------------------------------------- /apps/tasks/migrations/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/tasks/migrations/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apps/tasks/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth import get_user_model 3 | 4 | from apps.utils.models import Timestamps 5 | from apps.projects.models import Project 6 | 7 | 8 | class Task(Timestamps, models.Model): 9 | user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) 10 | project = models.ForeignKey(Project, on_delete=models.DO_NOTHING, 11 | null=True, blank=True) 12 | title = models.CharField(max_length=100) 13 | description = models.TextField(default='', blank=True) 14 | completed = models.BooleanField(default=False) 15 | date = models.DateField(null=True, blank=True) 16 | 17 | CATEGORIES = ( 18 | (1, 'inbox'), 19 | (2, 'next'), 20 | (3, 'maybe'), 21 | (4, 'project') 22 | ) 23 | category = models.IntegerField(choices=CATEGORIES, default=1) 24 | 25 | def __str__(self): 26 | return self.title 27 | 28 | # def tags(self): 29 | # return self.tags... 30 | 31 | 32 | class Tag(models.Model): 33 | text = models.CharField(max_length=25) 34 | color = models.CharField(max_length=6, default='424244') 35 | tasks = models.ManyToManyField(Task, blank=True) 36 | 37 | def __str__(self): 38 | return self.text 39 | -------------------------------------------------------------------------------- /apps/tasks/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/tasks/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | from rest_framework import routers 3 | from .views import TaskViewSet 4 | 5 | router = routers.DefaultRouter() 6 | router.register(r'', TaskViewSet) 7 | 8 | urlpatterns = [ 9 | path('', include(router.urls)), 10 | ] 11 | -------------------------------------------------------------------------------- /apps/tasks/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers, viewsets 2 | from django.contrib.auth import get_user_model 3 | 4 | from apps.projects.models import Project 5 | from .models import Task 6 | 7 | 8 | class TaskSerializer(serializers.HyperlinkedModelSerializer): 9 | user = serializers.PrimaryKeyRelatedField( 10 | queryset=get_user_model().objects.all()) 11 | project = serializers.PrimaryKeyRelatedField( 12 | queryset=Project.objects.all(), required=False, default='') 13 | 14 | class Meta: 15 | model = Task 16 | fields = ['id', 'title', 'description', 'user', 17 | 'completed', 'created_at', 'updated_at', 18 | 'date', 'category', 'project'] 19 | 20 | 21 | class TaskViewSet(viewsets.ModelViewSet): 22 | queryset = Task.objects.all() 23 | serializer_class = TaskSerializer 24 | -------------------------------------------------------------------------------- /apps/users/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/users/__init__.py -------------------------------------------------------------------------------- /apps/users/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/users/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apps/users/__pycache__/admin.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/users/__pycache__/admin.cpython-36.pyc -------------------------------------------------------------------------------- /apps/users/__pycache__/apps.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/users/__pycache__/apps.cpython-36.pyc -------------------------------------------------------------------------------- /apps/users/__pycache__/models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/users/__pycache__/models.cpython-36.pyc -------------------------------------------------------------------------------- /apps/users/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.auth.admin import UserAdmin as BaseUserAdmin 3 | from .models import User, UserProfile 4 | 5 | 6 | class UserAdmin(BaseUserAdmin): 7 | list_display = ('email', 'is_staff', 'is_admin') 8 | list_filter = ('is_admin',) 9 | fieldsets = ( 10 | (None, {'fields': ('email', 'password')}), 11 | ('Permissions', {'fields': ('is_admin', 'is_staff')}), 12 | ) 13 | 14 | add_fieldsets = ( 15 | (None, { 16 | 'classes': ('wide',), 17 | 'fields': ('email', 'password1', 'password2'), 18 | }), 19 | ) 20 | 21 | search_fields = ('email',) 22 | ordering = ('email',) 23 | filter_horizontal = () 24 | 25 | 26 | class UserProfileAdmin(admin.ModelAdmin): 27 | pass 28 | 29 | 30 | admin.site.register(User, UserAdmin) 31 | admin.site.register(UserProfile, UserProfileAdmin) 32 | -------------------------------------------------------------------------------- /apps/users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UsersConfig(AppConfig): 5 | name = 'users' 6 | -------------------------------------------------------------------------------- /apps/users/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-06-03 14:29 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='User', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('password', models.CharField(max_length=128, verbose_name='password')), 19 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 20 | ('email', models.EmailField(max_length=255, unique=True, verbose_name='email address')), 21 | ('is_active', models.BooleanField(default=True)), 22 | ('is_admin', models.BooleanField(default=False)), 23 | ], 24 | options={ 25 | 'abstract': False, 26 | }, 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /apps/users/migrations/0002_user_is_staff.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-06-03 14:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='user', 15 | name='is_staff', 16 | field=models.BooleanField(default=False), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/users/migrations/0003_userprofile.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-06-03 15:15 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('users', '0002_user_is_staff'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='UserProfile', 17 | fields=[ 18 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), 19 | ('first_name', models.CharField(max_length=100)), 20 | ('last_name', models.CharField(max_length=100)), 21 | ('image', models.ImageField(upload_to='profile-images')), 22 | ('fb_profile', models.CharField(max_length=100)), 23 | ('twitter_profile', models.CharField(max_length=100)), 24 | ('linkedin_profile', models.CharField(max_length=100)), 25 | ('website', models.CharField(max_length=100)), 26 | ], 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /apps/users/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/users/migrations/__init__.py -------------------------------------------------------------------------------- /apps/users/migrations/__pycache__/0001_initial.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/users/migrations/__pycache__/0001_initial.cpython-36.pyc -------------------------------------------------------------------------------- /apps/users/migrations/__pycache__/0002_user_is_staff.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/users/migrations/__pycache__/0002_user_is_staff.cpython-36.pyc -------------------------------------------------------------------------------- /apps/users/migrations/__pycache__/0003_userprofile.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/users/migrations/__pycache__/0003_userprofile.cpython-36.pyc -------------------------------------------------------------------------------- /apps/users/migrations/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/users/migrations/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apps/users/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import ( 3 | BaseUserManager, AbstractBaseUser 4 | ) 5 | 6 | 7 | class CustomUserManager(BaseUserManager): 8 | def create_user(self, email, password=None): 9 | if not email: 10 | raise ValueError('Users must have an email address') 11 | 12 | user = self.model( 13 | email=self.normalize_email(email), 14 | ) 15 | 16 | user.set_password(password) 17 | user.save(using=self._db) 18 | return user 19 | 20 | def create_superuser(self, email, password=None): 21 | user = self.create_user( 22 | email, 23 | password=password, 24 | ) 25 | user.is_admin = True 26 | user.is_staff = True 27 | user.save(using=self._db) 28 | return user 29 | 30 | 31 | class User(AbstractBaseUser): 32 | email = models.EmailField( 33 | verbose_name='email address', 34 | max_length=255, 35 | unique=True, 36 | ) 37 | is_active = models.BooleanField(default=True) 38 | is_admin = models.BooleanField(default=False) 39 | is_staff = models.BooleanField(default=False) 40 | 41 | USERNAME_FIELD = 'email' 42 | 43 | objects = CustomUserManager() 44 | 45 | def __str__(self): 46 | return self.email 47 | 48 | def has_perm(self, perm, obj=None): 49 | "Does the user have a specific permission?" 50 | # Simplest possible answer: Yes, always 51 | return True 52 | 53 | def has_module_perms(self, app_label): 54 | "Does the user have permissions to view the app `app_label`?" 55 | # Simplest possible answer: Yes, always 56 | return True 57 | 58 | 59 | class UserProfile(models.Model): 60 | user = models.OneToOneField( 61 | User, 62 | on_delete=models.CASCADE, 63 | primary_key=True, 64 | ) 65 | first_name = models.CharField(max_length=100) 66 | last_name = models.CharField(max_length=100) 67 | image = models.ImageField(upload_to='profile-images') 68 | fb_profile = models.CharField(max_length=100) 69 | twitter_profile = models.CharField(max_length=100) 70 | linkedin_profile = models.CharField(max_length=100) 71 | website = models.CharField(max_length=100) 72 | 73 | def __str__(self): 74 | return f'{self.first_name} {self.last_name}' 75 | -------------------------------------------------------------------------------- /apps/users/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/users/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | from rest_framework import routers 3 | from .views import UserViewSet 4 | 5 | router = routers.DefaultRouter() 6 | router.register(r'', UserViewSet) 7 | 8 | urlpatterns = [ 9 | path('', include(router.urls)), 10 | ] 11 | -------------------------------------------------------------------------------- /apps/users/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers, viewsets 2 | from django.contrib.auth import get_user_model 3 | 4 | from .models import User 5 | 6 | 7 | class UserSerializer(serializers.ModelSerializer): 8 | class Meta: 9 | model = get_user_model() 10 | fields = ('email', 'password') 11 | extra_kwargs = {'password': {'write_only': True, 'min_length': 8}} 12 | 13 | def create(self, validated_data): 14 | return get_user_model().objects.create_user(**validated_data) 15 | 16 | 17 | class UserViewSet(viewsets.ModelViewSet): 18 | queryset = User.objects.all() 19 | serializer_class = UserSerializer 20 | -------------------------------------------------------------------------------- /apps/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/utils/__init__.py -------------------------------------------------------------------------------- /apps/utils/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/utils/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apps/utils/__pycache__/admin.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/utils/__pycache__/admin.cpython-36.pyc -------------------------------------------------------------------------------- /apps/utils/__pycache__/models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/apps/utils/__pycache__/models.cpython-36.pyc -------------------------------------------------------------------------------- /apps/utils/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /apps/utils/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UtilsConfig(AppConfig): 5 | name = 'utils' 6 | -------------------------------------------------------------------------------- /apps/utils/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Timestamps(models.Model): 5 | created_at = models.DateField(auto_now_add=True) 6 | updated_at = models.DateField(auto_now=True) 7 | 8 | class Meta: 9 | abstract = True 10 | -------------------------------------------------------------------------------- /apps/utils/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/utils/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonable-productivity/web-back-end/3ac5fc51b06b7c63e595a573be266b3c4145cf43/config/__init__.py -------------------------------------------------------------------------------- /config/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for ReasonableProductivityAPI project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /config/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for ReasonableProductivityAPI project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.0.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.0/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/3.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '+w880x28691(irned1b9r#8%jnc0!$$fpe6w+zb+&-h8x!3jau' 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 | 'rest_framework', 41 | 'rest_framework.authtoken', 42 | 'rest_framework_nested', 43 | 'corsheaders', 44 | 'apps.users', 45 | 'apps.tasks', 46 | 'apps.projects', 47 | 'apps.lists', 48 | 'apps.utils', 49 | ] 50 | 51 | MIDDLEWARE = [ 52 | 'corsheaders.middleware.CorsMiddleware', 53 | 'django.middleware.security.SecurityMiddleware', 54 | 'django.contrib.sessions.middleware.SessionMiddleware', 55 | 'django.middleware.common.CommonMiddleware', 56 | 'django.middleware.csrf.CsrfViewMiddleware', 57 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 58 | 'django.contrib.messages.middleware.MessageMiddleware', 59 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 60 | ] 61 | 62 | AUTH_USER_MODEL = 'users.User' 63 | 64 | ROOT_URLCONF = 'config.urls' 65 | 66 | CORS_ORIGIN_ALLOW_ALL = True 67 | 68 | TEMPLATES = [ 69 | { 70 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 71 | 'DIRS': [], 72 | 'APP_DIRS': True, 73 | 'OPTIONS': { 74 | 'context_processors': [ 75 | 'django.template.context_processors.debug', 76 | 'django.template.context_processors.request', 77 | 'django.contrib.auth.context_processors.auth', 78 | 'django.contrib.messages.context_processors.messages', 79 | ], 80 | }, 81 | }, 82 | ] 83 | 84 | WSGI_APPLICATION = 'config.wsgi.application' 85 | 86 | 87 | # Database 88 | # https://docs.djangoproject.com/en/3.0/ref/settings/#databases 89 | 90 | DATABASES = { 91 | # 'default': { 92 | # 'ENGINE': 'django.db.backends.sqlite3', 93 | # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 94 | # } 95 | 'default': { 96 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 97 | 'NAME': 'reasonable_productivity', 98 | 'USER': 'rp_admin', 99 | 'PASSWORD': '', 100 | 'HOST': 'localhost', 101 | 'PORT': '', 102 | } 103 | } 104 | 105 | 106 | # Password validation 107 | # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators 108 | 109 | AUTH_PASSWORD_VALIDATORS = [ 110 | { 111 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 112 | }, 113 | { 114 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 115 | }, 116 | { 117 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 118 | }, 119 | { 120 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 121 | }, 122 | ] 123 | 124 | 125 | # Internationalization 126 | # https://docs.djangoproject.com/en/3.0/topics/i18n/ 127 | 128 | LANGUAGE_CODE = 'en-us' 129 | 130 | TIME_ZONE = 'UTC' 131 | 132 | USE_I18N = True 133 | 134 | USE_L10N = True 135 | 136 | USE_TZ = True 137 | 138 | 139 | # Static files (CSS, JavaScript, Images) 140 | # https://docs.djangoproject.com/en/3.0/howto/static-files/ 141 | 142 | STATIC_URL = '/static/' 143 | 144 | MEDIA_ROOT = 'media' 145 | MEDIA_URL = '/media/' 146 | -------------------------------------------------------------------------------- /config/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path, include 3 | 4 | urlpatterns = [ 5 | path('admin/', admin.site.urls), 6 | path('users/', include('apps.users.urls')), 7 | path('tasks/', include('apps.tasks.urls')), 8 | path('lists/', include('apps.lists.urls')), 9 | path('projects/', include('apps.projects.urls')) 10 | ] 11 | -------------------------------------------------------------------------------- /config/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for ReasonableProductivityAPI 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/3.0/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', 'config.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.2.7 2 | Django==3.0.6 3 | django-cors-headers==3.3.0 4 | djangorestframework==3.11.0 5 | drf-nested-routers==0.91 6 | flake8==3.8.2 7 | importlib-metadata==1.6.0 8 | mccabe==0.6.1 9 | Pillow==7.1.2 10 | psycopg2==2.8.5 11 | pycodestyle==2.6.0 12 | pyflakes==2.2.0 13 | pytz==2020.1 14 | sqlparse==0.3.1 15 | zipp==3.1.0 16 | --------------------------------------------------------------------------------