Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JAWS FESTA 2025 in 金沢の企業サポーター の抽選プログラムを衆人環視のもと ...

JAWS FESTA 2025 in 金沢の企業サポーター の抽選プログラムを衆人環視のもと Amazon Q Developer に即興で作ってもらった話

2025年8月30日の JAWS -UG 岡山の懇親会 LT でお話しした内容です。

岡山に向かう道中、姫路あたりで急に古里さんからアレで話せる?と言われて急遽資料を作ったやつです。

JAWS FESTA 2025 in 金沢の企業サポーターの抽選の際に、新たなチャレンジに対して技術的に解決する中で Amazon Q Developer for CLI の力を大きく借りた、というお話をしました。

#jawsoka #jawsug

Avatar for kazzpapa3

kazzpapa3

August 30, 2025
Tweet

More Decks by kazzpapa3

Other Decks in Technology

Transcript

  1. Biography { "Bio": { "Name": "kazzpapa3 a.k.a. ICHINO Kazuaki", "Organization":

    "A certain AWS partner company", "Role": "Technical Support Engineer", "Favorite AWS Services": [ "AWS CLI", "AWS CloudTrail" ], "Less Favorite AWS Service (as a Support Engineer)": [ "Amazon FSx for Windows" ], "Personal Interest": "初音ミク", "Socials": { "Twitter/X": "@kazzpapa3", "LinkedIn": "https://www.linkedin.com/in/kazzpapa3/" } } }
  2. この話は、昨晩 note に投稿済み JAWS FESTA 2025 in 金沢の企業サポーターの抽選に ついて として公開しています

    懇親会 LT で枠用意できそうだから、せっかくだから 喋れる?と、今朝姫路駅でえきそば食ってる時に古里 さんからご連絡をいただきました note の投稿を映しながらで良いんじゃない?と言っ ていただきましたが、せっかくなので姫路からの普通 電車の車内でまとめてみました 何分くらいの分量になっているかは不明です @kazzpapa3 / #jawsoka #jawsug 4 / 25
  3. 頼もしい相棒 Amazon Q Developer の出番 なんとなくの CSV ファイルを 2 つテストデータ的に作って、手元の

    Amazon Q Developer for CLI に聞いてみました 1. サポータープランと抽選実施順位、枠数を記載した plans.csv 2. 応募企業名と第一希望、落選時の希望を記載した entries.csv @kazzpapa3 / #jawsoka #jawsug 9 / 25
  4. 聞いてみた なんとなくの CSV ファイルを 2 つテストデータ的に作って、手元の Amazon Q Developer for

    CLI に聞いてみました 1. サポータープランと抽選実施順位、枠数を記載した plans.csv 2. 応募企業名と第一希望、落選時の希望を記載した entries.csv plans.csv にある抽選実施順で枠数を満たすまでのランダムな抽選シ ステムを作りたい。 ただし、第一希望のプランに落選した場合で、かつ下位プランへのス ライド応募希望が entries.csv にある場合は次回の下位プランの抽選 時に対象に含めたい “ “ @kazzpapa3 / #jawsoka #jawsug 10 / 25
  5. plans.csv(抽選順と枠数) order,name,slot 1,"ゴールド - セッション",5 2,"ゴールド - ブース",5 3,"シルバー -

    全国",5 4,"ブロンズ - 全国",5 5,"シルバー - 北陸",5 6,"ブロンズ - 北陸",5 7,"ゴールド - イベント",3 @kazzpapa3 / #jawsoka #jawsug 19 / 25
  6. entry.yml(ここではダミーデータ) # last_editing_history: 192 --- ゴールド - セッション: :base_plan: ゴールド

    - セッション :plans: ゴールド - セッション: :plan_name: ゴールド - セッション :sponsors: - :id: 9 :asset_file_id: 22 :base_plan: ゴールド - セッション :plan_name: ゴールド - セッション :slug: foo.co.jp :name: foo株式会社 :url: https://www.foo.co.jp/ :profile: 盛り上げるでー :note: 落選した場合はシルバーを希望 :custom: 落選した場合はシルバーを希望 シルバー - 北陸: :base_plan: シルバー - 北陸 :plans: シルバー - 北陸: :plan_name: シルバー - 北陸 :sponsors: - :id: 10 :asset_file_id: 23 :base_plan: シルバー - 北陸 :plan_name: シルバー - 北陸 :slug: bar.co.jp :name: 株式会社bar :url: https://bar.co.jp/ :profile: がんばるまっし :note: '' :custom: ''
  7. require.md(要件定義) # 実施したいこと - テックイベントのスポンサーを募集しています。 - 募集枠に対して応募総数が多いため抽選を行いたい。 - 後述する plans.csv

    で定義されている順に抽選を行い、募集枠が埋まるまで抽選を行いたい - 応募者の情報は entry.yml に記載があります - 上位プランから抽選をしていきますが、抽選に漏れた場合に希望があれば下位プランへのエントリにスライドさせ、次の抽選順になっているプラン名の抽選を行います - 下位プランへの自動スライドを希望するか否かは entry.yml のハッシュとして custom キーに対する値として保持しています - ランダム性のある公平な抽選システムとして bash によるシェルスクリプトとしてプログラムを作成したい # データレイアウトと意図 - 募集しているプランは plans.csv にあります。 plans.csv のデータレイアウトは以下の通りです。 - 1番目のカラム:抽選順 - 2番目のカラム:プラン名 - 3番目のカラム:募集枠 - 応募者の情報は entry.yml にあります。 このうち抽選に必要な entry.yml のデータレイアウトは以下の通りです。 - 配列の1番目:募集しているプラン名を示します - 配列の2番目 plans ハッシュ:応募者情報を束ねます - plans ハッシュが持つ配列: - plan_name:応募しているプラン名 - name:応募者名 - custom:応募しているプランに落選した場合の下位プランへの自動スライドを希望するか否かの意思表示 # 検討が必要だと思われること entry.yml の内容が YAML ファイルのままでは取り回しが困難かと考えられ、CSV 形式とした抽選に必要な情報のみを抽出したファイルの方が良いのではないかと考えている。
  8. lottery.sh 見えない ので詳細は note で #!/bin/bash # JAWS FESTA 2025

    スポンサー抽選プログラム # 要件に基づいて上位プランから順に抽選を実施し、 # 落選者の下位プランへのスライドも処理する set -euo pipefail # ファイルパス PLANS_CSV="plans.csv" ENTRY_YML="entry.yml" ENTRIES_CSV="entries.csv" RESULTS_DIR="results" LOG_FILE="lottery.log" # 結果ディレクトリの作成 mkdir -p "$RESULTS_DIR" # ログ関数 log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } # YAMLからCSV形式に変換する関数 convert_yaml_to_csv() { log "YAMLファイルからCSV形式への変換を開始" # CSVヘッダー echo "plan_name,name,slide_down" > "$ENTRIES_CSV" # YAMLファイルをbashで解析してCSVに変換 local current_plan="" local in_sponsors=false local sponsor_name="" local sponsor_custom="" while IFS= read -r line; do # プラン名の抽出 if [[ "$line" =~ ^[[:space:]]*:plan_name:[[:space:]]*(.+)$ ]]; then current_plan="${BASH_REMATCH[1]}" continue fi # スポンサー名の抽出 if [[ "$line" =~ ^[[:space:]]*:name:[[:space:]]*(.+)$ ]]; then sponsor_name="${BASH_REMATCH[1]}" continue fi # customフィールドの抽出 if [[ "$line" =~ ^[[:space:]]*:custom:[[:space:]]*(.*)$ ]]; then sponsor_custom="${BASH_REMATCH[1]}" # スポンサー情報が揃ったらCSVに出力 if [[ -n "$current_plan" && -n "$sponsor_name" ]]; then echo "\"$current_plan\",\"$sponsor_name\",\"$sponsor_custom\"" >> "$ENTRIES_CSV" fi # リセット sponsor_name="" sponsor_custom="" continue fi done < "$ENTRY_YML" log "CSV変換完了: $ENTRIES_CSV" } # 下位プランへのスライド希望を判定する関数 should_slide_down() { local custom_field="$1" # customフィールドが空でない場合はスライド希望とみなす if [[ -n "$custom_field" && "$custom_field" != '""' ]]; then return 0 # true else return 1 # false fi } # プラン名から次の下位プランを取得する関数 get_next_plan() { local current_plan="$1" case "$current_plan" in "ゴールド - セッション") echo "ゴールド - ブース" ;; "ゴールド - ブース") echo "シルバー - 全国" ;; "シルバー - 全国") echo "ブロンズ - 全国" ;; "シルバー - 北陸") echo "ブロンズ - 北陸" ;; *) echo "" # 下位プランなし ;; esac } # 抽選を実行する関数 conduct_lottery() { local plan_name="$1" local slot_count="$2" local round="$3" log "=== $plan_name の抽選開始 (募集枠: $slot_count) ===" # 該当プランの応募者を抽出 local applicants_file="$RESULTS_DIR/applicants_${plan_name// /_}_round${round}.txt" grep "^\"$plan_name\"," "$ENTRIES_CSV" | cut -d',' -f2 | sed 's/"//g' > "$applicants_file" local applicant_count=$(wc -l < "$applicants_file") log "$plan_name への応募者数: $applicant_count" if [[ $applicant_count -eq 0 ]]; then log "$plan_name への応募者がいません" return fi # 抽選実行 local winners_file="$RESULTS_DIR/winners_${plan_name// /_}_round${round}.txt" local losers_file="$RESULTS_DIR/losers_${plan_name// /_}_round${round}.txt" if [[ $applicant_count -le $slot_count ]]; then # 応募者数が募集枠以下の場合は全員当選 cp "$applicants_file" "$winners_file" touch "$losers_file" log "$plan_name: 全員当選 ($applicant_count/$slot_count)" else # ランダムに抽選 shuf "$applicants_file" > "$RESULTS_DIR/shuffled_${plan_name// /_}_round${round}.txt" head -n "$slot_count" "$RESULTS_DIR/shuffled_${plan_name// /_}_round${round}.txt" > "$winners_file" tail -n +$((slot_count + 1)) "$RESULTS_DIR/shuffled_${plan_name// /_}_round${round}.txt" > "$losers_file" log "$plan_name: 抽選完了 (当選: $slot_count, 落選: $((applicant_count - slot_count)))" fi # 当選者を表示 if [[ -s "$winners_file" ]]; then log "$plan_name 当選者:" while IFS= read -r winner; do log " ✓ $winner" done < "$winners_file" fi # 落選者の下位プランへのスライド処理 if [[ -s "$losers_file" ]]; then local next_plan=$(get_next_plan "$plan_name") if [[ -n "$next_plan" ]]; then log "$plan_name 落選者の下位プランスライド処理:" while IFS= read -r loser; do # 該当者のcustomフィールドをチェック local custom_field=$(grep "^\"$plan_name\",\"$loser\"," "$ENTRIES_CSV" | cut -d',' -f3) if should_slide_down "$custom_field"; then # 下位プランにエントリを追加 echo "\"$next_plan\",\"$loser\",\"$custom_field\"" >> "$ENTRIES_CSV" log " → $loser を $next_plan にスライド" else log " × $loser はスライドを希望していません" fi done < "$losers_file" fi fi # 当選者をエントリリストから除外 if [[ -s "$winners_file" ]]; then local temp_file=$(mktemp) while IFS= read -r winner; do grep -v ",\"$winner\"," "$ENTRIES_CSV" > "$temp_file" || true mv "$temp_file" "$ENTRIES_CSV" done < "$winners_file" fi } # メイン処理 main() { log "JAWS FESTA 2025 スポンサー抽選プログラム開始" # 必要ファイルの存在確認 if [[ ! -f "$PLANS_CSV" ]]; then log "エラー: $PLANS_CSV が見つかりません" exit 1 fi if [[ ! -f "$ENTRY_YML" ]]; then log "エラー: $ENTRY_YML が見つかりません" exit 1 fi # YAMLからCSVに変換 convert_yaml_to_csv # プランを抽選順でソート local plans_sorted=$(tail -n +2 "$PLANS_CSV" | sort -t',' -k1,1n) local round=1 # 各プランで抽選実行 while IFS=',' read -r order plan_name slot_count; do # クォートを除去 plan_name=$(echo "$plan_name" | sed 's/"//g') conduct_lottery "$plan_name" "$slot_count" "$round" echo ((round++)) done <<< "$plans_sorted" # 最終結果の集計 log "=== 最終結果集計 ===" local final_results="$RESULTS_DIR/final_results.txt" echo "JAWS FESTA 2025 スポンサー抽選結果" > "$final_results" echo "抽選実施日時: $(date)" >> "$final_results" echo "" >> "$final_results" while IFS=',' read -r order plan_name slot_count; do plan_name=$(echo "$plan_name" | sed 's/"//g')