この記事は木更津高専 Advent Calender 2024の7日目の記事です
前: Blenderとおでんの違い by @Nimono-sleep-well さん
後: CloudflareはDNSだけじゃない! by @toreis さん
経緯
teamsでpdfを開こうとするとたまに開けないことがある。学食にかかわらず開けない。というか結構な確率で開けない。ほんとにどうにかしてくれ
ということで普段使うチャットツールのDiscordのBOTでつくるか、となりました。
環境
- discord.js 14.15.3
- node.js 22.9.0
- python 3.10.12
実装済み機能
- 定期学食送信(平日20:00)
- 今日と明日の学食の送信
pdfから学食のデータを取り出す
まず学校からteamsに投稿されている学食のpdfの表からデータをpdfplumberで抽出していく
import pdfplumber
import pandas as pd
pdf_path = "../data/1209.pdf"
csv_path = "../data/output.csv"
# PDFから表を抽出
all_tables = []
with pdfplumber.open(pdf_path) as pdf:
for page in pdf.pages:
tables = page.extract_tables()
for table in tables:
# テーブルが空でないことを確認
if table:
df = pd.DataFrame(table[1:], columns=table[0])
all_tables.append(df)
combined_df = pd.concat(all_tables, ignore_index = True)
combined_df.to_csv(csv_path, index = False, encoding = "utf-8")
print(f"{csv_path}に書き出しました")
# CSVファイルを読み込む
df = pd.read_csv(csv_path, encoding="utf-8", header=None)
pdf内の表を抽出してcsvファイルに書き出す
csvじゃ扱いづらいのでjsonに吐きだしていく
これがかなーーーーーーーーーーーーりやっっかい
学食のpdfはとてもとてもすばらしいのでよくわからない小さい文字が書かれている
これが原因でcsvの行と列がおかしなことになるしpdfごとに結果がことなる
たとえばかけ うどんそば
これがなんと
メニュー名 | Mon | Tue |
---|---|---|
かけ | ~ | ~ |
うどんそば | ~ | ~ |
といった二行にわかれていたり、わかれてなかったりと摩訶不思議なことが発生します
このバケモノpdfとなかよしになるのに開発全体の7割くらいもってかれました
from createCsv import df
import string
import json
import pandas as pd
menus = ["Aset", "Bset", "curry", "dailySpecialUdonSoba", "UdonSoba", "dailySpecialRamenPasta", "ramen"]
idenMenus = ["Aセット", "Bセット", "カレー", "日替り", "かけ", "日替り", "ラーメン"]
week = ["Mon", "Tue", "Wed", "Thu", "Fri"]
weekIndex = []
menusIndex = []
with open("../data/lunch.json", "r") as f:
lunch = json.load(f)
col: int = 2
row: int = 2
#曜日からインデックスを取得
for i in week:
while True:
data: string = df.iloc[row][col]
if pd.isna(data):
data = "None"
if i in data:
weekIndex.append(col)
break
else:
col += 1
col = 0
row = 0
#メニューの行のインデックスを取得
for i in idenMenus:
while True:
data: string = df.iloc[row][col]
if pd.isna(data):
data = "None"
if i in data:
menusIndex.append(row)
row += 2
break
else:
row += 1
for i in range(len(menus)):
for j in range(len(week)):
if pd.isna(df.iloc[menusIndex[i]][weekIndex[j]]):
(lunch[menus[i]][week[j]]) = "今日はありません"
else:
(lunch[menus[i]][week[j]]) = df.iloc[menusIndex[i]][weekIndex[j]]
with open("../data/lunch.json", "w") as f:
json.dump(lunch, f,indent = 4 , ensure_ascii=False)
こんな感じで無理やりjsonに落しこみます
学食の表示
bot部分はdiscord.jsで書きます
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const lunch = require("../data/lunch.json");
const days = ["Mon", "Tue", "Wed", "Thu", "Fri"];
const dispMenu = ["Aセット", "Bセット", "カレー", "日替わりうどん・そば", "うどん・そば", "日替わりラーメン・パスタ", "ラーメン"];
const menu = ["Aset", "Bset", "curry", "dailySpecialUdonSoba", "UdonSoba", "dailySpecialRamenPasta", "ramen"];
module.exports = {
data: new SlashCommandBuilder()
.setName("tomorrow")
.setDescription("明日の学食を表示します")
.setDMPermission(false),
async execute(interaction) {
const today = new Date().getDay();
if ((today+1) === 0 || (today+1) === 6) {
await interaction.reply("明日は休日です。学食はありません");
return;
}
const embed = new EmbedBuilder()
.setTitle("明日の学食")
.setDescription("明日の学食は以下の通りです")
.setFooter({text: "※内容は変更される可能性があります"})
.setTimestamp()
dispMenu.forEach((item, index) => {
embed.addFields({
name: item,
value: lunch[menu[index]][days[today]],
inline: false
});
});
await interaction.reply({ embeds: [embed]});
}
};
discordからみるとこんな感じ
おいしそう
今日の学食や毎日20:00に送信されるものも基本的にこの表示です
定期実行はcronをつかっています
cron.schedule("0 20 * * * ", async() => {
const today = new Date().getDay();
const embed = new EmbedBuilder()
.setTitle("明日の学食")
.setDescription("明日の学食は以下の通りです")
.setFooter({text: "※内容は変更される可能性があります"})
.setTimestamp()
dispMenu.forEach((item, index) => {
embed.addFields({
name: item,
value: lunch[menu[index]][days[today]],
inline: false
});
});
if ((today+1) === 0 || (today+1) === 6) {
await interaction.channel.send("明日は休日です。学食はありません");
}
else {
await interaction.channel.send({ embeds: [embed]});
}
});
実装したい機能
- pdfの取得の自動化
- アプリケーション
- ファイル操作の自動実行
さいごに
pythonってすごい
学食はチゲうどんがいちばんおいしいです