-
Notifications
You must be signed in to change notification settings - Fork 1
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
feat: ✨ referral app #6
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# HTK Imports | ||
from htk.app_config import HtkAppConfig | ||
|
||
|
||
# isort: off | ||
|
||
|
||
class HtkReferralAppConfig(HtkAppConfig): | ||
name = 'htk.apps.referral' | ||
verbose_name = 'HTK Referral' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Referral app settings | ||
|
||
# Allow hash referral codes | ||
HTK_REFERRAL_ALLOW_HASH_CODE = True | ||
|
||
# Allow usernames as referral code | ||
HTK_REFERRAL_ALLOW_USERNAME = True | ||
|
||
# Redirect URL name for the referral URL | ||
HTK_REFERRAL_REDIRECT_URL_NAME = 'home' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Set it to None and require the developer to configure something. If it's not set, redirect to |
||
|
||
HTK_REFERRAL_QUERY_PARAM = 'ref' | ||
|
||
HTK_REFERRAL_MODEL = 'htk.Referral' | ||
|
||
HTK_REFERRAL_CODE_MODEL = 'htk.ReferralCode' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Django Imports | ||
from django.utils.deprecation import MiddlewareMixin | ||
|
||
# HTK Imports | ||
from htk.utils import htk_setting | ||
|
||
# Local Imports | ||
from .utils import get_referrer_from_code | ||
|
||
|
||
# isort: off | ||
|
||
|
||
class ReferralMiddleware(MiddlewareMixin): | ||
def __init__(self, *args, **kwargs): | ||
super(ReferralMiddleware, self).__init__(*args, **kwargs) | ||
self.referrer = None | ||
|
||
def process_request(self, request): | ||
key = htk_setting('HTK_REFERRAL_QUERY_PARAM_KEY') | ||
referral_code = request.GET.get(key) | ||
if referral_code: | ||
self.referrer = get_referrer_from_code(referral_code) | ||
|
||
def process_response(self, request, response): | ||
if self.referrer: | ||
request.session['referrer_id'] = self.referrer.id | ||
response.set_cookie('referrer_id', self.referrer.id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use all caps for cookie names; also define a constants file for magic strings |
||
return response |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Django Imports | ||
from django.conf import settings | ||
from django.db import models | ||
|
||
|
||
class ReferralCode(models.Model): | ||
"""Referral Code model""" | ||
|
||
code = models.CharField(max_length=56, unique=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Always put |
||
user = models.ForeignKey( | ||
settings.AUTH_USER_MODEL, | ||
related_name='referral_codes', | ||
on_delete=models.CASCADE, | ||
) | ||
created_at = models.DateTimeField(auto_now_add=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add an We will need to limit the number of referral codes a user can create, otherwise they will just create infinitely many referral codes. |
||
updated_at = models.DateTimeField(auto_now=True) | ||
|
||
class Meta: | ||
abstract = True | ||
unique_together = ('code',) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Django Imports | ||
from django.conf import settings | ||
from django.db import models | ||
|
||
|
||
# isort: off | ||
|
||
|
||
class Referral(models.Model): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
"""Referral model""" | ||
|
||
referrer = models.ForeignKey( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the first field can just be |
||
settings.AUTH_USER_MODEL, | ||
related_name='referrals', | ||
on_delete=models.CASCADE, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should have |
||
) | ||
referred = models.OneToOneField( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this can just be |
||
settings.AUTH_USER_MODEL, | ||
related_name='referred_by', | ||
on_delete=models.CASCADE, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should have |
||
) | ||
created_at = models.DateTimeField(auto_now_add=True) | ||
updated_at = models.DateTimeField(auto_now=True) | ||
|
||
class Meta: | ||
abstract = True | ||
unique_together = ('referrer', 'referred') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Django Imports | ||
from django.core.exceptions import AppRegistryNotReady | ||
|
||
# HTK Imports | ||
from htk.utils import ( | ||
htk_setting, | ||
resolve_model_dynamically, | ||
) | ||
|
||
|
||
try: | ||
Referral = resolve_model_dynamically(htk_setting('HTK_REFERRAL_MODEL')) | ||
ReferralCode = resolve_model_dynamically( | ||
htk_setting('HTK_REFERRAL_CODE_MODEL') | ||
) | ||
except (LookupError, AppRegistryNotReady): | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Django Imports | ||
from django.urls import re_path | ||
|
||
# Local Imports | ||
from . import views | ||
|
||
|
||
# isort: off | ||
|
||
|
||
urlpatterns = [ | ||
re_path( | ||
# if the referral code is prefixed with an '@', it is an username | ||
r'^referrals/(?P<referral_code>@?[a-zA-Z0-9_-]+)$', | ||
views.referral_view, | ||
name='htk_referral', | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Django Imports | ||
from django.contrib.auth import get_user_model | ||
|
||
# HTK Imports | ||
from htk.utils import htk_setting | ||
|
||
# Local Imports | ||
from .resolvers import ReferralCode | ||
|
||
|
||
# isort: off | ||
|
||
|
||
UserModel = get_user_model() | ||
|
||
|
||
def get_referrer_from_code(referral_code): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
referrer = None | ||
if htk_setting('HTK_REFERRAL_ALLOW_USERNAME') and referral_code.startswith( | ||
'@' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know if we want this explicitly. Not sure yet. |
||
): | ||
# referral code is an username | ||
username = referral_code[1:] | ||
try: | ||
referrer = UserModel.objects.get(username=username) | ||
except UserModel.DoesNotExist: | ||
pass | ||
elif htk_setting('HTK_REFERRAL_ALLOW_HASH_CODE'): | ||
try: | ||
referral_code = ReferralCode.objects.get(code=referral_code) | ||
referrer = referral_code.user | ||
except ReferralCode.DoesNotExist: | ||
pass | ||
else: | ||
raise ValueError( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know if you want to raise |
||
'Either usernames or hash codes must be allowed in referral codes.' | ||
) | ||
return referrer | ||
|
||
|
||
def get_referrer(request): | ||
referrer = None | ||
key = 'referrer_id' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's put this string in constants. Let's use an all-caps value. |
||
referrer_id = request.COOKIE.get(key) or request.session.get(key) | ||
|
||
if referrer_id: | ||
try: | ||
referrer = UserModel.objects.get(id=referrer_id) | ||
except UserModel.DoesNotExist: | ||
pass | ||
return referrer |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Django Imports | ||
from django.contrib.auth import get_user_model | ||
from django.http import Http404 | ||
from django.shortcuts import redirect | ||
|
||
# HTK Imports | ||
from htk.utils import htk_setting | ||
|
||
# Local Imports | ||
from .utils import get_referrer_from_code | ||
|
||
|
||
# isort: off | ||
|
||
|
||
UserModel = get_user_model() | ||
|
||
|
||
def referral_view(request, referral_code: str, *args, **kwargs): | ||
referrer = get_referrer_from_code(referral_code) | ||
|
||
if referrer: | ||
request.session['referrer_id'] = referrer.id | ||
request.set_cookie('referrer_id', referrer.id) | ||
response = redirect(htk_setting('HTK_REFERRAL_REDIRECT_URL')) | ||
else: | ||
response = Http404 | ||
|
||
return response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Directory name should be plural
referrals