├── .devcontainer ├── Dockerfile ├── devcontainer.json └── docker-compose.yml ├── .editorconfig ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── Dockerfile ├── LICENSE ├── README.md ├── api └── app.py ├── database ├── data.sql └── ddl.sql ├── docs ├── README.md ├── architecture.drawio.svg ├── compile-typescript-1.png ├── compile-typescript-2.png ├── connect-to-codespaces.png ├── debug-ui.png ├── delete-codespaces-1.png ├── delete-codespaces-2.png ├── delete-codespaces-3.png ├── frontend-breakpoint.png ├── frontend-debug-1.png ├── frontend-debug-2.png ├── github-repository.png ├── initialize-task.png ├── launch-python-1.png ├── launch-python-2.png ├── launch-python-3.png ├── mysql-1.png ├── mysql-2.png ├── mysql-3.png ├── mysql.png ├── sign-in-codespaces-2.png ├── sign-in-codespaces.png ├── start-codespaces-1.png ├── start-codespaces-2.png ├── start-codespaces-3.png ├── start-codespaces-4.png ├── start-codespaces-5.png ├── start-codespaces-6.png ├── start-codespaces-7.png ├── summary.md └── unknown-ports.png ├── frontend └── main.ts ├── initialize.py ├── package-lock.json ├── package.json ├── poetry.lock ├── public ├── css │ └── main.css ├── index.html └── js │ └── .gitignore ├── pyproject.toml ├── rest_client.http ├── samples └── universal │ └── devcontainer.json └── tsconfig.json /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Universal Container image 2 | FROM mcr.microsoft.com/devcontainers/universal:2-linux 3 | 4 | # 追加でインストール 5 | RUN apt-get update && apt-get install -y mysql-client-8.0 6 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // リファレンス 2 | // https://containers.dev/features 3 | { 4 | "name": "codespaces-handson", 5 | 6 | "dockerComposeFile": "./docker-compose.yml", 7 | "service": "app", 8 | "features": { 9 | // 追加 feature を指定 10 | // docker in docker 11 | "ghcr.io/devcontainers/features/docker-in-docker:2": {}, 12 | 13 | // Python のパッケージマーネージャ Poetry 14 | "ghcr.io/devcontainers-contrib/features/poetry:1": {} 15 | }, 16 | // Codespaces では以下の設定は無効になる(ローカルでのRemoteContainer機能では必要) 17 | "workspaceFolder": "/workspaces/codespaces-handson", 18 | // 起動直後に実行するコマンド 19 | "postCreateCommand": "./initialize.py", 20 | // インストールする拡張機能 21 | "extensions": [ 22 | "cweijan.vscode-mysql-client2", 23 | "ms-python.python", 24 | "bungcip.better-toml", 25 | "humao.rest-client", 26 | "EditorConfig.EditorConfig", 27 | "spmeesseman.vscode-taskexplorer", 28 | "ms-vsliveshare.vsliveshare", 29 | "marp-team.marp-vscode" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /.devcontainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | app: 4 | build: 5 | context: . 6 | dockerfile: Dockerfile 7 | depends_on: 8 | - mysql 9 | # ローカルDockerで動かすのに必要 10 | privileged: true 11 | # ワークスペースマウント 12 | volumes: 13 | - ..:/workspaces/codespaces-handson:cached 14 | mysql: 15 | image: mysql/mysql-server:8.0.27 16 | environment: 17 | - MYSQL_ROOT_HOST=% 18 | - MYSQL_DATABASE=main 19 | - MYSQL_ALLOW_EMPTY_PASSWORD=yes 20 | ports: 21 | - "3306" 22 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{yaml,json}] 8 | indent_style = space 9 | indent_size = 2 10 | 11 | [*.py] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.{js,ts,tsc}] 16 | indent_style = space 17 | indent_size = 4 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | __pycache__ 3 | node_modules 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "cweijan.vscode-mysql-client2", 4 | "ms-python.python", 5 | "bungcip.better-toml", 6 | "humao.rest-client", 7 | "EditorConfig.EditorConfig", 8 | "spmeesseman.vscode-taskexplorer", 9 | "ms-vsliveshare.vsliveshare", 10 | "marp-team.marp-vscode" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | // Web API のデバッグ 6 | "name": "Web API (Python Flask)", 7 | "type": "python", 8 | "request": "launch", 9 | "module": "flask", 10 | "env": { "FLASK_APP": "api/app.py", "FLASK_DEBUG": "1" }, 11 | "args": ["run", "--no-debugger", "--reload", "--port=50120"], 12 | "jinja": true, 13 | "justMyCode": true 14 | }, 15 | { 16 | // Chrome でのフロントエンドのデバッグ 17 | "name": "Launch Chrome", 18 | "request": "launch", 19 | "type": "chrome", 20 | "url": "http://localhost:50120", 21 | "webRoot": "${workspaceFolder}/public" 22 | } 23 | ], 24 | "compounds": [] 25 | } 26 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.defaultInterpreterPath": ".venv/bin/python", 3 | "python.linting.enabled": true, 4 | "python.linting.pylintEnabled": false, 5 | "rest-client.decodeEscapedUnicodeCharacters": true 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "typescript compile", 6 | "type": "shell", 7 | "command": ["npx", "tsc", "-w"], 8 | "problemMatcher": ["$tsc"], 9 | "presentation": { 10 | "reveal": "always" 11 | }, 12 | "group": "build" 13 | }, 14 | { 15 | "label": "initialize db", 16 | "type": "shell", 17 | "command": ["python", "initialize.py"], 18 | "problemMatcher": [] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.19-bullseye as node-build-env 2 | WORKDIR /workspace 3 | COPY package.json . 4 | COPY package-lock.json . 5 | RUN npm install 6 | COPY tsconfig.json 7 | COPY frontend/ . 8 | RUN mkdir -p /workspace/public/js/ 9 | RUN npx tsc 10 | 11 | FROM python:3.10-bullseye 12 | WORKDIR /app 13 | RUN pip install poetry==1.1.13 14 | COPY pyproject.toml poetry.lock ./ 15 | RUN poetry config virtualenvs.create false && poetry install 16 | 17 | COPY api ./api 18 | COPY --from=node-build-env public ./public 19 | 20 | CMD ["poetry", "run", "python", "api/app.py"] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022-2023 VS Code Meetup Japan 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub Codespaces 徹底活用ハンズオン-VS Code Conf JP 2022-23 2 | 3 | 全体アーキテクチャー図 4 | 5 |  6 | 7 | ## イベントページ 8 | 9 | https://vscode.connpass.com/event/269712/ 10 | 11 | ## ハンズオンテキスト 12 | 13 | [docs/README.md](docs/README.md) 14 | 15 | ## 事前準備 16 | 17 | - GitHub へのユーザ登録 → https://github.com/signup 18 | - VS Code のインストール → https://code.visualstudio.com/ 19 | - [インストールの仕方 https://github.com/vscodejp/handson-hello-vscode/blob/master/topic0.1/README.md](https://github.com/vscodejp/handson-hello-vscode/blob/master/topic0.1/README.md) 20 | - VS Code の拡張機能 GitHub Codespaces のインストール 21 | - VS Code から GitHub Codespaces への認証 22 | 23 | ### VS Code の拡張機能 GitHub Codespaces のインストール 24 | 25 | 拡張機能タブから、Codespaces と入力して検索し、GitHub Codespaces の項目をインストールしてください。 26 | 27 | 28 | 29 | ### VS Code から GitHub Codespaces への認証 30 | 31 | VS Code にて、アクティビティーバーからリモートエクスプローラー(図の 1)を選びます。 32 | 33 | 上部のプルダウン(図の 2)から「Codespaces」を選びます。 34 | 35 | 「Sign in to Codespaces」(図の 3)をクリックして、認証を進めます。 36 | 37 | 38 | 39 | 「Sign in to Codespaces」の代わりに GitHub アカウント ID が表示されていたら完了です。 40 | 41 | 42 | 43 | ## ハンズオン内容サマリー 44 | 45 | [docs/summary.md](docs/summary.md) 46 | 47 | ## GitHub Codespaces インスタンスの削除の仕方 48 | 49 | ### 方法 1 50 | 51 | VS Code 中のリモートエクスプローラーから、削除したい Codespaces を右クリックして、Delete を選択。 52 | 53 | 54 | 55 | ### 方法 2 56 | 57 | GitHub のリポジトリページから 58 | 59 | 60 | 61 | ### 方法 3 62 | 63 | GitHub のヘッダーから、[Codespaces インスタンスの一覧ページ](https://github.com/codespaces)に飛べます。 64 | 65 |  66 | -------------------------------------------------------------------------------- /api/app.py: -------------------------------------------------------------------------------- 1 | from typing import cast 2 | from mysql.connector.pooling import MySQLConnectionPool 3 | from flask import Flask, render_template, request 4 | 5 | app = Flask( 6 | __name__, 7 | static_url_path="", 8 | static_folder="../public", 9 | template_folder="../public", 10 | ) 11 | 12 | pool = MySQLConnectionPool(host="mysql", user="root", database="main") 13 | 14 | 15 | # index.html 16 | @app.route("/") 17 | def index(): 18 | return render_template("index.html") 19 | 20 | 21 | # 未完了のタスクの一覧を表示する 22 | # GET /api/tasks 23 | # 24 | # レスポンスBODY: 未完了のタスク型の配列のJSON 25 | # 例: 26 | # [ 27 | # { 28 | # "id": 2, 29 | # "content": "VS Code に Python 拡張機能のインストール", 30 | # "completed": false 31 | # }, 32 | # { 33 | # "id": 3, 34 | # "content": "GitHub Codespaces の体験 ", 35 | # "completed": false 36 | # } 37 | # ] 38 | @app.route("/api/tasks", methods=["GET"]) 39 | def show_remained_tasks(): 40 | records = [] 41 | with pool.get_connection() as conn: 42 | with conn.cursor() as cursor: 43 | sql = """ 44 | SELECT 45 | id, 46 | content, 47 | completed 48 | FROM tasks 49 | WHERE 50 | completed = 0 51 | """ 52 | cursor.execute(sql) 53 | for record_tuple in cursor: 54 | # (id, "タスクの内容", false) の形のタプルとしてとれる 55 | # JSON に変換できるように、dict 型にする 56 | records.append( 57 | { 58 | "id": record_tuple[0], 59 | "content": record_tuple[1], 60 | "completed": record_tuple[2] == 1, 61 | } 62 | ) 63 | 64 | # dict 型の配列を返す 65 | return records 66 | 67 | 68 | # タスクを登録する 69 | # POST /api/tasks 70 | # 71 | # リクエストBODY: 1 つのタスクを示すJSON。ただし、idと、completedは未入力。 72 | # 例: 73 | # {"content": "VS Code の 更新"} 74 | # 75 | # レスポンスBODY: 登録された1つのタスクを示すJSON 76 | # 例: 77 | # { 78 | # "id": 4, 79 | # "content": "VS Code の 更新", 80 | # "completed": false 81 | # } 82 | @app.route("/api/tasks", methods=["POST"]) 83 | def append_task(): 84 | request_param = cast(dict, request.get_json()) 85 | with pool.get_connection() as conn: 86 | with conn.cursor() as cursor: 87 | content = request_param["content"] 88 | sql = """ 89 | INSERT INTO tasks 90 | ( 91 | content, 92 | completed 93 | ) 94 | VALUES 95 | ( 96 | %s, 97 | %s 98 | ) 99 | """ 100 | cursor.execute(sql, (content, False)) 101 | 102 | # DB で INSERT クエリで振られる id 103 | new_id = cursor.lastrowid 104 | conn.commit() 105 | 106 | # 登録できたタスクの内容を返す 107 | return { 108 | "id": new_id, 109 | "content": content, 110 | "completed": False, 111 | } 112 | 113 | 114 | # タスクを完了にする 115 | # POST /api/tasks/<タスクのID>/done 116 | # 117 | # リクエストBODY: なし 118 | # 119 | # レスポンスBODY: 登録された1つのタスクを示すJSON 120 | # 例: 121 | # { 122 | # "id": 3, 123 | # "content": "GitHub Codespaces の体験 ", 124 | # "completed": true 125 | # } 126 | @app.route("/api/tasks//done", methods=["POST"]) 127 | def done_task(task_id: int): 128 | with pool.get_connection() as conn: 129 | with conn.cursor() as cursor: 130 | sql = """ 131 | SELECT 132 | id, 133 | content, 134 | completed 135 | FROM tasks 136 | WHERE 137 | completed = 0 138 | AND id = %s 139 | """ 140 | cursor.execute(sql, [task_id]) 141 | record_tuple = cursor.fetchone() 142 | if cursor.rowcount == 0: 143 | return {}, 404 144 | 145 | with conn.cursor() as cursor: 146 | sql = f""" 147 | UPDATE tasks 148 | SET 149 | completed = 1 150 | WHERE 151 | id = %s 152 | """ 153 | cursor.execute(sql, [task_id]) 154 | conn.commit() 155 | return { 156 | "id": task_id, 157 | "content": record_tuple[1], 158 | "completed": True, 159 | } 160 | 161 | 162 | if __name__ == "__main__": 163 | app.run(debug=True, host="0.0.0.0", port=50120) 164 | -------------------------------------------------------------------------------- /database/data.sql: -------------------------------------------------------------------------------- 1 | TRUNCATE TABLE tasks; 2 | INSERT INTO tasks (content, completed) VALUES 3 | ("VS Code のインストール", true), 4 | ("VS Code に Python 拡張機能のインストール", false), 5 | ("GitHub Codespaces の体験 ", false); -------------------------------------------------------------------------------- /database/ddl.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS tasks; 2 | 3 | CREATE TABLE tasks ( 4 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 5 | content TEXT NOT NULL, 6 | completed BOOLEAN NOT NULL 7 | )DEFAULT CHARSET=utf8mb4; -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | marp: true 3 | --- 4 | 5 | 6 | 7 | # GitHub Codespaces 徹底活用 Hands-on 8 | 9 | 2023/01/20 10 | 11 | --- 12 | 13 | # ハンズオンを皆で楽しもう 14 | 15 | 行動規範をお守りください。 16 | 17 | https://www.contributor-covenant.org/version/2/0/code_of_conduct/ 18 | 19 | 様々なレベルの方が参加されます! 20 | 21 | お互いの参加者を尊重し合って、ハンズオンを楽しみましょう! 22 | 23 | 内容を実施できなくても、基本的な質問をしても OK! 24 | 25 | --- 26 | 27 | ## 質問は 28 | 29 | YouTube コメントもしくは、GitHub Discussion で! 30 | 31 | GitHub Discussion ならスクリーンショットが貼れるよ。 32 | 33 | 参加者同士で回答しても OK ! 34 | 35 | https://github.com/vscodejp/codespaces-handson/discussions/3 36 | 37 | --- 38 | 39 | ## できたら 40 | 41 | GitHub Discussion で 👍 を押して、反応してみてください! 42 | 43 | https://github.com/vscodejp/codespaces-handson/discussions/2 44 | 45 | --- 46 | 47 | ## まずは Codespaces を起動しよう 1 48 | 49 | リポジトリにアクセス 50 | 51 | https://github.com/vscodejp/codespaces-handson 52 | 53 |  54 | 55 | --- 56 | 57 | ## まずは Codespaces を起動しよう 2 58 | 59 | Code を押して、Code ボタン → Codespaces タブ → オプションボタン → New with options ... 60 | 61 |  62 | 63 | --- 64 | 65 | ## まずは Codespaces を起動しよう 3 66 | 67 | 支払い設定が所属企業ではなく、個人になっていることを確認しよう 68 | 69 | オプションで 4-core に変更 70 | 71 |  72 | 73 | --- 74 | 75 | ## まずは Codespaces を起動しよう 4 76 | 77 | Web 版の起動が始まるけれど、ローカルの VS Code からつなごう! 78 | 79 |  80 | 81 | --- 82 | 83 | ## まずは Codespaces を起動しよう 5 84 | 85 | VS Code を起動して、GitHub Codespaces 拡張機能をインストールしていなければインストール。 86 | 87 |  88 | 89 | --- 90 | 91 | ## まずは Codespaces を起動しよう 6 92 | 93 | アクティビティーバーのリモートエクスプローラー(図の 1)をクリック。 94 | 上部のプルダウン(図の 2)から「GitHub Codespaces」を選択。 95 | もし GitHub にサインインしていない場合、図の 3 が表示されるのでクリックして認証を進めよう。 96 | 97 |  98 | 99 | --- 100 | 101 | ## まずは Codespaces を起動しよう 7 102 | 103 | vscodejp/codespaces-handson の表示を確認して、接続ボタン(コンセントマーク)をクリック。 104 | 105 |  106 | 107 | --- 108 | 109 | ## 先に Codespaces の削除を学ぼう 1 110 | 111 | 方法 1。VS Code 中のリモートエクスプローラーから、削除したい Codespaces を右クリックして、Delete を選択。 112 | 113 |  114 | 115 | --- 116 | 117 | ## 先に Codespaces の削除を学ぼう 2 118 | 119 | 方法 2。GitHub のリポジトリページから。 120 | 121 |  122 | 123 | --- 124 | 125 | ## 先に Codespaces の削除を学ぼう 2 126 | 127 | 方法 3。GitHub のヘッダーから、[Codespaces インスタンスの一覧ページ](https://github.com/codespaces)に飛べます。 128 | 129 |  130 | 131 | --- 132 | 133 | ## まずは座学へ 134 | 135 | 起動するまで別のお話を 136 | 137 | ☕☕☕ 138 | 139 | --- 140 | 141 | ## GitHub Codespaces とは 142 | 143 | VS Code の UI(Client)とコア機能(Server)を分離する「リモート開発機能」のうち、 144 | Docker コンテナ内で VS Code Server を動かす「リモートコンテナ開発機能」で、 145 | クラウド上のインスタンス上でコンテナを動かせるサービス。 146 | 147 | 手元の UI のマシンは非力でも、 148 | クラウド上の潤沢なリソースを従量課金で利用できる。 149 | 150 | --- 151 | 152 | ## リモートコンテナ開発機能とは 153 | 154 | Docker コンテナ上で VS Code Server を動かして開発する機能。 155 | 156 | 利用するコンテナを DevContainer と呼び、 157 | 開発環境に必要なツールを全てそのコンテナに格納する。 158 | 159 | 複数プロジェクトの作業している時に、 160 | それぞれのプロジェクトのツールがそれぞれのコンテナの中に収まるので、 161 | ツールが競合したりせず、クリーンに使える。 162 | 163 | --- 164 | 165 | ## リモートコンテナ開発機能で難しいところ 166 | 167 | - DevContainer に全てのツールを入れる必要がある 168 | - 手順書ではなく、全て設定とスクリプトで書く必要がある 169 | 170 | --- 171 | 172 | ## 今回のアーキテクチャー 173 | 174 | --- 175 | 176 | 177 | 178 |  179 | 180 | --- 181 | 182 | 183 | 184 | ## DevContainer の設定 1 185 | 186 | - MySQL も起動する 187 | - features で docker などツールをインストールする 188 | - 必要なツールを Dockerfile でインストールする 189 | - コンテナ起動後の初期化スクリプトを実行する 190 | - 拡張機能をインストールする 191 | 192 | --- 193 | 194 | ## DevContainer の設定 2 195 | 196 | .devcontainer/docker-compose.yml でコンテナを定義 197 | MySQL コンテナが横で起動している 198 | 199 | ```json 200 | // .devcontainer/devcontainer.json 抜粋 201 | { 202 | "service": "app", 203 | "dockerComposeFile": "./docker-compose.yml" 204 | } 205 | ``` 206 | 207 | ```yaml 208 | # .devcontainer/docker-compose.yml 抜粋 209 | services: 210 | app: 211 | build: 212 | dockerfile: Dockerfile 213 | mysql: 214 | image: mysql/mysql-server:8.0.27 215 | ``` 216 | 217 | --- 218 | 219 | ## DevContainer の設定 2 220 | 221 | features で追加ツールをインストール。 222 | 223 | feature のリスト → https://containers.dev/features 224 | 225 | ```json 226 | // .devcontainer/devcontainer.json 抜粋 227 | { 228 | "features": { 229 | // 追加 feature を指定 230 | // docker in docker 231 | "ghcr.io/devcontainers/features/docker-in-docker:2": {}, 232 | 233 | // Python のパッケージマーネージャ Poetry 234 | "ghcr.io/devcontainers-contrib/features/poetry:1": {} 235 | } 236 | } 237 | ``` 238 | 239 | --- 240 | 241 | ## DevContainer の設定 5 242 | 243 | docker-compose.yml からビルドする Dockerfile を指定している。 244 | features だけでは不足するツールを追加している。 245 | ストレージ費用が無料の Universal Container をベースにする。 246 | 247 | ```dockerfile 248 | # .devcontainer/Dockerfile 249 | 250 | # Universal Container image 251 | FROM mcr.microsoft.com/devcontainers/universal:2-linux 252 | 253 | # 追加でインストール 254 | RUN apt-get update && apt-get install -y mysql-client-8.0 255 | ``` 256 | 257 | --- 258 | 259 | ## DevContainer の設定 4 260 | 261 | コンテナ起動後の初期化スクリプト(initialize.py)を実行 262 | 263 | - Python、Nodejs のライブラリのインストール 264 | - DB に初期データを投入 265 | 266 | ```json 267 | // .devcontainer/devcontainer.json 抜粋 268 | { 269 | "postCreateCommand": "./initialize.py" 270 | } 271 | ``` 272 | 273 | --- 274 | 275 | ## DevContainer の設定 3 276 | 277 | 拡張機能を複数インストール 278 | 279 | ```json 280 | // .devcontainer/devcontainer.json 抜粋 281 | { 282 | "extensions": [ 283 | "cweijan.vscode-mysql-client2", 284 | "ms-python.python", 285 | "bungcip.better-toml", 286 | "humao.rest-client", 287 | "EditorConfig.EditorConfig", 288 | "spmeesseman.vscode-taskexplorer" 289 | ] 290 | } 291 | ``` 292 | 293 | --- 294 | 295 | ## MySQL につないでみよう 1 296 | 297 | 仕様 298 | 299 | - host 名: mysql 300 | - ユーザ名: root 301 | - パスワード: なし 302 | - データベース名: main 303 | - テーブル名: tasks 304 | 305 | --- 306 | 307 | ## MySQL につないでみよう 2 308 | 309 | アクティビティーバーから Database 選んで設定を作成 310 | 311 |  312 | 313 | --- 314 | 315 | ## MySQL につないでみよう 3 316 | 317 | MySQL の設定をして、Connect を押して接続を確認しよう 318 | 319 |  320 | 321 | --- 322 | 323 | ## MySQL につないでみよう 4 324 | 325 | テーブルの中身を確認しよう。結構使えるツールなので、SQL を変更したり、レコード追加したりしてみよう! 326 | 327 |  328 | 329 | --- 330 | 331 | ## テーブルがない場合 332 | 333 | 初期化スクリプトを再実行しよう。アクティビティーバーからタスクエクエスプローラーを開いて、vscode のタスクから initialize db を実行しよう。 334 | 335 |  336 | 337 | --- 338 | 339 | ## アクティビティータブに database アイコンがない場合 340 | 341 | 自動インストールに失敗しているよ。F1 → Developer: Reload Window を実行する。 342 | 343 | 次の起動で、このワークスペースには水晶の拡張機能があります、というダイアログが右下に表示されるので、インストールを押す。 344 | 345 | 出ない場合、[.vscode/extensions.json](./../.vscode/extensions.json) にある拡張機能を拡張機能タブから検索してインストール。 346 | 347 | --- 348 | 349 | ## Python REST API Web Server 350 | 351 | Python の REST API の Web Server が組まれているよ。 352 | flask という WebServer のミドルウエアを利用しているよ。 353 | 354 | [api/app.py](../api/app.py) 355 | 356 | --- 357 | 358 | ## Python Web Server の仕様 359 | 360 | REST API は 3 つ実装されているよ。 361 | 362 | 1. 未完了のタスクの一覧を表示する `GET /api/tasks` 363 | 2. タスクを登録する `POST /api/tasks` 364 | 3. タスクを完了にする `POST /api/tasks/<タスクのID>/done` 365 | 366 | --- 367 | 368 | ## Python Server を起動してみよう 369 | 370 | この Python をデバッグ実行しよう! 371 | .vscode/launch.json にこのプログラムをデバッグ実行する設定が書かれているよ。 372 | 実行してみよう。 373 | 374 |  375 | 376 | --- 377 | 378 | ## デバッグ UI が登場 379 | 380 | デバッグ実行に成功している場合、デバッグ UI が表示されるよ。 381 | 382 |  383 | 384 | --- 385 | 386 | ## REST Client を使ってリクエストしてみよう 387 | 388 | REST API をテストする設定がつくってあるよ。Send Request ボタンを押してリクエストしてみよう。 389 | タスクの登録や完了もテストしてみよう。 390 | 391 | [rest_client.http](../rest_client.http) 392 | 393 |  394 | 395 | --- 396 | 397 | ## ブレークポイントを設定してステップ実行してみよう 1 398 | 399 |  400 | 401 | 次のページに手順があるよ。 402 | 403 | --- 404 | 405 | ## ブレークポイントを設定してステップ実行してみよう 2 406 | 407 | 1. api.py の 56 行目の赤点の位置をクリックして、ブレークポイント追加 408 | 2. REST Client から GET /api/tasks のリクエストを実行 409 | 3. ブレークポイントで停止されたら、record_tuple の中身を確認 410 | 4. F5 を押して進めよう。3 レコードあれば、3 回止まるよ。 411 | 412 | --- 413 | 414 | ## デバッグ UI の使い方(再掲) 415 | 416 |  417 | 418 | --- 419 | 420 | ## フロントエンドの TypeScript を実行してみよう 421 | 422 | - flask サーバは /api/ 以外にリクエストがあると、public/ にあるファイルを表示するよ。 423 | - HTML: [public/index.html](../public/index.html) 424 | - public/js/main.js を読み込んむようになっているよ(まだないよ) 425 | - frontend/main.ts API にアクセスして、UI を更新するプログラム 426 | 427 | --- 428 | 429 | ## フロントエンドの仕事 1 430 | 431 | ### タスク一覧表示 fetchTasks() 432 | 433 | 1. 画面表示時に fetchTasks() メソッドが呼ばれる 434 | 2. GET /api/tasks にアクセスしてタスクの一覧を取得 435 | 3. 取得した内容で HTML を更新 436 | 437 | ### タスクの登録 createTask() 438 | 439 | 1. Add を押すと、createTask() メソッドが呼ばれる 440 | 2. 画面からタスクの入力を読み込み 441 | 3. POST /api/tasks にアクセス 442 | 4. fetchTasks() を呼んで、タスク一覧を再更新 443 | 444 | --- 445 | 446 | ## フロントエンドの仕事 2 447 | 448 | ### タスクの完了 completeTask() 449 | 450 | 1. Done を押すと、completeTask() メソッドが呼ばれる 451 | 2. POST /api/tasks/(id)/done にアクセス 452 | 3. fetchTasks() を呼んで、タスク一覧を再更新 453 | 454 | --- 455 | 456 | ## フロントエンドのコンパイル 1 457 | 458 | TypeScript を JavaScript にコンパイルするには以下のコマンドを実行。 459 | 460 | ``` 461 | npx tsc -w 462 | ``` 463 | 464 | すると、frontend/main.ts から public/js/main.js が作られるよ。 465 | 466 | これが VS Code のタスク設定で組まれているよ。 467 | 468 | [.vscode/tasks.json](../.vscode/tasks.json) 469 | 470 | --- 471 | 472 | ## フロントエンドのコンパイル 2 473 | 474 |  475 | 476 | 実行するとくるくるしたままになるよ。 477 | 478 |  479 | 480 | --- 481 | 482 | ## フロントエンドの実行 1 483 | 484 | デバッグ設定が [.vscode/launch.json](../.vscode/launch.json) に組まれているよ。 485 | 「Launch Chrome」を実行しよう。 486 | 487 |  488 | 489 | --- 490 | 491 | ## フロントエンドの実行 2 492 | 493 | ```json 494 | // .vscode/launch.json 抜粋 495 | { 496 | "configurations": [ 497 | { 498 | // Chrome でのフロントエンドのデバッグ 499 | "name": "Launch Chrome", 500 | "request": "launch", 501 | "type": "chrome", 502 | "url": "http://localhost:50120", 503 | "webRoot": "${workspaceFolder}/public" 504 | } 505 | ] 506 | } 507 | ``` 508 | 509 | --- 510 | 511 | ## フロントエンドの実行 2 512 | 513 |  514 | 515 | --- 516 | 517 | ## 画面が出ない場合 1 ポートを確認しよう 518 | 519 | 下部パネルのポートタブから、50120 ポートが、ローカルポートの何番になっているか確認しよう。 520 | 521 |  522 | 523 | --- 524 | 525 | ## 画面が出ない場合 2 Python デバッグが止まっていないか確認しよう 526 | 527 | Python Server を起動してみよう参照してね。 528 | 529 | --- 530 | 531 | ## フロントエンドをデバッグしよう 1 532 | 533 | frontend/main.ts の 49 行目にブレークポイントを設定 534 | 535 |  536 | 537 | --- 538 | 539 | ## フロントエンドをデバッグしよう 2 540 | 541 | 注目するところ 542 | 543 | - あくまで JavaScript を実行しているけれど、TypeScript でデバッグできるよ 544 | - public/js/main.js.map ファイルに対応関係が書かれているよ 545 | - launch.json の 546 | 547 | --- 548 | 549 | ## Try 1: フロントエンドとサーバサイドを同時にデバッグしよう 550 | 551 | フロントエンドとサーバサイドを連携してデバッグしてみよう。 552 | 553 | --- 554 | 555 | ## Try 2: 機能を追加してみよう 556 | 557 | 1. 完了したタスクを表示するボタンを作ってみよう (2h くらい) 558 | 2. 入寮項目に締め切りを追加して、締切順に表示できるようにしてみよう (2h くらい) 559 | 560 | --- 561 | 562 | ## 削除を忘れずに 1 563 | 564 | 方法 1。VS Code 中のリモートエクスプローラーから、削除したい Codespaces を右クリックして、Delete を選択。 565 | 566 |  567 | 568 | --- 569 | 570 | ## 削除を忘れずに 2 571 | 572 | 方法 3。GitHub のヘッダーから、[Codespaces インスタンスの一覧ページ](https://github.com/codespaces)に飛べます。 573 | 574 |  575 | -------------------------------------------------------------------------------- /docs/architecture.drawio.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | PC 12 | 13 | 14 | 15 | 16 | 17 | PC 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | GitHub Codespaces 29 | 30 | 31 | 32 | 33 | 34 | GitHub Codespaces 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | MySQLコンテナ(mysql) 46 | 47 | 48 | 49 | 50 | 51 | MySQLコンテナ(mysql) 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | ユーザ 68 | 69 | 70 | 71 | 72 | 73 | ユーザ 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | Dev Container 85 | 86 | 87 | 88 | 89 | 90 | Dev Container 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 静的ファイルディレクトリ 104 | 105 | public/ 106 | 107 | 108 | 109 | 110 | 111 | 静的ファイルディレクトリ 112 | public/ 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 編集 127 | 128 | 129 | 130 | 131 | 132 | 編集 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 編集 145 | 146 | 147 | 148 | 149 | 150 | 編集 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | タスク 163 | 164 | ”typescript compile” 165 | 166 | 起動 167 | 168 | 169 | 170 | 171 | 172 | タスク”typescript compile”... 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | デバッグ 185 | 186 | "Launch Web API(Flask)" 187 | 188 | の起動 189 | 190 | 191 | 192 | 193 | 194 | デバッグ"Launch Web API(Flask)"... 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | VS Code Server 206 | 207 | 208 | 209 | 210 | 211 | VS Code Server 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | デバッグ 224 | 225 | "Launch Chrome" 226 | 227 | の起動 228 | 229 | 230 | 231 | 232 | 233 | デバッグ"Launch Chrome"... 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | VS Code 245 | 246 | 247 | 248 | 249 | 250 | VS Code 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | DB MySQL Server 262 | 263 | 264 | 265 | 266 | 267 | DB MySQL Server 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | tasks 281 | 282 | テーブル 283 | 284 | 285 | 286 | 287 | 288 | tasks... 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | Pyrhonランタイムが 301 | 302 | 読み込んで実行 303 | 304 | 305 | 306 | 307 | 308 | Pyrhonランタイムが... 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | api/app.py 322 | 323 | 324 | Python Web API(Flask) 325 | 326 | のコード 327 | 328 | 329 | 330 | 331 | 332 | api/app.py... 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 参照 345 | 346 | 347 | 348 | 349 | 350 | 参照 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | public/index.html 364 | 365 | 366 | 表示するWebページの 367 | 368 | HTMLファイル 369 | 370 | 371 | 372 | 373 | 374 | public/index.html... 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | public/js/ 388 | 389 | app.js 390 | 391 | 392 | 表示するWebページの 393 | 394 | コード 395 | 396 | 397 | 398 | 399 | 400 | public/js/... 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 生成 413 | 414 | 415 | 416 | 417 | 418 | 生成 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | fronend/main.ts 432 | 433 | 434 | Webフロントエンド 435 | 436 | のコード 437 | 438 | 439 | 440 | 441 | 442 | fronend/main.ts... 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | TypeScript 454 | 455 | コンパイラ 456 | 457 | 458 | 459 | 460 | 461 | TypeScript... 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | Chromeブラウザ 476 | 477 | 478 | 479 | 480 | 481 | Chromeブラウザ 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | VS Code の 495 | 496 | ポート転送機能 497 | 498 | http://localhost:50120/ 499 | 500 | 501 | 502 | 503 | 504 | VS Code の... 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | コード 518 | 519 | 520 | 521 | 522 | 523 | コード 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 拡張機能 537 | 538 | MySQL 539 | 540 | 541 | 542 | 543 | 544 | 拡張機能 545 | MySQL 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 拡張機能 557 | 558 | REST Client 559 | 560 | 561 | 562 | 563 | 564 | 拡張機能 565 | REST Client 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 起動したプログラム 577 | 578 | (プロセス) 579 | 580 | 581 | 582 | 583 | 584 | 起動したプログラム 585 | (プロセス) 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 凡例 597 | 598 | 599 | 600 | 601 | 602 | 凡例 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | Flask Web Server を 615 | 616 | 通して読み込み 617 | 618 | 619 | 620 | 621 | 622 | Flask Web Server を... 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | js/app.js 636 | 637 | 638 | 読み込んだJSファイル 639 | 640 | 641 | 642 | 643 | 644 | js/app.js... 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 起動 657 | 658 | 659 | 660 | 661 | 662 | 起動 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | Pythonランタイム 674 | 675 | 676 | 677 | 678 | 679 | Pythonランタイム 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | Flask Web Server 691 | 692 | 693 | 694 | 695 | 696 | Flask Web Server 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | public/の内容を返す 713 | 714 | Webサーバに"も”なる 715 | 716 | 717 | 718 | 719 | 720 | public/の内容を返す... 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | アクセス 733 | 734 | host:mysql 735 | 736 | user:root 737 | 738 | pass:なし 739 | 740 | 741 | 742 | 743 | 744 | アクセスhost:mysql... 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 依存関係 760 | 761 | 762 | 763 | 764 | 765 | 依存関係 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | VS Codeの操作 777 | 778 | 779 | 780 | 781 | 782 | VS Codeの操作 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | Viewer does not support full SVG 1.1 792 | 793 | 794 | 795 | 796 | -------------------------------------------------------------------------------- /docs/compile-typescript-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/compile-typescript-1.png -------------------------------------------------------------------------------- /docs/compile-typescript-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/compile-typescript-2.png -------------------------------------------------------------------------------- /docs/connect-to-codespaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/connect-to-codespaces.png -------------------------------------------------------------------------------- /docs/debug-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/debug-ui.png -------------------------------------------------------------------------------- /docs/delete-codespaces-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/delete-codespaces-1.png -------------------------------------------------------------------------------- /docs/delete-codespaces-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/delete-codespaces-2.png -------------------------------------------------------------------------------- /docs/delete-codespaces-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/delete-codespaces-3.png -------------------------------------------------------------------------------- /docs/frontend-breakpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/frontend-breakpoint.png -------------------------------------------------------------------------------- /docs/frontend-debug-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/frontend-debug-1.png -------------------------------------------------------------------------------- /docs/frontend-debug-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/frontend-debug-2.png -------------------------------------------------------------------------------- /docs/github-repository.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/github-repository.png -------------------------------------------------------------------------------- /docs/initialize-task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/initialize-task.png -------------------------------------------------------------------------------- /docs/launch-python-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/launch-python-1.png -------------------------------------------------------------------------------- /docs/launch-python-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/launch-python-2.png -------------------------------------------------------------------------------- /docs/launch-python-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/launch-python-3.png -------------------------------------------------------------------------------- /docs/mysql-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/mysql-1.png -------------------------------------------------------------------------------- /docs/mysql-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/mysql-2.png -------------------------------------------------------------------------------- /docs/mysql-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/mysql-3.png -------------------------------------------------------------------------------- /docs/mysql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/mysql.png -------------------------------------------------------------------------------- /docs/sign-in-codespaces-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/sign-in-codespaces-2.png -------------------------------------------------------------------------------- /docs/sign-in-codespaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/sign-in-codespaces.png -------------------------------------------------------------------------------- /docs/start-codespaces-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/start-codespaces-1.png -------------------------------------------------------------------------------- /docs/start-codespaces-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/start-codespaces-2.png -------------------------------------------------------------------------------- /docs/start-codespaces-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/start-codespaces-3.png -------------------------------------------------------------------------------- /docs/start-codespaces-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/start-codespaces-4.png -------------------------------------------------------------------------------- /docs/start-codespaces-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/start-codespaces-5.png -------------------------------------------------------------------------------- /docs/start-codespaces-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/start-codespaces-6.png -------------------------------------------------------------------------------- /docs/start-codespaces-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/start-codespaces-7.png -------------------------------------------------------------------------------- /docs/summary.md: -------------------------------------------------------------------------------- 1 | ## このコードのメインは 2 つ 2 | 3 | - Python Web API のコード: [api/app.py](api/app.py) 4 | - Frontend のコード: [api/app.py](api/app.py) 5 | - Frontend のコードを使う HTML(これは演習中には編集しません): [public/index.html](public/index.html) 6 | 7 | ## 完成したアプリでできること 8 | 9 | - 未完了のタスクの一覧を表示する 10 | - タスクを追加する 11 | - タスクを完了にする 12 | 13 | それぞれ、Web API では以下の URL で行えるようになっています。 14 | 15 | - GET /api/tasks 未完了タスクを読み出す 16 | - POST /api/tasks タスクを追加する 17 | - POST /api/tasks/(id)/done id のタスクを完了にする 18 | 19 | ## 体験すること 20 | 21 | ### 拡張機能 MySQL を使って、DB の中身を確認する 22 | 23 | アクティビティーバー(画面左のアイコン列)から、MySQL を選び「Create Connection」を押します。 24 | 25 | 以下を入力する(ほかは初期値のまま) 26 | 27 | - ServerType: MySQL 28 | - Name: mysql 29 | - Host: mysql 30 | - Database: main 31 | - Username: root(初期値) 32 | - Password: 空(初期値で空) 33 | 34 | テーブル tasks の中身を、このツールで確認してみてください。 35 | 36 | ### Python の Web API をデバッグする 37 | 38 | Python Web API のコード [api/app.py](api/app.py) を開きます。各 Web API の実装が確認できるようになっています。 39 | 40 | この Web API を立ち上げるには、デバッグ設定「Launch Web API (Python Flask)」をデバッグ実行します。 41 | この[デバッグ設定](.vscode/launch.json)は、api/app.py を起点にして Flask を立ち上げる設定になっています。 42 | 43 | 各 API にアクセスするには、拡張機能 REST Client を使います。 44 | 45 | [REST Client の命令ファイル rest_client.http](rest_client.http) を開いて、各リクエストを実行できます。 46 | 47 | api/app.py のコードにブレークポイントを置き、REST Client でアクセスするとブレークされることを確認してください。 48 | 49 | ### Frontend をデバッグする 50 | 51 | Web Frontend のコード [frontend/main.ts](frontend/main.ts) を開きます。 52 | ブラウザの操作で各関数が呼び出されて、Web API にアクセスして仕事をするようになっています。 53 | 54 | Frontend は TypeScript で実装されており、コンパイルする必要があります。 55 | コンパイルするには、タスク「Typescript Compile」を実行してください。 56 | 実行すると、コードの編集の度に自動でコンパイルされる watch モードでコンパイラが実行されています。 57 | 58 | コンパイル後のコードは [public/js/main.js](public/js/main.js) に出力されています。 Flask Web API は、api の URL 以外の URL にアクセスされると、public ディレクトリにあるファイルを返すようになっています。 59 | 60 | Frontend のデバッグを開始するには、デバッグ設定「Launch Chrome」をデバッグ実行します。 61 | 実行すると、http://localhost:50120/ にアクセスするブラウザが立ち上がります。 62 | 表示されない場合には、下部ペインのポートタブを確認し、ポート転送されているポートを確認し、そのポートにアクセスしてください。 63 | 64 | Web Frontend のコード [frontend/main.ts](frontend/main.ts) にブレークポイントを設定し、ブラウザ上で操作をして、main.ts がデバッグできていることを確認してください。 65 | 66 | また、Web API にもブレークポイントを設定し、Web API とフロントエンドが組み合わされてデバッグできていることを確認してください。 67 | 68 | ## Docker コンテナを実行する 69 | 70 | この DevContainer では、docker in docker がサポートされています。 71 | 72 | 以下のコマンドを実行すると、docker コンテナのビルドが行えます。 73 | 74 | ``` 75 | docker build . 76 | ``` 77 | 78 | ## このリポジトリで実装されていること 79 | 80 | - [(.devcontainer/devcontainer.json)](.devcontainer/devcontainer.json) で MySQL DB を含めた全ての全環境が立ち上がる 81 | - initialize.py で、DB のテーブルの作成が行われる 82 | -------------------------------------------------------------------------------- /docs/unknown-ports.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscodejp/codespaces-handson/3f3695b074e43111d10e853d7972ffa9abfc9a93/docs/unknown-ports.png -------------------------------------------------------------------------------- /frontend/main.ts: -------------------------------------------------------------------------------- 1 | // API とのやり取りに使う1つのタスクを示す型 2 | interface Task { 3 | id?: number 4 | content: string 5 | completed: boolean 6 | } 7 | 8 | /** 9 | * 画面起動時、各ボタン押した後に画面にタスク一覧を表示する 10 | */ 11 | async function fetchTasks() { 12 | // Web API 呼び出し 13 | const res = await fetch("/api/tasks"); 14 | 15 | // API からの応答をパースする 16 | // Task 型の配列になっている 17 | const tasks = await res.json() as Task[]; 18 | 19 | 20 | // Task 型の配列を HTML に展開する 21 | const div = document.getElementById("tasksDiv") as HTMLDivElement; 22 | let html = ""; 23 | tasks.forEach((task) => { 24 | html += ` 25 | 26 | 27 | ${task.id} 28 | ${task.content} 29 | done 30 | 31 | 32 | ` 33 | }) 34 | div.innerHTML = html; 35 | } 36 | 37 | /** 38 | * タスク追加ボタンを押した時 39 | */ 40 | async function createTask() { 41 | // 画面の入力をタスク型に収める 42 | const input = document.getElementById("newTaskContent") as HTMLInputElement; 43 | const task = { 44 | content: input.value, 45 | completed: false, 46 | } as Task; 47 | 48 | // Web API 呼び出し 49 | const res = await fetch("/api/tasks", { 50 | method: "POST", 51 | headers: { 52 | 'Content-Type': 'application/json' 53 | }, 54 | body: JSON.stringify(task), 55 | }) 56 | 57 | // ステータスコード 200 なら成功とする 58 | if (res.status != 200) { 59 | alert("fail to create new task"); 60 | return; 61 | } 62 | 63 | // すでにある入力をクリアする 64 | input.value = ""; 65 | 66 | // 画面の一覧のリロード 67 | await fetchTasks(); 68 | } 69 | 70 | /** 71 | * 各タスクの完了ボタンを押した時 72 | */ 73 | async function completeTask(id: number) { 74 | // Web API 呼び出し 75 | const res = await fetch(`/api/tasks/${id}/done`, { 76 | method: "POST", 77 | headers: { 78 | 'Content-Type': 'application/json' 79 | }, 80 | }) 81 | 82 | // ステータスコード 200 なら成功とする 83 | if (res.status != 200) { 84 | alert("fail to make task completed"); 85 | 86 | return; 87 | } 88 | 89 | // 画面の一覧のリロード 90 | await fetchTasks(); 91 | } 92 | 93 | // 最初に読み出す 94 | fetchTasks(); 95 | -------------------------------------------------------------------------------- /initialize.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # 環境初期化スクリプト 3 | import os 4 | from os import path 5 | import subprocess 6 | import time 7 | 8 | 9 | def run(cmd: list[str]): 10 | """ コマンド実行時スニペット """ 11 | print("\033[1m" + " ".join(cmd) + "\033[0m") 12 | subprocess.run(cmd, check=True) 13 | 14 | 15 | def wait_mysql_started(): 16 | """ mysql の起動まで待機 """ 17 | print("wait mysql start", end="") 18 | while True: 19 | print(".", end="") 20 | result = subprocess.run(["mysql", "-h", "mysql", "-u", "root", "-e", "SHOW DATABASES;"], check=False, capture_output=True) 21 | if result.returncode > 0: 22 | time.sleep(1) 23 | continue 24 | if str(result.stdout).count("main") > 0: 25 | break 26 | time.sleep(1) 27 | print() 28 | 29 | venv_path = path.join(os.getcwd(), ".venv") 30 | if not path.exists(venv_path): 31 | # venv がない場合は作る 32 | run(["python", "-m", "venv", venv_path]) 33 | 34 | # 依存パッケージの読み込み 35 | run(["poetry", "install"]) 36 | run(["npm", "install"]) 37 | 38 | 39 | # MySQL が起動するまで待機 40 | wait_mysql_started() 41 | 42 | # DB の初期化 43 | run(["mysql", "-h", "mysql", "-u", "root", "main", "-e", "source ./database/ddl.sql"]) 44 | run(["mysql", "-h", "mysql", "-u", "root", "main", "-e", "source ./database/data.sql"]) 45 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codespaces-handson", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "codespaces-handson", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "typescript": "^4.9.4" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "4.9.4", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", 18 | "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", 19 | "bin": { 20 | "tsc": "bin/tsc", 21 | "tsserver": "bin/tsserver" 22 | }, 23 | "engines": { 24 | "node": ">=4.2.0" 25 | } 26 | } 27 | }, 28 | "dependencies": { 29 | "typescript": { 30 | "version": "4.9.4", 31 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", 32 | "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codespaces-handson", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/vscodejp/codespaces-handson.git" 12 | }, 13 | "author": "", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/vscodejp/codespaces-handson/issues" 17 | }, 18 | "homepage": "https://github.com/vscodejp/codespaces-handson#readme", 19 | "dependencies": { 20 | "typescript": "^4.9.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "black" 5 | version = "22.12.0" 6 | description = "The uncompromising code formatter." 7 | category = "dev" 8 | optional = false 9 | python-versions = ">=3.7" 10 | files = [ 11 | {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, 12 | {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, 13 | {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, 14 | {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, 15 | {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, 16 | {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, 17 | {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, 18 | {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, 19 | {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, 20 | {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, 21 | {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, 22 | {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, 23 | ] 24 | 25 | [package.dependencies] 26 | click = ">=8.0.0" 27 | mypy-extensions = ">=0.4.3" 28 | pathspec = ">=0.9.0" 29 | platformdirs = ">=2" 30 | tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} 31 | 32 | [package.extras] 33 | colorama = ["colorama (>=0.4.3)"] 34 | d = ["aiohttp (>=3.7.4)"] 35 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 36 | uvloop = ["uvloop (>=0.15.2)"] 37 | 38 | [[package]] 39 | name = "click" 40 | version = "8.1.3" 41 | description = "Composable command line interface toolkit" 42 | category = "main" 43 | optional = false 44 | python-versions = ">=3.7" 45 | files = [ 46 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 47 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 48 | ] 49 | 50 | [package.dependencies] 51 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 52 | 53 | [[package]] 54 | name = "colorama" 55 | version = "0.4.6" 56 | description = "Cross-platform colored terminal text." 57 | category = "main" 58 | optional = false 59 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 60 | files = [ 61 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 62 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 63 | ] 64 | 65 | [[package]] 66 | name = "flask" 67 | version = "2.2.2" 68 | description = "A simple framework for building complex web applications." 69 | category = "main" 70 | optional = false 71 | python-versions = ">=3.7" 72 | files = [ 73 | {file = "Flask-2.2.2-py3-none-any.whl", hash = "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"}, 74 | {file = "Flask-2.2.2.tar.gz", hash = "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b"}, 75 | ] 76 | 77 | [package.dependencies] 78 | click = ">=8.0" 79 | itsdangerous = ">=2.0" 80 | Jinja2 = ">=3.0" 81 | Werkzeug = ">=2.2.2" 82 | 83 | [package.extras] 84 | async = ["asgiref (>=3.2)"] 85 | dotenv = ["python-dotenv"] 86 | 87 | [[package]] 88 | name = "itsdangerous" 89 | version = "2.1.2" 90 | description = "Safely pass data to untrusted environments and back." 91 | category = "main" 92 | optional = false 93 | python-versions = ">=3.7" 94 | files = [ 95 | {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, 96 | {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, 97 | ] 98 | 99 | [[package]] 100 | name = "jinja2" 101 | version = "3.1.2" 102 | description = "A very fast and expressive template engine." 103 | category = "main" 104 | optional = false 105 | python-versions = ">=3.7" 106 | files = [ 107 | {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, 108 | {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, 109 | ] 110 | 111 | [package.dependencies] 112 | MarkupSafe = ">=2.0" 113 | 114 | [package.extras] 115 | i18n = ["Babel (>=2.7)"] 116 | 117 | [[package]] 118 | name = "markupsafe" 119 | version = "2.1.1" 120 | description = "Safely add untrusted strings to HTML/XML markup." 121 | category = "main" 122 | optional = false 123 | python-versions = ">=3.7" 124 | files = [ 125 | {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, 126 | {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, 127 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, 128 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, 129 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, 130 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, 131 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, 132 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, 133 | {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, 134 | {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, 135 | {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, 136 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, 137 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, 138 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, 139 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, 140 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, 141 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, 142 | {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, 143 | {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, 144 | {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, 145 | {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, 146 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, 147 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, 148 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, 149 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, 150 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, 151 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, 152 | {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, 153 | {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, 154 | {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, 155 | {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, 156 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, 157 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, 158 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, 159 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, 160 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, 161 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, 162 | {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, 163 | {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, 164 | {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, 165 | ] 166 | 167 | [[package]] 168 | name = "mypy-extensions" 169 | version = "0.4.3" 170 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 171 | category = "dev" 172 | optional = false 173 | python-versions = "*" 174 | files = [ 175 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 176 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 177 | ] 178 | 179 | [[package]] 180 | name = "mysql-connector-python" 181 | version = "8.0.31" 182 | description = "MySQL driver written in Python" 183 | category = "main" 184 | optional = false 185 | python-versions = "*" 186 | files = [ 187 | {file = "mysql-connector-python-8.0.31.tar.gz", hash = "sha256:0fbe8f5441ad781b4f65c54a10ac77c6a329591456607e042786528599519636"}, 188 | {file = "mysql_connector_python-8.0.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3e271d8de00d5e9f9bd4b212c8e23d2986dead0f20379010f3b274a3e24cbfcb"}, 189 | {file = "mysql_connector_python-8.0.31-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f3ee04a601f9cb90ace9618bbe2fa8e5bb59be3eb0c2bd8a5405fe69e05e446b"}, 190 | {file = "mysql_connector_python-8.0.31-cp310-cp310-manylinux1_i686.whl", hash = "sha256:f89b7a731885b8a04248e4d8d124705ca836f0ddd3b7cf0c789e21f4b32810ed"}, 191 | {file = "mysql_connector_python-8.0.31-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:48eb34f4e69a2fba56f310de6682862a15d46cd2bd51ee6eebc3a244e4ee0aa6"}, 192 | {file = "mysql_connector_python-8.0.31-cp310-cp310-win_amd64.whl", hash = "sha256:a570a72e0015b36b9c0775ae27c1d4946225f02f62129d16a14e9d77a38c0717"}, 193 | {file = "mysql_connector_python-8.0.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7ac859a52486ac319e37f61469bbb9023faef38018223efa74e953f1fe23d36"}, 194 | {file = "mysql_connector_python-8.0.31-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:79d6a6e8ce955df5ca0786cb8ed8fbd999745c9b50def89993a2a0f4732de721"}, 195 | {file = "mysql_connector_python-8.0.31-cp311-cp311-manylinux1_i686.whl", hash = "sha256:e60426af313dcd526028d018d70757a82c5cc0673776b2a614e2180b5970feed"}, 196 | {file = "mysql_connector_python-8.0.31-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:d0ca1ba3e5fb2f2cddcf271c320cd5c368f8d392c034ddab7a1c8dfd19510351"}, 197 | {file = "mysql_connector_python-8.0.31-cp311-cp311-win_amd64.whl", hash = "sha256:a1d8c1509c740649f352400d50360185e5473371507bb6498ceda0c6e877920c"}, 198 | {file = "mysql_connector_python-8.0.31-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:447847396d1b51edd9cfe05a8c5ba82836d8ea4866f25f36a836cab322fdc4f0"}, 199 | {file = "mysql_connector_python-8.0.31-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5e01a2f50378c13407a32e40dd4d225cfee5996d9d11968f76720ec28aa45421"}, 200 | {file = "mysql_connector_python-8.0.31-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ac85883ec3b3a9a0e36cacc89b8f5e666206842c432a5f69b09a7687ddf51d4a"}, 201 | {file = "mysql_connector_python-8.0.31-cp37-cp37m-win_amd64.whl", hash = "sha256:28cb3667be64ebfbd3d477bbd2c71e50d48bd5ed7ba2072dd460ae886d27e88e"}, 202 | {file = "mysql_connector_python-8.0.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:30f4542d4d20357c79604e6bf1a801e71dfc45c759c22b502ca5aa8122c3e859"}, 203 | {file = "mysql_connector_python-8.0.31-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:e9e5ad544adfc82ffbda2c74685c8c953bce2e212c56f117020079f05e2c68b2"}, 204 | {file = "mysql_connector_python-8.0.31-cp38-cp38-manylinux1_i686.whl", hash = "sha256:744c976569e81eecce5e8c7e8f80df2a1c3f64414829addc69c64aef8f56d091"}, 205 | {file = "mysql_connector_python-8.0.31-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:17d6ea22dacca7fa78a73a81f2b186d4c5c6e70b7be314e352526654e9ba4713"}, 206 | {file = "mysql_connector_python-8.0.31-cp38-cp38-win_amd64.whl", hash = "sha256:ae1b3d03802474a161cce8a97024484d18bef43b86d20114908cbc263817cade"}, 207 | {file = "mysql_connector_python-8.0.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:746df133c677fbe4687da33aad5a711abdd9bd2277bbc350e20f903f07c81ef5"}, 208 | {file = "mysql_connector_python-8.0.31-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:4d75e6c3a7f18004e8279cbd9f5edc70089d6aaf3cb64374e21098d9bf0b93c4"}, 209 | {file = "mysql_connector_python-8.0.31-cp39-cp39-manylinux1_i686.whl", hash = "sha256:8ad0d08f3f7c9e48d6d102c7de718e5e44f630f916ff2f4b4ff8a3756b5d10ac"}, 210 | {file = "mysql_connector_python-8.0.31-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:02526f16eacc3961ff681c5c8455d2306a9b45124f2f012ca75a1eac9ceb5165"}, 211 | {file = "mysql_connector_python-8.0.31-cp39-cp39-win_amd64.whl", hash = "sha256:b2bbf443f6346e46c26a3e91dd96a428a1038f2d3c5e466541078479c64a1833"}, 212 | {file = "mysql_connector_python-8.0.31-py2.py3-none-any.whl", hash = "sha256:9be9c4dcae987a2a3f07b2ad984984c24f90887dbfab3c8a971e631ad4ca5ccf"}, 213 | ] 214 | 215 | [package.dependencies] 216 | protobuf = ">=3.11.0,<=3.20.1" 217 | 218 | [package.extras] 219 | compression = ["lz4 (>=2.1.6,<=3.1.3)", "zstandard (>=0.12.0,<=0.15.2)"] 220 | dns-srv = ["dnspython (>=1.16.0,<=2.1.0)"] 221 | gssapi = ["gssapi (>=1.6.9,<=1.8.1)"] 222 | 223 | [[package]] 224 | name = "mysqlclient" 225 | version = "2.1.1" 226 | description = "Python interface to MySQL" 227 | category = "main" 228 | optional = false 229 | python-versions = ">=3.5" 230 | files = [ 231 | {file = "mysqlclient-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c1ed71bd6244993b526113cca3df66428609f90e4652f37eb51c33496d478b37"}, 232 | {file = "mysqlclient-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:c812b67e90082a840efb82a8978369e6e69fc62ce1bda4ca8f3084a9d862308b"}, 233 | {file = "mysqlclient-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:0d1cd3a5a4d28c222fa199002810e8146cffd821410b67851af4cc80aeccd97c"}, 234 | {file = "mysqlclient-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b355c8b5a7d58f2e909acdbb050858390ee1b0e13672ae759e5e784110022994"}, 235 | {file = "mysqlclient-2.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:996924f3483fd36a34a5812210c69e71dea5a3d5978d01199b78b7f6d485c855"}, 236 | {file = "mysqlclient-2.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:dea88c8d3f5a5d9293dfe7f087c16dd350ceb175f2f6631c9cf4caf3e19b7a96"}, 237 | {file = "mysqlclient-2.1.1.tar.gz", hash = "sha256:828757e419fb11dd6c5ed2576ec92c3efaa93a0f7c39e263586d1ee779c3d782"}, 238 | ] 239 | 240 | [[package]] 241 | name = "nodeenv" 242 | version = "1.7.0" 243 | description = "Node.js virtual environment builder" 244 | category = "dev" 245 | optional = false 246 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" 247 | files = [ 248 | {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, 249 | {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, 250 | ] 251 | 252 | [package.dependencies] 253 | setuptools = "*" 254 | 255 | [[package]] 256 | name = "pathspec" 257 | version = "0.10.3" 258 | description = "Utility library for gitignore style pattern matching of file paths." 259 | category = "dev" 260 | optional = false 261 | python-versions = ">=3.7" 262 | files = [ 263 | {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, 264 | {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, 265 | ] 266 | 267 | [[package]] 268 | name = "platformdirs" 269 | version = "2.6.2" 270 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 271 | category = "dev" 272 | optional = false 273 | python-versions = ">=3.7" 274 | files = [ 275 | {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, 276 | {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, 277 | ] 278 | 279 | [package.extras] 280 | docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] 281 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] 282 | 283 | [[package]] 284 | name = "protobuf" 285 | version = "3.20.1" 286 | description = "Protocol Buffers" 287 | category = "main" 288 | optional = false 289 | python-versions = ">=3.7" 290 | files = [ 291 | {file = "protobuf-3.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3cc797c9d15d7689ed507b165cd05913acb992d78b379f6014e013f9ecb20996"}, 292 | {file = "protobuf-3.20.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:ff8d8fa42675249bb456f5db06c00de6c2f4c27a065955917b28c4f15978b9c3"}, 293 | {file = "protobuf-3.20.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd68be2559e2a3b84f517fb029ee611546f7812b1fdd0aa2ecc9bc6ec0e4fdde"}, 294 | {file = "protobuf-3.20.1-cp310-cp310-win32.whl", hash = "sha256:9016d01c91e8e625141d24ec1b20fed584703e527d28512aa8c8707f105a683c"}, 295 | {file = "protobuf-3.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:32ca378605b41fd180dfe4e14d3226386d8d1b002ab31c969c366549e66a2bb7"}, 296 | {file = "protobuf-3.20.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9be73ad47579abc26c12024239d3540e6b765182a91dbc88e23658ab71767153"}, 297 | {file = "protobuf-3.20.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:097c5d8a9808302fb0da7e20edf0b8d4703274d140fd25c5edabddcde43e081f"}, 298 | {file = "protobuf-3.20.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e250a42f15bf9d5b09fe1b293bdba2801cd520a9f5ea2d7fb7536d4441811d20"}, 299 | {file = "protobuf-3.20.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cdee09140e1cd184ba9324ec1df410e7147242b94b5f8b0c64fc89e38a8ba531"}, 300 | {file = "protobuf-3.20.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:af0ebadc74e281a517141daad9d0f2c5d93ab78e9d455113719a45a49da9db4e"}, 301 | {file = "protobuf-3.20.1-cp37-cp37m-win32.whl", hash = "sha256:755f3aee41354ae395e104d62119cb223339a8f3276a0cd009ffabfcdd46bb0c"}, 302 | {file = "protobuf-3.20.1-cp37-cp37m-win_amd64.whl", hash = "sha256:62f1b5c4cd6c5402b4e2d63804ba49a327e0c386c99b1675c8a0fefda23b2067"}, 303 | {file = "protobuf-3.20.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:06059eb6953ff01e56a25cd02cca1a9649a75a7e65397b5b9b4e929ed71d10cf"}, 304 | {file = "protobuf-3.20.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:cb29edb9eab15742d791e1025dd7b6a8f6fcb53802ad2f6e3adcb102051063ab"}, 305 | {file = "protobuf-3.20.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:69ccfdf3657ba59569c64295b7d51325f91af586f8d5793b734260dfe2e94e2c"}, 306 | {file = "protobuf-3.20.1-cp38-cp38-win32.whl", hash = "sha256:dd5789b2948ca702c17027c84c2accb552fc30f4622a98ab5c51fcfe8c50d3e7"}, 307 | {file = "protobuf-3.20.1-cp38-cp38-win_amd64.whl", hash = "sha256:77053d28427a29987ca9caf7b72ccafee011257561259faba8dd308fda9a8739"}, 308 | {file = "protobuf-3.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f50601512a3d23625d8a85b1638d914a0970f17920ff39cec63aaef80a93fb7"}, 309 | {file = "protobuf-3.20.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:284f86a6207c897542d7e956eb243a36bb8f9564c1742b253462386e96c6b78f"}, 310 | {file = "protobuf-3.20.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7403941f6d0992d40161aa8bb23e12575637008a5a02283a930addc0508982f9"}, 311 | {file = "protobuf-3.20.1-cp39-cp39-win32.whl", hash = "sha256:db977c4ca738dd9ce508557d4fce0f5aebd105e158c725beec86feb1f6bc20d8"}, 312 | {file = "protobuf-3.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:7e371f10abe57cee5021797126c93479f59fccc9693dafd6bd5633ab67808a91"}, 313 | {file = "protobuf-3.20.1-py2.py3-none-any.whl", hash = "sha256:adfc6cf69c7f8c50fd24c793964eef18f0ac321315439d94945820612849c388"}, 314 | {file = "protobuf-3.20.1.tar.gz", hash = "sha256:adc31566d027f45efe3f44eeb5b1f329da43891634d61c75a5944e9be6dd42c9"}, 315 | ] 316 | 317 | [[package]] 318 | name = "pyright" 319 | version = "1.1.286" 320 | description = "Command line wrapper for pyright" 321 | category = "dev" 322 | optional = false 323 | python-versions = ">=3.7" 324 | files = [ 325 | {file = "pyright-1.1.286-py3-none-any.whl", hash = "sha256:b80761f303bf437d764b2d194b06f07fb4ed38960d7f3a8b2f7f41da37aced70"}, 326 | {file = "pyright-1.1.286.tar.gz", hash = "sha256:2bbf82114ac3838c31c91d1c32abe4858259bff094465cadff55eac465250f1b"}, 327 | ] 328 | 329 | [package.dependencies] 330 | nodeenv = ">=1.6.0" 331 | 332 | [package.extras] 333 | all = ["twine (>=3.4.1)"] 334 | dev = ["twine (>=3.4.1)"] 335 | 336 | [[package]] 337 | name = "setuptools" 338 | version = "65.6.3" 339 | description = "Easily download, build, install, upgrade, and uninstall Python packages" 340 | category = "dev" 341 | optional = false 342 | python-versions = ">=3.7" 343 | files = [ 344 | {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"}, 345 | {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"}, 346 | ] 347 | 348 | [package.extras] 349 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] 350 | testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] 351 | testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] 352 | 353 | [[package]] 354 | name = "tomli" 355 | version = "2.0.1" 356 | description = "A lil' TOML parser" 357 | category = "dev" 358 | optional = false 359 | python-versions = ">=3.7" 360 | files = [ 361 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 362 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 363 | ] 364 | 365 | [[package]] 366 | name = "types-mysqlclient" 367 | version = "2.1.5.1" 368 | description = "Typing stubs for mysqlclient" 369 | category = "main" 370 | optional = false 371 | python-versions = "*" 372 | files = [ 373 | {file = "types-mysqlclient-2.1.5.1.tar.gz", hash = "sha256:8f16d8b275e7f12a7bc463ac825980bd90f36c1a582183a0f73a78d472fcf397"}, 374 | {file = "types_mysqlclient-2.1.5.1-py3-none-any.whl", hash = "sha256:f38971ac8535b735021988118d4adbc1e3b35041596655b611e1534c3d3ef568"}, 375 | ] 376 | 377 | [[package]] 378 | name = "werkzeug" 379 | version = "2.2.2" 380 | description = "The comprehensive WSGI web application library." 381 | category = "main" 382 | optional = false 383 | python-versions = ">=3.7" 384 | files = [ 385 | {file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"}, 386 | {file = "Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"}, 387 | ] 388 | 389 | [package.dependencies] 390 | MarkupSafe = ">=2.1.1" 391 | 392 | [package.extras] 393 | watchdog = ["watchdog"] 394 | 395 | [metadata] 396 | lock-version = "2.0" 397 | python-versions = "^3.10" 398 | content-hash = "8063a081b932e07e7f9dc6ad0f66054d7e5ffa6d971418c7445a35a65c153f75" 399 | -------------------------------------------------------------------------------- /public/css/main.css: -------------------------------------------------------------------------------- 1 | input.form-control { 2 | border-color: #191970; 3 | } 4 | 5 | div.card { 6 | border-color: #191970; 7 | } 8 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | TODO Site 16 | 17 | 18 | 19 | 20 | Remember the Tasks! 21 | 22 | 23 | 24 | Task Title 25 | 31 | 32 | 37 | Add 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /public/js/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | *.d.ts 3 | *.js.map 4 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "codespaces-handson" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Atsushi Morimoto <74th.tech@gmail.com>"] 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.10" 10 | flask = "^2.2.2" 11 | mysql-connector-python = "^8.0.31" 12 | mysqlclient = "^2.1.1" 13 | types-mysqlclient = "^2.1.5.1" 14 | 15 | 16 | [tool.poetry.group.dev.dependencies] 17 | black = "^22.12.0" 18 | pyright = "^1.1.286" 19 | 20 | [build-system] 21 | requires = ["poetry-core"] 22 | build-backend = "poetry.core.masonry.api" 23 | -------------------------------------------------------------------------------- /rest_client.http: -------------------------------------------------------------------------------- 1 | ### タスクの一覧 2 | GET http://127.0.0.1:50120/api/tasks 3 | content-type: application/json 4 | 5 | ### タスクの追加 6 | POST http://127.0.0.1:50120/api/tasks 7 | content-type: application/json 8 | 9 | {"content": "VS Code の 更新"} 10 | 11 | ### タスクの完了 12 | POST http://127.0.0.1:50120/api/tasks/3/done 13 | content-type: application/json 14 | -------------------------------------------------------------------------------- /samples/universal/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Default Linux Universal", 3 | 4 | // Storage が無償の Universal Dev Container を使う場合 5 | "image": "mcr.microsoft.com/devcontainers/universal:2-linux", 6 | 7 | "features": { 8 | // 追加 feature を指定 9 | "ghcr.io/devcontainers/features/docker-in-docker:2": {} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2019", 4 | "module": "commonjs", 5 | "strict": true, 6 | "sourceMap": true, 7 | "esModuleInterop": true, 8 | "skipLibCheck": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "declaration": true, 11 | "rootDir": "frontend", 12 | "outDir": "public/js" 13 | } 14 | } 15 | --------------------------------------------------------------------------------
${task.content}