From 60ab45ceb2fd993dc13ac508c0d1ac711009e2a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=5B=E3=82=B6=E3=83=95=E3=83=BC=E3=83=AB?=
=?UTF-8?q?=5D?= <40970016+zafuru@users.noreply.github.com>
Date: Tue, 17 May 2022 00:21:26 -0400
Subject: [PATCH 01/15] Added TOTP value to AliasGeneratorEnum
---
app/models.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/app/models.py b/app/models.py
index e57a8d08f..1c995d3cd 100644
--- a/app/models.py
+++ b/app/models.py
@@ -1,6 +1,7 @@
import enum
import os
import random
+import string
import uuid
from email.utils import formataddr
from typing import List, Tuple, Optional
@@ -216,6 +217,7 @@ class SenderFormatEnum(EnumE):
class AliasGeneratorEnum(EnumE):
word = 1 # aliases are generated based on random words
uuid = 2 # aliases are generated based on uuid
+ totp = 3 # aliases are generated based on random characters
class AliasSuffixEnum(EnumE):
@@ -1188,8 +1190,11 @@ def generate_email(
if scheme == AliasGeneratorEnum.uuid.value:
name = uuid.uuid4().hex if in_hex else uuid.uuid4().__str__()
random_email = name + "@" + alias_domain
- else:
+ elif scheme == AliasGeneratorEnum.word.value:
random_email = random_words() + "@" + alias_domain
+ else:
+ name = "".join(random.choices(string.digits + string.ascii_lowercase, k=6))
+ random_email = name + "@" + alias_domain
random_email = random_email.lower().strip()
From 76bb7a3279efc78727e62f6215818a7f121d0574 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=5B=E3=82=B6=E3=83=95=E3=83=BC=E3=83=AB?=
=?UTF-8?q?=5D?= <40970016+zafuru@users.noreply.github.com>
Date: Tue, 17 May 2022 00:22:05 -0400
Subject: [PATCH 02/15] Implemented TOTP alias generation
---
app/api/views/new_random_alias.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/app/api/views/new_random_alias.py b/app/api/views/new_random_alias.py
index a2af4b6c6..632805c93 100644
--- a/app/api/views/new_random_alias.py
+++ b/app/api/views/new_random_alias.py
@@ -98,8 +98,10 @@ def new_random_alias():
scheme = AliasGeneratorEnum.word.value
elif mode == "uuid":
scheme = AliasGeneratorEnum.uuid.value
+ elif mode == "totp":
+ scheme = AliasGeneratorEnum.totp.value
else:
- return jsonify(error=f"{mode} must be either word or uuid"), 400
+ return jsonify(error=f"{mode} must be either word, uuid, or totp"), 400
alias = Alias.create_new_random(user=user, scheme=scheme, note=note)
Session.commit()
From dfe31fd8d0c8e256343b1bcb6c532fdd98ad0f4a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=5B=E3=82=B6=E3=83=95=E3=83=BC=E3=83=AB?=
=?UTF-8?q?=5D?= <40970016+zafuru@users.noreply.github.com>
Date: Tue, 17 May 2022 00:22:30 -0400
Subject: [PATCH 03/15] Added TOTP alias selection to settings template
---
templates/dashboard/setting.html | 3 +++
1 file changed, 3 insertions(+)
diff --git a/templates/dashboard/setting.html b/templates/dashboard/setting.html
index 8e2d0ab21..d700b419b 100644
--- a/templates/dashboard/setting.html
+++ b/templates/dashboard/setting.html
@@ -266,6 +266,9 @@
+
From 66f9c2932c83429f5356226d5c7204327ad16ad5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=5B=E3=82=B6=E3=83=95=E3=83=BC=E3=83=AB?=
=?UTF-8?q?=5D?= <40970016+zafuru@users.noreply.github.com>
Date: Tue, 17 May 2022 00:26:51 -0400
Subject: [PATCH 04/15] Added a tiny extra detail to CONTRIBUTING.md
---
CONTRIBUTING.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ebd12459e..7814e7dc3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -64,9 +64,10 @@ Install npm packages
cd static && npm install
```
-To run the code locally, please create a local setting file based on `example.env`:
+To run the code locally, go back to the root folder, and please create a local setting file based on `example.env`:
```
+cd ..
cp example.env .env
```
From 49e8f68e5b197dd0e5013e1809cec30bef111b44 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=5B=E3=82=B6=E3=83=95=E3=83=BC=E3=83=AB?=
=?UTF-8?q?=5D?= <40970016+zafuru@users.noreply.github.com>
Date: Tue, 17 May 2022 11:43:29 -0400
Subject: [PATCH 05/15] Renamed new mode to "random_string" and increased
keyspace to 9
---
app/models.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/models.py b/app/models.py
index 1c995d3cd..a66b6bf43 100644
--- a/app/models.py
+++ b/app/models.py
@@ -217,7 +217,7 @@ class SenderFormatEnum(EnumE):
class AliasGeneratorEnum(EnumE):
word = 1 # aliases are generated based on random words
uuid = 2 # aliases are generated based on uuid
- totp = 3 # aliases are generated based on random characters
+ random_string = 3 # aliases are generated based on a completely random string
class AliasSuffixEnum(EnumE):
@@ -1193,7 +1193,7 @@ def generate_email(
elif scheme == AliasGeneratorEnum.word.value:
random_email = random_words() + "@" + alias_domain
else:
- name = "".join(random.choices(string.digits + string.ascii_lowercase, k=6))
+ name = "".join(random.choices(string.digits + string.ascii_lowercase, k=9))
random_email = name + "@" + alias_domain
random_email = random_email.lower().strip()
From 97e85a528bc2b66806d0506ef838e8d334b31dbb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=5B=E3=82=B6=E3=83=95=E3=83=BC=E3=83=AB?=
=?UTF-8?q?=5D?= <40970016+zafuru@users.noreply.github.com>
Date: Tue, 17 May 2022 11:44:05 -0400
Subject: [PATCH 06/15] Renamed new mode to "random_string"
---
app/api/views/new_random_alias.py | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/app/api/views/new_random_alias.py b/app/api/views/new_random_alias.py
index 632805c93..6b064bba4 100644
--- a/app/api/views/new_random_alias.py
+++ b/app/api/views/new_random_alias.py
@@ -98,10 +98,15 @@ def new_random_alias():
scheme = AliasGeneratorEnum.word.value
elif mode == "uuid":
scheme = AliasGeneratorEnum.uuid.value
- elif mode == "totp":
- scheme = AliasGeneratorEnum.totp.value
+ elif mode == "random_string":
+ scheme = AliasGeneratorEnum.random_string.value
else:
- return jsonify(error=f"{mode} must be either word, uuid, or totp"), 400
+ return (
+ jsonify(
+ error=f"{mode} must be either word, uuid, or random_string"
+ ),
+ 400,
+ )
alias = Alias.create_new_random(user=user, scheme=scheme, note=note)
Session.commit()
From d583cae7aff4e997e7bfbe3456b0f988db68ff85 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=5B=E3=82=B6=E3=83=95=E3=83=BC=E3=83=AB?=
=?UTF-8?q?=5D?= <40970016+zafuru@users.noreply.github.com>
Date: Tue, 17 May 2022 12:15:01 -0400
Subject: [PATCH 07/15] Modified test case for alias generator selection
---
tests/api/test_setting.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/tests/api/test_setting.py b/tests/api/test_setting.py
index 3f545b3d2..29cab8c20 100644
--- a/tests/api/test_setting.py
+++ b/tests/api/test_setting.py
@@ -41,6 +41,10 @@ def test_update_settings_alias_generator(flask_client):
assert r.status_code == 200
assert user.alias_generator == AliasGeneratorEnum.uuid.value
+ r = flask_client.patch("/api/setting", json={"alias_generator": "random_string"})
+ assert r.status_code == 200
+ assert user.alias_generator == AliasGeneratorEnum.random_string.value
+
def test_update_settings_random_alias_default_domain(flask_client):
user = login(flask_client)
From cb5e1b3e1d9481d5158bfbafc924b94762506e3d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=5B=E3=82=B6=E3=83=95=E3=83=BC=E3=83=AB?=
=?UTF-8?q?=5D?= <40970016+zafuru@users.noreply.github.com>
Date: Tue, 17 May 2022 12:16:17 -0400
Subject: [PATCH 08/15] Added a test case for random_string mode
---
tests/api/test_new_random_alias.py | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/tests/api/test_new_random_alias.py b/tests/api/test_new_random_alias.py
index b8dbbdd7a..4a3ff6ba5 100644
--- a/tests/api/test_new_random_alias.py
+++ b/tests/api/test_new_random_alias.py
@@ -101,6 +101,32 @@ def test_custom_mode(flask_client):
assert ge.note == "test note"
+def test_random_string_mode(flask_client):
+ login(flask_client)
+
+ # without note
+ r = flask_client.post(
+ url_for("api.new_random_alias", mode="random_string"),
+ )
+
+ assert r.status_code == 201
+ # extract the uuid part
+ alias = r.json["alias"]
+ random_string_part: str = alias[: len(alias) - len(EMAIL_DOMAIN) - 1]
+ assert random_string_part.isalnum()
+
+ # with note
+ r = flask_client.post(
+ url_for("api.new_random_alias", mode="random_string"),
+ json={"note": "test note"},
+ )
+
+ assert r.status_code == 201
+ alias = r.json["alias"]
+ ge = Alias.get_by(email=alias)
+ assert ge.note == "test note"
+
+
def test_out_of_quota(flask_client):
user = login(flask_client)
user.trial_end = None
From 09afba32ada25395c3d8269922a463c3f72169f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=5B=E3=82=B6=E3=83=95=E3=83=BC=E3=83=AB?=
=?UTF-8?q?=5D?= <40970016+zafuru@users.noreply.github.com>
Date: Tue, 17 May 2022 12:19:41 -0400
Subject: [PATCH 09/15] Added a test case for random_string mode
---
tests/test_models.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/tests/test_models.py b/tests/test_models.py
index cc3c6b9fd..63057870f 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -8,6 +8,7 @@
from app.db import Session
from app.email_utils import parse_full_address
from app.models import (
+ AliasGeneratorEnum,
generate_email,
Alias,
Contact,
@@ -32,6 +33,14 @@ def test_generate_email(flask_client):
assert UUID(email_uuid.split("@")[0], version=4)
+def test_generate_email_with_random_string(flask_client):
+ email = generate_email(scheme=AliasGeneratorEnum.random_string.value)
+ assert email.endswith("@" + EMAIL_DOMAIN)
+
+ email_random_string = generate_email(scheme=3)
+ assert (email_random_string.split("@")[0]).isalnum()
+
+
def test_profile_picture_url(flask_client):
user = create_new_user()
From 31f50db7c3a071a46392596471b0af13c29533dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=5B=E3=82=B6=E3=83=95=E3=83=BC=E3=83=AB?=
=?UTF-8?q?=5D?= <40970016+zafuru@users.noreply.github.com>
Date: Tue, 17 May 2022 12:55:10 -0400
Subject: [PATCH 10/15] Included new mode into selection
---
app/api/views/setting.py | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/app/api/views/setting.py b/app/api/views/setting.py
index 58e314d60..afdc8e770 100644
--- a/app/api/views/setting.py
+++ b/app/api/views/setting.py
@@ -15,11 +15,14 @@
def setting_to_dict(user: User):
+ alias_generator = {
+ "word": AliasGeneratorEnum.word.value,
+ "uuid": AliasGeneratorEnum.uuid.value,
+ "random_string": AliasGeneratorEnum.random_string.value,
+ }
ret = {
"notification": user.notification,
- "alias_generator": "word"
- if user.alias_generator == AliasGeneratorEnum.word.value
- else "uuid",
+ "alias_generator": alias_generator.get(user.alias_generator, "word"),
"random_alias_default_domain": user.default_random_alias_domain(),
# return the default sender format (AT) in case user uses a non-supported sender format
"sender_format": SenderFormatEnum.get_name(user.sender_format)
@@ -48,7 +51,7 @@ def update_setting():
Update user setting
Input:
- notification: bool
- - alias_generator: word|uuid
+ - alias_generator: word|uuid|random_string
- random_alias_default_domain: str
"""
user = g.user
@@ -59,13 +62,15 @@ def update_setting():
if "alias_generator" in data:
alias_generator = data["alias_generator"]
- if alias_generator not in ["word", "uuid"]:
+ if alias_generator not in ["word", "uuid", "random_string"]:
return jsonify(error="Invalid alias_generator"), 400
if alias_generator == "word":
user.alias_generator = AliasGeneratorEnum.word.value
- else:
+ elif alias_generator == "uuid":
user.alias_generator = AliasGeneratorEnum.uuid.value
+ else:
+ user.alias_generator = AliasGeneratorEnum.random_string.value
if "sender_format" in data:
sender_format = data["sender_format"]
From 4ceec234d8e0ed1ae85151e57b792d2eaca19b84 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=5B=E3=82=B6=E3=83=95=E3=83=BC=E3=83=AB?=
=?UTF-8?q?=5D?= <40970016+zafuru@users.noreply.github.com>
Date: Tue, 17 May 2022 12:55:36 -0400
Subject: [PATCH 11/15] Included new mode into selection and fixed a typo
---
templates/dashboard/setting.html | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/templates/dashboard/setting.html b/templates/dashboard/setting.html
index d700b419b..1a0931986 100644
--- a/templates/dashboard/setting.html
+++ b/templates/dashboard/setting.html
@@ -266,9 +266,9 @@
-
+
@@ -300,7 +300,7 @@
Random word from our dictionary
From e951a6e764b1543d647b9054b7d9ece3512dcd1d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=28=E3=82=B6=E3=83=95=E3=83=BC=E3=83=AB?=
=?UTF-8?q?=29?= <40970016+zafuru@users.noreply.github.com>
Date: Thu, 26 May 2022 12:12:39 -0400
Subject: [PATCH 12/15] Update app/api/views/setting.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Adrià Casajús
---
app/api/views/setting.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/app/api/views/setting.py b/app/api/views/setting.py
index afdc8e770..4f0886eee 100644
--- a/app/api/views/setting.py
+++ b/app/api/views/setting.py
@@ -15,11 +15,10 @@
def setting_to_dict(user: User):
- alias_generator = {
- "word": AliasGeneratorEnum.word.value,
- "uuid": AliasGeneratorEnum.uuid.value,
- "random_string": AliasGeneratorEnum.random_string.value,
- }
+ try:
+ alias_generator = AliasGeneratorEnum[user.alias_generator].value
+ except KeyError:
+ alias_generator = AliasGeneratorEnum.word.value
ret = {
"notification": user.notification,
"alias_generator": alias_generator.get(user.alias_generator, "word"),
From e8c4f7a94ef6b143a179576ec3defa1d1bfecff4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=7C=20=E3=82=B6=E3=83=95=E3=83=BC?=
=?UTF-8?q?=E3=83=AB?= <40970016+zafuru@users.noreply.github.com>
Date: Thu, 26 May 2022 13:26:24 -0400
Subject: [PATCH 13/15] Integrated first suggestion
---
app/api/views/setting.py | 9 ++++-----
tests/api/test_setting.py | 6 +++++-
2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/app/api/views/setting.py b/app/api/views/setting.py
index 4f0886eee..122725316 100644
--- a/app/api/views/setting.py
+++ b/app/api/views/setting.py
@@ -15,13 +15,12 @@
def setting_to_dict(user: User):
- try:
- alias_generator = AliasGeneratorEnum[user.alias_generator].value
- except KeyError:
- alias_generator = AliasGeneratorEnum.word.value
ret = {
"notification": user.notification,
- "alias_generator": alias_generator.get(user.alias_generator, "word"),
+ # return the default alias generator in case user uses a non-supported alias generator
+ "alias_generator": user.alias_generator
+ if AliasGeneratorEnum.has_name(user.alias_generator)
+ else AliasGeneratorEnum.get_name(AliasGeneratorEnum.word.value),
"random_alias_default_domain": user.default_random_alias_domain(),
# return the default sender format (AT) in case user uses a non-supported sender format
"sender_format": SenderFormatEnum.get_name(user.sender_format)
diff --git a/tests/api/test_setting.py b/tests/api/test_setting.py
index 29cab8c20..b651224c9 100644
--- a/tests/api/test_setting.py
+++ b/tests/api/test_setting.py
@@ -12,13 +12,17 @@ def test_get_setting(flask_client):
r = flask_client.get("/api/setting")
assert r.status_code == 200
- assert r.json == {
+ e_json = {
"alias_generator": "word",
"notification": True,
"random_alias_default_domain": "sl.local",
"sender_format": "AT",
"random_alias_suffix": "random_string",
}
+ for key in e_json:
+ # Assert on a per-key basis for better error reporting
+ assert r.json[key] == e_json[key]
+ assert len(r.json) == len(e_json)
def test_update_settings_notification(flask_client):
From a50a080cd372badc2363e77c13f4eb148e00e821 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=7C=20=E3=82=B6=E3=83=95=E3=83=BC?=
=?UTF-8?q?=E3=83=AB?= <40970016+zafuru@users.noreply.github.com>
Date: Thu, 26 May 2022 13:46:08 -0400
Subject: [PATCH 14/15] Applied suggestion in other places
---
app/api/views/new_random_alias.py | 15 +++++----------
app/api/views/setting.py | 11 +++--------
app/models.py | 12 ++++++++----
3 files changed, 16 insertions(+), 22 deletions(-)
diff --git a/app/api/views/new_random_alias.py b/app/api/views/new_random_alias.py
index 6b064bba4..3bbc69e9f 100644
--- a/app/api/views/new_random_alias.py
+++ b/app/api/views/new_random_alias.py
@@ -94,17 +94,12 @@ def new_random_alias():
scheme = user.alias_generator
mode = request.args.get("mode")
if mode:
- if mode == "word":
- scheme = AliasGeneratorEnum.word.value
- elif mode == "uuid":
- scheme = AliasGeneratorEnum.uuid.value
- elif mode == "random_string":
- scheme = AliasGeneratorEnum.random_string.value
- else:
+ try:
+ scheme = AliasGeneratorEnum[mode].value
+ except KeyError:
+ modes = ", ".join(AliasGeneratorEnum.get_names())
return (
- jsonify(
- error=f"{mode} must be either word, uuid, or random_string"
- ),
+ jsonify(error=f"{mode} must be one of {modes}"),
400,
)
diff --git a/app/api/views/setting.py b/app/api/views/setting.py
index 122725316..98fdfe48d 100644
--- a/app/api/views/setting.py
+++ b/app/api/views/setting.py
@@ -60,16 +60,11 @@ def update_setting():
if "alias_generator" in data:
alias_generator = data["alias_generator"]
- if alias_generator not in ["word", "uuid", "random_string"]:
+ try:
+ user.alias_generator = AliasGeneratorEnum[alias_generator].value
+ except KeyError:
return jsonify(error="Invalid alias_generator"), 400
- if alias_generator == "word":
- user.alias_generator = AliasGeneratorEnum.word.value
- elif alias_generator == "uuid":
- user.alias_generator = AliasGeneratorEnum.uuid.value
- else:
- user.alias_generator = AliasGeneratorEnum.random_string.value
-
if "sender_format" in data:
sender_format = data["sender_format"]
if not SenderFormatEnum.has_name(sender_format):
diff --git a/app/models.py b/app/models.py
index a66b6bf43..ad1367d04 100644
--- a/app/models.py
+++ b/app/models.py
@@ -4,7 +4,7 @@
import string
import uuid
from email.utils import formataddr
-from typing import List, Tuple, Optional
+from typing import List, Set, Tuple, Optional
import arrow
import sqlalchemy as sa
@@ -183,6 +183,10 @@ def get_name(cls, value: int) -> Optional[str]:
return None
+ @classmethod
+ def get_names(cls) -> Set[str]:
+ return set(item.name for item in cls)
+
@classmethod
def has_name(cls, name: str) -> bool:
for item in cls:
@@ -1190,11 +1194,11 @@ def generate_email(
if scheme == AliasGeneratorEnum.uuid.value:
name = uuid.uuid4().hex if in_hex else uuid.uuid4().__str__()
random_email = name + "@" + alias_domain
- elif scheme == AliasGeneratorEnum.word.value:
- random_email = random_words() + "@" + alias_domain
- else:
+ elif scheme == AliasGeneratorEnum.random_string.value:
name = "".join(random.choices(string.digits + string.ascii_lowercase, k=9))
random_email = name + "@" + alias_domain
+ else: # use word.value as the default just like the original code
+ random_email = random_words() + "@" + alias_domain
random_email = random_email.lower().strip()
From 99c27add3a050f2407cdcd57552cf76aa3f849d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Za=20F=C5=ABru=20=7C=20=E3=82=B6=E3=83=95=E3=83=BC?=
=?UTF-8?q?=E3=83=AB?= <40970016+zafuru@users.noreply.github.com>
Date: Thu, 26 May 2022 14:11:30 -0400
Subject: [PATCH 15/15] Modified EnumE class so that it uses constant-time
lookups instead of linear searches
---
app/models.py | 38 ++++++++++++++++++--------------------
1 file changed, 18 insertions(+), 20 deletions(-)
diff --git a/app/models.py b/app/models.py
index ad1367d04..08038356a 100644
--- a/app/models.py
+++ b/app/models.py
@@ -171,37 +171,35 @@ def __repr__(self):
class EnumE(enum.Enum):
- @classmethod
- def has_value(cls, value: int) -> bool:
- return value in set(item.value for item in cls)
-
@classmethod
def get_name(cls, value: int) -> Optional[str]:
- for item in cls:
- if item.value == value:
- return item.name
-
- return None
+ try:
+ return cls(value).name
+ except ValueError:
+ return None
@classmethod
- def get_names(cls) -> Set[str]:
- return set(item.name for item in cls)
+ def get_names(cls) -> List[str]:
+ return [item.name for item in cls]
@classmethod
def has_name(cls, name: str) -> bool:
- for item in cls:
- if item.name == name:
- return True
-
- return False
+ return not cls.get_value(name) is None
@classmethod
def get_value(cls, name: str) -> Optional[int]:
- for item in cls:
- if item.name == name:
- return item.value
+ try:
+ return cls[name].value
+ except KeyError:
+ return None
- return None
+ @classmethod
+ def get_values(cls) -> List[int]:
+ return [item.value for item in cls]
+
+ @classmethod
+ def has_value(cls, value: int) -> bool:
+ return not cls.get_name(value) is None
class PlanEnum(EnumE):