■24/12/27 9:25PM
GitHub Actions
ChatGPTかGeminiかに聞けば良さそう
■GitHub Actionsの動作内容はリポジトリ内に設定されている.github/workflows内のYMLにあるsteps: - name: Checkout uses: actions/checkout@v4
@以下はバージョン、特定のコミットSHAにもできる @3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4 Commits · actions/checkout · GitHubonセクションにPushやPR作成やスケジュール実行等のトリガーや対象ブランチやパス等も書かれているSecretsや環境変数は、Terraformでクラウドプロバイダーにアクセスする場合等で、GitHub Actionsのsecrets で認証情報が設定されていることが多い。これらはリポジトリのSettings > Secrets and variables > Actions で確認可能。GitHub Actions でのシークレットの使用 - GitHub Docs
name: Deploy Terraform
on: push: branches: - main #この場合、mainブランチへのpushでトリガーされる
jobs: terraform: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Setup Terraform uses: hashicorp/setup-terraform@v2 with: terraform_version: 1.4.0 - name: Initialize Terraform run: terraform init - name: Apply Terraform run: terraform apply-auto-approve #terraform planなしじゃ
Comment (0)
■24/9/5 11:34PM
Flask
■formaction (hidden以外でのSubmit先変更方法) buttonや inputのtype=submit/image に付与できる ベースとなるのはformのidのformタグ<input... form="formのid">
<form id="form" method="post"><button type="submit" formaction="app.html" name="transition" value="a" form="form">戻る</button><button type="submit" formaction="ok.html" name="transition" value="b" form="form">送信</button>下記のような属性がある formaction formenctype formmethod formnovalidate formtarget
■jinja2【Flask】Jinja2のテンプレート継承でHTMLファイルを役割ごとに分割する | たぬハック (tanuhack.com)[Flask/Jinja2]extendsやincludeを使い、メンテしやすいテンプレート構築を目指す | pixelbeat sandboxJinjaテンプレートの書き方をがっつり調べてまとめてみた。 #Python - Qiita
///dictmy_dic['name'] = 1return render_template('index.html', message=my_dic)テンプレ側{{message.name}}
///リストnum_list = np.arange(10)return render_template('index.html', message=num_list)テンプレ側{% for num in num_list %}<div>{{ num }}</div>{% endfor %}
///改行
{%- xxx -%} jinjaタグの前後マイナスで改行空白を除外する
+はtrim_blocks(改行詰め)、Istrip_blocks(空白詰め)を無効化する設定で俺は使わないfrom jinja2 import Environmentjinja_env = Environment()jinja_env.trim_blocks = Truejinja_env.Istrip_blocks = True
///置換
{{ "aaagh" | replace("a","oh,","2") }}
-> oh,oh,agh
{{ input['txtarea'] | replace("\n","<br />") }}
///エスケープ
safeフィルタがなければhtmlエスケープがかかる{{ output | safe }}
あるいは
{{% autoescape False %}}{{ output }}{{% endautoescape %}}
///コメント(複数行ok){# note: commented-out we no longer use this {% for user in users %} …#}
///生{% raw %} <ul> {% for item in seq %} <li>{{ item )}</li> {% endfor %} </ul>{% endraw %}
///extend と block と include●flaskpage=page_a.model(user)return render_template("layout.html",title=title,page=page)
●layout.html (テンプレ切替と共通ブロック、共通ブロックはテンプレに書いた方分かり易い?) {% if page['destination']== 'confirm' %} {% extends "template_confirm.html" %} {% elif page['destination']== 'complete' %} {% extends "template_complete.html" %} {% else %} {% extends "template_html" %} {% endif %} {% block body %} {% include "header.html" %} <p>content here.</p> {% endblock %}
●template.html <html> <body> <h1>{{ title }}</h1> {% block body %} {% endblock %}
#0以上の入力データの確認 (空の確認やintへのキャスト)#{% if 'inquiry_id' in page['input'] and page['input']['inquiry_id'] is not none and page['input']['inquiry_id'][int > 0 %}
{% if page[input']['inquiry_subject'] %} <input name="inquiry_subject" type="text" placeholder="例)a" value="{{ page['input']['inquiry_subject'] }}"> {% else %)} <input name="inquiry subject" type="text" placeholder="例)a"> {% endif %}
{% include "footer.html" %} </body> </html>
●header.html <p>date:2024-8-27</p>
●footer.html <p>author.p</p>
●page_a.pydef model(user):value_return = ""input = {}error = {}value_return <br>under constructions taken place by user '<br>'
#入力があった、if request.method == "POST": transition = request form.get("transition")#get, post, 取得するもので書き方が違う#request.args.get("inquiry_id")#request.form.get("inquiry_id"#request.json.get("inquiry_id") logging warming('#####' + user + 'transition: ' + str(transition)+'#####')
if transition == "new" or transition == "error" or transition == "confirm back": flag_ng=0 #入力値の判定 error_txt_inquiry_subject = [] inquiry_subject = request.form.get("inquiry_subject") ff not checkRequire(inquiry_subject): error_txt_inquiry_subject.append("件名が空欄です”) flag_ng=1
input[inquiry_subject] = inquiry_subject if flag_ng == 1: error['error_txt_inquiry_subject] = error_txt_inquiry_subject #エラー画面を出す destination = "error" else: #確認画面を出す destination = "confirm" #End of transition == "new" or transition == "error" or transition == "confirm back": else: #transition == "confirm_proceed" #登録処理し完了画面を出す destination = "complete"else: #初期画面を出す destination = "new"return ("value":value_return, "destination":destination, "input":input, "error":error)
■改行
プレースホルダー内での改行は
:に置き換える<textarea placeholder="例) aaa bbb">
JINJA2のフォーム入力後の確認HTMLの改行は?置換ではhtmlエスケープが掛かり<br>がそのまま表示されてしまいダメ{{ input | replace("\n", "<br>") }}
htmlエスケープ(HTMLエスケープは<>&のみだった)• 入力があればhtmlエスケープ+改行<br/>変換• html表示はそのままhtmlエスケープ+改行<br/>変換状態で出力• DBにそのままhtmlエスケープ+改行<br/>変換状態で出力入れる• htmlフォーム内表示はhtmlエスケープを解除し表示↓
入力値はそのままinput変数に保持confirm画面でエスケープ (html.escape()+改行<br/>変換) しescape変数に保持DB保存時にはescape変数にプラスしてダブル/シングル/バッククォート、セミコロン、バックスラッシュをエスケープし保存DBから取り出す際はそれらをアンエスケープしescape変数に保持Docへはinput変数で保存画面表示時はアンエスケープ (改行<br />変換+html.unescape())
def escapeHtmlBr(text): if text is None: return text elif isinstance(text, list): list_escaped = [html.escape(item) for item in text] list_escaped [item.replace("\n', '<br>') for item in list_escaped] return list_escaped else: escaped_text = html escape(text)
return escaped_text.replace('\n', '<br>')
def unescapeHtmlBr(text): if text is None: return text elif isinstance(text, list): list_unescaped = [item.replace('<br>', '\n') for item in text] list_unescaped = [html.unescape(item) for item in list_unescaped] return list_unescaped else: text text.replace('<br>', '\n') return html.unescape(text)
def escapeDB(text): if text is None: return text elif isinstance(text, list): list_escaped = [item.replace(';', ';') for item in text] list_escaped = [item replace('"', '"') for item in list_escaped] list_escaped = [item.replace("'", ''') for item in list_escaped] list_escaped = [item.replace('\\', '\') for item in list_escaped] list_escaped = [item.replace('`', '`') for item in list_escaped] return list_escaped else: escaped_text = text.replace(';', ';') escaped_text = escaped_text.replace('"', '"') escaped_text = escaped_text.replace("'", ''') escaped_text = escaped_text.replace('\\', '\') escaped_text = escaped_text.replace('`', '`') return escaped text
def unescapeDB(text) if text is None return text elif isinstance(text, list): list_unescaped = [item replace('`', '`') for item in text] list_unescaped [item.replace('\','\\') for item in list_unescaped]
list_unescaped [item.replace(''', "'") for item in list_unescaped] list_unescaped [item.replace('"', '"') for item in list_unescaped] list_unescaped [item.replace(';', ';') for item in list_unescaped] return list_unescaped else: unescaped_text = text.replace('`','`') unescaped_text = unescaped_text.replace('\', '\\') unescaped_text = unescaped_text.replace(''', "'") unescaped_text unescaped_text replace('"', '"') unescaped_text = unescaped_text.replace(';', ';') return unescaped_text
■文字確認def check_special_characters(a):| #チェックする記号のセット special_characters = ['<', '>', '"', '&'] #特定の記号が含まれているかをチェック if any(char in a for char in special_characters): raise ValueError(f"変数 'a' に禁止されている文字が含まれています: {a}")try: a = "Hello & World" check_special_characters(a)except ValueError as e: print(e)
■DBのnull行の排除bq = bigquery.Client()sql = f"""SELECT a FROM `ds.b' WHERE c = '{pri}'"""results=bq.query(sql)list = list()for row in results: if row.a is not None: list.append(str(row.a))
■Flask-WTF CCSRF対策でhiddenに入れるhttps://qiita.com/RGS/items/c8c99970054a481ac80drequrement.txt Flask-WTF==1.2.1
from flask_wtf import CSRFProtectapp = Flask(__name__)app.config['SECRET_KEY'] = 'mysecretkey'csrf = CSRFProtect(app)
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
PRGパターンでGETで完了画面に行くがエラーとならない、WTFはPOSTのみ利くようだ
●完了画面でセッション変数がない場合はエラーとする保存処理でdoc_idをセッション変数に入れ不正で直接完了画面にいくとエラーとするhttps://xxxx.com/complete?doc id=ddd
■重複登録の回避策(PRG方式+JSボタン無効)・リダイレクト POST/Redirect/GET パターン・連打を避けるためボタンのJS無効化片方のみのためquerySelectorAll() を使う>ページ遷移しなくなった>下記JSを使う
@app.route('/submit', methods=['POST'])def page(): page = model if complete: session['doc_id'] = page['input']['doc_id'] redirect(url_for('thank_you')) else: retrun template('layout.html')
@app.route('/thank_you')def thank_you(): if 'doc_id' in session: page['input']['doc_id"] = session['doc_id"] session clear() return render_template('complete_layout.html', page=page) else: return render_template('error.html", message="missing arg")
@app.errorhandler(404)def page_not_found(e) return render_template('error html', message="Page not found"), 404
error.html側<h1>{{message }}</h1>
submit側連打を避けるためボタンの無効化片方のみのためquerySelectorAll() を使うとページ遷移しなくなる等がある<script>document.addEventListener('DOMContentLoaded', function(){ const form = document.getElementById('form'); const buttons = form.getElementsByTagName('button'); form.onsubmit= function(event) { //クリックされたボタンを取得 const clickedButton = event.submitter; //隠しフィールドを追加して、ボタンのname と value を送信 const hiddenField = document.createElement('input'); hiddenField type = 'hidden' hiddenField.name = clickedButton.name; hiddenField.value = clickedButton value; form.appendChild(hiddenField); // すべてのボタンを無効化して二重送信を防止 for (let i = 0; i < buttons.length; i++) { buttons[i].disabled = true; } }}</script>
Comment (0)
■24/6/21 11:00PM
BT
あそびはここで終わりにしようぜ~
Big TableCloud Bigtableを触ってみよう - Uzabase for Engineersでっかいテーブル、読み書き低レイテンシー、RDBは負荷高いときにレプ数位でスケールが難しいがBTはするので正規化せずに単一テーブルにしておく感じrow keyが主役データを追加するのに3パターンある(行追加、列追加、セル追加) 行に複数カラムファミリーにカラムが幾つか入れられるのでKVSだが結局Where句みたいに使う? 行キー「企業ID#日付」,COLUMN FAMILY「STOCK PRICE」,COLUMN「HI PRICE」「LO PRICE」に対してJSONデータを入れておく等 時間はバージョン管理として持っている 複雑な条件は無理でデータを事前整理して入れておき、JSONカラムを使ったりで一行にまとめスキャンを一発で済ます等で高スループットのみ Google検索のようにキーワードを入れると、検索結果が数多く一瞬で返る等 複雑な条件はDataprocを使うらしい
Big table構成Bigtableを徹底解説! - G-gen Tech Blogインスタンスの中に一つ以上のクラスタ(ゾーン別に設定しレプリケーション)> 各クラスタには1つ以上の同数のノード クラスタに table > 複数Column family > 複数Column > セルbigtable_app_profilesで転送クラスタ先の設定する(単一行トランザクション設定を含む) -マルチクラスタ(自動フェイルオーバ、単一行transaction不可でレプリケーションによる不整合あり) -シングルクラスタ(手動フェイルオーバ、一行transaction) デフォルトをマルチにして、通常のクラスタ転送をシングル、問題があるときだけアプリで判定しマルチに行くBigtableで複数クラスタ構成におけるデータ整合性の保証 - Carpe Diem (hatenablog.com)
スキーマ: テーブル 行キー(row key) カラムファミリー(カページコレクションポリシーを含む) カラム更新したデータはタイムスタンプによりセル内で保存される 解消するにはガベージコレクション 期限切れ値、バージョン数で設定する
仕様:KVS、行指向の行単位でスキャン各テーブルのインデックス (行キー)は1つのみで一意である必要がある行は、行キーの辞書順に並べ替えられます。列は、列ファミリー別にグループ化され、列ファミリー内で辞書順に並べ替えられます列ファミリーは特定の順序では保存されません集計列ファミリーには集計セルが含まれます行レベルでアトミック (複数行だと知らんという意) アトミック性:トランザクション整合性がある(一部の操作だけ実行した状態とならずに)
特定の行にread/writeが集中するより分散が良いBigtable のテーブルはスバース、空白行での消費はない
cbt CLI の概要 | Bigtable Documentation | Google Cloudcbt リファレンス | Bigtable Documentation | Google Cloudgcloud components updategcloud components install cbt(-/cbtrcに以下記載すれば-projectと-instance はデフォルト値で省略できる)cd ~echo project unco > ~/.cbtrcecho instance = chinco >> ~/.cbtrccbt -project unco listinstancescbt -instance chinco listclusterscbt -project unco -instance chinco ls | grep kuso-t テーブル名取得cht -project unco -instance chinco ls kuso-table カラムファミリやポリシー等取得cbt -project unco -instance chinco deletefamily kuso-table shikko-familycbt -project unco -instance chinco deletetable kuso-table テーブルを消せばカラムファミリも削除になる
Comment (0)
Navi: 1 | 2 | 3 | 4 >
-Home
-Column [133]
-Europe [9]
-Gadget [77]
-Web [136]
-Bike [4]
@/// BANGBOO BLOG ///

