django-dans-waitlist 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. django-dans-waitlist-0.0.1/AUTHORS +1 -0
  2. django-dans-waitlist-0.0.1/LICENSE +27 -0
  3. django-dans-waitlist-0.0.1/MANIFEST.in +5 -0
  4. django-dans-waitlist-0.0.1/PKG-INFO +120 -0
  5. django-dans-waitlist-0.0.1/README.md +89 -0
  6. django-dans-waitlist-0.0.1/django_dans_waitlist/__init__.py +0 -0
  7. django-dans-waitlist-0.0.1/django_dans_waitlist/admin.py +26 -0
  8. django-dans-waitlist-0.0.1/django_dans_waitlist/api_response.py +47 -0
  9. django-dans-waitlist-0.0.1/django_dans_waitlist/api_response_handler.py +171 -0
  10. django-dans-waitlist-0.0.1/django_dans_waitlist/apps.py +6 -0
  11. django-dans-waitlist-0.0.1/django_dans_waitlist/migrations/0001_initial.py +33 -0
  12. django-dans-waitlist-0.0.1/django_dans_waitlist/migrations/__init__.py +0 -0
  13. django-dans-waitlist-0.0.1/django_dans_waitlist/models.py +42 -0
  14. django-dans-waitlist-0.0.1/django_dans_waitlist/permissions.py +18 -0
  15. django-dans-waitlist-0.0.1/django_dans_waitlist/regex.py +6 -0
  16. django-dans-waitlist-0.0.1/django_dans_waitlist/serializers/__init__.py +0 -0
  17. django-dans-waitlist-0.0.1/django_dans_waitlist/serializers/base.py +60 -0
  18. django-dans-waitlist-0.0.1/django_dans_waitlist/serializers/waitlist.py +19 -0
  19. django-dans-waitlist-0.0.1/django_dans_waitlist/templates/emails/base.html +16 -0
  20. django-dans-waitlist-0.0.1/django_dans_waitlist/templates/emails/waitlist-email.html +8 -0
  21. django-dans-waitlist-0.0.1/django_dans_waitlist/templates/emails/widgets/signature.html +4 -0
  22. django-dans-waitlist-0.0.1/django_dans_waitlist/test/__init__.py +0 -0
  23. django-dans-waitlist-0.0.1/django_dans_waitlist/test/model_tests/__init__.py +0 -0
  24. django-dans-waitlist-0.0.1/django_dans_waitlist/test/model_tests/base.py +25 -0
  25. django-dans-waitlist-0.0.1/django_dans_waitlist/test/model_tests/waitlist/__init__.py +0 -0
  26. django-dans-waitlist-0.0.1/django_dans_waitlist/test/model_tests/waitlist/test_waitlist_entry.py +25 -0
  27. django-dans-waitlist-0.0.1/django_dans_waitlist/test/settings.py +81 -0
  28. django-dans-waitlist-0.0.1/django_dans_waitlist/urls.py +23 -0
  29. django-dans-waitlist-0.0.1/django_dans_waitlist/views.py +118 -0
  30. django-dans-waitlist-0.0.1/django_dans_waitlist.egg-info/PKG-INFO +120 -0
  31. django-dans-waitlist-0.0.1/django_dans_waitlist.egg-info/SOURCES.txt +39 -0
  32. django-dans-waitlist-0.0.1/django_dans_waitlist.egg-info/dependency_links.txt +1 -0
  33. django-dans-waitlist-0.0.1/django_dans_waitlist.egg-info/requires.txt +2 -0
  34. django-dans-waitlist-0.0.1/django_dans_waitlist.egg-info/top_level.txt +1 -0
  35. django-dans-waitlist-0.0.1/docs/apis.md +23 -0
  36. django-dans-waitlist-0.0.1/docs/models.md +33 -0
  37. django-dans-waitlist-0.0.1/docs/release.md +30 -0
  38. django-dans-waitlist-0.0.1/pyproject.toml +3 -0
  39. django-dans-waitlist-0.0.1/setup.cfg +38 -0
  40. django-dans-waitlist-0.0.1/setup.py +5 -0
@@ -0,0 +1 @@
1
+ Daniel Nazarian - dnaz@danielnazarian.com
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2024 Daniel Nazarian
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ * Neither the name of the Daniel Nazarian nor the names of its
15
+ contributors may be used to endorse or promote products derived from this
16
+ software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,5 @@
1
+ include LICENSE
2
+ include README.rst
3
+ recursive-include django_dans_waitlist/static *
4
+ recursive-include django_dans_waitlist/templates *
5
+ recursive-include docs *
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.1
2
+ Name: django-dans-waitlist
3
+ Version: 0.0.1
4
+ Summary: A Django app to different basic waitlists.
5
+ Home-page: https://www.github.com/dan1229/django_dans_waitlist
6
+ Author: Daniel Nazarian
7
+ Author-email: dnaz@danielnazarian.com
8
+ License: BSD-3-Clause
9
+ Platform: UNKNOWN
10
+ Classifier: Environment :: Web Environment
11
+ Classifier: Framework :: Django
12
+ Classifier: Framework :: Django :: 3.1
13
+ Classifier: Framework :: Django :: 3.2
14
+ Classifier: Framework :: Django :: 4.0
15
+ Classifier: Framework :: Django :: 4.1
16
+ Classifier: Framework :: Django :: 4.2
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: License :: OSI Approved :: BSD License
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Programming Language :: Python
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Topic :: Internet :: WWW/HTTP
24
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
25
+ Requires-Python: <3.12,>=3.10
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ License-File: AUTHORS
29
+
30
+ # Django Dans Waitlist
31
+
32
+ [![Lint](https://github.com/dan1229/django_dans_waitlist/actions/workflows/lint.yml/badge.svg)](https://github.com/dan1229/django_dans_waitlist/actions/workflows/lint.yml)
33
+ [![Test Python](https://github.com/dan1229/django_dans_waitlist/actions/workflows/test-python.yml/badge.svg)](https://github.com/dan1229/django_dans_waitlist/actions/workflows/test-python.yml)
34
+ [![codecov](https://codecov.io/gh/dan1229/django_dans_waitlist/branch/main/graph/badge.svg?token=TL09HDQWBJ)](https://codecov.io/gh/dan1229/django_dans_waitlist)
35
+
36
+ ## Description
37
+
38
+ A Django app to handle waitlist and basic functionality.
39
+
40
+ Support for `Waitlist` and `WaitlistEntry` models, as well as a `WaitlistManager` to handle common operations.
41
+
42
+ ## Quick start
43
+
44
+ 1. Install the package via pip:
45
+
46
+ ```bash
47
+ pip install django-dans-waitlist
48
+ ```
49
+
50
+ 2. Add "django_dans_waitlist" to your INSTALLED_APPS setting like this:
51
+
52
+ ```python
53
+ INSTALLED_APPS = [
54
+ ...
55
+ 'django_dans_waitlist',
56
+ ]
57
+ ```
58
+
59
+ 3. Include the URL configs in your project `urls.py` for the REST API endpoints like this:
60
+
61
+ ```python
62
+ path("api/waitlist/", include("django_dans_waitlist.urls")),
63
+ ```
64
+
65
+ 4. Run `python manage.py migrate` to update your database schema.
66
+
67
+ 5. Use the API endpoints, in code or your Django admin portal.
68
+
69
+ ### Requirements
70
+
71
+ - Python 3.10 - 3.11
72
+ - Django 3.1 or higher
73
+ - Django Rest Framework
74
+ - **NOTE:** not only must you have this installed, you must have set `DEFAULT_AUTHENTICATION_CLASSES` and `DEFAULT_PAGINATION_CLASS` in your `settings.py` to work with the APIs properly. An example config would be:
75
+
76
+ ```python
77
+ REST_FRAMEWORK = {
78
+ "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
79
+ "PAGE_SIZE": 20,
80
+ "DEFAULT_AUTHENTICATION_CLASSES": (
81
+ "rest_framework.authentication.TokenAuthentication",
82
+ ),
83
+ }
84
+ ```
85
+
86
+
87
+ ### Available Settings
88
+
89
+ Currently all available settings are optional:
90
+
91
+ - `TEAM_NAME` - Default team name to use for emails, can be added to message context manually as well still.
92
+
93
+
94
+ Add these to your `settings.py` file to customize the app's behavior like so:
95
+
96
+ ```python
97
+ TEAM_NAME = "My Team"
98
+ ```
99
+
100
+
101
+ ## Usage
102
+
103
+ TODO
104
+
105
+ ## Docs
106
+
107
+ TODO - which of these are still relevant?
108
+ #### [Model docs](https://github.com/dan1229/django_dans_waitlist/tree/main/docs/models.md).
109
+
110
+ #### [API docs](https://github.com/dan1229/django_dans_waitlist/tree/main/docs/apis.md).
111
+
112
+
113
+ -------------------------------------------------------
114
+
115
+ ##### [https://danielnazarian.com](https://danielnazarian.com)
116
+
117
+ ##### Copyright 2024 © Daniel Nazarian.
118
+
119
+
120
+
@@ -0,0 +1,89 @@
1
+ # Django Dans Waitlist
2
+
3
+ [![Lint](https://github.com/dan1229/django_dans_waitlist/actions/workflows/lint.yml/badge.svg)](https://github.com/dan1229/django_dans_waitlist/actions/workflows/lint.yml)
4
+ [![Test Python](https://github.com/dan1229/django_dans_waitlist/actions/workflows/test-python.yml/badge.svg)](https://github.com/dan1229/django_dans_waitlist/actions/workflows/test-python.yml)
5
+ [![codecov](https://codecov.io/gh/dan1229/django_dans_waitlist/branch/main/graph/badge.svg?token=TL09HDQWBJ)](https://codecov.io/gh/dan1229/django_dans_waitlist)
6
+
7
+ ## Description
8
+
9
+ A Django app to handle waitlist and basic functionality.
10
+
11
+ Support for `Waitlist` and `WaitlistEntry` models, as well as a `WaitlistManager` to handle common operations.
12
+
13
+ ## Quick start
14
+
15
+ 1. Install the package via pip:
16
+
17
+ ```bash
18
+ pip install django-dans-waitlist
19
+ ```
20
+
21
+ 2. Add "django_dans_waitlist" to your INSTALLED_APPS setting like this:
22
+
23
+ ```python
24
+ INSTALLED_APPS = [
25
+ ...
26
+ 'django_dans_waitlist',
27
+ ]
28
+ ```
29
+
30
+ 3. Include the URL configs in your project `urls.py` for the REST API endpoints like this:
31
+
32
+ ```python
33
+ path("api/waitlist/", include("django_dans_waitlist.urls")),
34
+ ```
35
+
36
+ 4. Run `python manage.py migrate` to update your database schema.
37
+
38
+ 5. Use the API endpoints, in code or your Django admin portal.
39
+
40
+ ### Requirements
41
+
42
+ - Python 3.10 - 3.11
43
+ - Django 3.1 or higher
44
+ - Django Rest Framework
45
+ - **NOTE:** not only must you have this installed, you must have set `DEFAULT_AUTHENTICATION_CLASSES` and `DEFAULT_PAGINATION_CLASS` in your `settings.py` to work with the APIs properly. An example config would be:
46
+
47
+ ```python
48
+ REST_FRAMEWORK = {
49
+ "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
50
+ "PAGE_SIZE": 20,
51
+ "DEFAULT_AUTHENTICATION_CLASSES": (
52
+ "rest_framework.authentication.TokenAuthentication",
53
+ ),
54
+ }
55
+ ```
56
+
57
+
58
+ ### Available Settings
59
+
60
+ Currently all available settings are optional:
61
+
62
+ - `TEAM_NAME` - Default team name to use for emails, can be added to message context manually as well still.
63
+
64
+
65
+ Add these to your `settings.py` file to customize the app's behavior like so:
66
+
67
+ ```python
68
+ TEAM_NAME = "My Team"
69
+ ```
70
+
71
+
72
+ ## Usage
73
+
74
+ TODO
75
+
76
+ ## Docs
77
+
78
+ TODO - which of these are still relevant?
79
+ #### [Model docs](https://github.com/dan1229/django_dans_waitlist/tree/main/docs/models.md).
80
+
81
+ #### [API docs](https://github.com/dan1229/django_dans_waitlist/tree/main/docs/apis.md).
82
+
83
+
84
+ -------------------------------------------------------
85
+
86
+ ##### [https://danielnazarian.com](https://danielnazarian.com)
87
+
88
+ ##### Copyright 2024 © Daniel Nazarian.
89
+
@@ -0,0 +1,26 @@
1
+ from django.contrib import admin
2
+ from .models import WaitlistEntry
3
+
4
+
5
+ #
6
+ # WAITLIST ENTRY ========================= #
7
+ #
8
+ class WaitlistEntryAdmin(admin.ModelAdmin):
9
+ list_display = (
10
+ "email",
11
+ "datetime_created",
12
+ )
13
+
14
+ search_fields = (
15
+ "id",
16
+ "email",
17
+ "datetime_created",
18
+ )
19
+ readonly_fields = ("id",)
20
+ list_per_page = 100
21
+
22
+
23
+ #
24
+ # REGISTER ========================= #
25
+ #
26
+ admin.site.register(WaitlistEntry, WaitlistEntryAdmin)
@@ -0,0 +1,47 @@
1
+ from typing import Any, Optional
2
+
3
+ """
4
+ ============================================================================================ #
5
+ API RESPONSE =============================================================================== #
6
+ ============================================================================================ #
7
+ """
8
+
9
+
10
+ class ApiResponse:
11
+ """
12
+ Response type to standardize response structure and conversions.
13
+
14
+ The point of this class is to standardize the response format for all API responses.
15
+ It does so by having properties that model the response structure you'd like.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ status: Optional[int] = None,
21
+ message: Optional[str] = None,
22
+ results: Optional[object | dict | list] = None,
23
+ error_fields: Optional[dict] = None,
24
+ **kwargs: Any
25
+ ) -> None:
26
+ self.status = status
27
+ self.message = message
28
+ self.results = results
29
+ self.error_fields = error_fields
30
+ self.extras = kwargs
31
+
32
+ def dict(self) -> dict:
33
+ """
34
+ Convert ApiResponse to dict. Primarily to use in actual Response object.
35
+
36
+ :returns: Dict containing ApiResponse object info
37
+ :rtype: dict
38
+ """
39
+ res = {
40
+ "status": self.status,
41
+ "message": self.message,
42
+ "results": self.results,
43
+ "error_fields": self.error_fields,
44
+ }
45
+ for key, value in self.extras.items():
46
+ res[key] = value
47
+ return res
@@ -0,0 +1,171 @@
1
+ import logging
2
+ from typing import Optional
3
+ from django.core.exceptions import ValidationError
4
+ from django.db import IntegrityError
5
+ from rest_framework.response import Response
6
+ from rest_framework.status import HTTP_200_OK, HTTP_400_BAD_REQUEST
7
+ from .api_response import ApiResponse
8
+
9
+ LOGGER_DJANGO = logging.getLogger("django")
10
+
11
+ """
12
+ ============================================================================================ #
13
+ API RESPONSE HANDLER ======================================================================= #
14
+ ============================================================================================ #
15
+ """
16
+
17
+
18
+ class ApiResponseHandler:
19
+ """
20
+ Generic response handler to help manage API responses
21
+
22
+ This is used out of the box to provide standardized response formats,
23
+ as well as a centralized place to edit/mangle said responses.
24
+
25
+ Mainly just 'wrap' Responses/data you are already returning, this class
26
+ can help enforce structure or format such that you can have responses
27
+ look however you choose.
28
+
29
+ It works very closely with the ApiResponse class.
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ message_error: str = "Error. Please try again later.",
35
+ message_success: str = "Successfully completed request.",
36
+ print_log: bool = True,
37
+ ):
38
+ self.message_error = message_error
39
+ self.message_success = message_success
40
+ self.print_log = print_log
41
+
42
+ @staticmethod
43
+ def _format_response(
44
+ response: Optional[Response] = None,
45
+ results: Optional[object | dict | list] = None,
46
+ message: Optional[str] = None,
47
+ status: Optional[int] = None,
48
+ error_fields: Optional[dict] = None,
49
+ ) -> Response:
50
+ """Internal function to format responses.
51
+
52
+ Args:
53
+ response (Response): Existing DRF response object to use for response.
54
+ results (list): List of results to include in response.
55
+ message (str): Message to include in response.
56
+ status (int): Status to use in response.
57
+ error_fields (dict, optional): Dictionary of field errors to include - typically provided by Django exceptions. Defaults to None.
58
+
59
+ Returns:
60
+ Response: DRF response object with desired format - can be used directly in views
61
+ """
62
+ api_response = ApiResponse(
63
+ message=message, status=status, results=results, error_fields=error_fields
64
+ )
65
+ if response: # response passed -> simply edit
66
+ api_response.extras = response.data
67
+ return Response(api_response.dict(), status=status)
68
+
69
+ def _handle_logging(self, msg: str, print_log: Optional[bool] = None) -> None:
70
+ """Internal function to handle logging for responses handled by this class.
71
+
72
+ Args:
73
+ print_log (boolean): Whether or not to actually print the message.
74
+ msg (str): Message to print.
75
+ """
76
+ if print_log is None: # print_log not passed, go by default
77
+ if self.print_log:
78
+ LOGGER_DJANGO.error(msg)
79
+ else: # print_log passed, go by that
80
+ if print_log:
81
+ LOGGER_DJANGO.error(msg)
82
+
83
+ #
84
+ # RESPONSES
85
+ #
86
+ def response_success(
87
+ self,
88
+ message: Optional[str] = None,
89
+ results: Optional[object | dict | list] = None,
90
+ response: Optional[Response] = None,
91
+ status: Optional[int] = HTTP_200_OK,
92
+ ) -> Response:
93
+ """
94
+ :param str message: message to include in response
95
+ :param object results: results object/list to include in response
96
+ :param Response response: response object to simply edit
97
+ :param int status: HTTP status to use
98
+
99
+ :returns: response of the desired format
100
+ :rtype: Response
101
+ """
102
+ # no message = use default
103
+ if not message or message == "":
104
+ message = self.message_success
105
+ message = str(message)
106
+
107
+ return self._format_response(
108
+ response=response, results=results, message=message, status=status
109
+ )
110
+
111
+ def response_error(
112
+ self,
113
+ error: Optional[str | Exception] = None,
114
+ error_fields: Optional[dict] = None,
115
+ message: Optional[str] = None,
116
+ results: Optional[object | dict | list] = None,
117
+ response: Optional[Response] = None,
118
+ status: Optional[int] = HTTP_400_BAD_REQUEST,
119
+ print_log: Optional[bool] = True,
120
+ ) -> Response:
121
+ """
122
+ :param str|Exception error: error message to log
123
+ :param dict error_fields: list of fields to include in error response
124
+ :param str message: message to include in response
125
+ :param object results: results object/list to include in response
126
+ :param Response response: response object to simply edit
127
+ :param int status: HTTP status to use
128
+ :param bool print_log: override whether to print this error
129
+
130
+ :returns: response of the desired format
131
+ :rtype: Response
132
+ """
133
+ # django validation error
134
+ if (
135
+ isinstance(error, ValidationError)
136
+ and hasattr(error, "error_dict")
137
+ and error.error_dict.get("__all__")
138
+ ):
139
+ # try to parse ValidationError
140
+ messageDict = error.message_dict.get("__all__")
141
+ if isinstance(messageDict, list) and len(messageDict) > 0:
142
+ messageRes = str(messageDict[0])
143
+ else:
144
+ messageRes = self.message_error
145
+ # integrity error
146
+ elif isinstance(error, IntegrityError):
147
+ messageRes = str(error)
148
+ # no message passed and error isn't one we can parse
149
+ elif not message or message == "": # use default message
150
+ messageRes = self.message_error
151
+ else: # message passed, ensure it's a string
152
+ messageRes = str(message)
153
+
154
+ # error NOT passed, pass as this is probably not intended to be logged
155
+ if not error or error == "":
156
+ pass
157
+ else: # error passed, log it somehow
158
+ if not message or message == "": # message not passed, log error itself
159
+ self._handle_logging(str(error), print_log)
160
+ elif message != error: # message and error different, log both
161
+ self._handle_logging(f"{message} - {error}", print_log)
162
+ else: # error and message both exist and are the same
163
+ self._handle_logging(str(error), print_log)
164
+
165
+ return self._format_response(
166
+ response=response,
167
+ results=results,
168
+ message=messageRes,
169
+ status=status,
170
+ error_fields=error_fields,
171
+ )
@@ -0,0 +1,6 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class DjangoDansWaitlistConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "django_dans_waitlist"
@@ -0,0 +1,33 @@
1
+ from django.db import migrations, models
2
+ import uuid
3
+
4
+
5
+ class Migration(migrations.Migration):
6
+ initial = True
7
+
8
+ dependencies = []
9
+
10
+ operations = [
11
+ migrations.CreateModel(
12
+ name="WaitlistEntry",
13
+ fields=[
14
+ (
15
+ "id",
16
+ models.UUIDField(
17
+ default=uuid.uuid4,
18
+ editable=False,
19
+ primary_key=True,
20
+ serialize=False,
21
+ ),
22
+ ),
23
+ ("datetime_created", models.DateTimeField(auto_now_add=True)),
24
+ ("datetime_modified", models.DateTimeField(auto_now=True)),
25
+ ("email", models.EmailField(max_length=254, unique=True)),
26
+ ],
27
+ options={
28
+ "verbose_name": "Waitlist Entry",
29
+ "verbose_name_plural": "Waitlist Entries",
30
+ "ordering": ["email"],
31
+ },
32
+ ),
33
+ ]
@@ -0,0 +1,42 @@
1
+ import uuid
2
+
3
+ from django.db import models
4
+
5
+ """
6
+ # ==================================================================================== #
7
+ # ABSTRACT BASE MODEL ================================================================ #
8
+ # ==================================================================================== #
9
+ """
10
+
11
+
12
+ class AbstractBaseModel(models.Model):
13
+ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
14
+
15
+ datetime_created = models.DateTimeField(auto_now_add=True, editable=False)
16
+ datetime_modified = models.DateTimeField(auto_now=True)
17
+
18
+ class Meta:
19
+ abstract = True
20
+ ordering = ["datetime_created"]
21
+
22
+ def __str__(self):
23
+ return "Abstract Base Model"
24
+
25
+
26
+ """
27
+ # ==================================================================================== #
28
+ # WAITLIST ENTRY ===================================================================== #
29
+ # ==================================================================================== #
30
+ """
31
+
32
+
33
+ class WaitlistEntry(AbstractBaseModel):
34
+ email = models.EmailField(unique=True)
35
+
36
+ def __str__(self) -> str:
37
+ return f"Waitlist Entry: {self.email}"
38
+
39
+ class Meta:
40
+ ordering = ["email"]
41
+ verbose_name = "Waitlist Entry"
42
+ verbose_name_plural = "Waitlist Entries"
@@ -0,0 +1,18 @@
1
+ from typing import Any
2
+ from rest_framework import permissions
3
+ from rest_framework.request import Request
4
+
5
+ """
6
+ ============================================================================================ #
7
+ BASE PERMISSIONS =========================================================================== #
8
+ ============================================================================================ #
9
+ """
10
+
11
+
12
+ class AdminOnly(permissions.BasePermission):
13
+ """Allows access only to Admins."""
14
+
15
+ def has_permission(self, request: Request, view: Any) -> bool:
16
+ if request.user.is_authenticated and request.user.is_staff:
17
+ return True
18
+ return False
@@ -0,0 +1,6 @@
1
+ # REGEX.py
2
+ # This file contains all the regular expression patterns used in the project
3
+ #
4
+
5
+ # Regular expression pattern for email validation
6
+ REGEX_EMAIL = r"^[\w\.-]+@[\w\.-]+\.\w+$"
@@ -0,0 +1,60 @@
1
+ from typing import Any, List
2
+ from rest_framework import serializers
3
+
4
+ """
5
+ # ===================================================================================
6
+ # BASE SERIALIZER ===================================================================
7
+ # ===================================================================================
8
+ """
9
+
10
+
11
+ class BaseSerializer(serializers.ModelSerializer):
12
+ """
13
+ BaseSerializer to inherit from
14
+
15
+ :param bool masked: If True, will mask the serializer.
16
+ Based on `masked_fields` property. Fields in `masked_fields` will only
17
+ show up if `masked` is False
18
+ :param bool ref_serializer: If True, will return a reference serializer.
19
+ Based on `ref_fields` property, fields in `ref_fields` will only show
20
+ up if `ref_serializer` is False. If `ref_fields` is not provided,
21
+ all fields will be returned.
22
+ :param str[] fields: Additional kwargs 'field' that controls which fields to include
23
+ """
24
+
25
+ ref_fields: List[str] = []
26
+ masked_fields: List[str] = []
27
+ masked = True
28
+ ref_serializer = False
29
+
30
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
31
+ # save kwargs for later
32
+ self.kwargs = kwargs
33
+
34
+ # handle 'fields' keyword argument
35
+ # Don't pass the 'fields' arg up to the superclass
36
+ fields = kwargs.pop("fields", None)
37
+
38
+ # if masked serializer, remove masked fields
39
+ self.masked = kwargs.pop("masked", self.masked)
40
+ masked_fields = getattr(self.Meta, "masked_fields", [])
41
+ if self.masked:
42
+ for field in masked_fields:
43
+ self.fields.pop(field)
44
+
45
+ # if ref serializer, remove ref fields
46
+ self.ref_serializer = kwargs.pop("ref_serializer", self.ref_serializer)
47
+ ref_fields = getattr(self.Meta, "ref_fields", [])
48
+ if self.ref_serializer:
49
+ for field in ref_fields:
50
+ self.fields.pop(field)
51
+
52
+ # Instantiate the superclass normally
53
+ super(BaseSerializer, self).__init__(*args, **kwargs)
54
+
55
+ # Drop any fields that are not specified in the `fields` argument.
56
+ if fields is not None:
57
+ allowed = set(fields)
58
+ existing = set(self.fields)
59
+ for field_name in existing - allowed:
60
+ self.fields.pop(field_name)