sample
1
from abc import ABC, abstractmethod # インターフェース(抽象) class IUserRepository(ABC): @abstractmethod def save(self, name: str) -> None: # ここには具体的な処理は書かない(passで何もしない) pass # 実体その1:MySQLに保存するクラス class MySQLDatabase(IUserRepository): def save(self, name: str) -> None: print(f"MySQLに「{name}」を保存しました!") # 実体その2:テスト用にメモリに仮保存するクラス class MockDatabase(IUserRepository): def save(self, name: str) -> None: print(f"テスト環境のメモリに「{name}」を仮保存しました!") class UserService: # クラスを作るときに、実体を外から受け取る def __init__(self, repository: IUserRepository): self.repository = repository # ユーザー登録のメインロジック def register_user(self, name: str) -> None: print("名前の文字数チェックなど、ビジネスルールの処理...") # 抽象レイヤーの機能を使う(裏で何が動いているかは気にしない) self.repository.save(name) # ① 本番環境で動かす場合 # MySQLの実体を作って、ビジネスロジックに渡す production_db = MySQLDatabase() production_service = UserService(production_db) print("【本番環境の実行】") production_service.register_user("山田太郎") print("-" * 30) # ② テスト環境で動かす場合 # テスト用の実体を作って、ビジネスロジックに渡す test_db = MockDatabase() test_service = UserService(test_db) print("【テスト環境の実行】") test_service.register_user("佐藤花子") (外界) Webブラウザ・スマホアプリ │ ▼ 【① コントローラー(追加!)】 ★外界との窓口(左側) │ 「WebからのJSONデータを受け取って、UserServiceに渡すよ」 │ ▼ 【② ユースケース(UserService)】 ★シナリオ進行役 │ 「登録処理を始めるぞ!」 │ │ ──? 【③ エンティティ(追加!)】 ★不変のルール │ 「名前は3文字以上かチェックするぞ!(UserNameクラスなど)」 │ ▼ 【④ 抽象レイヤー(IUserRepository)】 ★ユースケースが作った契約書 ▲ │ (依存の逆転・import) │ 【⑤ DBアダプター(MySQLDatabase)】 ★外界との窓口(右側) │ 「契約書通りに、MySQL用のSQLを発行するよ」 │ ▼ (外界) 本物のMySQLデータベース
2
from abc import ABC, abstractmethod from dataclasses import dataclass # ========================================== # 1. Domain Model (ドメインモデル) # ========================================== @dataclass class User: user_id: str name: str email: str # メールアドレスを追加 # ========================================== # 2. 抽象化レイヤー (Ports: ポート) # ※ここが「USBの差し込み口」になります # ========================================== # ① データベース用のインターフェース class IUserRepository(ABC): @abstractmethod def find_by_name(self, name: str) -> User | None: pass @abstractmethod def save(self, user: User) -> None: pass # ② 【NEW】メール送信用インターフェース class IEmailSender(ABC): @abstractmethod def send_welcome_email(self, email_address: str, name: str) -> None: pass # ========================================== # 3. Domain Service (ドメインサービス) # ========================================== class UserDuplicationService: def __init__(self, repository: IUserRepository): self.repository = repository def is_duplicated(self, name: str) -> bool: user = self.repository.find_by_name(name) return user is not None # ========================================== # 4. Application Service (ユースケース層) # ※ここがシステムの中核(マザーボード) # ========================================== class UserRegistrationAppService: # データベースとメール送信、2つの「抽象(ルール)」を受け取る! def __init__( self, repository: IUserRepository, dup_check_service: UserDuplicationService, email_sender: IEmailSender # NEW: メール送信機能を追加 ): self.repository = repository self.dup_check_service = dup_check_service self.email_sender = email_sender def register(self, user_id: str, name: str, email: str) -> None: user = User(user_id=user_id, name=name, email=email) if self.dup_check_service.is_duplicated(user.name): print(f"? エラー: 「{name}」さんは既に登録されています!") return # インフラへの命令(詳細は知らずに、ただルール通りに呼び出すだけ) self.repository.save(user) self.email_sender.send_welcome_email(user.email, user.name) print(f"?? 成功: 「{name}」さんの登録処理がすべて完了しました!\n") # ========================================== # 5. Infra (インフラ層 / Adapters: アダプター) # ※ここが「USBに挿す周辺機器」になります # ========================================== # ① DBの実体(メモリに保存) class InMemoryUserRepository(IUserRepository): def __init__(self): self.db: dict[str, User] = {} def find_by_name(self, name: str) -> User | None: for user in self.db.values(): if user.name == name: return user return None def save(self, user: User) -> None: self.db[user.user_id] = user print(f" [DBログ] データベースに {user.name} を保存しました。") # ② 【NEW】メール送信の実体(今回は画面に文字を出すだけ) class ConsoleEmailSender(IEmailSender): def send_welcome_email(self, email_address: str, name: str) -> None: print(f" [Mailログ] {email_address} 宛てに、{name}さんへの歓迎メールを送信しました!") # ========================================== # 6. UI (プレゼンテーション層) # ========================================== class UserController: def __init__(self, app_service: UserRegistrationAppService): self.app_service = app_service def handle_register_request(self, user_id: str, name: str, email: str) -> None: print(f"【UI】登録リクエスト受付: ID={user_id}, Name={name}") self.app_service.register(user_id, name, email) # ========================================== # メインプログラム (コンポジションルート) # ========================================== if __name__ == "__main__": # 1. 道具(インフラ周辺機器)の準備 repository = InMemoryUserRepository() email_sender = ConsoleEmailSender() # メール送信機を用意 # 2. ビジネスルールの準備 dup_service = UserDuplicationService(repository) # 3. 進行役(マザーボード)に周辺機器をすべてセット! app_service = UserRegistrationAppService(repository, dup_service, email_sender) # 4. 受付窓口(UI)の準備 ui = UserController(app_service) # --- 実行テスト --- ui.handle_register_request("001", "山田太郎", "yamada@example.com") my_project/ │ ├─ domain/ # ドメイン層(システムの中核) │ ├─ models.py # エンティティ(User) │ ├─ interfaces.py # 抽象化レイヤー(IUserRepositoryなど) │ └─ services.py # ドメインサービス(UserDuplicationService) │ ├─ application/ # ユースケース層 │ └─ services.py # アプリケーションサービス(UserRegistrationAppService) │ ├─ infrastructure/ # インフラ層(外の世界) │ ├─ repositories.py # DB実体(InMemoryUserRepository) │ └─ email.py # メール実体(ConsoleEmailSender) │ ├─ presentation/ # UI層(入力を受け取る) │ └─ controllers.py # UI実体(UserController) │ └─ main.py # メインプログラム(構成ルート・一番外側)
OpenAPI (Swagger)
編集ツール
サンプル
openapi: 3.0.0 info: title: Sample API description: A short description of API. version: 1.0.0 paths: # paths オブジェクト /users: # path item オブジェクト get: # GET # Operationオブジェクト tags: - users summary: Get all users. description: Returns an array of User model parameters: [] responses: # レスポンス定義 '200': # HTTP status description: A JSON array of User model content: application/json: # レスポンスの形式指定 schema: type: array items: $ref: '#/components/schemas/User' # 参照するモデル example: # サンプルデータ - uid: 1 name: John Doe - uid: 2 name: Jane Doe post: # POST tags: - users summary: Create a new User description: Create a new User parameters: [] requestBody: # リクエストボディ description: user to create content: application/json: schema: # POSTするオブジェクト $ref: '#/components/schemas/User' example: uid: 3 name: Richard Roe responses: '201': description: CREATED /users/{userId}: get: tags: - users summary: Get user by ID. description: Returns a single User model parameters: # リクエストパラメータ - name: userId in: path # パラメータをパス内に含める description: user id required: true schema: type: integer responses: '200': description: A single User model content: application/json: schema: type: object items: $ref: '#/components/schemas/User' example: uid: 1 name: John Doe /products: # path item オブジェクト get: # GET # Operationオブジェクト tags: - products summary: Get all products. description: Returns an array of Products model parameters: [] responses: # レスポンス定義 '200': # HTTP status description: A JSON array of Product model content: application/json: # レスポンスの形式指定 schema: type: array items: $ref: '#/components/schemas/Product' # 参照するモデル example: # サンプルデータ - pid: 1 name: tablet - pid: 2 name: iphone post: # POST tags: - products summary: Create a new User description: Create a new User parameters: [] requestBody: # リクエストボディ description: user to create content: application/json: schema: # POSTするオブジェクト $ref: '#/components/schemas/Product' example: pid: 3 name: disc responses: '201': description: CREATED components: schemas: # スキーマオブジェクトの定義 User: # モデル名 type: object # 型 required: # 必須フィールド - uid properties: uid: # プロパティ名 type: integer # 型 format: int64 # フォーマット(int32, int64等) name: type: string Product: type: object required: - pid - price properties: pid: type: integer format: int64 example: 1 name: type: string example: Laptop price: type: integer example: 1200
参考
Node.jsをUbuntuにローカルインストール
nvm
Node.jsをダウンロードしてインストールできるツール。
バージョンの切替方法は以下
qiita.com
npm
javascriptパッケージをインストールするツール。
インストール
以下を実行
git clone https://github.com/creationix/nvm.git ~/.nvm source .nvm/nvm.sh nvm install 10.15.3 #最新版
設定
bashrcもしくはbash_profileに以下を追記
if [[ -s ~/.nvm/nvm.sh ]]; then source ~/.nvm/nvm.sh fi
確認
hello.jsを作成
console.log("hello node.js")
hello.jsを実行
$ node hello.js hello node.js
参考
bashからpsqlを非対話で使う
#!/bin/bash
DATABASE="testcopy"
DIRPATH="/hogehoge/postgres/files/"
### database作成
psql -c "create database ${DATABASE}"
### テーブル定義
psql ${DATABASE} < ${DIRPATH}/ddl/s.txt
psql ${DATABASE} < ${DIRPATH}/ddl/s2.txt
psql -c "\c ${DATABASE}" -c "\dt"
### データのロード
psql -c "\c ${DATABASE}" -c "\copy mytable from ${DIRPATH}/csv/1.csv with csv header ;"
psql -c "\c ${DATABASE}" -c "\copy newtable from ${DIRPATH}/csv/4.csv with csv header ;"
### 確認
psql -c "\c ${DATABASE}" -c "select * from mytable ;"
psql -c "\c ${DATABASE}" -c "select * from newtable;"
postgreSQLのメモ
初期設定
- sudo apt update
- sudo apt install postgresql postgresql-contrib
ユーザー切り替え(ログイン)
sudo -i -u postgres
ダンプ
pg_dump {database} -t {table} -U postgres -s --file={filename}
復元
psql {database} < s.txt
接続(対話型)
psql {database}
接続時
- \l ## データベース一覧
- \q ## 終了
- \c {database} ## データベースの選択
- \d {table} ##テーブル詳細
DDL
test=# create table mytable ( test(# id integer, test(# name varchar(10) test(# );
- drop table {tablename}
DML
- insert into mytable values (1, 'hogehoge');
- select * from mytable;
- delete from mytable
その他
\copy mytable from /path/1.csv with csv header ;
備考
- 初期DBは制御用、templete0は接続不可
postgres template0 template1
data cluster
- 作成・開始・終了
/usr/lib/postgresql/10/bin/initdb -E UTF-8 --no-locale -D /home/develop/db/posgretest /usr/lib/postgresql/10/bin/pg_ctl -D /home/develop/db/posgretest/ start /usr/lib/postgresql/10/bin/pg_ctl -D /home/develop/db/posgretest/ stop psql -l
https://www.postgresql.jp/document/9.1/html/creating-cluster.html
pipenvメモ
Pipfile.lock
pipenv shell pipenv [package]
requirements.txt
pipenv --python 3.6 pipenv install -r requirements.txt pipenv shell pip freeze -l > requirements.txt
sarimax
import pandas as pd import statsmodels.api as sm import matplotlib import matplotlib.pyplot as plt ### pandas setting #pd.get_option("display.max_columns") #pd.set_option('display.max_columns', 5) pd.set_option("display.max_colwidth", 80) ### pyplot setting plt.style.use('ggplot') #font = {'family' : 'meiryo'} #matplotlib.rc('font', **font) plt.rcParams["figure.figsize"] = [10,10] df=pd.read_csv("./data/tokyo-2016.csv") df["TIMESTAMP"]=pd.to_datetime(df["DATE"]) #print(df["TIMESTAMP"]) ### グループ化 df_sum=df.groupby("TIMESTAMP")["POWER"].sum().reset_index() df_sum=df_sum.sort_values("TIMESTAMP") df_sum= df_sum.set_index(['TIMESTAMP']) #print(df_sum) ### plot #df_sum.plot(alpha=0.6) #plt.title("timeplot") #plt.savefig("./timelog/plot.png") ### トレンド、季節性、ノイズ res = sm.tsa.seasonal_decompose(df_sum) #res.plot() #plt.title("trend") #plt.savefig("./timelog/trend.png") observed=res.observed["POWER"] seasonal=res.seasonal["POWER"] trend=res.trend["POWER"] resid=res.resid["POWER"] df_new=pd.DataFrame(index=[]) df_new["observed"]=observed df_new["trend"]=trend df_new["seasonal"]=seasonal df_new["resid"]=resid #print(df_new) df_new["check"]=df_new["trend"]+df_new["seasonal"]+df_new["resid"] #print(df_new) ### 単位値検定 # トレンド項あり、定数項あり ct = sm.tsa.stattools.adfuller(df_sum["POWER"], regression="ct") # トレンド項なし、定数項あり c = sm.tsa.stattools.adfuller(df_sum["POWER"], regression="c") # トレンド項なし、定数項なし nc = sm.tsa.stattools.adfuller(df_sum["POWER"], regression="nc") # 確認 print("diff-0: ", ct[1],c[1],nc[1]) df_diff1=df_sum.diff(1) diff = df_diff1.dropna(inplace=True) # トレンド項あり、定数項あり ct = sm.tsa.stattools.adfuller(df_diff1["POWER"], regression="ct") # トレンド項なし、定数項あり c = sm.tsa.stattools.adfuller(df_diff1["POWER"], regression="c") # トレンド項なし、定数項なし nc = sm.tsa.stattools.adfuller(df_diff1["POWER"], regression="nc") # 確認 print("diff-1: ", ct[1],c[1],nc[1]) #df_diff1.plot() #plt.title("diff1") #plt.savefig("./timelog/diff1.png") #print(df_sum) df_train= df_sum[df_sum.index < "2016-12"] print(df_train) df_test = df_sum[df_sum.index >= "2016-12"] print(df_test) def paramselect(df_train): max_p = 3 #AR max_q = 3 #MA max_d = 2 #diff max_sp = 1 #seasonal AR max_sq = 1 #seasonal MA max_sd = 1 #seasonal diff pattern = max_p*(max_d + 1)*(max_q + 1)*(max_sp + 1)*(max_sq + 1)*(max_sd + 1) modelSelection = pd.DataFrame(index=range(pattern), columns=["model", "aic"]) # 自動SARIMA選択 num = 0 for p in range(1, max_p + 1): for d in range(0, max_d + 1): for q in range(0, max_q + 1): for sp in range(0, max_sp + 1): for sd in range(0, max_sd + 1): for sq in range(0, max_sq + 1): sarima = sm.tsa.SARIMAX( df_train, order=(p,d,q), seasonal_order=(sp,sd,sq,12), enforce_stationarity = False, enforce_invertibility = False ).fit() modelSelection.ix[num]["model"] = \ "order=(" + str(p) + ","+ str(d) + ","+ str(q) + "), \ season=("+ str(sp) + ","+ str(sd) + "," + str(sq) + ")" modelSelection.ix[num]["aic"] = sarima.aic num = num + 1 print(modelSelection.sort_values(by='aic').head()) ### parameter select #paramselect(df_train) ### SARIMAX model model = sm.tsa.SARIMAX(df_train, order=(3,0,3), seasonal_order=(1,1,1,12), enforce_stationarity=False,enforce_invertibility=False).fit() ljungbox_result = sm.stats.diagnostic.acorr_ljungbox(model.resid, lags=10) df_ljungbox_result = pd.DataFrame({"p-value":ljungbox_result[1]}) df_tmp=df_ljungbox_result[df_ljungbox_result["p-value"] < 0.05] print(df_tmp) ### pred(overfitting) pred=model.predict('2016-05-01', '2016-11-30') plt.figure() plt.plot(df_train, label="train" ,color="blue") plt.plot(pred, "r", label="pred" ,color="red") plt.legend() plt.title("sarimax_overfit") plt.savefig("./timelog/sarimax_overfit.png") ### pred pred=model.predict('2016-11-30', '2016-12-31') plt.figure() plt.plot(df_train, label="train" ,color="blue") plt.plot(pred, "r", label="pred" ,color="red") plt.legend() plt.title("sarimax_train") plt.savefig("./timelog/sarimax_pred.png") ### predcheck pred=model.predict('2016-11-30', '2016-12-31') plt.figure() plt.plot(df_test, label="test" ,color="blue") plt.plot(pred, "r", label="pred" ,color="red") plt.legend() plt.title("sarimax_test") plt.savefig("./timelog/sarimax_check.png")