-
Notifications
You must be signed in to change notification settings - Fork 2
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
workspace handles #554
base: master
Are you sure you want to change the base?
workspace handles #554
Changes from all commits
db866ca
d31ad04
76188b5
e111ce8
d093d64
52297b1
2ba54ca
fc2ca3e
a948a66
d5fe36c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Generated by Django 5.1.3 on 2025-01-02 14:43 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('app_users', '0023_alter_appusertransaction_workspace'), | ||
('bots', '0090_remove_publishedrun_bots_publis_workflo_a0953a_idx_and_more'), | ||
('workspaces', '0007_workspace_handle'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddIndex( | ||
model_name='publishedrun', | ||
index=models.Index(fields=['visibility', 'workspace', '-updated_at'], name='bots_publis_visibil_cf3dd8_idx'), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
from __future__ import annotations | ||
|
||
import re | ||
import typing | ||
|
||
from django.core.exceptions import ValidationError | ||
from django.core.validators import MaxLengthValidator, RegexValidator | ||
|
@@ -11,6 +12,11 @@ | |
from bots.custom_fields import CustomURLField | ||
from daras_ai_v2 import settings | ||
|
||
if typing.TYPE_CHECKING: | ||
from app_users.models import AppUser | ||
from workspaces.models import Workspace | ||
|
||
|
||
HANDLE_ALLOWED_CHARS = r"[A-Za-z0-9_\.-]+" | ||
HANDLE_REGEX = rf"^{HANDLE_ALLOWED_CHARS}$" | ||
HANDLE_MAX_LENGTH = 40 | ||
|
@@ -122,6 +128,7 @@ def clean(self): | |
lookups = [ | ||
self.has_redirect, | ||
self.has_user, | ||
self.has_workspace, | ||
] | ||
if sum(lookups) > 1: | ||
raise ValidationError("A handle must be exclusive") | ||
|
@@ -141,13 +148,29 @@ def has_user(self): | |
else: | ||
return True | ||
|
||
@property | ||
def has_workspace(self): | ||
try: | ||
self.workspace | ||
except Handle.workspace.RelatedObjectDoesNotExist: | ||
return False | ||
else: | ||
return True | ||
|
||
@property | ||
def has_redirect(self): | ||
return bool(self.redirect_url) | ||
|
||
@classmethod | ||
def create_default_for_user(cls, user: "AppUser"): | ||
for handle_name in _generate_handle_options(user): | ||
for handle_name in _generate_handle_options_for_user(user): | ||
if handle := _attempt_create_handle(handle_name): | ||
return handle | ||
return None | ||
|
||
@classmethod | ||
def create_default_for_workspace(cls, workspace: "Workspace"): | ||
for handle_name in _generate_handle_options_for_workspace(workspace): | ||
if handle := _attempt_create_handle(handle_name): | ||
return handle | ||
return None | ||
|
@@ -170,7 +193,7 @@ def _make_handle_from(name): | |
return name | ||
|
||
|
||
def _generate_handle_options(user): | ||
def _generate_handle_options_for_user(user: "AppUser") -> typing.Iterator[str]: | ||
if user.is_anonymous or not user.email: | ||
return | ||
|
||
|
@@ -212,7 +235,20 @@ def _generate_handle_options(user): | |
yield f"{email_handle[:HANDLE_MAX_LENGTH-1]}{i}" | ||
|
||
|
||
def _attempt_create_handle(handle_name): | ||
def _generate_handle_options_for_workspace( | ||
workspace: "Workspace", | ||
) -> typing.Iterator[str]: | ||
if workspace.is_personal: | ||
return None | ||
Comment on lines
+241
to
+242
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: returning None from a generator is incorrect - should use 'return' with no value or just 'return []' |
||
|
||
handle_name = _make_handle_from(workspace.display_name()) | ||
yield handle_name[:HANDLE_MAX_LENGTH] | ||
|
||
for i in range(1, 10): | ||
yield f"{handle_name[:HANDLE_MAX_LENGTH-1]}{i}" | ||
|
||
|
||
def _attempt_create_handle(handle_name: str): | ||
from handles.models import Handle | ||
|
||
handle = Handle(name=handle_name) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,7 @@ class WorkspaceAdmin(SafeDeleteAdmin): | |
fields = [ | ||
"name", | ||
"domain_name", | ||
"handle", | ||
"created_by", | ||
"is_personal", | ||
("is_paying", "stripe_customer_id"), | ||
|
@@ -72,7 +73,7 @@ class WorkspaceAdmin(SafeDeleteAdmin): | |
("created_at", "updated_at"), | ||
"open_in_stripe", | ||
] | ||
search_fields = ["name", "created_by__display_name", "domain_name"] | ||
search_fields = ["name", "created_by__display_name", "domain_name", "handle__name"] | ||
readonly_fields = [ | ||
"is_personal", | ||
"created_at", | ||
|
@@ -84,7 +85,7 @@ class WorkspaceAdmin(SafeDeleteAdmin): | |
] | ||
inlines = [WorkspaceMembershipInline, WorkspaceInviteInline] | ||
ordering = ["-created_at"] | ||
autocomplete_fields = ["created_by", "subscription"] | ||
autocomplete_fields = ["created_by", "handle", "subscription"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: handle added to autocomplete_fields but Handle model needs search_fields defined in its admin for autocomplete to work |
||
|
||
@admin.display(description="Name") | ||
def display_name(self, workspace: models.Workspace): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Generated by Django 5.1.3 on 2024-12-04 12:11 | ||
|
||
import django.db.models.deletion | ||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('handles', '0001_initial'), | ||
('workspaces', '0006_workspace_description'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='workspace', | ||
name='handle', | ||
field=models.OneToOneField(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workspace', to='handles.handle'), | ||
), | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could be a method on Handle?