paint-brush
怎么样才能为 Trello Board 处理建立 Python CLI 程序流程(第 1 一部分) 经由@elainechan01
2,334 讀數
2,334 讀數

如何为 Trello Board 管理创建 Python CLI 程序(第 1 部分)

經過 Elaine Yun Ru Chan19m2023/08/15
Read on Terminal Reader

太長; 讀書

正如维基百科所述,“命令行界面 (CLI) 是一种与设备或计算机程序进行交互的方式,其中包含来自用户或客户端的命令以及来自设备或程序的响应,以文本行的形式。” 换句话说,CLI 程序是用户使用命令行通过提供要执行的指令来与程序交互的程序。 许多日常软件都包装为 CLI 程序。以 vim 文本编辑器为例 - 这是任何 UNIX 系统附带的工具,只需在终端中运行 vim <FILE> 即可激活它。 关于 Google Cloud CLI,让我们深入剖析 CLI 程序。
featured image - 如何为 Trello Board 管理创建 Python CLI 程序(第 1 部分)
Elaine Yun Ru Chan HackerNoon profile picture
0-item
免责书面声明:本教程视频选用观众具备着 Python、API、Git 和标段检验的根基只是。
我会碰到过各方面有着最酷動畫的 CLI 软文,这我不想想要知道 - 我有无能够 升级系统我的“简单版”石眉刀布中小学品牌?


嗨,我们来玩吧!选择你的战士(石头、剪刀、布):石头

什么是 CLI 程序?

照句维基互动百科所论,“运行操作命令行页面 (CLI) 是一个种与装置或计算方法机软件程序流程图去通讯的途径,在这其中包函基玩家或买家高端的运行操作命令和基装置或软件程序流程图的卡死,以文件行的模式。”


与其说,CLI 源编译系统是用户的安全使用命令提示符行借助作为要来执行的指今来与源编译系统交互方式的源编译系统。


许多日常软件都包装为 CLI 程序。以vim文本编辑器为例 - 这是任何 UNIX 系统附带的工具,只需在终端中运行vim <FILE>即可激活它。


关干 ,我能们进一步分享一下 CLI 方式。

论点

技术指标表(技术指标表)是可以提供给过程的数据信息项。它大多数被称之为区域技术指标表,为了患者是依据其区域来标示的。


例如,当我们想要在核心部分设置project属性时,我们运行gcloud config set project <PROJECT_ID>


有必要目光的是,.我能够 将其转为为
争论不休的内容
精氨酸0可儿
精氨酸1配备
…… ……

命令

ps命令是向计算方法机出具汇编指令的参数表数组。


基于前面的示例,我们通过运行gcloud config set project <PROJECT_ID>在核心部分设置project属性


换句话说, set是一个命令。

可选命令

一般而言,强制性是须要的,但.我就会特例。基于程度的用例,.我就会设定必选强制性。


回顾一下gcloud config命令,正如其官方文档中所述, gcloud config是一个可让您修改属性的命令组。用法是这样的:

 gcloud config GROUP | COMMAND [GCLOUD_WIDE_FLAG … ]

其中 COMMAND 可以是setlist等等……(请注意,GROUP 是config

选项

按钮是调整指令道德行为的叁数的数据款式。两者是用“-”或“--”显示的键值对。


回到gcloud config命令组的使用,在本例中,选项是GCLOUD_WIDE_FLAG


例如,假设我们想显示命令的详细用法和说明,我们运行gcloud config set –help 。换句话说, --help是选项。


另一个例子是,当我们想要在特定项目的计算部分设置区域属性时,我们运行gcloud config set compute <ZONE_NAME> –project=<PROJECT_ID> 。换句话说, --project是一个保存值<PROJECT_ID>的选项。


一致主要的是要注重,用户的观点普通并不主要。

强制选项

选择项,比如它的姓名这样,常常是必选的,但也行全屋定制为强硬的。


例如,当我们想要创建 dataproc 集群时,我们运行gcloud dataproc clusters create <CLUSTER_NAME> –region=<REGION> 。正如他们的使用文档中所述:

 gcloud dataproc clusters create (CLUSTER: –region=REGION)

如果之前未配置过--region标志,则该标志是必需的。

空头期权与多头期权

短选项以-开头,后跟单个字母数字字符,而长选项以--开头,后跟多个字符。当用户确定自己想要什么时,可以将短选项视为快捷方式,而长选项则更具可读性。


你选择了摇滚!计算机现在将做出选择。

通过本教程我们将实现什么目标?

所以咧我撒了谎……各位并不会试试在线升级注意的石块眉刀布 CLI 编译程序。相反的成语,我们大家看说一下本质游戏的画面:

纲要和目标

您的团队使用 Trello 来跟踪项目的问题和进度。您的团队正在寻找一种更简化的与董事会交互的方式 - 类似于通过终端创建新的 GitHub 存储库。该团队请您创建一个 CLI 程序,其基本要求是能够将新卡添加到看板的“待办事项”列中。


会按照以上所述追求,我门借助名词解释其追求来起草、拟定我门的 CLI 源程序:


功能要求

  • 用户可以将新卡添加到板上的列中
    • 所需输入:列、卡名称
    • 可选输入:卡片描述、卡片标签(从现有中选择)

非功能性需求

  • 提示用户提供 Trello 帐户访问权限(授权)的程序
  • 提示用户设置要使用哪个 Trello 看板的程序(配置)

可选要求

  • 用户可以向板上添加新列
  • 用户可以向板上添加新标签
  • 用户可以看到所有列的简化/详细视图


特征提取上面相关内容,让我们还可以将 CLI 小程序的ftp命令和页面设置方式变为:

根据需求的 CLI 结构的详细表格视图


Ps 不要担心最后两列,我们稍后会了解......


而小编的方法堆栈,小编将始终坚持这这一点:


单元测试

  • py测试
  • pytest 模拟
  • cli 测试助手

特雷洛

  • py-trello(Trello SDK 的 Python 包装器)

命令行界面

  • 打字机
  • 富有的
  • 简单术语菜单

实用程序(杂项)

  • python-dotenv

时间线

让我们将营业部分清理在这个工作,下例是您能够 期待已久的段落:


第1部分

  • py-trello业务逻辑的实现

第2部分

  • CLI业务逻辑的实现
  • 将 CLI 程序作为包分发

第三部分

  • 可选功能要求的实现
  • 套餐更新


电脑选择了剪刀!让我们看看谁赢得了这场战斗……

让我们开始吧

文件夹结构

最终目标是将 CLI 应用程序用于包收发到上。由此,需用那样的设制:
 trellocli/ __init__.py __main__.py models.py cli.py trelloservice.py tests/ test_cli.py test_trelloservice.py README.md pyproject.toml .env .gitignore


接下来是对每项文件下载和/或目次的深刻科研:
  • trellocli :充当用户使用的包名称,例如pip install trellocli
    • __init__.py :代表包的根目录,使该文件夹符合Python包
    • __main__.py :定义入口点,并允许用户使用-m标志来运行模块而无需指定文件路径,例如, python -m <module_name>替换python -m <parent_folder>/<module_name>.py
    • models.py :存储全局使用的类,例如 API 响应预期符合的模型
    • cli.py :存储 CLI 命令和选项的业务逻辑
    • trelloservice.py :存储与py-trello交互的业务逻辑
  • tests :存储程序的单元测试
    • test_cli.py :存储 CLI 实现的单元测试
    • test_trelloservice.py :存储与py-trello交互的单元测试
  • README.md :存储程序的文档
  • pyproject.toml :存储包的配置和需求
  • .env :存储环境变量
  • .gitignore :指定版本控制期间要忽略(不跟踪)的文件


关以发布信息 Python 包的更图解原因分析,请察看以下的一篇经典文章很好的经典文章:

设置

在就开始很久,我门先知晓下包的设施。


从包中的__init__.py文件开始,该文件将存储包常量和变量,例如应用程序名称和版本。在我们的例子中,我们想要初始化以下内容:

  • 应用程序名称
  • 版本
  • 成功和错误常量
# trellocli/__init__.py __app_name__ = "trellocli" __version__ = "0.1.0" ( SUCCESS, TRELLO_WRITE_ERROR, TRELLO_READ_ERROR ) = range(3) ERRORS = { TRELLO_WRITE_ERROR: "Error when writing to Trello", TRELLO_READ_ERROR: "Error when reading from Trello" }


转到__main__.py文件,程序的主流程应该存储在此处。在我们的例子中,我们将存储 CLI 程序入口点,假设cli.py中有一个可调用函数。

 # trellocli/__main__.py from trellocli import cli def main(): # we'll modify this later - after the implementation of `cli.py` pass if __name__ == "__main__": main()


现在包已经设置完毕,让我们来看看更新我们的README.md文件(主要文档)。我们没有必须遵循的特定结构,但一个好的自述文件应包含以下内容:

  • 概述
  • 安装和要求
  • 入门和使用
如若您想更好入地知道,可以阅读写作另外篇挺好的论文:


她是想必该如何建设某项主要目的自述文件夹
<!--- README.md --> # Overview # Getting Started # Usage # Architecture ## Data Flow ## Tech Stack # Running Tests # Next Steps # References


大家公司还抹去骨架 - 大家公司稍前会换回这样相关问题。


立刻,我要们会按照分配包的元参数
# pyproject.toml [project] name = "trellocli_<YOUR_USERNAME>" version = "0.1.0" authors = [ { name = "<YOUR_NAME>", email = "<YOUR_EMAIL>" } ] description = "Program to modify your Trello boards from your computer's command line" readme = "README.md" requires-python = ">=3.7" classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] dependencies = [] [project.urls] "Homepage" = ""


请主意您肯定重设的占位符,这类您的粉丝名、您的真实姓名……


其他个部分,各位临时将首页导航 URL 留空。各位将在将其公布的到 GitHub 后进行改动。各位还将临时将依耐项个部分留空,并立刻获取。


列表中的下一个是.env文件,我们在其中存储 API 机密和密钥等环境变量。请务必注意,Git 不应跟踪此文件,因为它包含敏感信息。


在我们的例子中,我们将在此处存储 Trello 凭据。要在 Trello 中创建 Power-Up,请遵循。更具体地说,根据py-trello的使用情况,由于我们打算在应用程序中使用 OAuth,因此我们需要以下内容来与 Trello 交互:

  • API 密钥(用于我们的应用程序)
  • API Secret(对于我们的应用程序)
  • 令牌(用户授予对其数据的访问权限的令牌)


检索到 API 密钥和秘密后,将它们存储在.env文件中

# .env TRELLO_API_KEY=<your_api_key> TRELLO_API_SECRET=<your_api_secret>


最后但并非最不重要的一点是,让我们使用可以找到的模板 Python .gitignore 。请注意,这对于确保我们的.env文件永远不会被跟踪至关重要 - 如果在某个时刻,我们的.env文件被跟踪,即使我们在后续步骤中删除了该文件,损坏也已经完成,恶意行为者可以追踪到之前的文件敏感信息的补丁。


现在设置已完成,让我们将更改推送到 GitHub。根据pyproject.toml中指定的元数据,请记住相应地更新您的许可证和主页 URL。有关如何编写更好的提交的参考:


同一比较适合注重的方法:

单元测试

在着手创作软件检测检测在之前,请一定要目光,由于他们将要实用 API,因此 他们将颁布模以软件检测检测,便于就能软件检测检测他们的系统软件,而不会面临 API 停用的可能性。它是 Real Python 的另个篇有关模以软件检测检测的难忘一篇文章:


基于功能需求,我们主要关心的是允许用户添加新卡。引用py-trello中的方法: 。为此,我们必须从List类调用add_card方法,该方法可以从Board类的get_list函数中检索,而 Board 类可以检索该方法......


您知道了步骤 - 他们所需大多引导手段才行到了结果英文的地,让他们用于言来体现:
  • 测试检索客户端的令牌
  • 测试检索板
  • 测试检索板
  • 测试从板上检索列表
  • 测试检索列表
  • 测试从板上检索标签
  • 测试检索标签
  • 测试添加卡
  • 测试向卡片添加标签


一样的很重要的是要主意,在创作单元尺寸检测时,他们期盼他们的检测尽机会广泛应用 - 它能良好地工作不对吗?它包括了他们规划的不同几个方面吗?


但是,由于本技巧的目标,我门将采用仅审核胜利 装修案例来简单化时候。


在深入研究代码之前,让我们修改pyproject.toml文件以包含编写/运行单元测试所需的依赖项。

 # pyproject.toml [project] dependencies = [ "pytest==7.4.0", "pytest-mock==3.11.1" ]


接下来,让我们激活 virtualenv 并运行pip install .安装依赖项。


做好后,自己后面编程几个试验英文。一半来说一,自己的试验英文肯定包涵要刷新页面的模以仿真加载、完成利用模以仿真加载修护刷新页面值来试试试验英文的涵数的补丁怎么安装,、后面对涵数的资源调用。搜素用户账户仿问令牌的举例试验英文以下的:
 # tests/test_trelloservice.py # module imports from trellocli import SUCCESS from trellocli.trelloservice import TrelloService from trellocli.models import * # dependencies imports # misc imports def test_get_access_token(mocker): """Test to check success retrieval of user's access token""" mock_res = GetOAuthTokenResponse( token="test", token_secret="test", status_code=SUCCESS ) mocker.patch( "trellocli.trelloservice.TrelloService.get_user_oauth_token", return_value=mock_res ) trellojob = TrelloService() res = trellojob.get_user_oauth_token() assert res.status_code == SUCCESS


请注意,在我的示例代码中, GetOAuthTokenResponse是一个尚未在models.py中设置的模型。它提供了编写更清晰的代码的结构,我们稍后会看到它的实际效果。


要运行我们的测试,只需运行python -m pytest 。请注意我们的测试将如何失败,但这没关系 - 最终会成功。


挑战角💡你能尝试自己编写更多测试吗?请随意参考来看看我的测试是什么样的


现在,让我们构建trelloservice 。首先添加一个新的依赖项,即py-trello包装器。

 # pyproject.toml dependencies = [ "pytest==7.4.0", "pytest-mock==3.11.1", "py-trello==0.19.0" ]


再次运行pip install .安装依赖项。

楷模

现在,让我们开始构建我们的模型 - 来调节我们在trelloservice中期望的响应。对于这部分,最好参考我们的单元测试和py-trello源代码来了解我们期望的返回值类型。


例如,假设我们要检索用户的访问令牌,参考py-trellocreate_oauth_token函数( ),我们知道期望返回值是这样的

# trellocli/models.py # module imports # dependencies imports # misc imports from typing import NamedTuple class GetOAuthTokenResponse(NamedTuple): token: str token_secret: str status_code: int


另一方面,请注意命名约定的冲突。例如, py-trello模块有一个名为List的类。解决此问题的方法是在导入期间提供别名。

 # trellocli/models.py # dependencies imports from trello import List as Trellolist


您也应该随便应用这某个概率要根据您的系统的所需开发绘图。举例说明,比如您只需要退回值中的某个防御力,您应该重新构建绘图用来望从退回值中提炼上述值,而并不是将其最为某个产品存储器。
 # trellocli/models.py class GetBoardName(NamedTuple): """Model to store board id Attributes id (str): Extracted board id from Board value type """ id: str


挑战角💡你能尝试自己编写更多模型吗?请随意参考来看看我的模型是什么样子

商业逻辑

设置

模型下来了,让我们正式开始编写trelloservice代码。同样,我们应该参考我们创建的单元测试 - 假设当前的测试列表没有提供服务的完整覆盖,总是在需要时返回并添加更多测试。


像往常一样,将所有导入语句包含在顶部。然后按预期创建TrelloService类和占位符方法。我们的想法是,我们将在cli.py中初始化服务的共享实例并相应地调用其方法。此外,我们的目标是可扩展性,因此需要广泛的覆盖范围。

 # trellocli/trelloservice.py # module imports from trellocli import TRELLO_READ_ERROR, TRELLO_WRITE_ERROR, SUCCESS from trellocli.models import * # dependencies imports from trello import TrelloClient # misc imports class TrelloService: """Class to implement the business logic needed to interact with Trello""" def __init__(self) -> None: pass def get_user_oauth_token() -> GetOAuthTokenResponse: pass def get_all_boards() -> GetAllBoardsResponse: pass def get_board() -> GetBoardResponse: pass def get_all_lists() -> GetAllListsResponse: pass def get_list() -> GetListResponse: pass def get_all_labels() -> GetAllLabelsResponse: pass def get_label() -> GetLabelResponse: pass def add_card() -> AddCardResponse: pass


请注意,这一次当我们运行测试时,我们的测试将如何通过。事实上,这将有助于我们确保坚持正确的轨道。工作流程应该是扩展我们的功能、运行我们的测试、检查通过/失败并相应地进行重构。

授权和初始化 TrelloClient

让我们从__init__函数开始。这个想法是在此处调用get_user_oauth_token函数并初始化TrelloClient 。再次强调仅在.env文件中存储此类敏感信息的需要,我们将使用python-dotenv依赖项来检索敏感信息。相应地修改我们的pyproject.toml文件后,让我们开始实施授权步骤。

 # trellocli/trelloservice.py class TrelloService: """Class to implement the business logic needed to interact with Trello""" def __init__(self) -> None: self.__load_oauth_token_env_var() self.__client = TrelloClient( api_key=os.getenv("TRELLO_API_KEY"), api_secret=os.getenv("TRELLO_API_SECRET"), token=os.getenv("TRELLO_OAUTH_TOKEN") ) def __load_oauth_token_env_var(self) -> None: """Private method to store user's oauth token as an environment variable""" load_dotenv() if not os.getenv("TRELLO_OAUTH_TOKEN"): res = self.get_user_oauth_token() if res.status_code == SUCCESS: dotenv_path = find_dotenv() set_key( dotenv_path=dotenv_path, key_to_set="TRELLO_OAUTH_TOKEN", value_to_set=res.token ) else: print("User denied access.") self.__load_oauth_token_env_var() def get_user_oauth_token(self) -> GetOAuthTokenResponse: """Helper method to retrieve user's oauth token Returns GetOAuthTokenResponse: user's oauth token """ try: res = create_oauth_token() return GetOAuthTokenResponse( token=res["oauth_token"], token_secret=res["oauth_token_secret"], status_code=SUCCESS ) except: return GetOAuthTokenResponse( token="", token_secret="", status_code=TRELLO_AUTHORIZATION_ERROR )


在此实现中,我们创建了一个辅助方法来处理任何可预见的错误,例如,当用户在授权期间单击Deny时。此外,它被设置为递归地请求用户授权,直到返回有效响应为止,因为事实是,除非用户授权我们的应用程序访问其帐户数据,否则我们无法继续。


挑战角💡 注意TRELLO_AUTHORIZATION_ERROR吗?您可以将此错误声明为包常量吗?请参阅设置以获取更多信息

辅助函数

现今授权书这部分已达到,当我们仍然座谈助手指数函数,一方面搜索消费者的 Trello 看板。
 # trellocli/trelloservice.py def get_all_boards(self) -> GetAllBoardsResponse: """Method to list all boards from user's account Returns GetAllBoardsResponse: array of user's trello boards """ try: res = self.__client.list_boards() return GetAllBoardsResponse( res=res, status_code=SUCCESS ) except: return GetAllBoardsResponse( res=[], status_code=TRELLO_READ_ERROR ) def get_board(self, board_id: str) -> GetBoardResponse: """Method to retrieve board Required Args board_id (str): board id Returns GetBoardResponse: trello board """ try: res = self.__client.get_board(board_id=board_id) return GetBoardResponse( res=res, status_code=SUCCESS ) except: return GetBoardResponse( res=None, status_code=TRELLO_READ_ERROR )


至于检索列表(列),我们必须检查py-trelloBoard类,或者换句话说,我们必须接受Board值类型的新参数。

 # trellocli/trelloservice.py def get_all_lists(self, board: Board) -> GetAllListsResponse: """Method to list all lists (columns) from the trello board Required Args board (Board): trello board Returns GetAllListsResponse: array of trello lists """ try: res = board.all_lists() return GetAllListsResponse( res=res, status_code=SUCCESS ) except: return GetAllListsResponse( res=[], status_code=TRELLO_READ_ERROR ) def get_list(self, board: Board, list_id: str) -> GetListResponse: """Method to retrieve list (column) from the trello board Required Args board (Board): trello board list_id (str): list id Returns GetListResponse: trello list """ try: res = board.get_list(list_id=list_id) return GetListResponse( res=res, status_code=SUCCESS ) except: return GetListResponse( res=None, status_code=TRELLO_READ_ERROR )


挑战角💡你能自己实现get_all_labelsget_label函数吗?修改py-trelloBoard类。请随意参考来看看我的实现是什么样的

添加新卡功能

后来但并不是最不非常重要的一方面是,小编总于可达了小编老是起来的最终制定目标——放入一整张新卡。请系牢,小编没于这使用的以后申明的所有的涵数 - 手游辅助涵数的最终制定目标是提生可优化性。
 # trellocli/trelloservice.py def add_card( self, col: Trellolist, name: str, desc: str = "", labels: List[Label] = [] ) -> AddCardResponse: """Method to add a new card to a list (column) on the trello board Required Args col (Trellolist): trello list name (str): card name Optional Args desc (str): card description labels (List[Label]): list of labels to be added to the card Returns AddCardResponse: newly-added card """ try: # create new card new_card = col.add_card(name=name) # add optional description if desc: new_card.set_description(description=desc) # add optional labels if labels: for label in labels: new_card.add_label(label=label) return AddCardResponse( res=new_card, status_code=SUCCESS ) except: return AddCardResponse( res=new_card, status_code=TRELLO_WRITE_ERROR )


🎉 现再所有的都已进行并颗粒落定,请记牢有效地升级您的 README 并将您的码推荐到 GitHub。


恭喜!你赢了。再次玩(是/否)?

包起来

非常衷心感谢信心处理:)在本教程下载,您成就 学习了在开发象限测量时变现模似、整合内聚性模式、通读源代碼以找到主要功效及选用3.方包装设计器变现业务部思维逻辑。


请密切合作喜爱第 2 方面,人们将切实初探 CLI 系统自己的构建。


于此当天,我会们保持良好连续👀


GitHub 源二维码:https:


바카라사이트 바카라사이트 온라인바카라