mirror of
https://github.com/System-End/nephthys.git
synced 2026-04-19 18:35:14 +00:00
Begin migration to blockkit (#164)
* Add blockkit * Use blockkit for header components * Add get_header_components() * Add a APP_HOME_VIEWS list to reduce repetition * Re-add `buttons = Actions()` bc that merged wrongly somehow * Remove Question Tags view button * Switch to blockkit for dashboard view Also splits get_ticket_status_pie_chart into two functions! * Add some form of codebase contributing documentation * Rename helper.py => dashboard.py * Fix caption for fallback pie image
This commit is contained in:
parent
ea03248efd
commit
2fe9460e31
12 changed files with 191 additions and 138 deletions
|
|
@ -4,7 +4,6 @@ Nephthys is the bot powering many support channels in the Hack Club Slack such a
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|
||||||
### Category tags
|
### Category tags
|
||||||
|
|
||||||
Category tags are used to classify tickets into broader categories such as "Fulfillment", "Identity", or "Platform Issues". When a new ticket is created, AI analyzes the message content and automatically assigns the most relevant category tag.
|
Category tags are used to classify tickets into broader categories such as "Fulfillment", "Identity", or "Platform Issues". When a new ticket is created, AI analyzes the message content and automatically assigns the most relevant category tag.
|
||||||
|
|
@ -41,7 +40,9 @@ Stale ticket handling is not working at the moment, but more features for dealin
|
||||||
|
|
||||||
### Leaderboard
|
### Leaderboard
|
||||||
|
|
||||||
At midnight UK time each day, you get to see the stats for the day in the team channel! Helpers can also see more detailed stats at any time on the app home for the bot!
|
At midnight UK time each day, you get to see the stats for the day in the team channel, as well as a summary of any old tickets that have been waiting for a helper response for over 5 days.
|
||||||
|
|
||||||
|
Helpers can see more detailed stats at any time on the app home for the bot!
|
||||||
|
|
||||||
### Assigned Tickets
|
### Assigned Tickets
|
||||||
|
|
||||||
|
|
@ -132,6 +133,10 @@ Your Slack app should now be running and connected to your Slack workspace!
|
||||||
|
|
||||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||||
|
|
||||||
|
A work-in progress document with some codebase conventions can be found at [docs/contributing.md](docs/contributing.md).
|
||||||
|
|
||||||
|
The [#nephthys-dev](https://hackclub.enterprise.slack.com/archives/C09QR2BH3GE) channel in the Slack is available for technical discussion or questions.
|
||||||
|
|
||||||
### Scripts
|
### Scripts
|
||||||
|
|
||||||
The codebase contains some scripts in the `nephthys/scripts/` directory to help with development and testing. They are documented below.
|
The codebase contains some scripts in the `nephthys/scripts/` directory to help with development and testing. They are documented below.
|
||||||
|
|
|
||||||
38
docs/contributing.md
Normal file
38
docs/contributing.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Nephthys contributing guide
|
||||||
|
|
||||||
|
This is not a full guide by any means, but it documents some bits about the codebase that are useful to know.
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
See the [README](../README.md#prerequisites) for instructions on setting up a development environment for the bot!
|
||||||
|
|
||||||
|
### Pre-commit hooks
|
||||||
|
|
||||||
|
It's recommended to install the pre-commit hooks (for code formatting and import sorting):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run pre-commit install
|
||||||
|
```
|
||||||
|
|
||||||
|
However, if you aren't able to do that, you can run them manually (after making your changes) with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run pre-commit run --all-files
|
||||||
|
```
|
||||||
|
|
||||||
|
## Branches, PRs, and commits
|
||||||
|
|
||||||
|
See the [Hack Club Contribution Guidelines](https://github.com/hackclub/.github/blob/main/CONTRIBUTING.md) for information about the GitHub Flow, how to name commits, and similar things.
|
||||||
|
|
||||||
|
## File structure
|
||||||
|
|
||||||
|
All the Python code lives in `nephthys/`. Take a look around to get a feel for what all the subfolders are.
|
||||||
|
|
||||||
|
Tip: You can ignore a lot of of the subfolders most of the time, and just look at the ones relevant to your feature/change.
|
||||||
|
|
||||||
|
We prefer splitting code up into many Python files over having large files with a lot of code.
|
||||||
|
|
||||||
|
## Slack Block Kit
|
||||||
|
|
||||||
|
We now have the [`blockkit` library](https://blockkit.botsignals.co/) (!!) for building fancy Slack messages and views with buttons, dropdowns, etc.
|
||||||
|
All new code should use `blockkit`, but note that existing code likely still uses raw JSON objects.
|
||||||
|
|
@ -10,8 +10,8 @@ from nephthys.utils.env import env
|
||||||
from nephthys.utils.logging import send_heartbeat
|
from nephthys.utils.logging import send_heartbeat
|
||||||
from nephthys.utils.performance import perf_timer
|
from nephthys.utils.performance import perf_timer
|
||||||
from nephthys.views.home.assigned import get_assigned_tickets_view
|
from nephthys.views.home.assigned import get_assigned_tickets_view
|
||||||
|
from nephthys.views.home.dashboard import get_dashboard_view
|
||||||
from nephthys.views.home.error import get_error_view
|
from nephthys.views.home.error import get_error_view
|
||||||
from nephthys.views.home.helper import get_helper_view
|
|
||||||
from nephthys.views.home.loading import get_loading_view
|
from nephthys.views.home.loading import get_loading_view
|
||||||
from nephthys.views.home.stats import get_stats_view
|
from nephthys.views.home.stats import get_stats_view
|
||||||
from nephthys.views.home.team_tags import get_team_tags_view
|
from nephthys.views.home.team_tags import get_team_tags_view
|
||||||
|
|
@ -51,7 +51,7 @@ async def open_app_home(home_type: str, client: AsyncWebClient, user_id: str):
|
||||||
):
|
):
|
||||||
match home_type:
|
match home_type:
|
||||||
case "dashboard":
|
case "dashboard":
|
||||||
view = await get_helper_view(slack_user=user_id, db_user=user)
|
view = await get_dashboard_view(slack_user=user_id, db_user=user)
|
||||||
case "assigned-tickets":
|
case "assigned-tickets":
|
||||||
view = await get_assigned_tickets_view(user)
|
view = await get_assigned_tickets_view(user)
|
||||||
case "team-tags":
|
case "team-tags":
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ from nephthys.utils.logging import send_heartbeat
|
||||||
from nephthys.utils.old_tickets import get_unanswered_tickets
|
from nephthys.utils.old_tickets import get_unanswered_tickets
|
||||||
from nephthys.utils.stats import calculate_daily_stats
|
from nephthys.utils.stats import calculate_daily_stats
|
||||||
from nephthys.utils.ticket_methods import get_question_message_link
|
from nephthys.utils.ticket_methods import get_question_message_link
|
||||||
from nephthys.views.home.components.ticket_status_pie import get_ticket_status_pie_chart
|
from nephthys.views.home.components.ticket_status_pie import (
|
||||||
|
generate_ticket_status_pie_image,
|
||||||
|
)
|
||||||
from prisma.models import Ticket
|
from prisma.models import Ticket
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -93,8 +95,8 @@ async def send_daily_stats():
|
||||||
since=today_midnight_london - timedelta(days=5)
|
since=today_midnight_london - timedelta(days=5)
|
||||||
)
|
)
|
||||||
|
|
||||||
pie_chart = await get_ticket_status_pie_chart(
|
pie_chart = await generate_ticket_status_pie_image(
|
||||||
raw=True, tz=timezone(now_london.utcoffset() or timedelta(0))
|
tz=timezone(now_london.utcoffset() or timedelta(0))
|
||||||
)
|
)
|
||||||
|
|
||||||
msg = f"""
|
msg = f"""
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ from nephthys.options.category_tags import get_category_tags
|
||||||
from nephthys.options.team_tags import get_team_tags
|
from nephthys.options.team_tags import get_team_tags
|
||||||
from nephthys.utils.env import env
|
from nephthys.utils.env import env
|
||||||
from nephthys.utils.performance import perf_timer
|
from nephthys.utils.performance import perf_timer
|
||||||
|
from nephthys.views.home import APP_HOME_VIEWS
|
||||||
|
|
||||||
app = AsyncApp(token=env.slack_bot_token, signing_secret=env.slack_signing_secret)
|
app = AsyncApp(token=env.slack_bot_token, signing_secret=env.slack_signing_secret)
|
||||||
|
|
||||||
|
|
@ -71,10 +72,6 @@ async def app_home_opened_handler(event: dict[str, Any], client: AsyncWebClient)
|
||||||
await on_app_home_opened(event, client)
|
await on_app_home_opened(event, client)
|
||||||
|
|
||||||
|
|
||||||
@app.action("dashboard")
|
|
||||||
@app.action("assigned-tickets")
|
|
||||||
@app.action("team-tags")
|
|
||||||
@app.action("my-stats")
|
|
||||||
async def manage_home_switcher(ack: AsyncAck, body, client: AsyncWebClient):
|
async def manage_home_switcher(ack: AsyncAck, body, client: AsyncWebClient):
|
||||||
await ack()
|
await ack()
|
||||||
user_id = body["user"]["id"]
|
user_id = body["user"]["id"]
|
||||||
|
|
@ -83,6 +80,10 @@ async def manage_home_switcher(ack: AsyncAck, body, client: AsyncWebClient):
|
||||||
await open_app_home(action_id, client, user_id)
|
await open_app_home(action_id, client, user_id)
|
||||||
|
|
||||||
|
|
||||||
|
for view in APP_HOME_VIEWS:
|
||||||
|
app.action(view.id)(manage_home_switcher)
|
||||||
|
|
||||||
|
|
||||||
@app.event("member_joined_channel")
|
@app.event("member_joined_channel")
|
||||||
async def handle_member_joined_channel(event: Dict[str, Any], client: AsyncWebClient):
|
async def handle_member_joined_channel(event: Dict[str, Any], client: AsyncWebClient):
|
||||||
await channel_join(ack=AsyncAck(), event=event, client=client)
|
await channel_join(ack=AsyncAck(), event=event, client=client)
|
||||||
|
|
|
||||||
17
nephthys/views/home/__init__.py
Normal file
17
nephthys/views/home/__init__.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class View:
|
||||||
|
name: str
|
||||||
|
id: str
|
||||||
|
# Not today
|
||||||
|
# render: Callable[[User | None], dict]
|
||||||
|
|
||||||
|
|
||||||
|
APP_HOME_VIEWS: list[View] = [
|
||||||
|
View("Dashboard", "dashboard"),
|
||||||
|
View("Assigned Tickets", "assigned-tickets"),
|
||||||
|
View("Team Tags", "team-tags"),
|
||||||
|
View("My Stats", "my-stats"),
|
||||||
|
]
|
||||||
|
|
@ -1,64 +1,52 @@
|
||||||
|
from blockkit import Actions
|
||||||
|
from blockkit import Button
|
||||||
|
from blockkit import Divider
|
||||||
|
from blockkit import Header
|
||||||
|
from blockkit.core import ModalBlock
|
||||||
|
|
||||||
from nephthys.utils.env import env
|
from nephthys.utils.env import env
|
||||||
|
from nephthys.views.home import APP_HOME_VIEWS
|
||||||
from prisma.models import User
|
from prisma.models import User
|
||||||
|
|
||||||
|
|
||||||
def header_buttons(current_view: str, user: User | None):
|
def header_buttons(current_view: str):
|
||||||
buttons = []
|
buttons = Actions()
|
||||||
|
|
||||||
buttons.append(
|
for view in APP_HOME_VIEWS:
|
||||||
{
|
style = Button.PRIMARY if view.id == current_view else None
|
||||||
"type": "button",
|
buttons.add_element(
|
||||||
"text": {"type": "plain_text", "text": "Dashboard", "emoji": True},
|
Button(
|
||||||
"action_id": "dashboard",
|
text=view.name,
|
||||||
**({"style": "primary"} if current_view != "dashboard" else {}),
|
action_id=view.id,
|
||||||
}
|
style=style,
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
buttons.append(
|
return buttons
|
||||||
{
|
|
||||||
"type": "button",
|
|
||||||
"text": {"type": "plain_text", "text": "Assigned Tickets", "emoji": True},
|
|
||||||
"action_id": "assigned-tickets",
|
|
||||||
**({"style": "primary"} if current_view != "assigned-tickets" else {}),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
buttons.append(
|
|
||||||
{
|
|
||||||
"type": "button",
|
|
||||||
"text": {"type": "plain_text", "text": "Team Tags", "emoji": True},
|
|
||||||
"action_id": "team-tags",
|
|
||||||
**({"style": "primary"} if current_view != "team-tags" else {}),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
buttons.append(
|
|
||||||
{
|
|
||||||
"type": "button",
|
|
||||||
"text": {"type": "plain_text", "text": "My Stats", "emoji": True},
|
|
||||||
"action_id": "my-stats",
|
|
||||||
**({"style": "primary"} if current_view != "my-stats" else {}),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
blocks = {"type": "actions", "elements": buttons}
|
|
||||||
return blocks
|
|
||||||
|
|
||||||
|
|
||||||
def title_line():
|
def title_line():
|
||||||
return {
|
return Header(f":rac_cute: {env.app_title}")
|
||||||
"type": "header",
|
|
||||||
"text": {
|
|
||||||
"type": "plain_text",
|
|
||||||
"text": f":rac_cute: {env.app_title}",
|
|
||||||
"emoji": True,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_header(user: User | None, current: str = "dashboard") -> list[dict]:
|
def get_header(user: User | None, current: str = "dashboard") -> list[dict]:
|
||||||
|
"""Returns the app home header in Slack API JSON format
|
||||||
|
|
||||||
|
Deprecated over using blockkit and `get_header_components()`
|
||||||
|
"""
|
||||||
return [
|
return [
|
||||||
title_line(),
|
title_line().build(),
|
||||||
header_buttons(current, user),
|
header_buttons(current).build(),
|
||||||
{"type": "divider"},
|
{"type": "divider"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_header_components(
|
||||||
|
user: User | None, current: str = "dashboard"
|
||||||
|
) -> list[ModalBlock]:
|
||||||
|
"""Returns the app home header as blockkit components"""
|
||||||
|
return [
|
||||||
|
title_line(),
|
||||||
|
header_buttons(current),
|
||||||
|
Divider(),
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from blockkit import Header
|
||||||
|
from blockkit import Section
|
||||||
|
from blockkit import Text
|
||||||
|
|
||||||
from nephthys.utils.stats import calculate_daily_stats
|
from nephthys.utils.stats import calculate_daily_stats
|
||||||
from nephthys.utils.stats import calculate_overall_stats
|
from nephthys.utils.stats import calculate_overall_stats
|
||||||
|
|
||||||
|
|
||||||
async def get_leaderboard_view():
|
async def get_leaderboard_components():
|
||||||
stats = await calculate_overall_stats()
|
stats = await calculate_overall_stats()
|
||||||
overall_leaderboard_lines = [
|
overall_leaderboard_lines = [
|
||||||
f"{i + 1}. <@{entry['user'].slackId}> - {entry['count']} closed"
|
f"{i + 1}. <@{entry['user'].slackId}> - {entry['count']} closed"
|
||||||
|
|
@ -41,38 +45,28 @@ async def get_leaderboard_view():
|
||||||
)
|
)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
Section()
|
||||||
"type": "section",
|
.add_field(
|
||||||
"fields": [
|
Text(
|
||||||
{
|
f"*Total Tickets*\nTotal: {stats.tickets_total}, "
|
||||||
"type": "mrkdwn",
|
f"Open: {stats.tickets_open}, "
|
||||||
"text": f"*Total Tickets*\nTotal: {stats.tickets_total}, Open: {stats.tickets_open}, In Progress: {stats.tickets_in_progress}, Closed: {stats.tickets_closed}\nHang time: {avg_hang_time_str}",
|
f"In Progress: {stats.tickets_in_progress}, "
|
||||||
},
|
f"Closed: {stats.tickets_closed}\n"
|
||||||
{
|
f"Hang time: {avg_hang_time_str}",
|
||||||
"type": "mrkdwn",
|
)
|
||||||
"text": f"*Past 24 Hours*\nTotal: {prev_day.new_tickets_total}, Open: {prev_day.new_tickets_still_open}, In Progress: {prev_day.assigned_today_in_progress}, Closed: {prev_day.closed_today}, Closed Today: {prev_day.closed_today_from_today}\nHang time: {avg_prev_day_hang_time_str}",
|
)
|
||||||
},
|
.add_field(
|
||||||
],
|
Text(
|
||||||
},
|
f"*Past 24 Hours*\nTotal: {prev_day.new_tickets_total}, "
|
||||||
{
|
f"Open: {prev_day.new_tickets_still_open}, "
|
||||||
"type": "header",
|
f"In Progress: {prev_day.assigned_today_in_progress}, "
|
||||||
"text": {
|
f"Closed: {prev_day.closed_today}, "
|
||||||
"type": "plain_text",
|
f"Closed Today: {prev_day.closed_today_from_today}\n"
|
||||||
"text": ":rac_lfg: leaderboard",
|
f"Hang time: {avg_prev_day_hang_time_str}",
|
||||||
"emoji": True,
|
)
|
||||||
},
|
),
|
||||||
},
|
Header(":rac_lfg: leaderboard"),
|
||||||
{
|
Section()
|
||||||
"type": "section",
|
.add_field(Text(f"*:summer25: overall*\n{overall_leaderboard_str}"))
|
||||||
"fields": [
|
.add_field(Text(f"*:mc-clock: past 24 hours*\n{prev_day_leaderboard_str}")),
|
||||||
{
|
|
||||||
"type": "mrkdwn",
|
|
||||||
"text": f"*:summer25: overall*\n{overall_leaderboard_str}",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "mrkdwn",
|
|
||||||
"text": f"*:mc-clock: past 24 hours*\n{prev_day_leaderboard_str}",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ from datetime import timezone
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from blockkit import Image
|
||||||
|
|
||||||
from nephthys.utils.bucky import upload_file
|
from nephthys.utils.bucky import upload_file
|
||||||
from nephthys.utils.env import env
|
from nephthys.utils.env import env
|
||||||
|
|
@ -12,10 +13,12 @@ from nephthys.utils.performance import perf_timer
|
||||||
from nephthys.utils.time import is_day
|
from nephthys.utils.time import is_day
|
||||||
from prisma.enums import TicketStatus
|
from prisma.enums import TicketStatus
|
||||||
|
|
||||||
|
LAST_DAYS = 7
|
||||||
|
|
||||||
async def get_ticket_status_pie_chart(
|
|
||||||
tz: timezone | None = None, raw: bool = False
|
async def generate_ticket_status_pie_image(tz: timezone | None = None) -> bytes:
|
||||||
) -> dict | bytes:
|
"""Generates a pie chart showing percentages of open/closed/in progress
|
||||||
|
tickets over the last 7 days, renders it as a PNG and returns it as bytes."""
|
||||||
is_daytime = is_day(tz) if tz else True
|
is_daytime = is_day(tz) if tz else True
|
||||||
|
|
||||||
if is_daytime:
|
if is_daytime:
|
||||||
|
|
@ -26,7 +29,7 @@ async def get_ticket_status_pie_chart(
|
||||||
bg_colour = "#181A1E"
|
bg_colour = "#181A1E"
|
||||||
|
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
one_week_ago = now - timedelta(days=7)
|
one_week_ago = now - timedelta(days=LAST_DAYS)
|
||||||
|
|
||||||
async with perf_timer("Fetching ticket counts from DB"):
|
async with perf_timer("Fetching ticket counts from DB"):
|
||||||
recently_closed_tickets = await env.db.ticket.count(
|
recently_closed_tickets = await env.db.ticket.count(
|
||||||
|
|
@ -76,29 +79,28 @@ async def get_ticket_status_pie_chart(
|
||||||
format="png",
|
format="png",
|
||||||
)
|
)
|
||||||
|
|
||||||
if raw:
|
return b.getvalue()
|
||||||
return b.getvalue()
|
|
||||||
|
|
||||||
|
async def ticket_status_pie_chart_component(tz: timezone | None = None):
|
||||||
|
pie_chart_image = await generate_ticket_status_pie_image(tz)
|
||||||
|
|
||||||
async with perf_timer("Uploading pie chart"):
|
async with perf_timer("Uploading pie chart"):
|
||||||
url = await upload_file(
|
url = await upload_file(
|
||||||
file=b.getvalue(),
|
file=pie_chart_image,
|
||||||
filename="ticket_status.png",
|
filename="ticket_status.png",
|
||||||
content_type="image/png",
|
content_type="image/png",
|
||||||
)
|
)
|
||||||
|
|
||||||
caption = "Ticket stats"
|
|
||||||
|
|
||||||
if not url:
|
if not url:
|
||||||
url = f"{env.base_url}/public/binoculars.png"
|
return Image(
|
||||||
caption = "looks like heidi's scrounging around for tickets in the trash"
|
image_url=f"{env.base_url}/public/binoculars.png",
|
||||||
|
alt_text="Heidi looking for tickets with binoculars",
|
||||||
|
title="looks like heidi's scrounging around for tickets in the trash",
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return Image(
|
||||||
"type": "image",
|
image_url=url,
|
||||||
"title": {
|
alt_text="Ticket Stats",
|
||||||
"type": "plain_text",
|
title=f"Ticket stats (last {LAST_DAYS} days)",
|
||||||
"text": caption,
|
)
|
||||||
"emoji": True,
|
|
||||||
},
|
|
||||||
"image_url": url,
|
|
||||||
"alt_text": "Ticket Stats",
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
|
from blockkit import Header
|
||||||
|
from blockkit import Home
|
||||||
|
|
||||||
from nephthys.utils.env import env
|
from nephthys.utils.env import env
|
||||||
from nephthys.utils.performance import perf_timer
|
from nephthys.utils.performance import perf_timer
|
||||||
from nephthys.views.home.components.header import get_header
|
from nephthys.views.home.components.header import get_header_components
|
||||||
from nephthys.views.home.components.leaderboards import get_leaderboard_view
|
from nephthys.views.home.components.leaderboards import get_leaderboard_components
|
||||||
from nephthys.views.home.components.ticket_status_pie import get_ticket_status_pie_chart
|
from nephthys.views.home.components.ticket_status_pie import (
|
||||||
|
ticket_status_pie_chart_component,
|
||||||
|
)
|
||||||
from nephthys.views.home.error import get_error_view
|
from nephthys.views.home.error import get_error_view
|
||||||
from prisma.models import User
|
from prisma.models import User
|
||||||
|
|
||||||
|
|
||||||
async def get_helper_view(slack_user: str, db_user: User | None):
|
async def get_dashboard_view(slack_user: str, db_user: User | None):
|
||||||
async with perf_timer("Fetching user info"):
|
async with perf_timer("Fetching user info"):
|
||||||
user_info_response = await env.slack_client.users_info(user=slack_user)
|
user_info_response = await env.slack_client.users_info(user=slack_user)
|
||||||
user_info = user_info_response.get("user")
|
user_info = user_info_response.get("user")
|
||||||
|
|
@ -27,26 +31,16 @@ async def get_helper_view(slack_user: str, db_user: User | None):
|
||||||
tz = pytz.timezone(tz_string)
|
tz = pytz.timezone(tz_string)
|
||||||
|
|
||||||
async with perf_timer("Rendering pie chart (total time)"):
|
async with perf_timer("Rendering pie chart (total time)"):
|
||||||
pie_chart = await get_ticket_status_pie_chart(tz)
|
pie_chart = await ticket_status_pie_chart_component(tz) # type: ignore (it works)
|
||||||
|
|
||||||
async with perf_timer("Generating leaderboard"):
|
async with perf_timer("Generating leaderboard"):
|
||||||
leaderboard = await get_leaderboard_view()
|
leaderboard = await get_leaderboard_components()
|
||||||
|
|
||||||
header = get_header(db_user, "dashboard")
|
return Home(
|
||||||
|
[
|
||||||
return {
|
*get_header_components(db_user, "dashboard"),
|
||||||
"type": "home",
|
Header(":rac_graph: funny circle and line things"),
|
||||||
"blocks": [
|
|
||||||
*header,
|
|
||||||
{
|
|
||||||
"type": "header",
|
|
||||||
"text": {
|
|
||||||
"type": "plain_text",
|
|
||||||
"text": ":rac_graph: funny circle and line things",
|
|
||||||
"emoji": True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pie_chart,
|
pie_chart,
|
||||||
*leaderboard,
|
*leaderboard,
|
||||||
],
|
]
|
||||||
}
|
).build()
|
||||||
|
|
@ -8,6 +8,7 @@ dependencies = [
|
||||||
"aiohttp>=3.11.14",
|
"aiohttp>=3.11.14",
|
||||||
"apscheduler>=3.10.4",
|
"apscheduler>=3.10.4",
|
||||||
"astral>=3.2",
|
"astral>=3.2",
|
||||||
|
"blockkit>=2.1.2",
|
||||||
"matplotlib>=3.10.3",
|
"matplotlib>=3.10.3",
|
||||||
"numpy>=2.3.1",
|
"numpy>=2.3.1",
|
||||||
"openai>=2.8.0",
|
"openai>=2.8.0",
|
||||||
|
|
|
||||||
11
uv.lock
generated
11
uv.lock
generated
|
|
@ -112,6 +112,15 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" },
|
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blockkit"
|
||||||
|
version = "2.1.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/83/21/ad8c9c05251d4a56d6c4a4b95db73e3385e33754e0034f65f1361156c57a/blockkit-2.1.2.tar.gz", hash = "sha256:65788c6ac924a432deae1f2790f716a311176e1947070068f5bb646d98c8186a", size = 67151, upload-time = "2025-09-28T17:37:37.48Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f8/77/44c5c471b08a600196751465665f1115f28806553f328a5a9ae31e935646/blockkit-2.1.2-py3-none-any.whl", hash = "sha256:f8e6bcffc73b2f168b2b5a7c5e9303961e9b5c167759eeda4811910072bfc706", size = 20131, upload-time = "2025-09-28T17:37:36.092Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2025.6.15"
|
version = "2025.6.15"
|
||||||
|
|
@ -640,6 +649,7 @@ dependencies = [
|
||||||
{ name = "aiohttp" },
|
{ name = "aiohttp" },
|
||||||
{ name = "apscheduler" },
|
{ name = "apscheduler" },
|
||||||
{ name = "astral" },
|
{ name = "astral" },
|
||||||
|
{ name = "blockkit" },
|
||||||
{ name = "matplotlib" },
|
{ name = "matplotlib" },
|
||||||
{ name = "numpy" },
|
{ name = "numpy" },
|
||||||
{ name = "openai" },
|
{ name = "openai" },
|
||||||
|
|
@ -669,6 +679,7 @@ requires-dist = [
|
||||||
{ name = "aiohttp", specifier = ">=3.11.14" },
|
{ name = "aiohttp", specifier = ">=3.11.14" },
|
||||||
{ name = "apscheduler", specifier = ">=3.10.4" },
|
{ name = "apscheduler", specifier = ">=3.10.4" },
|
||||||
{ name = "astral", specifier = ">=3.2" },
|
{ name = "astral", specifier = ">=3.2" },
|
||||||
|
{ name = "blockkit", specifier = ">=2.1.2" },
|
||||||
{ name = "matplotlib", specifier = ">=3.10.3" },
|
{ name = "matplotlib", specifier = ">=3.10.3" },
|
||||||
{ name = "numpy", specifier = ">=2.3.1" },
|
{ name = "numpy", specifier = ">=2.3.1" },
|
||||||
{ name = "openai", specifier = ">=2.8.0" },
|
{ name = "openai", specifier = ">=2.8.0" },
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue