Skip to content

chore: LEAP-1090: django 4.2 upgrade (WIP) #5896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
10dd8d5
chore: LEAP-1090: django 4.2 upgrade
jombooth May 17, 2024
d37b246
Revert "chore: LEAP-1090: django 4.2 upgrade"
jombooth May 17, 2024
7764798
trigger followmerge...
jombooth May 17, 2024
9b86967
Revert "trigger followmerge..."
jombooth May 17, 2024
4843b39
Revert "Revert "chore: LEAP-1090: django 4.2 upgrade""
jombooth May 17, 2024
59fd93a
fix custom save methods for compatibility with new-style update_fields
jombooth May 18, 2024
d74f89e
add no verification email backend for django 3 compatibility
jombooth May 18, 2024
d6e0b20
bump DRF
jombooth May 18, 2024
556b3f3
checkpoint
jombooth May 30, 2024
f785d31
Merge branch 'develop' into 'fb-LEAP-1090/django-4.2'
triklozoid Aug 5, 2024
d5e99d8
Update lock file
triklozoid Aug 5, 2024
406a26d
Fix changed APPEND_SLASH behavior
triklozoid Aug 7, 2024
4e7250b
Fix removed middleware
triklozoid Aug 7, 2024
f23a06f
Merge branch 'develop' into 'fb-LEAP-1090/django-4.2'
triklozoid Aug 7, 2024
234e58f
Add migrations
triklozoid Aug 7, 2024
a23159a
trigger ci
triklozoid Aug 7, 2024
b8e9bd4
Upgrade rules library
triklozoid Aug 8, 2024
687fcd0
Fix double swagger decorator
triklozoid Aug 9, 2024
b277a06
Merge branch 'develop' into 'fb-LEAP-1090/django-4.2'
triklozoid Aug 12, 2024
87fc18e
Fix postgres version
triklozoid Aug 12, 2024
b84b466
Merge branch 'develop' into 'fb-LEAP-1090/django-4.2'
triklozoid Aug 13, 2024
7044110
Update lock file
triklozoid Aug 13, 2024
f6599b9
Fix migrations
triklozoid Aug 13, 2024
22a3907
Merge branch 'develop' into 'fb-LEAP-1090/django-4.2'
triklozoid Aug 13, 2024
1703933
Empty commit to test relock success
jombooth Aug 13, 2024
0182903
Fix migrations
triklozoid Aug 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ services:
command: label-studio-uwsgi

db:
image: postgres:11.5
image: pgautoupgrade/pgautoupgrade:13-alpine
hostname: db
restart: unless-stopped
# Optional: Enable TLS on PostgreSQL
Expand All @@ -62,4 +62,3 @@ services:
volumes:
- ${POSTGRES_DATA_DIR:-./postgres-data}:/var/lib/postgresql/data
- ./deploy/pgsql/certs:/var/lib/postgresql/certs:ro

15 changes: 8 additions & 7 deletions docs/source/guide/email_setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ tier: enterprise
type: guide
order: 0
order_enterprise: 90
meta_title: Email backends in Label Studio
meta_title: Email backends in Label Studio
section: "Install & Setup"

---

In Label Studio Enterprise, you can configure email backends to enable password reset via email and receive notifications. There are three available options for setting up email backends:
* Dummy console email backend - all emails will be printed in the app console.
* SMTP backend
* [Sendgrid backend](https://sendgrid.com/)
* [Sendgrid backend](https://sendgrid.com/)


### Dummy Console Backend
### Dummy Console Backend

The Dummy Console Email Backend is a simple option for testing purposes. When this backend is configured, all emails generated by the application will be printed in the application console, rather than being sent to recipients.

Expand All @@ -31,10 +31,11 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

The SMTP (Simple Mail Transfer Protocol) Backend allows you to use a custom SMTP server to send emails. This provides flexibility in configuring email delivery options and can integrate with various email service providers.

More details can be found here: https://docs.djangoproject.com/en/3.2/topics/email/#smtp-backend
More details can be found here: https://docs.djangoproject.com/en/4.2/topics/email/#smtp-backend. If your SMTP configuration is not compatible with certificate or hostname validation, we provide the
`label_studio.core.utils.mail.NoVerificationEmailBackend` which can be used instead of Django's default `smtp.EmailBackend`, but note that this option is less secure. See https://docs.djangoproject.com/en/5.0/releases/4.2/#miscellaneous for more information.

```bash
# SMTP server,
# SMTP server,
FROM_EMAIL=Label Studio <hello@labelstud.io>
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=smtp.gmail.com
Expand All @@ -52,8 +53,8 @@ EMAIL_TIMEOUT=60

The Sendgrid Backend utilizes the Sendgrid API to send emails. This option requires an active Sendgrid account and API key for authentication. The Sendgrid backend offers an easy-to-use and reliable email delivery service.

```bash
```bash
# option 2: Sendgrid
EMAIL_BACKEND=sendgrid_backend.SendgridBackend
SENDGRID_API_KEY=<SENDGRID-API-KEY>
```
```
8 changes: 8 additions & 0 deletions label_studio/core/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ def process_response(self, request, response):

return response

def should_redirect_with_slash(self, request):
"""
Override the original method to keep global APPEND_SLASH setting false
"""
if not request.path_info.endswith('/'):
return True
return False


class SetSessionUIDMiddleware(CommonMiddleware):
def process_request(self, request):
Expand Down
2 changes: 1 addition & 1 deletion label_studio/core/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'core.middleware.CommonMiddlewareAppendSlashWithoutRedirect', # instead of 'CommonMiddleware'
'core.middleware.CommonMiddleware',
'django_user_agents.middleware.UserAgentMiddleware',
'core.middleware.SetSessionUIDMiddleware',
'core.middleware.ContextLogMiddleware',
Expand Down Expand Up @@ -762,3 +761,4 @@ def collect_versions_dummy(**kwargs):
}

OPENAI_API_VERSION = get_env('OPENAI_API_VERSION', '2024-06-01')
APPEND_SLASH = False
24 changes: 24 additions & 0 deletions label_studio/core/utils/mail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import ssl

Check warning on line 1 in label_studio/core/utils/mail.py

View check run for this annotation

Codecov / codecov/patch

label_studio/core/utils/mail.py#L1

Added line #L1 was not covered by tests

from django.core.mail.backends.smtp import EmailBackend
from django.utils.functional import cached_property

Check warning on line 4 in label_studio/core/utils/mail.py

View check run for this annotation

Codecov / codecov/patch

label_studio/core/utils/mail.py#L3-L4

Added lines #L3 - L4 were not covered by tests


class NoVerificationEmailBackend(EmailBackend):

Check warning on line 7 in label_studio/core/utils/mail.py

View check run for this annotation

Codecov / codecov/patch

label_studio/core/utils/mail.py#L7

Added line #L7 was not covered by tests
"""SMTP email backend that does not verify SSL certificates or hostname
if no certfile or keyfile is provided. This is equivalent to the behavior
of Django's smtp.EmailBackend prior to Django 4. If EmailBackend
works for you, prefer that as it's more secure than this.
"""

@cached_property
def ssl_context(self):
if self.ssl_certfile or self.ssl_keyfile:
ssl_context = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_CLIENT)
ssl_context.load_cert_chain(self.ssl_certfile, self.ssl_keyfile)
return ssl_context

Check warning on line 19 in label_studio/core/utils/mail.py

View check run for this annotation

Codecov / codecov/patch

label_studio/core/utils/mail.py#L14-L19

Added lines #L14 - L19 were not covered by tests
else:
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
return ssl_context

Check warning on line 24 in label_studio/core/utils/mail.py

View check run for this annotation

Codecov / codecov/patch

label_studio/core/utils/mail.py#L21-L24

Added lines #L21 - L24 were not covered by tests
2 changes: 1 addition & 1 deletion label_studio/core/utils/static_serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
raise Http404(_('“%(path)s” does not exist') % {'path': fullpath})
# Respect the If-Modified-Since header.
statobj = fullpath.stat()
if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), statobj.st_mtime, statobj.st_size):
if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), statobj.st_mtime):

Check warning on line 44 in label_studio/core/utils/static_serve.py

View check run for this annotation

Codecov / codecov/patch

label_studio/core/utils/static_serve.py#L44

Added line #L44 was not covered by tests
return HttpResponseNotModified()
content_type, encoding = mimetypes.guess_type(str(fullpath))
content_type = content_type or 'application/octet-stream'
Expand Down
2 changes: 1 addition & 1 deletion label_studio/data_manager/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from data_manager.prepare_params import ConjunctionEnum
from django.conf import settings
from django.contrib.postgres.aggregates import ArrayAgg
from django.contrib.postgres.fields.jsonb import KeyTextTransform
from django.db import models
from django.db.models import (
Aggregate,
Expand All @@ -28,6 +27,7 @@
Value,
When,
)
from django.db.models.fields.json import KeyTextTransform
from django.db.models.functions import Cast, Coalesce, Concat
from pydantic import BaseModel

Expand Down
27 changes: 27 additions & 0 deletions label_studio/data_manager/migrations/0012_alter_view_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.2.13 on 2024-08-07 11:24

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("data_manager", "0011_auto_20240718_1355"),
]

operations = [
migrations.AlterField(
model_name="view",
name="user",
field=models.ForeignKey(
help_text="User who made this view",
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)ss",
to=settings.AUTH_USER_MODEL,
),
)
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# Generated by Django 4.2.13 on 2024-08-07 11:24

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("projects", "0026_auto_20231103_0020"),
("tasks", "0047_merge_20240318_2210"),
("io_storages", "0016_add_aws_sse_kms_key"),
]

operations = [
migrations.AlterField(
model_name="azureblobexportstorage",
name="project",
field=models.ForeignKey(
help_text="A unique integer value identifying this project.",
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)ss",
to="projects.project",
),
),
migrations.AlterField(
model_name="azureblobexportstoragelink",
name="annotation",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to="tasks.annotation",
),
),
migrations.AlterField(
model_name="azureblobimportstorage",
name="project",
field=models.ForeignKey(
help_text="A unique integer value identifying this project.",
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)ss",
to="projects.project",
),
),
migrations.AlterField(
model_name="azureblobimportstoragelink",
name="task",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to="tasks.task",
),
),
migrations.AlterField(
model_name="gcsexportstorage",
name="project",
field=models.ForeignKey(
help_text="A unique integer value identifying this project.",
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)ss",
to="projects.project",
),
),
migrations.AlterField(
model_name="gcsexportstoragelink",
name="annotation",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to="tasks.annotation",
),
),
migrations.AlterField(
model_name="gcsimportstorage",
name="project",
field=models.ForeignKey(
help_text="A unique integer value identifying this project.",
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)ss",
to="projects.project",
),
),
migrations.AlterField(
model_name="gcsimportstoragelink",
name="task",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to="tasks.task",
),
),
migrations.AlterField(
model_name="localfilesexportstorage",
name="project",
field=models.ForeignKey(
help_text="A unique integer value identifying this project.",
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)ss",
to="projects.project",
),
),
migrations.AlterField(
model_name="localfilesexportstoragelink",
name="annotation",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to="tasks.annotation",
),
),
migrations.AlterField(
model_name="localfilesimportstorage",
name="project",
field=models.ForeignKey(
help_text="A unique integer value identifying this project.",
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)ss",
to="projects.project",
),
),
migrations.AlterField(
model_name="localfilesimportstoragelink",
name="task",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to="tasks.task",
),
),
migrations.AlterField(
model_name="redisexportstorage",
name="project",
field=models.ForeignKey(
help_text="A unique integer value identifying this project.",
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)ss",
to="projects.project",
),
),
migrations.AlterField(
model_name="redisexportstoragelink",
name="annotation",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to="tasks.annotation",
),
),
migrations.AlterField(
model_name="redisimportstorage",
name="project",
field=models.ForeignKey(
help_text="A unique integer value identifying this project.",
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)ss",
to="projects.project",
),
),
migrations.AlterField(
model_name="redisimportstoragelink",
name="task",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to="tasks.task",
),
),
migrations.AlterField(
model_name="s3exportstorage",
name="project",
field=models.ForeignKey(
help_text="A unique integer value identifying this project.",
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)ss",
to="projects.project",
),
),
migrations.AlterField(
model_name="s3exportstoragelink",
name="annotation",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to="tasks.annotation",
),
),
migrations.AlterField(
model_name="s3importstorage",
name="project",
field=models.ForeignKey(
help_text="A unique integer value identifying this project.",
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)ss",
to="projects.project",
),
),
migrations.AlterField(
model_name="s3importstoragelink",
name="task",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to="tasks.task",
),
),
]
Loading
Loading