Compare commits

..

No commits in common. "feature/initial_models" and "main" have entirely different histories.

36 changed files with 15 additions and 422 deletions

7
.gitignore vendored
View file

@ -396,4 +396,9 @@ pyrightconfig.json
.ionide
# End of https://www.toptal.com/developers/gitignore/api/django,pycharm,python,visualstudiocode
/.idea
/.idea/inspectionProfiles/profiles_settings.xml
/.idea/.gitignore
/.idea/misc.xml
/.idea/mixr-api.iml
/.idea/modules.xml
/.idea/vcs.xml

View file

@ -1,9 +0,0 @@
# Mixr API
## Setup
### Install requirements
If there are any issues with `psycopg2` and that it cannot be imported, you might be missing `libpq-dev`. If you are using a virtual environment, install it with `sudo apt-get install libpq-dev`.
If there are any issues with `djangorestframework` or rather the import of `rest_framework`, try installing it again with the active environment using `python -m pip install djangorestframework`.

View file

View file

@ -1,14 +0,0 @@
from django.contrib import admin
from api_v1.cafeteria.model import Cafeteria
from api_v1.institution.model import Institution
from api_v1.location.model import Location
from api_v1.mood.model import Mood
from api_v1.profile.model import Profile
# Register your models here.
admin.site.register(Profile)
admin.site.register(Location)
admin.site.register(Cafeteria)
admin.site.register(Institution)
admin.site.register(Mood)

View file

@ -1,6 +0,0 @@
from django.apps import AppConfig
class ApiV1Config(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'api_v1'

View file

@ -1,25 +0,0 @@
from django.views import View
from django.contrib.auth.models import User
from django.http import HttpResponse
from api_v1.profile.profile_services import acreate_profile
class RegistrationView(View):
async def post(self, request):
# Read parameters from body
username = request.POST.get('username')
password = request.POST.get('password')
email = request.POST.get('email')
display_name = request.POST.get('display_name')
# Check if profile already exists
if User.objects.filter(username=username).exists():
return HttpResponse(status=400, content="User already exists")
# Create profile
user = await User.objects.acreate_user(username, email, password)
# Create UserInformation
await acreate_profile(user, display_name=display_name)
return HttpResponse(status=201, content="User created")

View file

@ -1,19 +0,0 @@
import uuid
from django.db import models
from ..institution.model import Institution
from ..location.model import Location
class Cafeteria(models.Model):
cafeteria_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=50, blank=False, null=False)
location = models.OneToOneField(Location, on_delete=models.DO_NOTHING)
website = models.URLField()
institutes = models.ManyToManyField(Institution)
def __str__(self):
return '{} ({})'.format(self.name, self.location)

View file

@ -1,22 +0,0 @@
import uuid
from django.db import models
from api_v1.location.model import Location
class Institution(models.Model):
institution_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=150, blank=False, null=False)
description = models.TextField(null=True)
location = models.OneToOneField(Location, on_delete=models.DO_NOTHING, null=True, blank=True)
website = models.URLField(null=True)
def __str__(self):
result = self.name
if self.description:
result += f' - {self.description}'
return result

View file

@ -1,22 +0,0 @@
import uuid
from django.db import models
class Location(models.Model):
location_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
longitude = models.DecimalField(max_digits=9, decimal_places=6)
latitude = models.DecimalField(max_digits=9, decimal_places=6)
country_code = models.CharField(max_length=2) # Following ISO-3166-1
country_name = models.CharField(max_length=50)
city = models.CharField(max_length=50)
postal_code = models.CharField(max_length=20)
street = models.CharField(max_length=50)
number = models.CharField(max_length=20)
def __str__(self):
return '{} {}, {} {}, {} {}'.format(self.street, self.number, self.postal_code, self.city, self.country_code,
self.country_name)

View file

@ -1,24 +0,0 @@
# Generated by Django 5.0.2 on 2024-02-28 12:44
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='UserInformation',
fields=[
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),
('display_name', models.CharField(max_length=150)),
],
),
]

View file

@ -1,19 +0,0 @@
# Generated by Django 5.0.2 on 2024-02-28 19:41
from django.conf import settings
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('api_v1', '0001_createBasicUserInformation'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RenameModel(
old_name='UserInformation',
new_name='Profile',
),
]

View file

@ -1,28 +0,0 @@
# Generated by Django 5.0.2 on 2024-02-28 20:03
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api_v1', '0002_rebrandUserInformationToProfile'),
]
operations = [
migrations.CreateModel(
name='Location',
fields=[
('location_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('longitude', models.DecimalField(decimal_places=6, max_digits=9)),
('latitude', models.DecimalField(decimal_places=6, max_digits=9)),
('country_code', models.CharField(max_length=2)),
('country_name', models.CharField(max_length=50)),
('city', models.CharField(max_length=50)),
('postal_code', models.CharField(max_length=20)),
('street', models.CharField(max_length=50)),
('number', models.CharField(max_length=20)),
],
),
]

View file

@ -1,24 +0,0 @@
# Generated by Django 5.0.2 on 2024-02-28 20:03
import django.db.models.deletion
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api_v1', '0003_createdLocationModel'),
]
operations = [
migrations.CreateModel(
name='Cafeteria',
fields=[
('cafeteria_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=50)),
('website', models.URLField()),
('location', models.OneToOneField(on_delete=django.db.models.deletion.DO_NOTHING, to='api_v1.location')),
],
),
]

View file

@ -1,30 +0,0 @@
# Generated by Django 5.0.2 on 2024-02-28 20:23
import django.db.models.deletion
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api_v1', '0004_createdCafeteriaModel'),
]
operations = [
migrations.CreateModel(
name='Institution',
fields=[
('institution_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=150)),
('description', models.TextField(null=True)),
('website', models.URLField(null=True)),
('location', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='api_v1.location')),
],
),
migrations.AddField(
model_name='cafeteria',
name='institutes',
field=models.ManyToManyField(to='api_v1.institution'),
),
]

View file

@ -1,27 +0,0 @@
# Generated by Django 5.0.2 on 2024-02-28 22:55
import colorfield.fields
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api_v1', '0005_addedInstitutionModel'),
]
operations = [
migrations.CreateModel(
name='Mood',
fields=[
('mood_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=30)),
('description', models.CharField(max_length=150)),
('color', colorfield.fields.ColorField(default='#FFFFFF', image_field=None, max_length=25, samples=None)),
('is_available', models.BooleanField(default=True)),
('is_reliable', models.BooleanField(default=True)),
('is_going', models.BooleanField(default=False)),
],
),
]

View file

@ -1,5 +0,0 @@
from api_v1.profile.model import Profile
from api_v1.location.model import Location
from api_v1.cafeteria.model import Cafeteria
from api_v1.institution.model import Institution
from api_v1.mood.model import Mood

View file

@ -1,20 +0,0 @@
import uuid
from django.db import models
from colorfield.fields import ColorField
class Mood(models.Model):
mood_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=30)
description = models.CharField(max_length=150, blank=False, null=False)
color = ColorField(format='hex')
is_available = models.BooleanField(default=True) # Is available to go
is_reliable = models.BooleanField(default=True) # Will always do the same in this mood
is_going = models.BooleanField(default=False) # Is going to the cafeteria
def __str__(self):
return self.name

View file

@ -1,11 +0,0 @@
from django.contrib.auth.models import User
from django.db import models
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
display_name = models.CharField(max_length=150, null=False)
def __str__(self):
return '{} ({})'.format(self.display_name, self.user)

View file

@ -1,48 +0,0 @@
from api_v1.profile.model import Profile
from django.contrib.auth.models import User
from typing import Optional
async def acreate_profile(user: User, *, display_name: Optional[str] = None) -> Profile:
"""Async. Create a Profile object for the given profile.
@param user: The profile to create the Profile object for.
@param display_name: The display name for the profile. If None, the profile's username will be used.
@return: The created Profile object.
"""
profile = await Profile.objects.acreate(user=user, display_name=user.username)
# Use the given display name if provided.
if display_name is not None:
profile.display_name = display_name
await profile.asave()
return profile
async def aget_profile(user: User) -> Profile:
"""Async. Get the Profile object for the given profile.
@param user: The profile to get the Profile object for.
@return: The Profile object for the given profile.
"""
return await Profile.objects.aget(user)
async def aset_user_display_name(user: User, display_name: Optional[str] = None) -> Profile:
"""Async. Set the display name for the given profile.
@param user: The profile to set the display name for.
@param display_name: The display name to set for the profile. If None, the profile's username will be used.
@return: The Profile object for the given profile.
"""
profile = await Profile.objects.aget(user)
# Set the display name to the given display name, or the profile's username if None.
if display_name is not None:
profile.display_name = display_name
else:
profile.display_name = user.username
await profile.asave()
return profile

View file

@ -1,13 +0,0 @@
import uuid
from django.db import models
from api_v1.profile.model import Profile
class Schedule(models.Model):
schedule_id = models.UUIDField(primary_key=True, default=uuid.uuid4)
profile = models.ForeignKey(Profile, on_delete=models.DO_NOTHING, null=False)
slots = models.ManyToOneRel()

View file

@ -1,34 +0,0 @@
import uuid
from django.core.exceptions import ValidationError
from django.db import models
def validate_weekday(value: int):
if value < 0 or value > 6:
raise ValidationError("Weekday must be between 0 (Mo) and 6 (Su)")
class Slot(models.Model):
slot_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
start_time = models.TimeField()
end_time = models.TimeField()
duration = models.DurationField()
start_weekday = models.SmallIntegerField(validators=[validate_weekday])
end_weekday = models.SmallIntegerField(validators=[validate_weekday])
def __str__(self):
days = [
"Mo",
"Tu",
"We",
"Th",
"Fr",
"Sa",
"Su"
]
return "[{}] {} - [{}] {}".format(days[self.start_weekday], self.start_time, days[self.end_weekday],
self.end_time)

View file

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View file

@ -1,5 +0,0 @@
from django.urls import path, include
# Assign register url
urlpatterns = [
]

View file

@ -1,3 +0,0 @@
from django.shortcuts import render
# Create your views here.

View file

@ -15,6 +15,7 @@ from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
@ -26,6 +27,7 @@ DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
@ -35,10 +37,6 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api_v1.apps.ApiV1Config',
'rest_framework',
'colorfield',
]
MIDDLEWARE = [
@ -71,6 +69,7 @@ TEMPLATES = [
WSGI_APPLICATION = 'mixr_api.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
@ -85,6 +84,7 @@ DATABASES = {
}
}
# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
@ -103,6 +103,7 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/
@ -114,6 +115,7 @@ USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/

View file

@ -15,9 +15,8 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
path("v1/", include("api_v1.urls")),
]

View file

@ -1,4 +1,2 @@
Django~=4.2
Django~=5.0.2
psycopg2-binary~=2.9.9
djangorestframework~=3.14.0
django-colorfield~=0.11.0