We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Rename and reorder custom profile field types
<!-- Issue description -->

In the custom profile field type dropdown, we should:

1. Make the following naming changes:
   * Short text -> Text (short)
   * Long text -> Text (long)
   * Date picker -> Date
   * Person picker -> Users
2. Order the fields alphabetically.

Current menu:
![Screenshot 2024-01-09 at 3 12 38 PM](https://github.com/zulip/zulip/assets/2090066/e378f417-bd7c-49a3-abf0-833e1861fc5c)


<!-- Link to a message in the chat.zulip.org discussion. Message links will still work even if the topic is renamed or resolved. Link back to this issue from the chat.zulip.org thread. -->

[CZO thread](https://chat.zulip.org/#narrow/stream/137-feedback/topic/Improvements.20for.20Custom.20profile.20fields/near/1712583)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `zerver/models/custom_profile_fields.py`
Content:
```
1 from typing import Any, Callable, Dict, List, Tuple
2 
3 import orjson
4 from django.core.exceptions import ValidationError
5 from django.db import models
6 from django.db.models import CASCADE, QuerySet
7 from django.utils.translation import gettext as _
8 from django.utils.translation import gettext_lazy
9 from django_stubs_ext import StrPromise
10 from typing_extensions import override
11 
12 from zerver.lib.types import (
13     ExtendedFieldElement,
14     ExtendedValidator,
15     FieldElement,
16     ProfileDataElementBase,
17     ProfileDataElementValue,
18     RealmUserValidator,
19     UserFieldElement,
20     Validator,
21 )
22 from zerver.lib.validator import (
23     check_date,
24     check_int,
25     check_list,
26     check_long_string,
27     check_short_string,
28     check_url,
29     validate_select_field,
30 )
31 from zerver.models.realms import Realm
32 from zerver.models.users import UserProfile, get_user_profile_by_id_in_realm
33 
34 
35 def check_valid_user_ids(realm_id: int, val: object, allow_deactivated: bool = False) -> List[int]:
36     user_ids = check_list(check_int)("User IDs", val)
37     realm = Realm.objects.get(id=realm_id)
38     for user_id in user_ids:
39         # TODO: Structurally, we should be doing a bulk fetch query to
40         # get the users here, not doing these in a loop.  But because
41         # this is a rarely used feature and likely to never have more
42         # than a handful of users, it's probably mostly OK.
43         try:
44             user_profile = get_user_profile_by_id_in_realm(user_id, realm)
45         except UserProfile.DoesNotExist:
46             raise ValidationError(_("Invalid user ID: {user_id}").format(user_id=user_id))
47 
48         if not allow_deactivated and not user_profile.is_active:
49             raise ValidationError(
50                 _("User with ID {user_id} is deactivated").format(user_id=user_id)
51             )
52 
53         if user_profile.is_bot:
54             raise ValidationError(_("User with ID {user_id} is a bot").format(user_id=user_id))
55 
56     return user_ids
57 
58 
59 class CustomProfileField(models.Model):
60     """Defines a form field for the per-realm custom profile fields feature.
61 
62     See CustomProfileFieldValue for an individual user's values for one of
63     these fields.
64     """
65 
66     HINT_MAX_LENGTH = 80
67     NAME_MAX_LENGTH = 40
68     MAX_DISPLAY_IN_PROFILE_SUMMARY_FIELDS = 2
69 
70     realm = models.ForeignKey(Realm, on_delete=CASCADE)
71     name = models.CharField(max_length=NAME_MAX_LENGTH)
72     hint = models.CharField(max_length=HINT_MAX_LENGTH, default="")
73 
74     # Sort order for display of custom profile fields.
75     order = models.IntegerField(default=0)
76 
77     # Whether the field should be displayed in smaller summary
78     # sections of a page displaying custom profile fields.
79     display_in_profile_summary = models.BooleanField(default=False)
80 
81     SHORT_TEXT = 1
82     LONG_TEXT = 2
83     SELECT = 3
84     DATE = 4
85     URL = 5
86     USER = 6
87     EXTERNAL_ACCOUNT = 7
88     PRONOUNS = 8
89 
90     # These are the fields whose validators require more than var_name
91     # and value argument. i.e. SELECT require field_data, USER require
92     # realm as argument.
93     SELECT_FIELD_TYPE_DATA: List[ExtendedFieldElement] = [
94         (SELECT, gettext_lazy("List of options"), validate_select_field, str, "SELECT"),
95     ]
96     USER_FIELD_TYPE_DATA: List[UserFieldElement] = [
97         (USER, gettext_lazy("Person picker"), check_valid_user_ids, orjson.loads, "USER"),
98     ]
99 
100     SELECT_FIELD_VALIDATORS: Dict[int, ExtendedValidator] = {
101         item[0]: item[2] for item in SELECT_FIELD_TYPE_DATA
102     }
103     USER_FIELD_VALIDATORS: Dict[int, RealmUserValidator] = {
104         item[0]: item[2] for item in USER_FIELD_TYPE_DATA
105     }
106 
107     FIELD_TYPE_DATA: List[FieldElement] = [
108         # Type, display name, validator, converter, keyword
109         (SHORT_TEXT, gettext_lazy("Short text"), check_short_string, str, "SHORT_TEXT"),
110         (LONG_TEXT, gettext_lazy("Long text"), check_long_string, str, "LONG_TEXT"),
111         (DATE, gettext_lazy("Date picker"), check_date, str, "DATE"),
112         (URL, gettext_lazy("Link"), check_url, str, "URL"),
113         (
114             EXTERNAL_ACCOUNT,
115             gettext_lazy("External account"),
116             check_short_string,
117             str,
118             "EXTERNAL_ACCOUNT",
119         ),
120         (PRONOUNS, gettext_lazy("Pronouns"), check_short_string, str, "PRONOUNS"),
121     ]
122 
123     ALL_FIELD_TYPES = [*FIELD_TYPE_DATA, *SELECT_FIELD_TYPE_DATA, *USER_FIELD_TYPE_DATA]
124 
125     FIELD_VALIDATORS: Dict[int, Validator[ProfileDataElementValue]] = {
126         item[0]: item[2] for item in FIELD_TYPE_DATA
127     }
128     FIELD_CONVERTERS: Dict[int, Callable[[Any], Any]] = {
129         item[0]: item[3] for item in ALL_FIELD_TYPES
130     }
131     FIELD_TYPE_CHOICES: List[Tuple[int, StrPromise]] = [
132         (item[0], item[1]) for item in ALL_FIELD_TYPES
133     ]
134 
135     field_type = models.PositiveSmallIntegerField(
136         choices=FIELD_TYPE_CHOICES,
137         default=SHORT_TEXT,
138     )
139 
140     # A JSON blob of any additional data needed to define the field beyond
141     # type/name/hint.
142     #
143     # The format depends on the type.  Field types SHORT_TEXT, LONG_TEXT,
144     # DATE, URL, and USER leave this empty.  Fields of type SELECT store the
145     # choices' descriptions.
146     #
147     # Note: There is no performance overhead of using TextField in PostgreSQL.
148     # See https://www.postgresql.org/docs/9.0/static/datatype-character.html
149     field_data = models.TextField(default="")
150 
151     class Meta:
152         unique_together = ("realm", "name")
153 
154     @override
155     def __str__(self) -> str:
156         return f"{self.realm!r} {self.name} {self.field_type} {self.order}"
157 
158     def as_dict(self) -> ProfileDataElementBase:
159         data_as_dict: ProfileDataElementBase = {
160             "id": self.id,
161             "name": self.name,
162             "type": self.field_type,
163             "hint": self.hint,
164             "field_data": self.field_data,
165             "order": self.order,
166         }
167         if self.display_in_profile_summary:
168             data_as_dict["display_in_profile_summary"] = True
169 
170         return data_as_dict
171 
172     def is_renderable(self) -> bool:
173         if self.field_type in [CustomProfileField.SHORT_TEXT, CustomProfileField.LONG_TEXT]:
174             return True
175         return False
176 
177 
178 def custom_profile_fields_for_realm(realm_id: int) -> QuerySet[CustomProfileField]:
179     return CustomProfileField.objects.filter(realm=realm_id).order_by("order")
180 
181 
182 class CustomProfileFieldValue(models.Model):
183     user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE)
184     field = models.ForeignKey(CustomProfileField, on_delete=CASCADE)
185     value = models.TextField()
186     rendered_value = models.TextField(null=True, default=None)
187 
188     class Meta:
189         unique_together = ("user_profile", "field")
190 
191     @override
192     def __str__(self) -> str:
193         return f"{self.user_profile!r} {self.field!r} {self.value}"
194
```

Path: `zerver/migrations/0417_alter_customprofilefield_field_type.py`
Content:
```
1 # Generated by Django 4.0.7 on 2022-10-04 13:59
2 
3 from django.db import migrations, models
4 
5 
6 class Migration(migrations.Migration):
7     dependencies = [
8         ("zerver", "0416_set_default_emoji_style"),
9     ]
10 
11     operations = [
12         migrations.AlterField(
13             model_name="customprofilefield",
14             name="field_type",
15             field=models.PositiveSmallIntegerField(
16                 choices=[
17                     (1, "Short text"),
18                     (2, "Long text"),
19                     (4, "Date picker"),
20                     (5, "Link"),
21                     (7, "External account"),
22                     (8, "Pronouns"),
23                     (3, "List of options"),
24                     (6, "Person picker"),
25                 ],
26                 default=1,
27             ),
28         ),
29     ]
30
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/zerver/migrations/0417_alter_customprofilefield_field_type.py b/zerver/migrations/0417_alter_customprofilefield_field_type.py
--- a/zerver/migrations/0417_alter_customprofilefield_field_type.py
+++ b/zerver/migrations/0417_alter_customprofilefield_field_type.py
@@ -14,14 +14,14 @@
             name="field_type",
             field=models.PositiveSmallIntegerField(
                 choices=[
-                    (1, "Short text"),
-                    (2, "Long text"),
-                    (4, "Date picker"),
-                    (5, "Link"),
+                    (4, "Date"),
                     (7, "External account"),
-                    (8, "Pronouns"),
+                    (5, "Link"),
                     (3, "List of options"),
-                    (6, "Person picker"),
+                    (8, "Pronouns"),
+                    (2, "Text (long)"),
+                    (1, "Text (short)"),
+                    (6, "Users"),
                 ],
                 default=1,
             ),
diff --git a/zerver/models/custom_profile_fields.py b/zerver/models/custom_profile_fields.py
--- a/zerver/models/custom_profile_fields.py
+++ b/zerver/models/custom_profile_fields.py
@@ -94,7 +94,7 @@
         (SELECT, gettext_lazy("List of options"), validate_select_field, str, "SELECT"),
     ]
     USER_FIELD_TYPE_DATA: List[UserFieldElement] = [
-        (USER, gettext_lazy("Person picker"), check_valid_user_ids, orjson.loads, "USER"),
+        (USER, gettext_lazy("Users"), check_valid_user_ids, orjson.loads, "USER"),
     ]
 
     SELECT_FIELD_VALIDATORS: Dict[int, ExtendedValidator] = {
@@ -106,9 +106,9 @@
 
     FIELD_TYPE_DATA: List[FieldElement] = [
         # Type, display name, validator, converter, keyword
-        (SHORT_TEXT, gettext_lazy("Short text"), check_short_string, str, "SHORT_TEXT"),
-        (LONG_TEXT, gettext_lazy("Long text"), check_long_string, str, "LONG_TEXT"),
-        (DATE, gettext_lazy("Date picker"), check_date, str, "DATE"),
+        (SHORT_TEXT, gettext_lazy("Text (short)"), check_short_string, str, "SHORT_TEXT"),
+        (LONG_TEXT, gettext_lazy("Text (long)"), check_long_string, str, "LONG_TEXT"),
+        (DATE, gettext_lazy("Date"), check_date, str, "DATE"),
         (URL, gettext_lazy("Link"), check_url, str, "URL"),
         (
             EXTERNAL_ACCOUNT,
@@ -120,7 +120,9 @@
         (PRONOUNS, gettext_lazy("Pronouns"), check_short_string, str, "PRONOUNS"),
     ]
 
-    ALL_FIELD_TYPES = [*FIELD_TYPE_DATA, *SELECT_FIELD_TYPE_DATA, *USER_FIELD_TYPE_DATA]
+    ALL_FIELD_TYPES = sorted(
+        [*FIELD_TYPE_DATA, *SELECT_FIELD_TYPE_DATA, *USER_FIELD_TYPE_DATA], key=lambda x: x[1]
+    )
 
     FIELD_VALIDATORS: Dict[int, Validator[ProfileDataElementValue]] = {
         item[0]: item[2] for item in FIELD_TYPE_DATA

{"golden_diff": "diff --git a/zerver/migrations/0417_alter_customprofilefield_field_type.py b/zerver/migrations/0417_alter_customprofilefield_field_type.py\n--- a/zerver/migrations/0417_alter_customprofilefield_field_type.py\n+++ b/zerver/migrations/0417_alter_customprofilefield_field_type.py\n@@ -14,14 +14,14 @@\n             name=\"field_type\",\n             field=models.PositiveSmallIntegerField(\n                 choices=[\n-                    (1, \"Short text\"),\n-                    (2, \"Long text\"),\n-                    (4, \"Date picker\"),\n-                    (5, \"Link\"),\n+                    (4, \"Date\"),\n                     (7, \"External account\"),\n-                    (8, \"Pronouns\"),\n+                    (5, \"Link\"),\n                     (3, \"List of options\"),\n-                    (6, \"Person picker\"),\n+                    (8, \"Pronouns\"),\n+                    (2, \"Text (long)\"),\n+                    (1, \"Text (short)\"),\n+                    (6, \"Users\"),\n                 ],\n                 default=1,\n             ),\ndiff --git a/zerver/models/custom_profile_fields.py b/zerver/models/custom_profile_fields.py\n--- a/zerver/models/custom_profile_fields.py\n+++ b/zerver/models/custom_profile_fields.py\n@@ -94,7 +94,7 @@\n         (SELECT, gettext_lazy(\"List of options\"), validate_select_field, str, \"SELECT\"),\n     ]\n     USER_FIELD_TYPE_DATA: List[UserFieldElement] = [\n-        (USER, gettext_lazy(\"Person picker\"), check_valid_user_ids, orjson.loads, \"USER\"),\n+        (USER, gettext_lazy(\"Users\"), check_valid_user_ids, orjson.loads, \"USER\"),\n     ]\n \n     SELECT_FIELD_VALIDATORS: Dict[int, ExtendedValidator] = {\n@@ -106,9 +106,9 @@\n \n     FIELD_TYPE_DATA: List[FieldElement] = [\n         # Type, display name, validator, converter, keyword\n-        (SHORT_TEXT, gettext_lazy(\"Short text\"), check_short_string, str, \"SHORT_TEXT\"),\n-        (LONG_TEXT, gettext_lazy(\"Long text\"), check_long_string, str, \"LONG_TEXT\"),\n-        (DATE, gettext_lazy(\"Date picker\"), check_date, str, \"DATE\"),\n+        (SHORT_TEXT, gettext_lazy(\"Text (short)\"), check_short_string, str, \"SHORT_TEXT\"),\n+        (LONG_TEXT, gettext_lazy(\"Text (long)\"), check_long_string, str, \"LONG_TEXT\"),\n+        (DATE, gettext_lazy(\"Date\"), check_date, str, \"DATE\"),\n         (URL, gettext_lazy(\"Link\"), check_url, str, \"URL\"),\n         (\n             EXTERNAL_ACCOUNT,\n@@ -120,7 +120,9 @@\n         (PRONOUNS, gettext_lazy(\"Pronouns\"), check_short_string, str, \"PRONOUNS\"),\n     ]\n \n-    ALL_FIELD_TYPES = [*FIELD_TYPE_DATA, *SELECT_FIELD_TYPE_DATA, *USER_FIELD_TYPE_DATA]\n+    ALL_FIELD_TYPES = sorted(\n+        [*FIELD_TYPE_DATA, *SELECT_FIELD_TYPE_DATA, *USER_FIELD_TYPE_DATA], key=lambda x: x[1]\n+    )\n \n     FIELD_VALIDATORS: Dict[int, Validator[ProfileDataElementValue]] = {\n         item[0]: item[2] for item in FIELD_TYPE_DATA\n", "issue": "Rename and reorder custom profile field types\n<!-- Issue description -->\r\n\r\nIn the custom profile field type dropdown, we should:\r\n\r\n1. Make the following naming changes:\r\n   * Short text -> Text (short)\r\n   * Long text -> Text (long)\r\n   * Date picker -> Date\r\n   * Person picker -> Users\r\n2. Order the fields alphabetically.\r\n\r\nCurrent menu:\r\n![Screenshot 2024-01-09 at 3 12 38\u202fPM](https://github.com/zulip/zulip/assets/2090066/e378f417-bd7c-49a3-abf0-833e1861fc5c)\r\n\r\n\r\n<!-- Link to a message in the chat.zulip.org discussion. Message links will still work even if the topic is renamed or resolved. Link back to this issue from the chat.zulip.org thread. -->\r\n\r\n[CZO thread](https://chat.zulip.org/#narrow/stream/137-feedback/topic/Improvements.20for.20Custom.20profile.20fields/near/1712583)\r\n\n", "before_files": [{"content": "from typing import Any, Callable, Dict, List, Tuple\n\nimport orjson\nfrom django.core.exceptions import ValidationError\nfrom django.db import models\nfrom django.db.models import CASCADE, QuerySet\nfrom django.utils.translation import gettext as _\nfrom django.utils.translation import gettext_lazy\nfrom django_stubs_ext import StrPromise\nfrom typing_extensions import override\n\nfrom zerver.lib.types import (\n    ExtendedFieldElement,\n    ExtendedValidator,\n    FieldElement,\n    ProfileDataElementBase,\n    ProfileDataElementValue,\n    RealmUserValidator,\n    UserFieldElement,\n    Validator,\n)\nfrom zerver.lib.validator import (\n    check_date,\n    check_int,\n    check_list,\n    check_long_string,\n    check_short_string,\n    check_url,\n    validate_select_field,\n)\nfrom zerver.models.realms import Realm\nfrom zerver.models.users import UserProfile, get_user_profile_by_id_in_realm\n\n\ndef check_valid_user_ids(realm_id: int, val: object, allow_deactivated: bool = False) -> List[int]:\n    user_ids = check_list(check_int)(\"User IDs\", val)\n    realm = Realm.objects.get(id=realm_id)\n    for user_id in user_ids:\n        # TODO: Structurally, we should be doing a bulk fetch query to\n        # get the users here, not doing these in a loop.  But because\n        # this is a rarely used feature and likely to never have more\n        # than a handful of users, it's probably mostly OK.\n        try:\n            user_profile = get_user_profile_by_id_in_realm(user_id, realm)\n        except UserProfile.DoesNotExist:\n            raise ValidationError(_(\"Invalid user ID: {user_id}\").format(user_id=user_id))\n\n        if not allow_deactivated and not user_profile.is_active:\n            raise ValidationError(\n                _(\"User with ID {user_id} is deactivated\").format(user_id=user_id)\n            )\n\n        if user_profile.is_bot:\n            raise ValidationError(_(\"User with ID {user_id} is a bot\").format(user_id=user_id))\n\n    return user_ids\n\n\nclass CustomProfileField(models.Model):\n    \"\"\"Defines a form field for the per-realm custom profile fields feature.\n\n    See CustomProfileFieldValue for an individual user's values for one of\n    these fields.\n    \"\"\"\n\n    HINT_MAX_LENGTH = 80\n    NAME_MAX_LENGTH = 40\n    MAX_DISPLAY_IN_PROFILE_SUMMARY_FIELDS = 2\n\n    realm = models.ForeignKey(Realm, on_delete=CASCADE)\n    name = models.CharField(max_length=NAME_MAX_LENGTH)\n    hint = models.CharField(max_length=HINT_MAX_LENGTH, default=\"\")\n\n    # Sort order for display of custom profile fields.\n    order = models.IntegerField(default=0)\n\n    # Whether the field should be displayed in smaller summary\n    # sections of a page displaying custom profile fields.\n    display_in_profile_summary = models.BooleanField(default=False)\n\n    SHORT_TEXT = 1\n    LONG_TEXT = 2\n    SELECT = 3\n    DATE = 4\n    URL = 5\n    USER = 6\n    EXTERNAL_ACCOUNT = 7\n    PRONOUNS = 8\n\n    # These are the fields whose validators require more than var_name\n    # and value argument. i.e. SELECT require field_data, USER require\n    # realm as argument.\n    SELECT_FIELD_TYPE_DATA: List[ExtendedFieldElement] = [\n        (SELECT, gettext_lazy(\"List of options\"), validate_select_field, str, \"SELECT\"),\n    ]\n    USER_FIELD_TYPE_DATA: List[UserFieldElement] = [\n        (USER, gettext_lazy(\"Person picker\"), check_valid_user_ids, orjson.loads, \"USER\"),\n    ]\n\n    SELECT_FIELD_VALIDATORS: Dict[int, ExtendedValidator] = {\n        item[0]: item[2] for item in SELECT_FIELD_TYPE_DATA\n    }\n    USER_FIELD_VALIDATORS: Dict[int, RealmUserValidator] = {\n        item[0]: item[2] for item in USER_FIELD_TYPE_DATA\n    }\n\n    FIELD_TYPE_DATA: List[FieldElement] = [\n        # Type, display name, validator, converter, keyword\n        (SHORT_TEXT, gettext_lazy(\"Short text\"), check_short_string, str, \"SHORT_TEXT\"),\n        (LONG_TEXT, gettext_lazy(\"Long text\"), check_long_string, str, \"LONG_TEXT\"),\n        (DATE, gettext_lazy(\"Date picker\"), check_date, str, \"DATE\"),\n        (URL, gettext_lazy(\"Link\"), check_url, str, \"URL\"),\n        (\n            EXTERNAL_ACCOUNT,\n            gettext_lazy(\"External account\"),\n            check_short_string,\n            str,\n            \"EXTERNAL_ACCOUNT\",\n        ),\n        (PRONOUNS, gettext_lazy(\"Pronouns\"), check_short_string, str, \"PRONOUNS\"),\n    ]\n\n    ALL_FIELD_TYPES = [*FIELD_TYPE_DATA, *SELECT_FIELD_TYPE_DATA, *USER_FIELD_TYPE_DATA]\n\n    FIELD_VALIDATORS: Dict[int, Validator[ProfileDataElementValue]] = {\n        item[0]: item[2] for item in FIELD_TYPE_DATA\n    }\n    FIELD_CONVERTERS: Dict[int, Callable[[Any], Any]] = {\n        item[0]: item[3] for item in ALL_FIELD_TYPES\n    }\n    FIELD_TYPE_CHOICES: List[Tuple[int, StrPromise]] = [\n        (item[0], item[1]) for item in ALL_FIELD_TYPES\n    ]\n\n    field_type = models.PositiveSmallIntegerField(\n        choices=FIELD_TYPE_CHOICES,\n        default=SHORT_TEXT,\n    )\n\n    # A JSON blob of any additional data needed to define the field beyond\n    # type/name/hint.\n    #\n    # The format depends on the type.  Field types SHORT_TEXT, LONG_TEXT,\n    # DATE, URL, and USER leave this empty.  Fields of type SELECT store the\n    # choices' descriptions.\n    #\n    # Note: There is no performance overhead of using TextField in PostgreSQL.\n    # See https://www.postgresql.org/docs/9.0/static/datatype-character.html\n    field_data = models.TextField(default=\"\")\n\n    class Meta:\n        unique_together = (\"realm\", \"name\")\n\n    @override\n    def __str__(self) -> str:\n        return f\"{self.realm!r} {self.name} {self.field_type} {self.order}\"\n\n    def as_dict(self) -> ProfileDataElementBase:\n        data_as_dict: ProfileDataElementBase = {\n            \"id\": self.id,\n            \"name\": self.name,\n            \"type\": self.field_type,\n            \"hint\": self.hint,\n            \"field_data\": self.field_data,\n            \"order\": self.order,\n        }\n        if self.display_in_profile_summary:\n            data_as_dict[\"display_in_profile_summary\"] = True\n\n        return data_as_dict\n\n    def is_renderable(self) -> bool:\n        if self.field_type in [CustomProfileField.SHORT_TEXT, CustomProfileField.LONG_TEXT]:\n            return True\n        return False\n\n\ndef custom_profile_fields_for_realm(realm_id: int) -> QuerySet[CustomProfileField]:\n    return CustomProfileField.objects.filter(realm=realm_id).order_by(\"order\")\n\n\nclass CustomProfileFieldValue(models.Model):\n    user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE)\n    field = models.ForeignKey(CustomProfileField, on_delete=CASCADE)\n    value = models.TextField()\n    rendered_value = models.TextField(null=True, default=None)\n\n    class Meta:\n        unique_together = (\"user_profile\", \"field\")\n\n    @override\n    def __str__(self) -> str:\n        return f\"{self.user_profile!r} {self.field!r} {self.value}\"\n", "path": "zerver/models/custom_profile_fields.py"}, {"content": "# Generated by Django 4.0.7 on 2022-10-04 13:59\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [\n        (\"zerver\", \"0416_set_default_emoji_style\"),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"customprofilefield\",\n            name=\"field_type\",\n            field=models.PositiveSmallIntegerField(\n                choices=[\n                    (1, \"Short text\"),\n                    (2, \"Long text\"),\n                    (4, \"Date picker\"),\n                    (5, \"Link\"),\n                    (7, \"External account\"),\n                    (8, \"Pronouns\"),\n                    (3, \"List of options\"),\n                    (6, \"Person picker\"),\n                ],\n                default=1,\n            ),\n        ),\n    ]\n", "path": "zerver/migrations/0417_alter_customprofilefield_field_type.py"}], "after_files": [{"content": "from typing import Any, Callable, Dict, List, Tuple\n\nimport orjson\nfrom django.core.exceptions import ValidationError\nfrom django.db import models\nfrom django.db.models import CASCADE, QuerySet\nfrom django.utils.translation import gettext as _\nfrom django.utils.translation import gettext_lazy\nfrom django_stubs_ext import StrPromise\nfrom typing_extensions import override\n\nfrom zerver.lib.types import (\n    ExtendedFieldElement,\n    ExtendedValidator,\n    FieldElement,\n    ProfileDataElementBase,\n    ProfileDataElementValue,\n    RealmUserValidator,\n    UserFieldElement,\n    Validator,\n)\nfrom zerver.lib.validator import (\n    check_date,\n    check_int,\n    check_list,\n    check_long_string,\n    check_short_string,\n    check_url,\n    validate_select_field,\n)\nfrom zerver.models.realms import Realm\nfrom zerver.models.users import UserProfile, get_user_profile_by_id_in_realm\n\n\ndef check_valid_user_ids(realm_id: int, val: object, allow_deactivated: bool = False) -> List[int]:\n    user_ids = check_list(check_int)(\"User IDs\", val)\n    realm = Realm.objects.get(id=realm_id)\n    for user_id in user_ids:\n        # TODO: Structurally, we should be doing a bulk fetch query to\n        # get the users here, not doing these in a loop.  But because\n        # this is a rarely used feature and likely to never have more\n        # than a handful of users, it's probably mostly OK.\n        try:\n            user_profile = get_user_profile_by_id_in_realm(user_id, realm)\n        except UserProfile.DoesNotExist:\n            raise ValidationError(_(\"Invalid user ID: {user_id}\").format(user_id=user_id))\n\n        if not allow_deactivated and not user_profile.is_active:\n            raise ValidationError(\n                _(\"User with ID {user_id} is deactivated\").format(user_id=user_id)\n            )\n\n        if user_profile.is_bot:\n            raise ValidationError(_(\"User with ID {user_id} is a bot\").format(user_id=user_id))\n\n    return user_ids\n\n\nclass CustomProfileField(models.Model):\n    \"\"\"Defines a form field for the per-realm custom profile fields feature.\n\n    See CustomProfileFieldValue for an individual user's values for one of\n    these fields.\n    \"\"\"\n\n    HINT_MAX_LENGTH = 80\n    NAME_MAX_LENGTH = 40\n    MAX_DISPLAY_IN_PROFILE_SUMMARY_FIELDS = 2\n\n    realm = models.ForeignKey(Realm, on_delete=CASCADE)\n    name = models.CharField(max_length=NAME_MAX_LENGTH)\n    hint = models.CharField(max_length=HINT_MAX_LENGTH, default=\"\")\n\n    # Sort order for display of custom profile fields.\n    order = models.IntegerField(default=0)\n\n    # Whether the field should be displayed in smaller summary\n    # sections of a page displaying custom profile fields.\n    display_in_profile_summary = models.BooleanField(default=False)\n\n    SHORT_TEXT = 1\n    LONG_TEXT = 2\n    SELECT = 3\n    DATE = 4\n    URL = 5\n    USER = 6\n    EXTERNAL_ACCOUNT = 7\n    PRONOUNS = 8\n\n    # These are the fields whose validators require more than var_name\n    # and value argument. i.e. SELECT require field_data, USER require\n    # realm as argument.\n    SELECT_FIELD_TYPE_DATA: List[ExtendedFieldElement] = [\n        (SELECT, gettext_lazy(\"List of options\"), validate_select_field, str, \"SELECT\"),\n    ]\n    USER_FIELD_TYPE_DATA: List[UserFieldElement] = [\n        (USER, gettext_lazy(\"Users\"), check_valid_user_ids, orjson.loads, \"USER\"),\n    ]\n\n    SELECT_FIELD_VALIDATORS: Dict[int, ExtendedValidator] = {\n        item[0]: item[2] for item in SELECT_FIELD_TYPE_DATA\n    }\n    USER_FIELD_VALIDATORS: Dict[int, RealmUserValidator] = {\n        item[0]: item[2] for item in USER_FIELD_TYPE_DATA\n    }\n\n    FIELD_TYPE_DATA: List[FieldElement] = [\n        # Type, display name, validator, converter, keyword\n        (SHORT_TEXT, gettext_lazy(\"Text (short)\"), check_short_string, str, \"SHORT_TEXT\"),\n        (LONG_TEXT, gettext_lazy(\"Text (long)\"), check_long_string, str, \"LONG_TEXT\"),\n        (DATE, gettext_lazy(\"Date\"), check_date, str, \"DATE\"),\n        (URL, gettext_lazy(\"Link\"), check_url, str, \"URL\"),\n        (\n            EXTERNAL_ACCOUNT,\n            gettext_lazy(\"External account\"),\n            check_short_string,\n            str,\n            \"EXTERNAL_ACCOUNT\",\n        ),\n        (PRONOUNS, gettext_lazy(\"Pronouns\"), check_short_string, str, \"PRONOUNS\"),\n    ]\n\n    ALL_FIELD_TYPES = sorted(\n        [*FIELD_TYPE_DATA, *SELECT_FIELD_TYPE_DATA, *USER_FIELD_TYPE_DATA], key=lambda x: x[1]\n    )\n\n    FIELD_VALIDATORS: Dict[int, Validator[ProfileDataElementValue]] = {\n        item[0]: item[2] for item in FIELD_TYPE_DATA\n    }\n    FIELD_CONVERTERS: Dict[int, Callable[[Any], Any]] = {\n        item[0]: item[3] for item in ALL_FIELD_TYPES\n    }\n    FIELD_TYPE_CHOICES: List[Tuple[int, StrPromise]] = [\n        (item[0], item[1]) for item in ALL_FIELD_TYPES\n    ]\n\n    field_type = models.PositiveSmallIntegerField(\n        choices=FIELD_TYPE_CHOICES,\n        default=SHORT_TEXT,\n    )\n\n    # A JSON blob of any additional data needed to define the field beyond\n    # type/name/hint.\n    #\n    # The format depends on the type.  Field types SHORT_TEXT, LONG_TEXT,\n    # DATE, URL, and USER leave this empty.  Fields of type SELECT store the\n    # choices' descriptions.\n    #\n    # Note: There is no performance overhead of using TextField in PostgreSQL.\n    # See https://www.postgresql.org/docs/9.0/static/datatype-character.html\n    field_data = models.TextField(default=\"\")\n\n    class Meta:\n        unique_together = (\"realm\", \"name\")\n\n    @override\n    def __str__(self) -> str:\n        return f\"{self.realm!r} {self.name} {self.field_type} {self.order}\"\n\n    def as_dict(self) -> ProfileDataElementBase:\n        data_as_dict: ProfileDataElementBase = {\n            \"id\": self.id,\n            \"name\": self.name,\n            \"type\": self.field_type,\n            \"hint\": self.hint,\n            \"field_data\": self.field_data,\n            \"order\": self.order,\n        }\n        if self.display_in_profile_summary:\n            data_as_dict[\"display_in_profile_summary\"] = True\n\n        return data_as_dict\n\n    def is_renderable(self) -> bool:\n        if self.field_type in [CustomProfileField.SHORT_TEXT, CustomProfileField.LONG_TEXT]:\n            return True\n        return False\n\n\ndef custom_profile_fields_for_realm(realm_id: int) -> QuerySet[CustomProfileField]:\n    return CustomProfileField.objects.filter(realm=realm_id).order_by(\"order\")\n\n\nclass CustomProfileFieldValue(models.Model):\n    user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE)\n    field = models.ForeignKey(CustomProfileField, on_delete=CASCADE)\n    value = models.TextField()\n    rendered_value = models.TextField(null=True, default=None)\n\n    class Meta:\n        unique_together = (\"user_profile\", \"field\")\n\n    @override\n    def __str__(self) -> str:\n        return f\"{self.user_profile!r} {self.field!r} {self.value}\"\n", "path": "zerver/models/custom_profile_fields.py"}, {"content": "# Generated by Django 4.0.7 on 2022-10-04 13:59\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [\n        (\"zerver\", \"0416_set_default_emoji_style\"),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"customprofilefield\",\n            name=\"field_type\",\n            field=models.PositiveSmallIntegerField(\n                choices=[\n                    (4, \"Date\"),\n                    (7, \"External account\"),\n                    (5, \"Link\"),\n                    (3, \"List of options\"),\n                    (8, \"Pronouns\"),\n                    (2, \"Text (long)\"),\n                    (1, \"Text (short)\"),\n                    (6, \"Users\"),\n                ],\n                default=1,\n            ),\n        ),\n    ]\n", "path": "zerver/migrations/0417_alter_customprofilefield_field_type.py"}]}