Compare commits

...

2 Commits

Author SHA1 Message Date
e24ca197cb added user profiles and mobilephone 2025-03-01 14:43:12 +01:00
339f4b3a5e added user profiles and mobilephone 2025-03-01 14:41:47 +01:00
21 changed files with 435 additions and 42 deletions

0
mobile_phone/__init__.py Normal file
View File

3
mobile_phone/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
mobile_phone/apps.py Normal file
View File

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

View File

21
mobile_phone/models.py Normal file
View File

@ -0,0 +1,21 @@
import os
from twilio.rest import Client
from django.db import models
from mobile_phone.validators.swedishmobile import SwedishTelephoneValidator
from user_profile.models import User_Profile
class Phone_Swedish(models.Model):
profile = models.ForeignKey(User_Profile, on_delete=models.CASCADE, related_name="mobilephone_swe")
about = models.CharField("about this phone", max_length=20)
mobile_number = models.CharField("phone number", max_length=12, validators=[SwedishTelephoneValidator,], unique=True)
def send_sms(self, message):
account_sid = os.environ["PUCKOPRUTT_TWILIO_SID"]
auth_token = os.environ["PUCKOPRUTT_TWILIO_TOKEN"]
client = Client(account_sid, auth_token)
return client.messages.create(
body=message,
from_=os.environ["PUCKOPRUTT_TWILIO_PHONENR"],
to=self.mobile_number
)

3
mobile_phone/tests.py Normal file
View File

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

View File

@ -0,0 +1,11 @@
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.utils.translation import gettext_lazy as _
class SwedishTelephoneValidator(RegexValidator):
def __init__(self, inverse_match=None, flags=None):
regex = r"^(+46)(7[02369])(\d{7})$"
message = _("Invalid swedish phonenumber. a swedish phonenumber needs to start with +467 followed by 0,2,3,6 or 9 then 7 digits")
code = "invalid_phonenumber"
super(SwedishTelephoneValidator, self).__init__(regex=regex, message=message, code=code, inverse_match=inverse_match, flags=flags)

3
mobile_phone/views.py Normal file
View File

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

View File

@ -62,6 +62,8 @@ INSTALLED_APPS = [
# my shit # my shit
'users', 'users',
'user_profile',
'mobile_phone',
'spiders' 'spiders'
] ]

View File

@ -25,6 +25,8 @@ from drf_spectacular.views import SpectacularSwaggerView
urlpatterns = [ urlpatterns = [
path("auth/", include('users.urls')), path("auth/", include('users.urls')),
path("spiders/", include('spiders.urls')), path("spiders/", include('spiders.urls')),
path("my-profile/", include('user_profile.my_urls')),
path("puckopruttisar/", include('user_profile.urls')),
# Spectacular # Spectacular
path("schema/", SpectacularAPIView.as_view(), name="schema-text"), path("schema/", SpectacularAPIView.as_view(), name="schema-text"),

0
user_profile/__init__.py Normal file
View File

3
user_profile/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
user_profile/apps.py Normal file
View File

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

View File

99
user_profile/models.py Normal file
View File

@ -0,0 +1,99 @@
import hashlib
from django.template.defaultfilters import slugify
from django.utils import timezone
from django.contrib.auth import get_user_model
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save
def _create_unique_string():
time = f"pucko {timezone.now} prutt"
m = hashlib.blake2s()
m.update(str.encode(time))
return m.hexdigest()[16:52]
class User_Profile(models.Model):
class Meta:
verbose_name = "User profile"
verbose_name_plural = "User profiles"
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, related_name="profile")
friends = models.ManyToManyField("self", blank=True)
nickname = models.CharField("Nickname", max_length=36, default=_create_unique_string)
first_name = models.CharField("First name", max_length=35, null=True, blank=True)
last_name = models.CharField("Last name", max_length=50, null=True, blank=True)
birthday = models.DateField("Birthday", null=True, blank=True)
updated_at = models.DateTimeField("Last updated", auto_now=True)
created_at = models.DateTimeField("Created at", auto_now_add=True)
slug = models.CharField(max_length=36, blank=True)
@classmethod
def search_for(cls, query):
search_filter = models.Q(nickname__icontains=query)|models.Q(first_name__icontains=query)|models.Q(last_name__icontains=query)
return cls.objects.filter(search_filter).distinct()
def friend_request_send(self, user):
if self.friends.filter(pk=user.pk).exists():
return "you are already friends!"
_, created = Friend_Request.objects.get_or_create(from_user=self, to_user=user)
if created:
return "friend request sent."
return "friend request already sent."
def friend_request_accept(self, requestID):
friend_request = Friend_Request.objects.get(pk=requestID)
if friend_request.to_user == self:
self.friends.add(friend_request.from_user)
friend_request.from_user.friends.add(friend_request.to_user)
friend_request.delete()
return 0
else:
return -1
def friend_request_deny(self, requestID):
friend_request = Friend_Request.objects.get(pk=requestID)
if friend_request.to_user == self:
friend_request.delete()
return 0
else:
return -1
def save(self, *args, **kwargs):
self.slug = slugify(self.nickname)
if not self.first_name is None:
self.first_name = self.first_name.title()
if not self.last_name is None:
self.last_name = self.last_name.title()
return super(User_Profile, self).save()
@property
def full_name(self):
return f"{self.first_name} {self.last_name}"
@property
def initials(self):
return "".join(x[0] for x in self.full_name.split(" "))
def __str__(self):
return f"[Userprofile] {self.nickname}"
def __repr__(self):
return self.__str__()
class Friend_Request(models.Model):
from_user = models.ForeignKey(User_Profile, on_delete=models.CASCADE, related_name="friend_requests_sent")
to_user = models.ForeignKey(User_Profile, on_delete=models.CASCADE, related_name="friend_requests_recieved")
asked_at = models.DateTimeField("asked at", auto_now_add=True)
def __str__(self):
return f"[Friend Request] from {self.from_user.user.username} to {self.to_user.user.username}"
def __repr__(self):
return self.__str__()
@receiver(post_save, sender=get_user_model())
def create_profile_for_new_user(sender, created, instance, **kwargs):
if created:
profile = User_Profile(user = instance)
profile.save()

17
user_profile/my_urls.py Normal file
View File

@ -0,0 +1,17 @@
from django.urls import path
from user_profile.views import Friend_Request_Recieved_Slug_View
from user_profile.views import Friend_Requests_Recieved_View
from user_profile.views import Friend_Request_Sent_Slug_View
from user_profile.views import Friend_Requests_Sent_View
from user_profile.views import My_Friends_View
from user_profile.views import My_Profile_View
urlpatterns = [
path("", My_Profile_View.as_view({"get": "my_profile", "put": "update", "patch": "update_partial"}), name="my-user-profile"),
path("friends/", My_Friends_View.as_view({"get": "list"}), name="my-friends"),
path("friend_requests/", Friend_Requests_Recieved_View.as_view({"get": "list"}), name="my-recieved-friend-requests"),
path("friend_requests/<int:friend_request_id>/", Friend_Request_Recieved_Slug_View.as_view({"delete": "deny", "post": "accept"}), name="my-recieved-friend-request-slug"),
path("friend_requests/sent/", Friend_Requests_Sent_View.as_view({"get": "list"}), name="my-sent-friend-requests"),
path("friend_requests/sent/<int:friend_request_id>/", Friend_Request_Sent_Slug_View.as_view({"delete": "destroy"}), name="my-send-friend-request-delete")
]

View File

@ -0,0 +1,86 @@
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _
from settings.puckoviews import PuckoView
from settings.puckoviews import PuckoSlugView
from rest_framework import serializers
from mobile_phone.models import Phone_Swedish
from .models import User_Profile
from .models import Friend_Request
class Friend_Request_Serializer(serializers.ModelSerializer):
asked_by = serializers.CharField(source="from_user.nickname")
to = serializers.CharField(source="to_user.nickname")
nickname = serializers.CharField(write_only=True)
class Meta:
model = Friend_Request
fields = ("id", "nickname", "asked_by", "to")
read_only_fields = ["id", "asked_by", "to"]
extra_kwargs = {
"url": {"lookup_field": "pk"}
}
class User_Searchalizer(serializers.ModelSerializer):
search = serializers.CharField(max_length=50, required=True, write_only=True)
class Meta:
model = User_Profile
fields = ("search", "nickname", "birthday", "first_name", "last_name", "slug")
read_only_fields = ("nickname", "birthday", "first_name", "last_name", "slug")
extra_kwargs = {
"url": {"lookup_field": "slug"}
}
def validate(self, attrs):
if not isinstance(attrs["search"], str):
raise serializers.ValidationError({"search": "needs to be a string."})
return attrs
class User_Profile_Serializer(serializers.ModelSerializer):
class Meta:
model = User_Profile
read_only_fields = ["slug"]
exclude = ("id", "user", "friends", "created_at", "updated_at")
extra_kwargs = {
"url": {"lookup_field": "pk"}
}
class User_Profile_Small_Serializer(serializers.ModelSerializer):
class Meta:
model = User_Profile
read_only_fields = ["slug"]
exclude = ("id", "user", "friends", "created_at", "updated_at", "first_name", "last_name", "birthday")
extra_kwargs = {
"url": {"lookup_field": "pk"}
}
class My_Profile_Serializer(serializers.ModelSerializer):
class Meta:
model = User_Profile
read_only_fields = ["slug", "created_at", "updated_at"]
exclude = ("id", "user", "friends")
extra_kwargs = {
"url": {"lookup_field": "slug"}
}
def update_profile(self, instance, validated_data):
instance.nickname = validated_data.get("nickname", instance.nickname)
instance.first_name = validated_data.get("first_name", instance.first_name)
instance.last_name = validated_data.get("last_name", instance.last_name)
instance.birthday = validated_data.get("birthday", instance.birthday)
return instance
def create(self, validated_data):
profile = User_Profile.objects.create(**validated_data)
profile.save()
return profile
def update(self, instance, validated_data):
instance = self.update_profile(instance=instance, validated_data=validated_data)
instance.save()
return instance

3
user_profile/tests.py Normal file
View File

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

13
user_profile/urls.py Normal file
View File

@ -0,0 +1,13 @@
from django.urls import path
from user_profile.views import User_Search_View
from user_profile.views import User_Profile_View
from user_profile.views import Send_Friend_Request_View
urlpatterns = [
path("search/", User_Search_View.as_view({"post": "list"}), name="puckopruttisar-search"),
path("profile/<slug:slug>/", User_Profile_View.as_view({"get": "profile"}, name="puckopruttis-profile")),
path("profile/<slug:slug>/send_friend_request/", Send_Friend_Request_View.as_view({"get": "send"}), name="puckopruttis-ask-friendship")
]

157
user_profile/views.py Normal file
View File

@ -0,0 +1,157 @@
from django.shortcuts import render
from settings.puckoviews import PuckoView, PuckoPaginatedView, PuckoSearchView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from settings.puckolizers import Message_Serializer
from user_profile.models import User_Profile
from user_profile.models import Friend_Request
from user_profile.serializer import Friend_Request_Serializer
from user_profile.serializer import User_Profile_Small_Serializer
from user_profile.serializer import User_Profile_Serializer
from user_profile.serializer import My_Profile_Serializer
from user_profile.serializer import User_Searchalizer
class My_Profile_View(PuckoView):
permission_classes = [IsAuthenticated,]
serializer_class = My_Profile_Serializer
model_class = User_Profile
def my_profile(self, request):
profile = request.user.profile
serializer = self.serializer_class(profile)
return Response(serializer.data, status=200)
def update(self, request):
obj = request.user.profile
serializer = self.serializer_class(obj, data=request.data, partial=False)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
def update_partial(self, request):
obj = request.user.profile
serializer = self.serializer_class(obj, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
class My_Friends_View(PuckoPaginatedView):
permission_classes = (IsAuthenticated,)
serializer_class = User_Profile_Small_Serializer
model_class = User_Profile
def get_queryset(self, request=None):
return request.user.profile.friends.all()
class User_Search_View(PuckoSearchView):
permission_classes = [IsAuthenticated,]
serializer_class = User_Searchalizer
model_class = User_Profile
def get_object(self, data):
try:
ret = self.model_class.search_for(query=data["search"])
except Exception:
ret = None
return ret
class User_Profile_View(PuckoView):
permission_classes = [IsAuthenticated,]
serializer_class = User_Profile_Serializer
model_class = User_Profile
def profile(self, request, slug):
try:
obj = User_Profile.objects.get(slug=slug)
except:
serializer = Message_Serializer({"message": "user not found"})
return Response(serializer.data, status=404)
serializer = self.serializer_class(obj, many=False)
return Response(serializer.data, status=200)
class Send_Friend_Request_View(PuckoView):
permission_classes = [IsAuthenticated,]
serializer_class = Message_Serializer
model_class = Friend_Request
def send(self, request, slug):
user = request.user.profile
try:
friend = User_Profile.objects.get(slug=slug)
except Exception:
serializer = Message_Serializer({"message": "user not found"})
return Response(serializer.data, status=404)
if friend == user:
serializer = Message_Serializer({"message": "no need to befriend yourself, duuh"})
else:
serializer = self.serializer_class({"message": user.friend_request_send(friend)})
return Response(serializer.data, status=200)
class Friend_Requests_Sent_View(PuckoPaginatedView):
permission_classes = (IsAuthenticated,)
serializer_class = Friend_Request_Serializer
model_class = Friend_Request
def get_queryset(self, request=None):
return request.user.profile.friend_requests_sent.all()
class Friend_Request_Recieved_Slug_View(PuckoView):
permission_classes = (IsAuthenticated,)
serializer_class = Message_Serializer
model_class = User_Profile
def deny(self, request, friend_request_id):
"""Deny a friend request
"""
if not Friend_Request.objects.filter(pk=friend_request_id, to_user=request.user.profile.pk).exists():
serializer = self.serializer_class({"message": "Unknown friend request."})
return Response(serializer.data, status=404)
req = Friend_Request.objects.get(pk=friend_request_id)
message = f"friend request from {req.from_user} have been denied."
if request.user.profile.friend_request_deny(friend_request_id):
serializer = self.serializer_class({"message": "Could not deny friend request."})
return Response(serializer.data, status=403)
serializer = self.serializer_class({"message": message})
return Response(serializer.data, status=204)
def accept(self, request, friend_request_id):
"""Accept a friend request
"""
if not Friend_Request.objects.filter(pk=friend_request_id, to_user=request.user.profile.pk).exists():
serializer = self.serializer_class({"message": "Unknown friend request."})
return Response(serializer.data, status=404)
new_friend = Friend_Request.objects.get(pk=friend_request_id, to_user=request.user.profile.pk).from_user
if request.user.profile.friend_request_accept(friend_request_id):
serializer = self.serializer_class({"message": "Could not accept friend request."})
return Response(serializer.data, status=403)
return Response({"message": f"You are now friends with {new_friend.nickname}"}, status=200)
class Friend_Requests_Recieved_View(PuckoPaginatedView):
permission_classes = (IsAuthenticated,)
serializer_class = Friend_Request_Serializer
model_class = Friend_Request
def get_queryset(self, request=None):
return request.user.profile.friend_requests_recieved.all()
class Friend_Request_Sent_Slug_View(PuckoView):
permission_classes = (IsAuthenticated,)
serializer_class = Message_Serializer
model_class = User_Profile
def destroy(self, request, friend_request_id):
"""Delete a sent friend request
"""
if not Friend_Request.objects.filter(pk=friend_request_id, from_user=request.user.profile.pk).exists():
serializer = self.serializer_class({"message": "Unknown friend request."})
return Response(serializer.data, status=404)
req = Friend_Request.objects.get(pk=friend_request_id)
message = f"friend request to {req.to_user} have been removed."
req.delete()
serializer = self.serializer_class({"message": message})
return Response(serializer.data, status=204)

View File

@ -1,42 +0,0 @@
# Generated by Django 5.1.3 on 2025-02-18 19:46
import django.utils.timezone
import users.base
import users.validators.username
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='Puckopruttis',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(max_length=150, unique=True, validators=[users.validators.username.usernameValidator], verbose_name='username')),
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='email address')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('is_active', models.BooleanField(default=True, verbose_name='active')),
('is_staff', models.BooleanField(default=False, verbose_name='staff status')),
('slug', models.SlugField(blank=True, max_length=70, null=True, unique=True)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'puckopruttis',
'verbose_name_plural': 'puckopruttare',
},
managers=[
('objects', users.base.PuckoBase_Manager()),
],
),
]