style: 🎨 make code ruff compliant

This commit is contained in:
m5ka 2024-03-26 12:18:47 +00:00
parent 11ed6bd2a1
commit a4f8275383
21 changed files with 819 additions and 238 deletions

View file

@ -3,7 +3,6 @@ from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from moku import models
for model_name in models.__all__:
model = getattr(models, model_name)
if model.__name__ != "User":
@ -14,32 +13,11 @@ for model_name in models.__all__:
class UserAdmin(BaseUserAdmin):
fieldsets = (
(None, {"fields": ("username", "email", "password")}),
(
"Profile",
{
"fields": (
"pronouns", "location", "bio"
),
},
),
(
"Status",
{
"fields": (
"email_confirmed_at",
)
},
),
("Profile", {"fields": ("pronouns", "location", "bio")}),
("Status", {"fields": ("email_confirmed_at",)}),
(
"Permissions",
{
"fields": (
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
{"fields": ("is_staff", "is_superuser", "groups", "user_permissions")},
),
("Dates", {"fields": ("date_joined", "last_seen_at",)}),
("Dates", {"fields": ("date_joined", "last_seen_at")}),
)

View file

@ -1,4 +1,3 @@
import sys
from pathlib import Path
import environ
@ -71,9 +70,7 @@ TEMPLATES = [
"django.template.context_processors.tz",
"django.contrib.messages.context_processors.messages",
],
"filters": {
"unemoji": "moku.filters.unemoji",
},
"filters": {"unemoji": "moku.filters.unemoji"},
"policies": {"ext.i18n.trimmed": True},
},
},

View file

@ -14,6 +14,7 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
@ -50,7 +51,11 @@ urlpatterns = [
path("recipes/<str:uuid>", ShowRecipeView.as_view(), name="recipe.show"),
path("recipes/<str:uuid>/delete", DeleteRecipeView.as_view(), name="recipe.delete"),
path("recipes/<str:uuid>/<str:step>", EditStepView.as_view(), name="step.edit"),
path("recipes/<str:uuid>/<str:step>/delete", DeleteStepView.as_view(), name="step.delete"),
path(
"recipes/<str:uuid>/<str:step>/delete",
DeleteStepView.as_view(),
name="step.delete",
),
]
if settings.DEBUG_TOOLBAR:

View file

@ -22,53 +22,228 @@ EMOJI_CATEGORIES = [
(
_("fruit & veg"),
(
"🍏", "🍎", "🍐", "🍊", "🍋", "🍌", "🍉", "🍇", "🍓", "🫐", "🍈", "🍒", "🍑", "🥭", "🍍",
"🥥", "🥝", "🍅", "🍆", "🥑", "🫛", "🥦", "🥬", "🥒", "🌶️", "🫑", "🌽", "🥕", "🫒", "🧄",
"🧅", "🥔", "🍠", "🫚",
"🍏",
"🍎",
"🍐",
"🍊",
"🍋",
"🍌",
"🍉",
"🍇",
"🍓",
"🫐",
"🍈",
"🍒",
"🍑",
"🥭",
"🍍",
"🥥",
"🥝",
"🍅",
"🍆",
"🥑",
"🫛",
"🥦",
"🥬",
"🥒",
"🌶️",
"🫑",
"🌽",
"🥕",
"🫒",
"🧄",
"🧅",
"🥔",
"🍠",
"🫚",
),
),
(
_("savoury dishes"),
(
"🥯", "🍞", "🥖", "🥨", "🧀", "🥚", "🍳", "🧈", "🥞", "🧇", "🥓", "🥩", "🍗", "🍖", "🦴",
"🌭", "🍔", "🍟", "🍕", "🫓", "🥪", "🥙", "🧆", "🌮", "🌯", "🫔", "🥗", "🥘", "🫕", "🥫",
"🫙", "🍝", "🍜", "🍲", "🍛", "🍣", "🍱", "🥟", "🦪", "🍤", "🍙", "🍚", "🍘", "🍥", "🌰",
"🥜", "🫘", "🧊",
"🥯",
"🍞",
"🥖",
"🥨",
"🧀",
"🥚",
"🍳",
"🧈",
"🥞",
"🧇",
"🥓",
"🥩",
"🍗",
"🍖",
"🦴",
"🌭",
"🍔",
"🍟",
"🍕",
"🫓",
"🥪",
"🥙",
"🧆",
"🌮",
"🌯",
"🫔",
"🥗",
"🥘",
"🫕",
"🥫",
"🫙",
"🍝",
"🍜",
"🍲",
"🍛",
"🍣",
"🍱",
"🥟",
"🦪",
"🍤",
"🍙",
"🍚",
"🍘",
"🍥",
"🌰",
"🥜",
"🫘",
"🧊",
),
),
(
_("sweet treats"),
(
"🥠", "🥮", "🍢", "🍡", "🍧", "🍨", "🍦", "🥧", "🧁", "🍰", "🎂", "🍮", "🍭", "🍬", "🍫",
"🥐", "🍿", "🍩", "🍪", "🍯",
"🥠",
"🥮",
"🍢",
"🍡",
"🍧",
"🍨",
"🍦",
"🥧",
"🧁",
"🍰",
"🎂",
"🍮",
"🍭",
"🍬",
"🍫",
"🥐",
"🍿",
"🍩",
"🍪",
"🍯",
),
),
(
_("drinks"),
(
"🥛", "🫗", "🍼", "🫖", "☕️", "🍵", "🧃", "🥤", "🧋", "🍶", "🍺", "🍻", "🥂", "🍷", "🥃",
"🍸", "🍹", "🧉", "🍾",
"🥛",
"🫗",
"🍼",
"🫖",
"☕️",
"🍵",
"🧃",
"🥤",
"🧋",
"🍶",
"🍺",
"🍻",
"🥂",
"🍷",
"🥃",
"🍸",
"🍹",
"🧉",
"🍾",
),
),
(
_("people"),
(
"😀", "😃", "😄", "😁", "😆", "🥹", "😅", "😂", "🤣", "🥲", "😊", "😇", "🙂", "🙃", "😉",
"😌", "😌", "😍", "🥰", "😘", "😗", "😙", "😚", "😋", "😛", "😝", "😜", "🤪", "🤨", "🧐",
"😀",
"😃",
"😄",
"😁",
"😆",
"🥹",
"😅",
"😂",
"🤣",
"🥲",
"😊",
"😇",
"🙂",
"🙃",
"😉",
"😌",
"😌",
"😍",
"🥰",
"😘",
"😗",
"😙",
"😚",
"😋",
"😛",
"😝",
"😜",
"🤪",
"🤨",
"🧐",
),
),
(
_("animals"),
(
"🐶", "🐱", "🐭", "🐹", "🐰", "🦊", "🐻", "🐼", "🐨", "🐯", "🦁", "🐮", "🐷", "🐽", "🐸",
"🐵", "🙈", "🙉", "🙊", "🐒", "🐔", "🐧", "🐦", "🐤", "🐣", "🐥", "🪿", "🦆", "🐦‍⬛", "🦅",
"🦉", "🦇", "🐺", "🐗", "🐴", "🦄", "🫎", "🐝", "🪱", "🐛", "🦋", "🐌", "🐞", "🐜", "🪰",
"🐶",
"🐱",
"🐭",
"🐹",
"🐰",
"🦊",
"🐻",
"🐼",
"🐨",
"🐯",
"🦁",
"🐮",
"🐷",
"🐽",
"🐸",
"🐵",
"🙈",
"🙉",
"🙊",
"🐒",
"🐔",
"🐧",
"🐦",
"🐤",
"🐣",
"🐥",
"🪿",
"🦆",
"🐦‍⬛",
"🦅",
"🦉",
"🦇",
"🐺",
"🐗",
"🐴",
"🦄",
"🫎",
"🐝",
"🪱",
"🐛",
"🦋",
"🐌",
"🐞",
"🐜",
"🪰",
),
),
(
_("tools & things"),
(
"🥄", "🍴", "🍽️", "🥣", "🥡", "🥢", "🧂", "🔪", "🪓",
)
)
(_("tools & things"), ("🥄", "🍴", "🍽️", "🥣", "🥡", "🥢", "🧂", "🔪", "🪓")),
]

View file

@ -1,4 +1,4 @@
from django.forms import Form, ModelForm
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from moku.models.recipe import Recipe, RecipeStep
@ -8,9 +8,7 @@ class RecipeForm(ModelForm):
class Meta:
model = Recipe
fields = ("title",)
labels = {
"title": _("recipe title"),
}
labels = {"title": _("recipe title")}
class RecipeStepForm(ModelForm):

View file

@ -1,7 +1,6 @@
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.utils.translation import gettext_lazy as _
from django_recaptcha.fields import ReCaptchaField
from moku.models.user import User, UserSettings
@ -14,10 +13,7 @@ class UserForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = User
fields = ("username", "email", "password1", "password2")
labels = {
"username": _("username"),
"email": _("email address"),
}
labels = {"username": _("username"), "email": _("email address")}
help_texts = {
"username": User._meta.get_field("username").help_text,
"email": User._meta.get_field("email").help_text,
@ -26,10 +22,10 @@ class UserForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["captcha"].error_messages = {
"required": _("make sure you've ticked the captcha."),
"required": _("make sure you've ticked the captcha.")
}
self.fields["check"].error_messages = {
"required": _("you must agree to the terms of use and privacy policy."),
"required": _("you must agree to the terms of use and privacy policy.")
}
@ -37,9 +33,7 @@ class UserSettingsForm(forms.ModelForm):
class Meta:
model = UserSettings
fields = ("language",)
labels = {
"language": _("language"),
}
labels = {"language": _("language")}
class ProfileForm(forms.ModelForm):

View file

@ -1,49 +1,162 @@
# ruff: noqa: E501
# Generated by Django 5.0.3 on 2024-03-24 17:24
import re
import django.contrib.auth.models
import django.core.validators
import django.utils.timezone
import moku.validators
import re
from django.db import migrations, models
import moku.validators
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
dependencies = [("auth", "0012_alter_user_first_name_max_length")]
operations = [
migrations.CreateModel(
name='User',
name="User",
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')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('username', models.CharField(db_index=True, help_text="this is the unique identifier you'll use to log in. it may only contain letters, numbers, hyphens, dashes and dots.", max_length=64, unique=True, validators=[django.core.validators.RegexValidator(re.compile('^[a-zA-Z0-9-_.]+\\Z'), 'Username may only contain letters, numbers, hyphens, underscores and dots.', 'invalid'), moku.validators.validate_username_length], verbose_name='username')),
('email', models.EmailField(help_text="this should be your email address. make sure it's valid and that you have access to it.", max_length=128, unique=True, verbose_name='email address')),
('email_confirmed_at', models.DateTimeField(blank=True, null=True)),
('pronouns', models.CharField(blank=True, help_text='what pronouns should people use when referring to you?', max_length=64, verbose_name='pronouns')),
('location', models.CharField(blank=True, help_text='where in the world are you?', max_length=64, verbose_name='location')),
('bio', models.TextField(blank=True, help_text='write something about yourself!', verbose_name='about me')),
('last_seen_at', models.DateTimeField(blank=True, help_text='the last time this user accessed the site.', null=True, verbose_name='last seen at')),
('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')),
(
"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",
),
),
(
"is_staff",
models.BooleanField(
default=False,
help_text="Designates whether the user can log into this admin site.",
verbose_name="staff status",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
verbose_name="active",
),
),
(
"date_joined",
models.DateTimeField(
default=django.utils.timezone.now, verbose_name="date joined"
),
),
(
"username",
models.CharField(
db_index=True,
help_text="this is the unique identifier you'll use to log in. it may only contain letters, numbers, hyphens, dashes and dots.",
max_length=64,
unique=True,
validators=[
django.core.validators.RegexValidator(
re.compile("^[a-zA-Z0-9-_.]+\\Z"),
"Username may only contain letters, numbers, hyphens, underscores and dots.",
"invalid",
),
moku.validators.validate_username_length,
],
verbose_name="username",
),
),
(
"email",
models.EmailField(
help_text="this should be your email address. make sure it's valid and that you have access to it.",
max_length=128,
unique=True,
verbose_name="email address",
),
),
("email_confirmed_at", models.DateTimeField(blank=True, null=True)),
(
"pronouns",
models.CharField(
blank=True,
help_text="what pronouns should people use when referring to you?",
max_length=64,
verbose_name="pronouns",
),
),
(
"location",
models.CharField(
blank=True,
help_text="where in the world are you?",
max_length=64,
verbose_name="location",
),
),
(
"bio",
models.TextField(
blank=True,
help_text="write something about yourself!",
verbose_name="about me",
),
),
(
"last_seen_at",
models.DateTimeField(
blank=True,
help_text="the last time this user accessed the site.",
null=True,
verbose_name="last seen at",
),
),
(
"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': 'user',
'verbose_name_plural': 'users',
'abstract': False,
"verbose_name": "user",
"verbose_name_plural": "users",
"abstract": False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
managers=[("objects", django.contrib.auth.models.UserManager())],
)
]

View file

@ -1,32 +1,104 @@
# ruff: noqa: E501
# Generated by Django 5.0.3 on 2024-03-25 10:04
import django.db.models.deletion
import moku.models.post
import moku.validators
import shortuuid.django_fields
from django.conf import settings
from django.db import migrations, models
import moku.models.post
import moku.validators
class Migration(migrations.Migration):
dependencies = [
('moku', '0001_initial'),
]
dependencies = [("moku", "0001_initial")]
operations = [
migrations.CreateModel(
name='Post',
name="Post",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', shortuuid.django_fields.ShortUUIDField(alphabet=None, help_text='the unique id that identifies this post.', length=22, max_length=22, prefix='', verbose_name='unique id')),
('emoji', models.CharField(help_text='an emoji to accompany your post!', max_length=8, validators=[moku.validators.validate_emoji], verbose_name='emoji')),
('verb', models.CharField(choices=[('ate', '%(user)s ate %(food)s'), ('made', '%(user)s made %(food)s'), ('cooked', '%(user)s cooked %(food)s'), ('baked', '%(user)s baked %(food)s'), ('ordered', '%(user)s ordered %(food)s')], help_text='how should we best phrase this entry?', max_length=32, verbose_name='verb')),
('food', models.CharField(help_text='what did you eat?', max_length=128, verbose_name='food')),
('image', models.ImageField(blank=True, help_text='here you can upload a picture of what you ate!', upload_to=moku.models.post.post_image_filename, verbose_name='image')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='when this post was created.')),
('updated_at', models.DateTimeField(auto_now=True, help_text='when this post was last updated.')),
('created_by', models.ForeignKey(db_column='created_by_user_id', on_delete=django.db.models.deletion.CASCADE, related_name='posts', to=settings.AUTH_USER_MODEL)),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"uuid",
shortuuid.django_fields.ShortUUIDField(
alphabet=None,
help_text="the unique id that identifies this post.",
length=22,
max_length=22,
prefix="",
verbose_name="unique id",
),
),
(
"emoji",
models.CharField(
help_text="an emoji to accompany your post!",
max_length=8,
validators=[moku.validators.validate_emoji],
verbose_name="emoji",
),
),
(
"verb",
models.CharField(
choices=[
("ate", "%(user)s ate %(food)s"),
("made", "%(user)s made %(food)s"),
("cooked", "%(user)s cooked %(food)s"),
("baked", "%(user)s baked %(food)s"),
("ordered", "%(user)s ordered %(food)s"),
],
help_text="how should we best phrase this entry?",
max_length=32,
verbose_name="verb",
),
),
(
"food",
models.CharField(
help_text="what did you eat?",
max_length=128,
verbose_name="food",
),
),
(
"image",
models.ImageField(
blank=True,
help_text="here you can upload a picture of what you ate!",
upload_to=moku.models.post.post_image_filename,
verbose_name="image",
),
),
(
"created_at",
models.DateTimeField(
auto_now_add=True, help_text="when this post was created."
),
),
(
"updated_at",
models.DateTimeField(
auto_now=True, help_text="when this post was last updated."
),
),
(
"created_by",
models.ForeignKey(
db_column="created_by_user_id",
on_delete=django.db.models.deletion.CASCADE,
related_name="posts",
to=settings.AUTH_USER_MODEL,
),
),
],
),
)
]

View file

@ -1,19 +1,23 @@
# ruff: noqa: E501
# Generated by Django 5.0.3 on 2024-03-25 13:49
import moku.models.user
from django.db import migrations, models
import moku.models.user
class Migration(migrations.Migration):
dependencies = [
('moku', '0002_post'),
]
dependencies = [("moku", "0002_post")]
operations = [
migrations.AddField(
model_name='user',
name='avatar',
field=models.ImageField(blank=True, help_text='a little picture to show up on your profile.', upload_to=moku.models.user.user_avatar_filename, verbose_name='avatar'),
),
model_name="user",
name="avatar",
field=models.ImageField(
blank=True,
help_text="a little picture to show up on your profile.",
upload_to=moku.models.user.user_avatar_filename,
verbose_name="avatar",
),
)
]

View file

@ -1,18 +1,28 @@
# ruff: noqa: E501
# Generated by Django 5.0.3 on 2024-03-25 15:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('moku', '0003_user_avatar'),
]
dependencies = [("moku", "0003_user_avatar")]
operations = [
migrations.AlterField(
model_name='post',
name='verb',
field=models.CharField(choices=[('ate', '%(user)s ate %(food)s'), ('drank', '%(user)s drank %(food)s'), ('made', '%(user)s made %(food)s'), ('cooked', '%(user)s cooked %(food)s'), ('baked', '%(user)s baked %(food)s'), ('ordered', '%(user)s ordered %(food)s')], help_text='how should we best phrase this entry?', max_length=32, verbose_name='verb'),
),
model_name="post",
name="verb",
field=models.CharField(
choices=[
("ate", "%(user)s ate %(food)s"),
("drank", "%(user)s drank %(food)s"),
("made", "%(user)s made %(food)s"),
("cooked", "%(user)s cooked %(food)s"),
("baked", "%(user)s baked %(food)s"),
("ordered", "%(user)s ordered %(food)s"),
],
help_text="how should we best phrase this entry?",
max_length=32,
verbose_name="verb",
),
)
]

View file

@ -1,3 +1,4 @@
# ruff: noqa: E501
# Generated by Django 5.0.3 on 2024-03-25 17:02
import django.db.models.deletion
@ -6,18 +7,139 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('moku', '0004_alter_post_verb'),
]
dependencies = [("moku", "0004_alter_post_verb")]
operations = [
migrations.CreateModel(
name='UserSettings',
name="UserSettings",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('language', models.CharField(choices=[('af', 'Afrikaans'), ('ar', 'Arabic'), ('ar-dz', 'Algerian Arabic'), ('ast', 'Asturian'), ('az', 'Azerbaijani'), ('bg', 'Bulgarian'), ('be', 'Belarusian'), ('bn', 'Bengali'), ('br', 'Breton'), ('bs', 'Bosnian'), ('ca', 'Catalan'), ('ckb', 'Central Kurdish (Sorani)'), ('cs', 'Czech'), ('cy', 'Welsh'), ('da', 'Danish'), ('de', 'German'), ('dsb', 'Lower Sorbian'), ('el', 'Greek'), ('en', 'English'), ('en-au', 'Australian English'), ('en-gb', 'British English'), ('eo', 'Esperanto'), ('es', 'Spanish'), ('es-ar', 'Argentinian Spanish'), ('es-co', 'Colombian Spanish'), ('es-mx', 'Mexican Spanish'), ('es-ni', 'Nicaraguan Spanish'), ('es-ve', 'Venezuelan Spanish'), ('et', 'Estonian'), ('eu', 'Basque'), ('fa', 'Persian'), ('fi', 'Finnish'), ('fr', 'French'), ('fy', 'Frisian'), ('ga', 'Irish'), ('gd', 'Scottish Gaelic'), ('gl', 'Galician'), ('he', 'Hebrew'), ('hi', 'Hindi'), ('hr', 'Croatian'), ('hsb', 'Upper Sorbian'), ('hu', 'Hungarian'), ('hy', 'Armenian'), ('ia', 'Interlingua'), ('id', 'Indonesian'), ('ig', 'Igbo'), ('io', 'Ido'), ('is', 'Icelandic'), ('it', 'Italian'), ('ja', 'Japanese'), ('ka', 'Georgian'), ('kab', 'Kabyle'), ('kk', 'Kazakh'), ('km', 'Khmer'), ('kn', 'Kannada'), ('ko', 'Korean'), ('ky', 'Kyrgyz'), ('lb', 'Luxembourgish'), ('lt', 'Lithuanian'), ('lv', 'Latvian'), ('mk', 'Macedonian'), ('ml', 'Malayalam'), ('mn', 'Mongolian'), ('mr', 'Marathi'), ('ms', 'Malay'), ('my', 'Burmese'), ('nb', 'Norwegian Bokmål'), ('ne', 'Nepali'), ('nl', 'Dutch'), ('nn', 'Norwegian Nynorsk'), ('os', 'Ossetic'), ('pa', 'Punjabi'), ('pl', 'Polish'), ('pt', 'Portuguese'), ('pt-br', 'Brazilian Portuguese'), ('ro', 'Romanian'), ('ru', 'Russian'), ('sk', 'Slovak'), ('sl', 'Slovenian'), ('sq', 'Albanian'), ('sr', 'Serbian'), ('sr-latn', 'Serbian Latin'), ('sv', 'Swedish'), ('sw', 'Swahili'), ('ta', 'Tamil'), ('te', 'Telugu'), ('tg', 'Tajik'), ('th', 'Thai'), ('tk', 'Turkmen'), ('tr', 'Turkish'), ('tt', 'Tatar'), ('udm', 'Udmurt'), ('ug', 'Uyghur'), ('uk', 'Ukrainian'), ('ur', 'Urdu'), ('uz', 'Uzbek'), ('vi', 'Vietnamese'), ('zh-hans', 'Simplified Chinese'), ('zh-hant', 'Traditional Chinese')], default='en', help_text='what language do you want to use moku.blog in?', max_length=16, verbose_name='language')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='settings', to=settings.AUTH_USER_MODEL)),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"language",
models.CharField(
choices=[
("af", "Afrikaans"),
("ar", "Arabic"),
("ar-dz", "Algerian Arabic"),
("ast", "Asturian"),
("az", "Azerbaijani"),
("bg", "Bulgarian"),
("be", "Belarusian"),
("bn", "Bengali"),
("br", "Breton"),
("bs", "Bosnian"),
("ca", "Catalan"),
("ckb", "Central Kurdish (Sorani)"),
("cs", "Czech"),
("cy", "Welsh"),
("da", "Danish"),
("de", "German"),
("dsb", "Lower Sorbian"),
("el", "Greek"),
("en", "English"),
("en-au", "Australian English"),
("en-gb", "British English"),
("eo", "Esperanto"),
("es", "Spanish"),
("es-ar", "Argentinian Spanish"),
("es-co", "Colombian Spanish"),
("es-mx", "Mexican Spanish"),
("es-ni", "Nicaraguan Spanish"),
("es-ve", "Venezuelan Spanish"),
("et", "Estonian"),
("eu", "Basque"),
("fa", "Persian"),
("fi", "Finnish"),
("fr", "French"),
("fy", "Frisian"),
("ga", "Irish"),
("gd", "Scottish Gaelic"),
("gl", "Galician"),
("he", "Hebrew"),
("hi", "Hindi"),
("hr", "Croatian"),
("hsb", "Upper Sorbian"),
("hu", "Hungarian"),
("hy", "Armenian"),
("ia", "Interlingua"),
("id", "Indonesian"),
("ig", "Igbo"),
("io", "Ido"),
("is", "Icelandic"),
("it", "Italian"),
("ja", "Japanese"),
("ka", "Georgian"),
("kab", "Kabyle"),
("kk", "Kazakh"),
("km", "Khmer"),
("kn", "Kannada"),
("ko", "Korean"),
("ky", "Kyrgyz"),
("lb", "Luxembourgish"),
("lt", "Lithuanian"),
("lv", "Latvian"),
("mk", "Macedonian"),
("ml", "Malayalam"),
("mn", "Mongolian"),
("mr", "Marathi"),
("ms", "Malay"),
("my", "Burmese"),
("nb", "Norwegian Bokmål"),
("ne", "Nepali"),
("nl", "Dutch"),
("nn", "Norwegian Nynorsk"),
("os", "Ossetic"),
("pa", "Punjabi"),
("pl", "Polish"),
("pt", "Portuguese"),
("pt-br", "Brazilian Portuguese"),
("ro", "Romanian"),
("ru", "Russian"),
("sk", "Slovak"),
("sl", "Slovenian"),
("sq", "Albanian"),
("sr", "Serbian"),
("sr-latn", "Serbian Latin"),
("sv", "Swedish"),
("sw", "Swahili"),
("ta", "Tamil"),
("te", "Telugu"),
("tg", "Tajik"),
("th", "Thai"),
("tk", "Turkmen"),
("tr", "Turkish"),
("tt", "Tatar"),
("udm", "Udmurt"),
("ug", "Uyghur"),
("uk", "Ukrainian"),
("ur", "Urdu"),
("uz", "Uzbek"),
("vi", "Vietnamese"),
("zh-hans", "Simplified Chinese"),
("zh-hant", "Traditional Chinese"),
],
default="en",
help_text="what language do you want to use moku.blog in?",
max_length=16,
verbose_name="language",
),
),
(
"user",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="settings",
to=settings.AUTH_USER_MODEL,
),
),
],
),
)
]

View file

@ -1,3 +1,4 @@
# ruff: noqa: E501
# Generated by Django 5.0.3 on 2024-03-25 21:09
import django.db.models.deletion
@ -7,36 +8,114 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('moku', '0005_usersettings'),
]
dependencies = [("moku", "0005_usersettings")]
operations = [
migrations.CreateModel(
name='Recipe',
name="Recipe",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', shortuuid.django_fields.ShortUUIDField(alphabet=None, help_text='the unique id that identifies this recipe.', length=22, max_length=22, prefix='', verbose_name='unique id')),
('title', models.CharField(help_text='give the recipe a title, just so you know the recipe is for.', max_length=64, verbose_name='recipe title')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('created_by', models.ForeignKey(db_column='created_by_user_id', on_delete=django.db.models.deletion.CASCADE, related_name='recipes', to=settings.AUTH_USER_MODEL)),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"uuid",
shortuuid.django_fields.ShortUUIDField(
alphabet=None,
help_text="the unique id that identifies this recipe.",
length=22,
max_length=22,
prefix="",
verbose_name="unique id",
),
),
(
"title",
models.CharField(
help_text="give the recipe a title, just so you know the recipe is for.",
max_length=64,
verbose_name="recipe title",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"created_by",
models.ForeignKey(
db_column="created_by_user_id",
on_delete=django.db.models.deletion.CASCADE,
related_name="recipes",
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.AddField(
model_name='post',
name='recipe',
field=models.ForeignKey(blank=True, help_text='the recipe for what you ate, if you have one.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='moku.recipe', verbose_name='recipe'),
model_name="post",
name="recipe",
field=models.ForeignKey(
blank=True,
help_text="the recipe for what you ate, if you have one.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="+",
to="moku.recipe",
verbose_name="recipe",
),
),
migrations.CreateModel(
name='RecipeStep',
name="RecipeStep",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', shortuuid.django_fields.ShortUUIDField(alphabet=None, help_text='the unique id that identifies this step.', length=22, max_length=22, prefix='', verbose_name='step id')),
('instructions', models.CharField(help_text='the instructions for this step of the recipe. try to keep it clear and concise!', max_length=128, verbose_name='step instructions')),
('order', models.IntegerField(db_index=True, default=0, help_text='which step in the recipe is this. this affects the order the recipe steps are shown.', verbose_name='step number')),
('recipe', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='steps', to='moku.recipe')),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"uuid",
shortuuid.django_fields.ShortUUIDField(
alphabet=None,
help_text="the unique id that identifies this step.",
length=22,
max_length=22,
prefix="",
verbose_name="step id",
),
),
(
"instructions",
models.CharField(
help_text="the instructions for this step of the recipe. try to keep it clear and concise!",
max_length=128,
verbose_name="step instructions",
),
),
(
"order",
models.IntegerField(
db_index=True,
default=0,
help_text="which step in the recipe is this. this affects the order the recipe steps are shown.",
verbose_name="step number",
),
),
(
"recipe",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="steps",
to="moku.recipe",
),
),
],
),
]

View file

@ -2,11 +2,4 @@ from moku.models.post import Post
from moku.models.recipe import Recipe, RecipeStep
from moku.models.user import User, UserSettings
__all__ = [
"Post",
"Recipe",
"RecipeStep",
"User",
"UserSettings",
]
__all__ = ["Post", "Recipe", "RecipeStep", "User", "UserSettings"]

View file

@ -14,11 +14,17 @@ def post_image_filename(instance, _):
class PostManager(models.Manager):
def get_queryset(self):
return super().get_queryset().select_related("created_by") \
return (
super()
.get_queryset()
.select_related("created_by")
.prefetch_related(
models.Prefetch("recipe__steps", queryset=RecipeStep.objects.order_by("order"))
) \
models.Prefetch(
"recipe__steps", queryset=RecipeStep.objects.order_by("order")
)
)
.order_by("-created_at")
)
class Post(models.Model):
@ -41,9 +47,7 @@ class Post(models.Model):
help_text=_("how should we best phrase this entry?"),
)
food = models.CharField(
verbose_name=_("food"),
max_length=128,
help_text=_("what did you eat?"),
verbose_name=_("food"), max_length=128, help_text=_("what did you eat?")
)
image = models.ImageField(
verbose_name=_("image"),
@ -68,12 +72,10 @@ class Post(models.Model):
on_delete=models.CASCADE,
)
created_at = models.DateTimeField(
auto_now_add=True,
help_text=_("when this post was created."),
auto_now_add=True, help_text=_("when this post was created.")
)
updated_at = models.DateTimeField(
auto_now=True,
help_text=_("when this post was last updated."),
auto_now=True, help_text=_("when this post was last updated.")
)
objects = PostManager()
@ -84,6 +86,9 @@ class Post(models.Model):
@property
def text(self):
return self.get_verb_display() % {
"user": f"<a href=\"{self.created_by.get_absolute_url()}\">@{self.created_by.username}</a>",
"user": (
f'<a href="{self.created_by.get_absolute_url()}">'
f"@{self.created_by.username}</a>"
),
"food": escape(self.food),
}

View file

@ -1,15 +1,18 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from shortuuid.django_fields import ShortUUIDField
class RecipeManager(models.Manager):
def get_queryset(self):
return super().get_queryset() \
return (
super()
.get_queryset()
.prefetch_related(
models.Prefetch("steps", queryset=RecipeStep.objects.order_by("order"))
)
)
class Recipe(models.Model):
@ -31,12 +34,8 @@ class Recipe(models.Model):
db_column="created_by_user_id",
on_delete=models.CASCADE,
)
created_at = models.DateTimeField(
auto_now_add=True,
)
updated_at = models.DateTimeField(
auto_now=True,
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = RecipeManager()
@ -57,19 +56,22 @@ class RecipeStep(models.Model):
instructions = models.CharField(
verbose_name=_("step instructions"),
max_length=128,
help_text=_("the instructions for this step of the recipe. try to keep it clear and concise!"),
help_text=_(
"the instructions for this step of the recipe. try to keep it clear and "
"concise!"
),
)
order = models.IntegerField(
verbose_name=_("step number"),
default=0,
db_index=True,
help_text=_("which step in the recipe is this. this affects the order the recipe steps are shown."),
help_text=_(
"which step in the recipe is this. this affects the order the recipe steps "
"are shown."
),
)
recipe = models.ForeignKey(
"Recipe",
related_name="steps",
db_index=True,
on_delete=models.CASCADE,
"Recipe", related_name="steps", db_index=True, on_delete=models.CASCADE
)
def __str__(self):

View file

@ -1,10 +1,10 @@
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from moku.validators import validate_username_regex, validate_username_length
from moku.validators import validate_username_length, validate_username_regex
def user_avatar_filename(instance, _):
@ -21,7 +21,7 @@ class User(AbstractUser):
help_text=_(
"this is the unique identifier you'll use to log in. it may only contain "
"letters, numbers, hyphens, dashes and dots."
)
),
)
email = models.EmailField(
verbose_name=_("email address"),
@ -30,12 +30,9 @@ class User(AbstractUser):
help_text=_(
"this should be your email address. make sure it's valid and that you have "
"access to it."
)
)
email_confirmed_at = models.DateTimeField(
blank=True,
null=True,
),
)
email_confirmed_at = models.DateTimeField(blank=True, null=True)
pronouns = models.CharField(
verbose_name=_("pronouns"),
max_length=64,
@ -82,9 +79,7 @@ class User(AbstractUser):
class UserSettings(models.Model):
user = models.OneToOneField(
"User",
related_name="settings",
on_delete=models.CASCADE,
"User", related_name="settings", on_delete=models.CASCADE
)
language = models.CharField(
verbose_name=_("language"),

View file

@ -1,8 +1,9 @@
from django.contrib import messages
from django.contrib.auth.views import LoginView as BaseLoginView, LogoutView as BaseLogoutView
from django.contrib.auth.views import LoginView as BaseLoginView
from django.contrib.auth.views import LogoutView as BaseLogoutView
from django.shortcuts import redirect
from django.utils.translation import gettext as _
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from moku.views.base import View
@ -17,7 +18,11 @@ class LoginView(View, BaseLoginView):
def get_success_url(self):
if self.request.user.is_authenticated:
messages.success(self.request, _("welcome back, %(username)s!") % {"username": self.request.user.username})
messages.success(
self.request,
_("welcome back, %(username)s!")
% {"username": self.request.user.username},
)
return self.request.GET.get("next", reverse_lazy("feed"))

View file

@ -7,7 +7,9 @@ class View(generic.TemplateView):
def get_context_data(self, **kwargs):
return {
**super().get_context_data(**kwargs),
"header_emoji": random.choice(["🍔", "🍕", "🍟", "🥪", "🥘", "🍰", "🍻", "🧁", "🍞", "🥯", "🥐"])
"header_emoji": random.choice(
["🍔", "🍕", "🍟", "🥪", "🥘", "🍰", "🍻", "🧁", "🍞", "🥯", "🥐"]
),
}

View file

@ -23,8 +23,13 @@ class FeedView(FormView):
def form_valid(self, form):
if not self.request.user.is_authenticated:
raise PermissionDenied
if form.instance.recipe and form.instance.recipe.created_by.id != self.request.user.id:
messages.error(self.request, _("you can't add someone else's recipe to your post!"))
if (
form.instance.recipe
and form.instance.recipe.created_by.id != self.request.user.id
):
messages.error(
self.request, _("you can't add someone else's recipe to your post!")
)
return redirect("feed")
form.instance.created_by = self.request.user
if "image" in form.changed_data and form.instance.image is not None:
@ -36,7 +41,7 @@ class FeedView(FormView):
def get_context_data(self, **kwargs):
context = {
**super().get_context_data(**kwargs),
"posts": Post.objects.all()[:128]
"posts": Post.objects.all()[:128],
}
if self.request.user.is_authenticated:
return self.get_authenticated_context_data(context)
@ -47,39 +52,54 @@ class FeedView(FormView):
**context,
"emoji": EMOJI_CATEGORIES,
"verbs": (
(verb[0], verb[1] % {"user": f"@{self.request.user.username}", "food": "..."})
(
verb[0],
verb[1] % {"user": f"@{self.request.user.username}", "food": "..."},
)
for verb in Verbs.CHOICES
)
),
}
def get_form(self, form_class=None):
form = super().get_form(form_class)
if self.request.user.is_authenticated:
form.fields["recipe"].queryset = Recipe.objects.filter(created_by=self.request.user)
form.fields["recipe"].queryset = Recipe.objects.filter(
created_by=self.request.user
)
return form
class LatestPostJSONView(BaseView):
def get(self, request, *args, **kwargs):
post = Post.objects.prefetch_related("recipe__steps") \
.filter(created_by__username=kwargs.get("username")) \
.order_by("-created_at").first()
post = (
Post.objects.prefetch_related("recipe__steps")
.filter(created_by__username=kwargs.get("username"))
.order_by("-created_at")
.first()
)
if not post:
return HttpResponse(json.dumps({"post": None}), content_type="application/json")
return HttpResponse(
json.dumps({"post": None}), content_type="application/json"
)
post_data = {
"date": str(post.created_at),
"text": post.get_verb_display() % {"user": f"@{post.created_by.username}", "food": post.food},
"text": post.get_verb_display()
% {"user": f"@{post.created_by.username}", "food": post.food},
"food": post.food,
"verb": {
"id": post.verb,
"pattern": post.get_verb_display() % {"user": "$1", "food": "$2"},
},
"image": f"{settings.SITE_ROOT_URL}{post.image.url}" if post.image else None,
"image": f"{settings.SITE_ROOT_URL}{post.image.url}"
if post.image
else None,
"user": {
"username": post.created_by.username,
"url": f"{settings.SITE_ROOT_URL}{post.created_by.get_absolute_url()}",
},
}
if post.recipe:
post_data["recipe"] = [step.instructions for step in post.recipe.steps.all()]
post_data["recipe"] = [
step.instructions for step in post.recipe.steps.all()
]
return HttpResponse(json.dumps(post_data), content_type="application/json")

View file

@ -17,10 +17,7 @@ class DeleteRecipeView(LoginRequiredMixin, UserPassesTestMixin, View):
@cached_property
def recipe(self):
return get_object_or_404(
Recipe,
uuid=self.kwargs.get("uuid"),
)
return get_object_or_404(Recipe, uuid=self.kwargs.get("uuid"))
def test_func(self):
return self.request.user.id == self.recipe.created_by.id
@ -77,7 +74,9 @@ class IndexRecipeView(LoginRequiredMixin, View):
def get_context_data(self, **kwargs):
return {
**super().get_context_data(**kwargs),
"recipes": Recipe.objects.filter(created_by=self.request.user).order_by("-created_by"),
"recipes": Recipe.objects.filter(created_by=self.request.user).order_by(
"-created_by"
),
}
@ -88,7 +87,10 @@ class NewRecipeView(LoginRequiredMixin, FormView):
def form_valid(self, form):
form.instance.created_by = self.request.user
form.save()
messages.success(self.request, _("recipe made successfully! now you can start adding the steps."))
messages.success(
self.request,
_("recipe made successfully! now you can start adding the steps."),
)
return redirect(form.instance.get_absolute_url())
@ -101,12 +103,17 @@ class ShowRecipeView(FormView):
return Recipe.objects.get(uuid=self.kwargs.get("uuid"))
def form_valid(self, form):
if not self.request.user.is_authenticated or self.request.user.id != self.recipe.created_by.id:
if (
not self.request.user.is_authenticated
or self.request.user.id != self.recipe.created_by.id
):
messages.error(self.request, _("that's not yours!"))
return redirect(form.instance.get_absolute_url())
order = self.recipe.steps.count()
if order >= 16:
messages.error(self.request, _("sorry! you can't add any more steps to this recipe."))
messages.error(
self.request, _("sorry! you can't add any more steps to this recipe.")
)
return redirect(self.recipe.get_absolute_url())
form.instance.recipe = self.recipe
form.instance.order = order
@ -115,7 +122,4 @@ class ShowRecipeView(FormView):
return redirect(self.recipe.get_absolute_url())
def get_context_data(self, **kwargs):
return {
**super().get_context_data(**kwargs),
"recipe": self.recipe,
}
return {**super().get_context_data(**kwargs), "recipe": self.recipe}

View file

@ -4,8 +4,8 @@ from django.db import IntegrityError
from django.shortcuts import redirect
from django.utils.translation import gettext as _
from moku.images import process_avatar_image
from moku.forms.user import ProfileForm, UserForm, UserSettingsForm
from moku.images import process_avatar_image
from moku.models.user import User
from moku.views.base import FormView, View
@ -34,14 +34,18 @@ class EditSettingsView(LoginRequiredMixin, FormView):
try:
form.save()
except IntegrityError:
messages.error(self.request, _("uh oh. i think something went a little bit oopsie."))
messages.error(
self.request, _("uh oh. i think something went a little bit oopsie.")
)
return self.form_invalid(form)
messages.success(self.request, _("settings updated!"))
return redirect("settings")
def get_form(self):
if hasattr(self.request.user, "settings"):
return self.form_class(instance=self.request.user.settings, **self.get_form_kwargs())
return self.form_class(
instance=self.request.user.settings, **self.get_form_kwargs()
)
return super().get_form()
@ -66,9 +70,13 @@ class SignupView(FormView):
try:
form.save()
except IntegrityError:
messages.error(self.request, _("sorry! someone else got to that username first."))
messages.error(
self.request, _("sorry! someone else got to that username first.")
)
return self.form_invalid(form)
messages.success(self.request, _("that's it! just log in, and you're ready to go."))
messages.success(
self.request, _("that's it! just log in, and you're ready to go.")
)
return redirect("login")
def get(self, request, *args, **kwargs):