zou 0.20.38__py3-none-any.whl → 0.20.39__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. zou/__init__.py +1 -1
  2. zou/app/api.py +32 -41
  3. zou/app/blueprints/crud/__init__.py +6 -0
  4. zou/app/blueprints/crud/budget.py +21 -0
  5. zou/app/blueprints/crud/budget_entry.py +15 -0
  6. zou/app/blueprints/crud/plugin.py +13 -0
  7. zou/app/blueprints/crud/salary_scale.py +73 -0
  8. zou/app/blueprints/playlists/resources.py +15 -0
  9. zou/app/blueprints/projects/__init__.py +17 -0
  10. zou/app/blueprints/projects/resources.py +402 -7
  11. zou/app/blueprints/shots/resources.py +1 -0
  12. zou/app/mixin.py +12 -1
  13. zou/app/models/budget.py +39 -0
  14. zou/app/models/budget_entry.py +65 -0
  15. zou/app/models/person.py +17 -1
  16. zou/app/models/plugin.py +21 -0
  17. zou/app/models/salary_scale.py +28 -0
  18. zou/app/services/budget_service.py +195 -0
  19. zou/app/services/comments_service.py +1 -1
  20. zou/app/services/exception.py +8 -0
  21. zou/app/services/plugin_service.py +195 -0
  22. zou/app/services/user_service.py +1 -3
  23. zou/app/utils/fields.py +10 -0
  24. zou/cli.py +109 -1
  25. zou/event_stream.py +23 -5
  26. zou/migrations/versions/2762a797f1f9_add_people_salary_information.py +52 -0
  27. zou/migrations/versions/45f739ef962a_add_people_salary_scale_table.py +70 -0
  28. zou/migrations/versions/4aab1f84ad72_introduce_plugin_table.py +68 -0
  29. zou/migrations/versions/7a16258f2fab_add_currency_field_to_budgets.py +33 -0
  30. zou/migrations/versions/83e2f33a9b14_add_project_bugdet_table.py +57 -0
  31. zou/migrations/versions/8ab98c178903_add_budget_entry_table.py +123 -0
  32. zou/migrations/versions/d25118cddcaa_modify_salary_scale_model.py +133 -0
  33. zou/plugin_template/__init__.py +39 -0
  34. zou/plugin_template/routes.py +6 -0
  35. {zou-0.20.38.dist-info → zou-0.20.39.dist-info}/METADATA +6 -3
  36. {zou-0.20.38.dist-info → zou-0.20.39.dist-info}/RECORD +40 -21
  37. {zou-0.20.38.dist-info → zou-0.20.39.dist-info}/WHEEL +1 -1
  38. {zou-0.20.38.dist-info → zou-0.20.39.dist-info}/entry_points.txt +0 -0
  39. {zou-0.20.38.dist-info → zou-0.20.39.dist-info}/licenses/LICENSE +0 -0
  40. {zou-0.20.38.dist-info → zou-0.20.39.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,70 @@
1
+ """add people salary scale table
2
+
3
+ Revision ID: 45f739ef962a
4
+ Revises: 2762a797f1f9
5
+ Create Date: 2025-04-02 10:11:12.544243
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+ import sqlalchemy_utils
12
+ import sqlalchemy_utils
13
+ import uuid
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision = "45f739ef962a"
17
+ down_revision = "2762a797f1f9"
18
+ branch_labels = None
19
+ depends_on = None
20
+
21
+
22
+ def upgrade():
23
+ # ### commands auto generated by Alembic - please adjust! ###
24
+ op.create_table(
25
+ "salary_scale",
26
+ sa.Column(
27
+ "department_id",
28
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
29
+ default=uuid.uuid4,
30
+ nullable=False,
31
+ ),
32
+ sa.Column("senior_supervisor_salary", sa.Integer(), nullable=False),
33
+ sa.Column("mid_supervisor_salary", sa.Integer(), nullable=False),
34
+ sa.Column("junior_supervisor_salary", sa.Integer(), nullable=False),
35
+ sa.Column("senior_lead_salary", sa.Integer(), nullable=False),
36
+ sa.Column("mid_lead_salary", sa.Integer(), nullable=False),
37
+ sa.Column("junior_lead_salary", sa.Integer(), nullable=False),
38
+ sa.Column("senior_artist_salary", sa.Integer(), nullable=False),
39
+ sa.Column("mid_artist_salary", sa.Integer(), nullable=False),
40
+ sa.Column("junior_artist_salary", sa.Integer(), nullable=False),
41
+ sa.Column(
42
+ "id",
43
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
44
+ default=uuid.uuid4,
45
+ nullable=False,
46
+ ),
47
+ sa.Column("created_at", sa.DateTime(), nullable=True),
48
+ sa.Column("updated_at", sa.DateTime(), nullable=True),
49
+ sa.ForeignKeyConstraint(
50
+ ["department_id"],
51
+ ["department.id"],
52
+ ),
53
+ sa.PrimaryKeyConstraint("id"),
54
+ )
55
+ with op.batch_alter_table("salary_scale", schema=None) as batch_op:
56
+ batch_op.create_index(
57
+ batch_op.f("ix_salary_scale_department_id"),
58
+ ["department_id"],
59
+ unique=False,
60
+ )
61
+ # ### end Alembic commands ###
62
+
63
+
64
+ def downgrade():
65
+ # ### commands auto generated by Alembic - please adjust! ###
66
+ with op.batch_alter_table("salary_scale", schema=None) as batch_op:
67
+ batch_op.drop_index(batch_op.f("ix_salary_scale_department_id"))
68
+
69
+ op.drop_table("salary_scale")
70
+ # ### end Alembic commands ###
@@ -0,0 +1,68 @@
1
+ """Introduce Plugin table
2
+
3
+ Revision ID: 4aab1f84ad72
4
+ Revises: d25118cddcaa
5
+ Create Date: 2025-04-14 15:18:29.346896
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+ import sqlalchemy_utils
12
+ import sqlalchemy_utils
13
+ import uuid
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision = "4aab1f84ad72"
17
+ down_revision = "d25118cddcaa"
18
+ branch_labels = None
19
+ depends_on = None
20
+
21
+
22
+ def upgrade():
23
+ # ### commands auto generated by Alembic - please adjust! ###
24
+ op.create_table(
25
+ "plugin",
26
+ sa.Column("plugin_id", sa.String(length=80), nullable=False),
27
+ sa.Column("name", sa.String(length=80), nullable=False),
28
+ sa.Column("description", sa.Text(), nullable=True),
29
+ sa.Column("version", sa.String(length=50), nullable=False),
30
+ sa.Column("maintainer_name", sa.String(length=200), nullable=False),
31
+ sa.Column(
32
+ "maintainer_email",
33
+ sqlalchemy_utils.types.email.EmailType(length=255),
34
+ nullable=True,
35
+ ),
36
+ sa.Column(
37
+ "website", sqlalchemy_utils.types.url.URLType(), nullable=True
38
+ ),
39
+ sa.Column("license", sa.String(length=80), nullable=False),
40
+ sa.Column(
41
+ "id",
42
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
43
+ default=uuid.uuid4,
44
+ nullable=False,
45
+ ),
46
+ sa.Column("created_at", sa.DateTime(), nullable=True),
47
+ sa.Column("updated_at", sa.DateTime(), nullable=True),
48
+ sa.PrimaryKeyConstraint("id"),
49
+ )
50
+ with op.batch_alter_table("plugin", schema=None) as batch_op:
51
+ batch_op.create_index(
52
+ batch_op.f("ix_plugin_name"), ["name"], unique=False
53
+ )
54
+ batch_op.create_index(
55
+ batch_op.f("ix_plugin_plugin_id"), ["plugin_id"], unique=True
56
+ )
57
+
58
+ # ### end Alembic commands ###
59
+
60
+
61
+ def downgrade():
62
+ # ### commands auto generated by Alembic - please adjust! ###
63
+ with op.batch_alter_table("plugin", schema=None) as batch_op:
64
+ batch_op.drop_index(batch_op.f("ix_plugin_plugin_id"))
65
+ batch_op.drop_index(batch_op.f("ix_plugin_name"))
66
+
67
+ op.drop_table("plugin")
68
+ # ### end Alembic commands ###
@@ -0,0 +1,33 @@
1
+ """add currency field to budgets
2
+
3
+ Revision ID: 7a16258f2fab
4
+ Revises: 8ab98c178903
5
+ Create Date: 2025-04-21 22:27:22.703525
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "7a16258f2fab"
15
+ down_revision = "8ab98c178903"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade():
21
+ # ### commands auto generated by Alembic - please adjust! ###
22
+ with op.batch_alter_table("budget", schema=None) as batch_op:
23
+ batch_op.add_column(sa.Column("currency", sa.String(length=3)))
24
+ # ### end Alembic commands ###
25
+
26
+
27
+ def downgrade():
28
+ # ### commands auto generated by Alembic - please adjust! ###
29
+ with op.batch_alter_table("task", schema=None) as batch_op:
30
+ batch_op.alter_column(
31
+ "difficulty", existing_type=sa.INTEGER(), nullable=True
32
+ )
33
+ # ### end Alembic commands ###
@@ -0,0 +1,57 @@
1
+ """add project bugdet table
2
+
3
+ Revision ID: 83e2f33a9b14
4
+ Revises: 45f739ef962a
5
+ Create Date: 2025-04-09 20:17:08.331320
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+ import sqlalchemy_utils
12
+ import sqlalchemy_utils
13
+ import uuid
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision = "83e2f33a9b14"
17
+ down_revision = "45f739ef962a"
18
+ branch_labels = None
19
+ depends_on = None
20
+
21
+
22
+ def upgrade():
23
+ # ### commands auto generated by Alembic - please adjust! ###
24
+ op.create_table(
25
+ "budget",
26
+ sa.Column(
27
+ "project_id",
28
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
29
+ default=uuid.uuid4,
30
+ nullable=False,
31
+ ),
32
+ sa.Column("revision", sa.Integer(), nullable=False),
33
+ sa.Column("name", sa.String(length=255), nullable=False),
34
+ sa.Column(
35
+ "id",
36
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
37
+ default=uuid.uuid4,
38
+ nullable=False,
39
+ ),
40
+ sa.Column("created_at", sa.DateTime(), nullable=True),
41
+ sa.Column("updated_at", sa.DateTime(), nullable=True),
42
+ sa.PrimaryKeyConstraint("id"),
43
+ )
44
+ with op.batch_alter_table("budget", schema=None) as batch_op:
45
+ batch_op.create_index(
46
+ batch_op.f("ix_budget_project_id"), ["project_id"], unique=False
47
+ )
48
+ # ### end Alembic commands ###
49
+
50
+
51
+ def downgrade():
52
+ # ### commands auto generated by Alembic - please adjust! ###
53
+ with op.batch_alter_table("budget", schema=None) as batch_op:
54
+ batch_op.drop_index(batch_op.f("ix_budget_project_id"))
55
+
56
+ op.drop_table("budget")
57
+ # ### end Alembic commands ###
@@ -0,0 +1,123 @@
1
+ """add budget entry table
2
+
3
+ Revision ID: 8ab98c178903
4
+ Revises: 83e2f33a9b14
5
+ Create Date: 2025-04-15 16:56:25.451489
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+ import sqlalchemy_utils
12
+ import sqlalchemy_utils
13
+ import uuid
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision = "8ab98c178903"
17
+ down_revision = "83e2f33a9b14"
18
+ branch_labels = None
19
+ depends_on = None
20
+
21
+ from zou.app.models.person import POSITION_TYPES, SENIORITY_TYPES
22
+
23
+
24
+ def upgrade():
25
+ # ### commands auto generated by Alembic - please adjust! ###
26
+ op.create_table(
27
+ "budget_entry",
28
+ sa.Column(
29
+ "budget_id",
30
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
31
+ default=uuid.uuid4,
32
+ nullable=False,
33
+ ),
34
+ sa.Column(
35
+ "department_id",
36
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
37
+ default=uuid.uuid4,
38
+ nullable=False,
39
+ ),
40
+ sa.Column(
41
+ "person_id",
42
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
43
+ default=uuid.uuid4,
44
+ nullable=True,
45
+ ),
46
+ sa.Column("start_date", sa.Date(), nullable=False),
47
+ sa.Column("months_duration", sa.Integer(), nullable=False),
48
+ sa.Column("daily_salary", sa.Float(), nullable=False),
49
+ sa.Column(
50
+ "position",
51
+ sqlalchemy_utils.types.choice.ChoiceType(POSITION_TYPES),
52
+ nullable=True,
53
+ ),
54
+ sa.Column(
55
+ "seniority",
56
+ sqlalchemy_utils.types.choice.ChoiceType(SENIORITY_TYPES),
57
+ nullable=True,
58
+ ),
59
+ sa.Column(
60
+ "id",
61
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
62
+ default=uuid.uuid4,
63
+ nullable=False,
64
+ ),
65
+ sa.Column("created_at", sa.DateTime(), nullable=True),
66
+ sa.Column("updated_at", sa.DateTime(), nullable=True),
67
+ sa.ForeignKeyConstraint(
68
+ ["budget_id"],
69
+ ["budget.id"],
70
+ ),
71
+ sa.ForeignKeyConstraint(
72
+ ["department_id"],
73
+ ["department.id"],
74
+ ),
75
+ sa.ForeignKeyConstraint(
76
+ ["person_id"],
77
+ ["person.id"],
78
+ ),
79
+ sa.PrimaryKeyConstraint("id"),
80
+ )
81
+ with op.batch_alter_table("budget_entry", schema=None) as batch_op:
82
+ batch_op.create_index(
83
+ batch_op.f("ix_budget_entry_budget_id"),
84
+ ["budget_id"],
85
+ unique=False,
86
+ )
87
+ batch_op.create_index(
88
+ batch_op.f("ix_budget_entry_department_id"),
89
+ ["department_id"],
90
+ unique=False,
91
+ )
92
+ batch_op.create_index(
93
+ batch_op.f("ix_budget_entry_person_id"),
94
+ ["person_id"],
95
+ unique=False,
96
+ )
97
+
98
+ with op.batch_alter_table("budget", schema=None) as batch_op:
99
+ batch_op.alter_column(
100
+ "project_id", existing_type=sa.UUID(), nullable=True
101
+ )
102
+ batch_op.create_foreign_key(
103
+ "budget_project_id_fkey", "project", ["project_id"], ["id"]
104
+ )
105
+
106
+ # ### end Alembic commands ###
107
+
108
+
109
+ def downgrade():
110
+ # ### commands auto generated by Alembic - please adjust! ###
111
+ with op.batch_alter_table("budget", schema=None) as batch_op:
112
+ batch_op.drop_constraint("budget_project_id_fkey", type_="foreignkey")
113
+ batch_op.alter_column(
114
+ "project_id", existing_type=sa.UUID(), nullable=False
115
+ )
116
+
117
+ with op.batch_alter_table("budget_entry", schema=None) as batch_op:
118
+ batch_op.drop_index(batch_op.f("ix_budget_entry_person_id"))
119
+ batch_op.drop_index(batch_op.f("ix_budget_entry_department_id"))
120
+ batch_op.drop_index(batch_op.f("ix_budget_entry_budget_id"))
121
+
122
+ op.drop_table("budget_entry")
123
+ # ### end Alembic commands ###
@@ -0,0 +1,133 @@
1
+ """modify salary scale model
2
+
3
+ Revision ID: d25118cddcaa
4
+ Revises: 7a16258f2fab
5
+ Create Date: 2025-04-23 22:49:26.645851
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+ import sqlalchemy_utils
12
+
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision = "d25118cddcaa"
16
+ down_revision = "7a16258f2fab"
17
+ branch_labels = None
18
+ depends_on = None
19
+
20
+ from zou.app.models.person import POSITION_TYPES, SENIORITY_TYPES
21
+
22
+
23
+ def upgrade():
24
+ # ### commands auto generated by Alembic - please adjust! ###
25
+ with op.batch_alter_table("salary_scale", schema=None) as batch_op:
26
+ batch_op.add_column(
27
+ sa.Column(
28
+ "position",
29
+ sqlalchemy_utils.types.choice.ChoiceType(POSITION_TYPES),
30
+ nullable=False,
31
+ )
32
+ )
33
+ batch_op.add_column(
34
+ sa.Column(
35
+ "seniority",
36
+ sqlalchemy_utils.types.choice.ChoiceType(SENIORITY_TYPES),
37
+ nullable=False,
38
+ )
39
+ )
40
+ batch_op.add_column(sa.Column("salary", sa.Integer(), nullable=False))
41
+ batch_op.drop_column("mid_artist_salary")
42
+ batch_op.drop_column("junior_lead_salary")
43
+ batch_op.drop_column("mid_lead_salary")
44
+ batch_op.drop_column("mid_supervisor_salary")
45
+ batch_op.drop_column("junior_supervisor_salary")
46
+ batch_op.drop_column("senior_artist_salary")
47
+ batch_op.drop_column("senior_lead_salary")
48
+ batch_op.drop_column("junior_artist_salary")
49
+ batch_op.drop_column("senior_supervisor_salary")
50
+
51
+ # ### end Alembic commands ###
52
+
53
+
54
+ def downgrade():
55
+ # ### commands auto generated by Alembic - please adjust! ###
56
+ with op.batch_alter_table("salary_scale", schema=None) as batch_op:
57
+ batch_op.add_column(
58
+ sa.Column(
59
+ "senior_supervisor_salary",
60
+ sa.INTEGER(),
61
+ autoincrement=False,
62
+ nullable=False,
63
+ )
64
+ )
65
+ batch_op.add_column(
66
+ sa.Column(
67
+ "junior_artist_salary",
68
+ sa.INTEGER(),
69
+ autoincrement=False,
70
+ nullable=False,
71
+ )
72
+ )
73
+ batch_op.add_column(
74
+ sa.Column(
75
+ "senior_lead_salary",
76
+ sa.INTEGER(),
77
+ autoincrement=False,
78
+ nullable=False,
79
+ )
80
+ )
81
+ batch_op.add_column(
82
+ sa.Column(
83
+ "senior_artist_salary",
84
+ sa.INTEGER(),
85
+ autoincrement=False,
86
+ nullable=False,
87
+ )
88
+ )
89
+ batch_op.add_column(
90
+ sa.Column(
91
+ "junior_supervisor_salary",
92
+ sa.INTEGER(),
93
+ autoincrement=False,
94
+ nullable=False,
95
+ )
96
+ )
97
+ batch_op.add_column(
98
+ sa.Column(
99
+ "mid_supervisor_salary",
100
+ sa.INTEGER(),
101
+ autoincrement=False,
102
+ nullable=False,
103
+ )
104
+ )
105
+ batch_op.add_column(
106
+ sa.Column(
107
+ "mid_lead_salary",
108
+ sa.INTEGER(),
109
+ autoincrement=False,
110
+ nullable=False,
111
+ )
112
+ )
113
+ batch_op.add_column(
114
+ sa.Column(
115
+ "junior_lead_salary",
116
+ sa.INTEGER(),
117
+ autoincrement=False,
118
+ nullable=False,
119
+ )
120
+ )
121
+ batch_op.add_column(
122
+ sa.Column(
123
+ "mid_artist_salary",
124
+ sa.INTEGER(),
125
+ autoincrement=False,
126
+ nullable=False,
127
+ )
128
+ )
129
+ batch_op.drop_column("salary")
130
+ batch_op.drop_column("seniority")
131
+ batch_op.drop_column("position")
132
+
133
+ # ### end Alembic commands ###
@@ -0,0 +1,39 @@
1
+ from . import routes
2
+ from flask import Blueprint
3
+ from zou.app.utils.api import configure_api_from_blueprint
4
+
5
+
6
+ def init_plugin(app, manifest):
7
+ """
8
+ Init the plugin.
9
+ """
10
+ app.logger.info("Loading plugin...")
11
+ routes_tuples = [(f"/hello-world", routes.HelloWorld)]
12
+
13
+ blueprint = Blueprint(manifest["id"], manifest["id"])
14
+ configure_api_from_blueprint(blueprint, routes_tuples)
15
+ app.register_blueprint(blueprint, url_prefix=f"/{manifest['id']}")
16
+
17
+
18
+ def pre_install(manifest):
19
+ """
20
+ Pre install the plugin.
21
+ """
22
+
23
+
24
+ def post_install(manifest):
25
+ """
26
+ Post install the plugin.
27
+ """
28
+
29
+
30
+ def pre_uninstall(manifest):
31
+ """
32
+ Pre uninstall the plugin.
33
+ """
34
+
35
+
36
+ def post_uninstall(manifest):
37
+ """
38
+ Post uninstall the plugin.
39
+ """
@@ -0,0 +1,6 @@
1
+ from flask_restful import Resource
2
+
3
+
4
+ class HelloWorld(Resource):
5
+ def get(self):
6
+ return {"message": "Hello, World!"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zou
3
- Version: 0.20.38
3
+ Version: 0.20.39
4
4
  Summary: API to store and manage the data of your animation production
5
5
  Home-page: https://zou.cg-wire.com
6
6
  Author: CG Wire
@@ -42,7 +42,7 @@ Requires-Dist: flask-socketio==5.5.1
42
42
  Requires-Dist: flask==3.1.0
43
43
  Requires-Dist: gazu==0.10.31
44
44
  Requires-Dist: gevent-websocket==0.10.1
45
- Requires-Dist: gevent==25.4.1
45
+ Requires-Dist: gevent==25.4.2
46
46
  Requires-Dist: gunicorn==23.0.0
47
47
  Requires-Dist: isoweek==1.3.3
48
48
  Requires-Dist: itsdangerous==2.2.0
@@ -68,9 +68,12 @@ Requires-Dist: pytz==2025.2
68
68
  Requires-Dist: redis==5.2.1
69
69
  Requires-Dist: requests==2.32.3
70
70
  Requires-Dist: rq==2.3.2
71
+ Requires-Dist: semver==3.0.4
71
72
  Requires-Dist: slackclient==2.9.4
73
+ Requires-Dist: spdx-license-list==3.26.0
72
74
  Requires-Dist: sqlalchemy_utils==0.41.2
73
75
  Requires-Dist: sqlalchemy==2.0.40
76
+ Requires-Dist: tomlkit==0.13.2
74
77
  Requires-Dist: ua-parser==1.0.1
75
78
  Requires-Dist: werkzeug==3.1.3
76
79
  Provides-Extra: prod
@@ -86,7 +89,7 @@ Requires-Dist: pytest==8.3.5; extra == "test"
86
89
  Provides-Extra: monitoring
87
90
  Requires-Dist: prometheus-flask-exporter==0.23.2; extra == "monitoring"
88
91
  Requires-Dist: pygelf==0.4.2; extra == "monitoring"
89
- Requires-Dist: sentry-sdk==2.26.1; extra == "monitoring"
92
+ Requires-Dist: sentry-sdk==2.27.0; extra == "monitoring"
90
93
  Provides-Extra: lint
91
94
  Requires-Dist: autoflake==2.3.1; extra == "lint"
92
95
  Requires-Dist: black==25.1.0; extra == "lint"