Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 160 hangouts endpoint first pass #193

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
dcbab36
Added osprojects to admin interface.
BethanyG Sep 7, 2020
31edf0f
Added osprojects to admin interface.
BethanyG Sep 7, 2020
d585cdd
Added OSProjects to admin interface.
BethanyG Sep 7, 2020
606c871
added django-res-auth and associated basic settings to test DRF and a…
BethanyG Sep 8, 2020
30936fe
Working state Mon Sept 14 - dj-rest-auth and django-allauth
BethanyG Sep 14, 2020
cdadba1
Removed django-rest-jwt and django-rest-auth added simplejwt and dj-r…
BethanyG Sep 22, 2020
782e05b
Added routes for registration and allauth.
BethanyG Sep 22, 2020
040c6f3
Added password reset email template.
BethanyG Sep 22, 2020
ef3a5ad
Added confirm_email method. Will probably remove it as uneeded later.
BethanyG Sep 22, 2020
7e6ff72
Added CustomUserDetailsSerializer. Stubbed other serializers for con…
BethanyG Sep 22, 2020
e97153b
Added routes for registration, email confirmation, password reset, lo…
BethanyG Sep 22, 2020
b6c4ac2
Added CustomVerifyEmailView to enable email verify emails from the ba…
BethanyG Sep 22, 2020
f06a74c
Added tests for registration flow, token authorization, email verific…
BethanyG Sep 22, 2020
221b7b6
Fixed tests that were failing due to auth change.
BethanyG Sep 24, 2020
e8197e7
Added final simpleJWT tests for auth.
BethanyG Sep 24, 2020
f220224
Added skips for User tests broken due to auth changes.
BethanyG Sep 24, 2020
bb47eb0
Removed django-rest_auth and django-rest-jwt from requirememts.
BethanyG Sep 24, 2020
9fd6eb7
Removed unused import that was causing an error.
BethanyG Sep 24, 2020
2670780
Removed excess space from email_confirmation_subject.txt filename.
BethanyG Sep 24, 2020
9451986
Revert "Removed excess space from email_confirmation_subject.txt file…
BethanyG Sep 24, 2020
aa6218a
Attempted to fix intermittent email verification post test failure by…
BethanyG Sep 24, 2020
f91a22d
Changed order of included URLs.
BethanyG Sep 24, 2020
a2b2b4b
Added specific path for email link verification post and changed path…
BethanyG Sep 24, 2020
569ee99
Fun trick: If your regex fails to capture all the key generations cas…
BethanyG Sep 24, 2020
35c5610
Removed print statement from email post verificaton test case.
BethanyG Sep 24, 2020
75e9461
Corrected circular reference that was breaking tests and renamed file…
BethanyG Sep 24, 2020
b2ba2c2
Removed regex from path url to avoid djang deprecation and migration …
BethanyG Sep 24, 2020
6934291
New stub files after running create app script for django.
BethanyG Sep 26, 2020
738f7c1
Registered Hangouts as an app.
BethanyG Sep 26, 2020
11c40e2
WIP for registering Hangouts in admin. Issues with properly inlining…
BethanyG Sep 26, 2020
92b8944
Inital migration file for hangouts, hangoutsessions, and hangoutrespo…
BethanyG Sep 26, 2020
afa7f25
First pass at hangouts model.
BethanyG Sep 26, 2020
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
70 changes: 47 additions & 23 deletions project/config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,13 @@
"allauth.account",
"allauth.socialaccount",
"rest_framework",
"rest_framework.authtoken",
"corsheaders",
"taggit",
"django_celery_beat",
"taggit_serializer"
"taggit_serializer",
"dj_rest_auth",
"dj_rest_auth.registration",
]

LOCAL_APPS = [
Expand All @@ -89,7 +92,8 @@
"resources.apps.ResourcesConfig",
"tagging.apps.TaggingConfig",
'userauth.apps.UserauthConfig',
'osprojects.apps.OsprojectsConfig'
'osprojects.apps.OsprojectsConfig',
'hangouts.apps.HangoutsConfig'
# Your stuff: custom apps go here
]
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
Expand Down Expand Up @@ -287,21 +291,49 @@
# CELERY_TASK_SOFT_TIME_LIMIT = 60
# # http://docs.celeryproject.org/en/latest/userguide/configuration.html#beat-scheduler
# CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
# # django-allauth
# # ------------------------------------------------------------------------------
# ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)
# # https://django-allauth.readthedocs.io/en/latest/configuration.html
# ACCOUNT_AUTHENTICATION_METHOD = "username"
# # https://django-allauth.readthedocs.io/en/latest/configuration.html
# ACCOUNT_EMAIL_REQUIRED = True
# # https://django-allauth.readthedocs.io/en/latest/configuration.html
# ACCOUNT_EMAIL_VERIFICATION = "mandatory"
# # https://django-allauth.readthedocs.io/en/latest/configuration.html
# ACCOUNT_ADAPTER = "users.adapters.AccountAdapter"


# # django-allauth config
# # https://django-allauth.readthedocs.io/en/latest/configuration.html
# # ------------------------------------------------------------------------------
ACCOUNT_ADAPTER = "userauth.adapter.CustomAccountAdapter"
CUSTOM_ACCOUNT_CONFIRM_EMAIL_URL = "verify-email/?key={0}"
CUSTOM_ACCOUNT_PASSWORD_RESET_CONFIRM_URL = "password/reset/<uidb64>/<token>/"
#URL_FRONT = "http://localhost:8000/"
#ACCOUNT_ADAPTER = "users.adapters.AccountAdapter"
ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_AUTHENTICATION_METHOD = "username_email"
ACCOUNT_CONFIRM_EMAIL_ON_GET = False
#ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = None
#ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = reverse_lazy('account_confirm_complete')
ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 3
ACCOUNT_EMAIL_CONFIRMATION_HMAC = True
ACCOUNT_EMAIL_SUBJECT_PREFIX = "Codebuddies: "
ACCOUNT_DEFAULT_HTTP_PROTOCOL = "http"
ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True
ACCOUNT_LOGOUT_ON_GET = False

# SOCIALACCOUNT_ADAPTER = "users.adapters.SocialAccountAdapter"


# #dj-rest-auth config
# #https://dj-rest-auth.readthedocs.io/en/latest/configuration.html
# # ---------------------------------------------------------------------------------
REST_USE_JWT = True
JWT_AUTH_COOKIE = 'cb-auth'
OLD_PASSWORD_FIELD_ENABLED = True
LOGOUT_ON_PASSWORD_CHANGE = True

REST_AUTH_SERIALIZERS = {
'USER_DETAILS_SERIALIZER': 'userauth.serializers.CustomUserDetailSerializer',
'PASSWORD_RESET_SERIALIZER' : 'userauth.serializers.CustomPasswordResetSerializer',
'PASSWORD_RESET_CONFIRM_SERIALIZER': 'userauth.serializers.CustomPasswordResetConfirmSerializer',
}


REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],

Expand All @@ -320,22 +352,14 @@
'rest_framework.renderers.BrowsableAPIRenderer',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication', ],

'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
}


JWT_AUTH = {
'JWT_AUTH_HEADER_PREFIX': 'Bearer',
'JWT_RESPONSE_PAYLOAD_HANDLER': 'core.utils.my_jwt_response_handler',
'JWT_ALLOW_REFRESH': True,
'JWT_EXPIRATION_DELTA': timedelta(hours=1),
'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=3),
}

CORS_ORIGIN_WHITELIST = (
'https://127.0.0.1:3000',
'http://localhost:3000',
Expand Down
26 changes: 19 additions & 7 deletions project/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,35 @@
from django.contrib import admin
from django.views.generic import TemplateView
from django.views import defaults as default_views
from rest_framework.exceptions import server_error
from rest_framework import routers, serializers, viewsets
from resources.urls import router as resources_router
from userauth.views import CustomVerifyEmailView
from userauth.urls import router as userauth_router

router = routers.DefaultRouter()
router.registry.extend(resources_router.registry)
router.registry.extend(userauth_router.registry)

urlpatterns = [
path("", TemplateView.as_view(template_name="pages/home.html"), name="home"),
path(
"about/", TemplateView.as_view(template_name="pages/about.html"), name="about"
),
path("about/", TemplateView.as_view(template_name="pages/about.html"), name="about"),

# Django Admin, use {% url 'admin:index' %}
path(settings.ADMIN_URL, admin.site.urls),

# User management
#currently an unused endpoint, but can be used if needed for extended user profiles, etc.
path("users/", include("users.urls", namespace="users")),
path("accounts/", include("allauth.urls")),

# Your stuff: custom urls includes go here
path('api/v1/', include('resources.urls')),
path('auth/', include('userauth.urls', namespace="userauth")),
#this is a route for logging into the "browsable api" if not needed for testing, it should be omitted.
path('api/v1/', include('rest_framework.urls', namespace='rest_framework')),
path('api/v1/auth/', include(('userauth.urls', 'userauth'), namespace="userauth")),
path('api/v1/', include('resources.urls', namespace='resources')),

#we have to include these for registration email validation, but otherwise these paths are NOT used
path("accounts/", include("allauth.urls")),


] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{% load account %}{% user_display user as user_display %}{% load i18n %}{% autoescape off %}{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Hello from {{ site_name }}!

You're receiving this e-mail because user {{ user_display }} has used this e-mail address to register an account on {{ site_domain }}.

To confirm this is correct, go to {{ activate_url }}
{% endblocktrans %}
{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Thank you from {{ site_name }}!
{{ site_domain }}{% endblocktrans %}
{% endautoescape %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Please Confirm Your E-mail Address{% endblocktrans %}
{% endautoescape %}
14 changes: 14 additions & 0 deletions project/core/templates/registration/password_reset_email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% load i18n %}{% autoescape off %}
2 {% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
3
4 {% trans "Please go to the following page and choose a new password:" %}
5 {% block reset_link %}
6 {{ protocol }}://{{ domain }}{% url 'userauth:password_reset_confirm' uidb64=uid token=token %}
7 {% endblock %}
8 {% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
9
10 {% trans "Thanks for using our site!" %}
11
12 {% blocktrans %}The {{ site_name }} team{% endblocktrans %}
13
14 {% endautoescape %}
Empty file added project/hangouts/__init__.py
Empty file.
30 changes: 30 additions & 0 deletions project/hangouts/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from django.contrib import admin
from .models import Hangout, HangoutSessions, HangoutResponses

# Register your models here.
class HangoutSessionsInline(admin.StackedInline):
model = HangoutSessions
readonly_fields = ('id', 'guid')


class HangoutResponsesInline(admin.StackedInline):
model = HangoutResponses
readonly_fields = ('id', 'guid')


class HangoutAdmin(admin.ModelAdmin):
model = Hangout
readonly_fields = ('id', 'guid')
list_display = ['tag_list']
inlines = ['HangoutSessionsInline', 'HangoutResponsesInline']

def get_queryset(self, request):
return super().get_queryset(request).prefetch_related('tags')

def tag_list(self, obj):
return u", ".join(o.name for o in obj.tags.all())


admin.site.register(Hangout)
admin.site.register(HangoutResponses)
admin.site.register(HangoutSessions)
5 changes: 5 additions & 0 deletions project/hangouts/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class HangoutsConfig(AppConfig):
name = 'hangouts'
76 changes: 76 additions & 0 deletions project/hangouts/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Generated by Django 2.2.4 on 2020-09-26 08:22

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import hangouts.models
import taggit.managers
import uuid


class Migration(migrations.Migration):

initial = True

dependencies = [
('tagging', '0003_auto_20200508_1230'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('resources', '0007_auto_20200303_1258'),
]

operations = [
migrations.CreateModel(
name='Hangout',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('guid', models.UUIDField(default=uuid.uuid1, editable=False)),
('status', models.CharField(blank=True, max_length=200)),
('hangout_type', models.CharField(choices=[('WATCH', 'Watch Me Code'), ('PRES', 'Presentation'), ('COWRK', 'Co-work with Me'), ('STUDY', 'Study Group'), ('PAIR', 'Pairing'), ('ACNT', 'Keep Me Accountable'), ('DISC', 'Discussion'), ('TEACH', 'I have something to teach')], max_length=6)),
('title', models.CharField(max_length=200)),
('slug', models.SlugField(allow_unicode=True, max_length=100, verbose_name='Slug')),
('short_description', models.TextField(max_length=300)),
('long_description', models.TextField(blank=True, max_length=600, null=True)),
('open_to_RSVP', models.BooleanField(default=False)),
('start_time', models.DateTimeField(default=django.utils.timezone.now)),
('end_time', models.DateTimeField()),
('created', models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(default=django.utils.timezone.now)),
('recurring', models.BooleanField(default=False)),
('internal_platform', models.BooleanField(default=True)),
('external_platform_link', models.URLField(blank=True, max_length=300, null=True)),
('related_resources', models.ManyToManyField(blank=True, related_name='related_hangouts', to='resources.Resource')),
('tags', taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='tagging.TaggedItems', to='tagging.CustomTag', verbose_name='Tags')),
('user', models.ForeignKey(on_delete=models.SET(hangouts.models.get_sentinel_user), to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='HangoutSessions',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('guid', models.UUIDField(default=uuid.uuid1, editable=False)),
('status', models.CharField(blank=True, max_length=200)),
('start_time', models.DateTimeField(default=django.utils.timezone.now)),
('end_time', models.DateTimeField()),
('created', models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(default=django.utils.timezone.now)),
('hangout_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='related_sessions', to='hangouts.Hangout')),
('related_resources', models.ManyToManyField(blank=True, related_name='related_hangout_sessions', to='resources.Resource')),
],
),
migrations.CreateModel(
name='HangoutResponses',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('guid', models.UUIDField(default=uuid.uuid1, editable=False)),
('express_interest', models.BooleanField(default=False)),
('request_to_join', models.BooleanField(default=False)),
('rsvp', models.BooleanField(default=False)),
('response_comment', models.TextField(blank=True, max_length=300, null=True)),
('status', models.TextField(max_length=10)),
('hangout_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='related_responses', to='hangouts.Hangout')),
('hangout_session_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='related_session_responses', to='hangouts.HangoutSessions')),
('user_id', models.ForeignKey(on_delete=models.SET(hangouts.models.get_sentinel_user), to=settings.AUTH_USER_MODEL)),
],
),
]
Empty file.
99 changes: 99 additions & 0 deletions project/hangouts/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import uuid
import datetime
from taggit.managers import TaggableManager
from django.conf import settings
from django.db import models
from django.utils import timezone
from django.contrib.auth import get_user_model
from django.utils.translation import ugettext_lazy as _
from resources.models import Resource
from tagging.managers import CustomTaggableManager
from tagging.models import CustomTag, TaggedItems


def get_sentinel_user():
return get_user_model().objects.get_or_create(username='deleted')[0]


def get_tags_display(self):
return self.tags.values_list('name', flat=True)


class Hangout(models.Model):
HANGOUT_TYPES = [
('WATCH', 'Watch Me Code'),
('PRES', 'Presentation'),
('COWRK', 'Co-work with Me'),
('STUDY', 'Study Group'),
('PAIR', 'Pairing'),
('ACNT', 'Keep Me Accountable'),
('DISC', 'Discussion'),
('TEACH', 'I have something to teach'),
]

guid = models.UUIDField(default=uuid.uuid1, editable=False)

#One of scheduled, pending, rescheduled, stale, hold, closed, completed
status = models.CharField(blank=True, max_length=200)
hangout_type = models.CharField(max_length=6, choices=HANGOUT_TYPES)

#we are going to require a title
title = models.CharField(max_length=200, blank=False)
slug = models.SlugField(verbose_name=_("Slug"), max_length=100, allow_unicode=True)
short_description = models.TextField(max_length=300, blank=False, null=False)
long_description = models.TextField(max_length=600, blank=True, null=True)

# user who "owns" the hangout we'll pull this from their TOKEN
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET(get_sentinel_user))

#sort of a public/private thing and confirmed/not confirmed thing
open_to_RSVP = models.BooleanField(blank=False, null=False, default=False)

#Calendar date + start time would be derived from the datetime object
start_time = models.DateTimeField(default=timezone.now)

#Calendar date + end time would be derived from the datetime object
end_time = models.DateTimeField(blank=False, null=False)

# creation date of hangout entry
created = models.DateTimeField(auto_now_add=True)

# modification date of hangout entry
modified = models.DateTimeField(default=timezone.now)

recurring = models.BooleanField(null=False, default=False)

internal_platform = models.BooleanField(null=False, default=True)
external_platform_link = models.URLField(max_length=300, blank=True, null=True)

related_resources = models.ManyToManyField(Resource, blank=True, related_name='related_hangouts')

# Allow tags to be used across entities
# E.g. so we can create composite views showing all entities sharing a common tag
tags = TaggableManager(through=TaggedItems, manager=CustomTaggableManager, blank=True)


class HangoutSessions(models.Model):
guid = models.UUIDField(default=uuid.uuid1, editable=False)
hangout_id = models.ForeignKey(Hangout, on_delete=models.CASCADE, blank=True, null=True, related_name='related_sessions')
status = models.CharField(blank=True, max_length=200) #scheduled, pending, rescheduled, stale, hold, closed, completed
start_time = models.DateTimeField(default=timezone.now)
end_time = models.DateTimeField(blank=False, null=False)
related_resources = models.ManyToManyField(Resource, blank=True, related_name='related_hangout_sessions')
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(default=timezone.now)


class HangoutResponses(models.Model):
guid = models.UUIDField(default=uuid.uuid1, editable=False)
hangout_id = models.ForeignKey(Hangout, on_delete=models.CASCADE, blank=True, null=True,
related_name='related_responses')
hangout_session_id = models.ForeignKey(HangoutSessions, on_delete=models.CASCADE, blank=True, null=True,
related_name='related_session_responses')
user_id = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET(get_sentinel_user))
express_interest = models.BooleanField(blank=False, null=False, default=False)
request_to_join = models.BooleanField(blank=False, null=False, default=False)
rsvp = models.BooleanField(blank=False, null=False, default=False)
response_comment = models.TextField(max_length=300, blank=True, null=True)
status = models.TextField(max_length=10, blank=False, null=False)

Loading