Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev -> Master focalboard #315

Merged
merged 3 commits into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"token": "do_not_set_here_please_go_to_config_override",
"board_id": "do_not_set_here_please_go_to_config_override"
},
"focalboard": {
"token": "do_not_set_here_please_go_to_config_override",
"url": "do_not_set_here_please_go_to_config_override"
},
"sheets": {
"api_key_path": "do_not_set_here_please_go_to_config_override",
"authors_sheet_key": "do_not_set_here_please_go_to_config_override",
Expand Down
4 changes: 4 additions & 0 deletions src/app_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .tg.sender import TelegramSender
from .tg.tg_client import TgClient
from .trello.trello_client import TrelloClient
from .focalboard.focalboard_client import FocalboardClient
from .utils.singleton import Singleton
from .vk.vk_client import VkClient

Expand Down Expand Up @@ -54,6 +55,9 @@ def __init__(
self.trello_client = TrelloClient(
trello_config=config_manager.get_trello_config()
)
self.focalboard_client = FocalboardClient(
focalboard_config=config_manager.get_focalboard_config()
)
self.facebook_client = FacebookClient(
facebook_config=config_manager.get_facebook_config()
)
Expand Down
6 changes: 6 additions & 0 deletions src/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@ def init_handlers(self):
direct_message_only(handlers.get_tasks_report_advanced),
"получить список задач из Trello (расширенный)",
)
self.add_manager_handler(
"get_tasks_report_focalboard",
CommandCategories.SUMMARY,
direct_message_only(handlers.get_tasks_report_focalboard),
"получить список задач из Focalboard",
)
self.add_manager_handler(
"get_articles_arts",
CommandCategories.SUMMARY,
Expand Down
3 changes: 3 additions & 0 deletions src/config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ def get_latest_jobs_config(self):
def get_trello_config(self):
return self.get_latest_config().get(consts.TRELLO_CONFIG, {})

def get_focalboard_config(self):
return self.get_latest_config().get(consts.FOCALBOARD_CONFIG, {})

def get_telegram_config(self):
return self.get_latest_config().get(consts.TELEGRAM_CONFIG, {})

Expand Down
2 changes: 2 additions & 0 deletions src/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class AppSource(Enum):
# Upper level config keys
TELEGRAM_CONFIG = "telegram"
TRELLO_CONFIG = "trello"
FOCALBOARD_CONFIG = "focalboard"
SHEETS_CONFIG = "sheets"
DRIVE_CONFIG = "drive"
FACEBOOK_CONFIG = "facebook"
Expand Down Expand Up @@ -166,6 +167,7 @@ class GetTasksReportData:
LISTS = "lists"
INTRO_TEXT = "introduction"
INCLUDE_LABELS = "include_labels"
USE_FOCALBOARD = "use_focalboard"


class ManageRemindersData:
Expand Down
161 changes: 161 additions & 0 deletions src/focalboard/focalboard_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import json
import logging
from typing import List
from urllib.parse import quote, urljoin

import requests

from ..strings import load
from ..utils.singleton import Singleton
from ..trello import trello_objects as objects

logger = logging.getLogger(__name__)


class FocalboardClient(Singleton):
def __init__(self, focalboard_config=None):
if self.was_initialized():
return

self._focalboard_config = focalboard_config
self._update_from_config()
logger.info("FocalboardClient successfully initialized")

def get_boards_for_user(self, user_id=None):
_, data = self._make_request("api/v2/teams/0/boards")
boards = [objects.TrelloBoard.from_focalboard_dict(board) for board in data]
logger.debug(f"get_boards_for_user: {boards}")
return boards

def get_lists(self, board_id):
# if not board_id:
# board_id = self.board_id
# TODO make it more efficient
# essentially all list information is already passed via boards handler
_, data = self._make_request(f"api/v2/teams/0/boards")
list_data = [
prop
for prop in [board for board in data if board["id"] == board_id][0][
"cardProperties"
]
if prop["name"] == "List"
][0]
lists_data = list_data["options"]
lists = [
objects.TrelloList.from_focalboard_dict(trello_list, board_id)
for trello_list in lists_data
]
logger.debug(f"get_lists: {lists}")
return lists

def get_list(self, board_id, list_id):
_, data = self._make_request(f"api/v2/teams/0/boards")
lists_data = [
prop
for prop in [board for board in data if board["id"] == board_id][0][
"cardProperties"
]
if prop["name"] == "List"
][0]["options"]
lst = [
objects.TrelloList.from_focalboard_dict(trello_list, board_id)
for trello_list in lists_data
if trello_list["id"] == list_id
][0]
logger.debug(f"get_list: {lst}")
return lst

def _get_list_property(self, board_id):
_, data = self._make_request(f"api/v2/teams/0/boards")
return [
prop
for prop in [board for board in data if board["id"] == board_id][0][
"cardProperties"
]
if prop["name"] == "List"
][0]["id"]

def _get_member_property(self, board_id):
_, data = self._make_request(f"api/v2/teams/0/boards")
return [
prop
for prop in [board for board in data if board["id"] == board_id][0][
"cardProperties"
]
if prop["name"] == "Assignee"
][0]["id"]

def get_members(self, board_id) -> List[objects.TrelloMember]:
_, data = self._make_request(f"api/v2/boards/{board_id}/members")
members = []
for member in data:
_, data = self._make_request(f"api/v2/users/{member['userId']}")
members.append(objects.TrelloMember.from_focalboard_dict(data))
logger.debug(f"get_members: {members}")
return members

def get_cards(self, list_ids, board_id):
_, data = self._make_request(f"api/v2/boards/{board_id}/blocks?all=true")
cards = []
# TODO: move this to app state
members = self.get_members(board_id)
lists = self.get_lists(board_id)
list_prop = self._get_list_property(board_id)
member_prop = self._get_member_property(board_id)
view_id = [card_dict for card_dict in data if card_dict["type"] == "view"][0][
"id"
]
data = [
card_dict
for card_dict in data
if card_dict["type"] == "card"
and card_dict["fields"]["properties"].get(list_prop, "") in list_ids
]
for card_dict in data:
card = objects.TrelloCard.from_focalboard_dict(card_dict)
card.url = urljoin(self.url, f"{board_id}/{view_id}/{card.id}")
print(card.url)
# TODO: move this to app state
for trello_list in lists:
if trello_list.id == card_dict["fields"]["properties"].get(
list_prop, ""
):
card.lst = trello_list
break
else:
logger.error(f"List name not found for {card}")
# TODO: move this to app state
if len(card_dict["fields"]["properties"].get(member_prop, [])) > 0:
for member in members:
if member.id in card_dict["fields"]["properties"].get(
member_prop, []
):
card.members.append(member)
if len(card.members) == 0:
logger.error(f"Member username not found for {card}")
cards.append(card)
logger.debug(f"get_cards: {cards}")
return cards

def update_config(self, new_focalboard_config):
"""To be called after config automatic update"""
self._focalboard_config = new_focalboard_config
self._update_from_config()

def _update_from_config(self):
"""Update attributes according to current self._focalboard_config"""
self.token = self._focalboard_config["token"]
self.url = self._focalboard_config["url"]
self.headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": f"Bearer {self.token}",
"X-Requested-With": "XMLHttpRequest",
}

def _make_request(self, uri, payload={}):
response = requests.get(
urljoin(self.url, uri), params=payload, headers=self.headers
)
logger.debug(f"{response.url}")
return response.status_code, response.json()
4 changes: 4 additions & 0 deletions src/jobs/config_updater_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ def _execute(
app_context.trello_client.update_config(
job_scheduler.config_manager.get_trello_config()
)
# update config['focalboard']
app_context.focalboard_client.update_config(
job_scheduler.config_manager.get_focalboard_config()
)
# update config['sheets']
app_context.sheets_client.update_config(
job_scheduler.config_manager.get_sheets_config()
Expand Down
4 changes: 3 additions & 1 deletion src/tg/handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@

# Admin (developer) handlers
from .get_roles_for_member_handler import get_roles_for_member
from .get_tasks_report_handler import get_tasks_report, get_tasks_report_advanced
from .get_tasks_report_handler import (
get_tasks_report, get_tasks_report_advanced, get_tasks_report_focalboard
)
from .help_handler import help
from .list_chats_handler import list_chats
from .list_job_handler import list_jobs
Expand Down
2 changes: 2 additions & 0 deletions src/tg/handlers/get_board_credentials_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@


def get_board_credentials(update: telegram.Update, tg_context):
if update.message.chat_id < 0:
return
member = next((
member for member in DBClient().get_all_members()
if member.telegram == f"@{get_sender_username(update)}"
Expand Down
31 changes: 26 additions & 5 deletions src/tg/handlers/get_tasks_report_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ def get_tasks_report(update: telegram.Update, tg_context: telegram.ext.CallbackC
return


@manager_only
def get_tasks_report_focalboard(update: telegram.Update, tg_context: telegram.ext.CallbackContext):
_get_task_report_base(update, tg_context, advanced=False, use_focalboard=True)

return


@manager_only
def get_tasks_report_advanced(
update: telegram.Update, tg_context: telegram.ext.CallbackContext
Expand All @@ -34,11 +41,17 @@ def get_tasks_report_advanced(


def _get_task_report_base(
update: telegram.Update, tg_context: telegram.ext.CallbackContext, advanced: bool
update: telegram.Update,
tg_context: telegram.ext.CallbackContext,
advanced: bool,
use_focalboard: bool = False
):
app_context = AppContext()

boards_list = app_context.trello_client.get_boards_for_user()
if use_focalboard:
boards_list = app_context.focalboard_client.get_boards_for_user()
else:
boards_list = app_context.trello_client.get_boards_for_user()
boards_list_formatted = "\n".join(
[f"{i + 1}) {brd.name}" for i, brd in enumerate(boards_list)]
)
Expand All @@ -47,6 +60,7 @@ def _get_task_report_base(
tg_context.chat_data[consts.GetTasksReportData.LISTS] = [
lst.to_dict() for lst in boards_list
]
tg_context.chat_data[consts.GetTasksReportData.USE_FOCALBOARD] = use_focalboard
tg_context.chat_data[TASK_NAME] = {
consts.NEXT_ACTION: consts.PlainTextUserAction.GET_TASKS_REPORT__ENTER_BOARD_NUMBER.value
}
Expand All @@ -60,15 +74,22 @@ def _get_task_report_base(


def generate_report_messages(
board_id: str, list_id: str, introduction: str, add_labels: bool
board_id: str, list_id: str, introduction: str, add_labels: bool, use_focalboard: bool
) -> List[str]:
app_context = AppContext()
paragraphs = [] # list of paragraph strings

trello_list = app_context.trello_client.get_list(list_id)
if use_focalboard:
trello_list = app_context.focalboard_client.get_list(board_id, list_id)
else:
trello_list = app_context.trello_client.get_list(list_id)
paragraphs.append(load("common__bold_wrapper", arg=trello_list.name))

list_cards = app_context.trello_client.get_cards([list_id], board_id)
if use_focalboard:
list_cards = app_context.focalboard_client.get_cards([list_id], board_id)
else:
list_cards = app_context.trello_client.get_cards([list_id], board_id)
print(list_cards)
paragraphs += _create_paragraphs_from_cards(
list_cards, introduction, add_labels, app_context
)
Expand Down
15 changes: 12 additions & 3 deletions src/tg/handlers/user_message_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from ...strings import load
from ...tg.handlers import get_tasks_report_handler
from ...trello.trello_client import TrelloClient
from ...focalboard.focalboard_client import FocalboardClient
from .utils import get_chat_id, get_chat_name, get_sender_id, reply

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -84,13 +85,19 @@ def handle_user_message(
return
elif next_action == PlainTextUserAction.GET_TASKS_REPORT__ENTER_BOARD_NUMBER:
trello_client = TrelloClient()
focalboard_client = FocalboardClient()
try:
board_list = tg_context.chat_data[consts.GetTasksReportData.LISTS]
use_focalboard = tg_context.chat_data[consts.GetTasksReportData.USE_FOCALBOARD]
list_idx = int(user_input) - 1
assert 0 <= list_idx < len(board_list)
board_id = board_list[list_idx]["id"]
trello_lists = trello_client.get_lists(board_id)
trello_lists = trello_lists[::-1]
if use_focalboard:
trello_lists = focalboard_client.get_lists(board_id)
trello_lists = trello_lists[::-1]
else:
trello_lists = trello_client.get_lists(board_id)
trello_lists = trello_lists[::-1]
except Exception as e:
logger.warning(e)
reply(
Expand All @@ -103,6 +110,7 @@ def handle_user_message(
return

command_data[consts.GetTasksReportData.BOARD_ID] = board_id
command_data[consts.GetTasksReportData.USE_FOCALBOARD] = use_focalboard
command_data[consts.GetTasksReportData.LISTS] = [
lst.to_dict() for lst in trello_lists
]
Expand Down Expand Up @@ -552,8 +560,9 @@ def handle_task_report(command_data, add_labels, update):
board_id = command_data[consts.GetTasksReportData.BOARD_ID]
list_id = command_data[consts.GetTasksReportData.LIST_ID]
introduction = command_data[consts.GetTasksReportData.INTRO_TEXT]
use_focalboard = command_data[consts.GetTasksReportData.USE_FOCALBOARD]
messages = get_tasks_report_handler.generate_report_messages(
board_id, list_id, introduction, add_labels
board_id, list_id, introduction, add_labels, use_focalboard=use_focalboard
)
for message in messages:
reply(message, update)
Expand Down
Loading
Loading