diff --git a/backend/.gitignore b/backend/.gitignore index 01ac695..ba81ec7 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,2 +1,3 @@ /env -__pycache__ \ No newline at end of file +__pycache__ +/media \ No newline at end of file diff --git a/backend/BookShelf/settings.py b/backend/BookShelf/settings.py index 1ff94ac..de93e7f 100644 --- a/backend/BookShelf/settings.py +++ b/backend/BookShelf/settings.py @@ -12,6 +12,8 @@ from pathlib import Path +import os + # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -28,17 +30,25 @@ ALLOWED_HOSTS = [] -# Application definition - -INSTALLED_APPS = [ +DJANGO_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', +] +LIBRARY_APPS = [ + 'rest_framework', + 'drf_yasg', +] +PROJECT_APPS = [ 'user_api', + 'author_api', + 'publisher_api', + 'book_api', ] +INSTALLED_APPS = DJANGO_APPS + LIBRARY_APPS + PROJECT_APPS MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', @@ -81,6 +91,14 @@ } } +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ), + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticated', + ), +} # Password validation # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators @@ -117,6 +135,8 @@ # https://docs.djangoproject.com/en/5.1/howto/static-files/ STATIC_URL = 'static/' +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # Default primary key field type # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field diff --git a/backend/BookShelf/urls.py b/backend/BookShelf/urls.py index 50f44a0..2bb47c5 100644 --- a/backend/BookShelf/urls.py +++ b/backend/BookShelf/urls.py @@ -15,8 +15,35 @@ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import path +from django.urls import path, include +from django.conf.urls.static import static +from django.conf import settings +from rest_framework import permissions +from drf_yasg.views import get_schema_view +from drf_yasg import openapi + + +schema_view = get_schema_view( + openapi.Info( + title="Book Shelf", + default_version='v1', + description="API documentation for Book Shelf", + terms_of_service="https://www.google.com/policies/terms/", + contact=openapi.Contact(email="contact@example.com"), + license=openapi.License(name="BSD License"), + ), + public=True, + permission_classes=(permissions.AllowAny,), +) urlpatterns = [ + # admin URL path('admin/', admin.site.urls), -] + # app URLs + path('user/', include('user_api.urls')), + # Swagger documentation URLs + path('swagger/', schema_view.with_ui('swagger', + cache_timeout=0), name='schema-swagger-ui'), + path('redoc/', schema_view.with_ui('redoc', + cache_timeout=0), name='schema-redoc'), +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/backend/author_api/__init__.py b/backend/author_api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/author_api/admin.py b/backend/author_api/admin.py new file mode 100644 index 0000000..9f267f0 --- /dev/null +++ b/backend/author_api/admin.py @@ -0,0 +1,15 @@ +from django.contrib import admin + +from author_api.models import Author + + +@admin.register(Author) +class AuthorAdmin(admin.ModelAdmin): + list_display = [ + 'id', 'full_name', 'birth_date', + 'died_date', 'is_alive', + ] + list_display_links = ('id', 'full_name',) + list_filter = [] + search_fields = ('first_name', 'last_name', 'birth_date') + readonly_fields = ('id',) diff --git a/backend/author_api/apps.py b/backend/author_api/apps.py new file mode 100644 index 0000000..bf011ee --- /dev/null +++ b/backend/author_api/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AuthorApiConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'author_api' diff --git a/backend/author_api/migrations/0001_initial.py b/backend/author_api/migrations/0001_initial.py new file mode 100644 index 0000000..895f89a --- /dev/null +++ b/backend/author_api/migrations/0001_initial.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.1 on 2024-09-06 19:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Author', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('first_name', models.CharField(max_length=100)), + ('last_name', models.CharField(max_length=100)), + ('biography', models.TextField(blank=True, null=True)), + ('birth_date', models.DateField(blank=True, null=True)), + ], + ), + ] diff --git a/backend/author_api/migrations/0002_author_added_by_author_added_date_time_and_more.py b/backend/author_api/migrations/0002_author_added_by_author_added_date_time_and_more.py new file mode 100644 index 0000000..6814495 --- /dev/null +++ b/backend/author_api/migrations/0002_author_added_by_author_added_date_time_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 5.1.1 on 2024-09-06 19:46 + +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('author_api', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='author', + name='added_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='author', + name='added_date_time', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='author', + name='is_deleted', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='author', + name='updated_date_time', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/backend/author_api/migrations/0003_author_died_date.py b/backend/author_api/migrations/0003_author_died_date.py new file mode 100644 index 0000000..fd9513b --- /dev/null +++ b/backend/author_api/migrations/0003_author_died_date.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.1 on 2024-09-06 19:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('author_api', '0002_author_added_by_author_added_date_time_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='author', + name='died_date', + field=models.DateField(blank=True, null=True), + ), + ] diff --git a/backend/author_api/migrations/__init__.py b/backend/author_api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/author_api/models.py b/backend/author_api/models.py new file mode 100644 index 0000000..f8da94c --- /dev/null +++ b/backend/author_api/models.py @@ -0,0 +1,29 @@ +from django.db import models + + +class Author(models.Model): + first_name = models.CharField(max_length=100) + last_name = models.CharField(max_length=100) + biography = models.TextField(blank=True, null=True) # Optional bio field + birth_date = models.DateField(null=True, blank=True) + died_date = models.DateField(null=True, blank=True) + added_by = models.ForeignKey( + 'user_api.User', + blank=True, + null=True, + on_delete=models.CASCADE + ) + is_deleted = models.BooleanField(default=False) + added_date_time = models.DateTimeField(auto_now_add=True) + updated_date_time = models.DateTimeField(auto_now=True) + + @property + def full_name(self): + return f"{self.first_name} {self.last_name}" + + @property + def is_alive(self): + return True if not self.died_date else False + + def __str__(self): + return f'{self.first_name} {self.last_name}' diff --git a/backend/author_api/tests.py b/backend/author_api/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/author_api/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/user_api/views.py b/backend/author_api/views.py similarity index 100% rename from backend/user_api/views.py rename to backend/author_api/views.py diff --git a/backend/book_api/__init__.py b/backend/book_api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/book_api/admin.py b/backend/book_api/admin.py new file mode 100644 index 0000000..503dec2 --- /dev/null +++ b/backend/book_api/admin.py @@ -0,0 +1,24 @@ +from django.contrib import admin + +from book_api.models import Book + + +@admin.register(Book) +class BookAdmin(admin.ModelAdmin): + list_display = [ + 'id', 'title', 'edition', 'isbn', + 'published_date', 'language', + 'is_deleted', + ] + list_display_links = [ + 'title', + ] + list_filter = [ + 'authors', 'publisher', + ] + search_fields = [ + 'title', 'edition', 'isbn', + 'published_date', 'language', + 'description', + ] + readonly_fields = ['id',] diff --git a/backend/book_api/apps.py b/backend/book_api/apps.py new file mode 100644 index 0000000..69b22e6 --- /dev/null +++ b/backend/book_api/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class BookApiConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'book_api' diff --git a/backend/book_api/migrations/0001_initial.py b/backend/book_api/migrations/0001_initial.py new file mode 100644 index 0000000..fcf7c78 --- /dev/null +++ b/backend/book_api/migrations/0001_initial.py @@ -0,0 +1,39 @@ +# Generated by Django 5.1.1 on 2024-09-06 20:28 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('author_api', '0003_author_died_date'), + ('publisher_api', '0002_publisher_added_by_publisher_added_date_time_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Book', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255)), + ('description', models.TextField(blank=True, null=True)), + ('edition', models.CharField(blank=True, max_length=50, null=True)), + ('isbn', models.CharField(blank=True, max_length=13, null=True, unique=True)), + ('pages', models.PositiveIntegerField(blank=True, null=True)), + ('published_date', models.DateField(blank=True, null=True)), + ('pdf_file', models.FileField(blank=True, null=True, upload_to='books')), + ('cover_image', models.ImageField(blank=True, null=True, upload_to='book_covers')), + ('is_deleted', models.BooleanField(default=False)), + ('added_date_time', models.DateTimeField(auto_now_add=True)), + ('updated_date_time', models.DateTimeField(auto_now=True)), + ('added_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('authors', models.ManyToManyField(related_name='books', to='author_api.author')), + ('publisher', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='books', to='publisher_api.publisher')), + ], + ), + ] diff --git a/backend/book_api/migrations/0002_rename_pdf_file_book_book.py b/backend/book_api/migrations/0002_rename_pdf_file_book_book.py new file mode 100644 index 0000000..45ccf97 --- /dev/null +++ b/backend/book_api/migrations/0002_rename_pdf_file_book_book.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.1 on 2024-09-06 20:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('book_api', '0001_initial'), + ] + + operations = [ + migrations.RenameField( + model_name='book', + old_name='pdf_file', + new_name='book', + ), + ] diff --git a/backend/book_api/migrations/0003_book_language.py b/backend/book_api/migrations/0003_book_language.py new file mode 100644 index 0000000..e25fb26 --- /dev/null +++ b/backend/book_api/migrations/0003_book_language.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.1 on 2024-09-06 20:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('book_api', '0002_rename_pdf_file_book_book'), + ] + + operations = [ + migrations.AddField( + model_name='book', + name='language', + field=models.CharField(blank=True, max_length=200, null=True), + ), + ] diff --git a/backend/book_api/migrations/0004_alter_book_book.py b/backend/book_api/migrations/0004_alter_book_book.py new file mode 100644 index 0000000..a850cfd --- /dev/null +++ b/backend/book_api/migrations/0004_alter_book_book.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.1 on 2024-09-06 20:43 + +import book_api.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('book_api', '0003_book_language'), + ] + + operations = [ + migrations.AlterField( + model_name='book', + name='book', + field=models.FileField(blank=True, null=True, upload_to=book_api.models.book_upload_path), + ), + ] diff --git a/backend/book_api/migrations/__init__.py b/backend/book_api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/book_api/models.py b/backend/book_api/models.py new file mode 100644 index 0000000..017ee3e --- /dev/null +++ b/backend/book_api/models.py @@ -0,0 +1,52 @@ +from django.db import models +from django.utils.timezone import now + +import os +import uuid + + +def book_upload_path(instance, filename): + """ + Generates a unique file path and renames the file with a unique code. + """ + ext = filename.split('.')[-1] + current_time = now().strftime('%Y%m%d%H%M%S%f') + unique_code = str(uuid.uuid4().int)[:10] + new_filename = f'{unique_code}{current_time}.{ext}' + + return os.path.join('books', new_filename) + + +class Book(models.Model): + title = models.CharField(max_length=255) + authors = models.ManyToManyField( + 'author_api.Author', + related_name='books') + publisher = models.ForeignKey( + 'publisher_api.Publisher', + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name='books') + description = models.TextField(blank=True, null=True) + edition = models.CharField(max_length=50, blank=True, null=True) + isbn = models.CharField(max_length=13, blank=True, null=True, unique=True) + pages = models.PositiveIntegerField(blank=True, null=True) + published_date = models.DateField(null=True, blank=True) + language = models.CharField(max_length=200, blank=True, null=True) + book = models.FileField( + upload_to=book_upload_path, blank=True, null=True) + cover_image = models.ImageField( + upload_to='book_covers', blank=True, null=True) + added_by = models.ForeignKey( + 'user_api.User', + blank=True, + null=True, + on_delete=models.CASCADE + ) + is_deleted = models.BooleanField(default=False) + added_date_time = models.DateTimeField(auto_now_add=True) + updated_date_time = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title diff --git a/backend/book_api/tests.py b/backend/book_api/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/book_api/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/book_api/views.py b/backend/book_api/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/book_api/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/backend/db.sqlite3 b/backend/db.sqlite3 index e42db9e..a3319bb 100644 Binary files a/backend/db.sqlite3 and b/backend/db.sqlite3 differ diff --git a/backend/publisher_api/__init__.py b/backend/publisher_api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/publisher_api/admin.py b/backend/publisher_api/admin.py new file mode 100644 index 0000000..1850ea4 --- /dev/null +++ b/backend/publisher_api/admin.py @@ -0,0 +1,22 @@ +from django.contrib import admin + +from publisher_api.models import Publisher + + +@admin.register(Publisher) +class PublisherAdmin(admin.ModelAdmin): + list_display = [ + 'id', 'name', 'address', 'website', + 'email', 'phone_number', + 'established_year', + ] + list_display_links = [ + 'name', + ] + list_filter = [] + search_fields = [ + 'name', 'address', 'website', + 'email', 'phone_number', + 'established_year', + ] + readonly_fields = ['id',] diff --git a/backend/publisher_api/apps.py b/backend/publisher_api/apps.py new file mode 100644 index 0000000..56537ee --- /dev/null +++ b/backend/publisher_api/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PublisherApiConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'publisher_api' diff --git a/backend/publisher_api/migrations/0001_initial.py b/backend/publisher_api/migrations/0001_initial.py new file mode 100644 index 0000000..891193c --- /dev/null +++ b/backend/publisher_api/migrations/0001_initial.py @@ -0,0 +1,29 @@ +# Generated by Django 5.1.1 on 2024-09-06 20:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Publisher', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('address', models.CharField(blank=True, max_length=255, null=True)), + ('website', models.URLField(blank=True, null=True)), + ('email', models.EmailField(blank=True, max_length=254, null=True)), + ('phone_number', models.CharField(blank=True, max_length=20, null=True)), + ('established_year', models.PositiveIntegerField(blank=True, null=True)), + ('contact_person', models.CharField(blank=True, max_length=255, null=True)), + ('country', models.CharField(blank=True, max_length=100, null=True)), + ('city', models.CharField(blank=True, max_length=100, null=True)), + ], + ), + ] diff --git a/backend/publisher_api/migrations/0002_publisher_added_by_publisher_added_date_time_and_more.py b/backend/publisher_api/migrations/0002_publisher_added_by_publisher_added_date_time_and_more.py new file mode 100644 index 0000000..634fe95 --- /dev/null +++ b/backend/publisher_api/migrations/0002_publisher_added_by_publisher_added_date_time_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 5.1.1 on 2024-09-06 20:24 + +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('publisher_api', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='publisher', + name='added_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='publisher', + name='added_date_time', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='publisher', + name='is_deleted', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='publisher', + name='updated_date_time', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/backend/publisher_api/migrations/__init__.py b/backend/publisher_api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/publisher_api/models.py b/backend/publisher_api/models.py new file mode 100644 index 0000000..4166a44 --- /dev/null +++ b/backend/publisher_api/models.py @@ -0,0 +1,26 @@ +from django.db import models + + +class Publisher(models.Model): + name = models.CharField(max_length=255) + address = models.CharField(max_length=255, blank=True, null=True) + website = models.URLField(blank=True, null=True) + email = models.EmailField(blank=True, null=True) + phone_number = models.CharField(max_length=20, blank=True, null=True) + established_year = models.PositiveIntegerField(blank=True, null=True) + # Contact person at the publisher + contact_person = models.CharField(max_length=255, blank=True, null=True) + country = models.CharField(max_length=100, blank=True, null=True) + city = models.CharField(max_length=100, blank=True, null=True) + added_by = models.ForeignKey( + 'user_api.User', + blank=True, + null=True, + on_delete=models.CASCADE + ) + is_deleted = models.BooleanField(default=False) + added_date_time = models.DateTimeField(auto_now_add=True) + updated_date_time = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.name diff --git a/backend/publisher_api/tests.py b/backend/publisher_api/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/publisher_api/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/publisher_api/views.py b/backend/publisher_api/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/publisher_api/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/backend/requirements.txt b/backend/requirements.txt index e4134c9..8c99b3e 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,3 +1,6 @@ Django==5.1.1 djangorestframework==3.15.2 - +drf-yasg +flake8 +djangorestframework-simplejwt==5.3.1 +Pillow diff --git a/backend/user_api/serializers/__init__.py b/backend/user_api/serializers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/user_api/serializers/v1.py b/backend/user_api/serializers/v1.py new file mode 100644 index 0000000..4e1c04a --- /dev/null +++ b/backend/user_api/serializers/v1.py @@ -0,0 +1,19 @@ +from rest_framework import serializers + +from user_api.models import User + + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ('id', 'email', 'username', 'password',) + extra_kwargs = {'password': {'write_only': True}} + + def create(self, validated_data): + user = User.objects.create_user( + email=validated_data['email'], + username=validated_data.get('username', None), + password=validated_data['password'] + ) + + return user diff --git a/backend/user_api/urls/__init__.py b/backend/user_api/urls/__init__.py new file mode 100644 index 0000000..afaaaba --- /dev/null +++ b/backend/user_api/urls/__init__.py @@ -0,0 +1,8 @@ +from django.urls import path, include + +from user_api.urls.v1 import urlpatterns as v1 + + +urlpatterns = [ + path('v1/', include(v1)) +] diff --git a/backend/user_api/urls/v1.py b/backend/user_api/urls/v1.py new file mode 100644 index 0000000..c0b1338 --- /dev/null +++ b/backend/user_api/urls/v1.py @@ -0,0 +1,19 @@ +from django.urls import path + +from user_api.views import v1 + + +urlpatterns = [ + path( + 'registration', + v1.UserRegistrationView.as_view(), + ), + path( + 'login', + v1.UserLoginView.as_view(), + ), + path( + 'profile', + v1.UserProfileView.as_view(), + ), +] diff --git a/backend/user_api/views/__init__.py b/backend/user_api/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/user_api/views/v1.py b/backend/user_api/views/v1.py new file mode 100644 index 0000000..54eb37f --- /dev/null +++ b/backend/user_api/views/v1.py @@ -0,0 +1,129 @@ +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.permissions import AllowAny +from rest_framework import status + +from rest_framework_simplejwt.tokens import RefreshToken + +from user_api.models import User + +from user_api.serializers.v1 import UserSerializer + + +class UserRegistrationView(APIView): + permission_classes = [AllowAny] + """ + post: + Register a new user. + + Takes in user details and creates a new user in the system. + + Parameters: + None + + Returns: + message: A success message if the registration is successful. + errors: If the validation fails, returns a list of errors with + their descriptions. + """ + def post(self, request, *args, **kwargs): + serializer = UserSerializer(data=request.data) + + if serializer.is_valid(): + serializer.save() + print(f"Serializer Data: {serializer.data}") + + return Response({ + 'message': 'User registered successfully', + }) + + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class UserLoginView(APIView): + permission_classes = [AllowAny] + + def post(self, request, *args, **kwargs): + credential = request.data.get('credential', None) + password = request.data.get('password', None) + + if not credential: + return Response({ + 'errors': ['Log in credential missing.'], + }, status=status.HTTP_400_BAD_REQUEST) + + try: + user = User.objects.get(email=credential) + except User.DoesNotExist: + try: + user = User.objects.get(username=credential) + except User.DoesNotExist: + user = None + + if user is None: + return Response({ + 'errors': ['User not found.'], + }, status=status.HTTP_404_NOT_FOUND) + + if not user.check_password(password): + return Response({ + 'errors': ['Invalid password'], + }, status=status.HTTP_401_UNAUTHORIZED) + + if user is not None: + refresh = RefreshToken.for_user(user) + + return Response({ + 'refresh': str(refresh), + 'access': str(refresh.access_token), + 'message': 'Successfully log in!', + }) + else: + return Response({ + 'detail': 'Invalid credentials.' + }, status=status.HTTP_401_UNAUTHORIZED) + + +class UserProfileView(APIView): + """ + v1 User Profile API. + + Parameters: + None + + Returns: + message: A success message if the profile is successful. + errors: If the validation fails, returns a list of errors with + their descriptions. + """ + def get(self, request, *args, **kwargs): + pk = kwargs.get('pk') + + if pk: + try: + user = User.objects.get(pk=pk) + except User.DoesNotExist: + return Response({ + 'details': 'User not found', + }, status=status.HTTP_404_NOT_FOUND) + + if user.is_private and request.user.is_anonymous: + return Response({ + 'details': 'User is not authenticated', + }, status=status.HTTP_401_UNAUTHORIZED) + + serializer = UserSerializer(user) + + return Response(serializer.data) + + user = request.user + + if user.is_anonymous: + return Response({ + 'details': 'User is not authenticated', + }, status=status.HTTP_401_UNAUTHORIZED) + + serializer = UserSerializer(user) + data = serializer.data + + return Response(data) diff --git a/backup/db/postgresql/README.md b/backup/db/postgresql/README.md deleted file mode 100644 index 276c396..0000000 --- a/backup/db/postgresql/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# psql -psql is a terminal-based front-end tool for using the PostgreSQL database management system. It allows you to interact with your database directly, performing various tasks - -#