aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils')
-rw-r--r--src/utils/sanpiStats.ts112
1 files changed, 107 insertions, 5 deletions
diff --git a/src/utils/sanpiStats.ts b/src/utils/sanpiStats.ts
index b2bfed3..33d5231 100644
--- a/src/utils/sanpiStats.ts
+++ b/src/utils/sanpiStats.ts
@@ -24,12 +24,13 @@ export interface SanpiChartRow {
/** グラフの説明文。 */
export const SANPI_CHART_CAPTION =
- "市が提出した議案(請願・意見書・決議・条例制定・動議などの議員提出議案を除く)に対する、会派ごとの議員の賛成・反対の割合です。安竹洋平が議員になってからの累計で、反対の割合が高い順に並べています。会派が分かれた場合は表の各列の票を1件として数えています。会派に合流している期間(中江美和議員のフォーラム、石津はるか議員の政和会)は、会派の票を個人に充当しています。件数が少ない会派は参考程度にご覧ください。";
+ "市が提出した議案(請願・意見書・決議(附帯決議を含む)・条例制定・動議などの議員提出議案を除く)に対する、会派ごとの議員の賛成・反対の割合です。安竹洋平が議員になってからの累計で、反対の割合が高い順に並べています。会派が分かれた場合は表の各列の票を1件として数えています。会派に合流している期間(中江美和議員のフォーラム、石津はるか議員の政和会)は、会派の票を個人に充当しています。件数が少ない会派は参考程度にご覧ください。";
// グラフの表示名(議員名を添える)。
const DISPLAY_NAME: Record<string, string> = {
れ新: "れ新(中倉)",
市民: "市民(水口)",
+ 橋本久雄: "橋本久雄(元議員)",
};
const ME_NAME = "安竹洋平";
@@ -54,6 +55,10 @@ function groupLabel(headerText: string): string {
const label = m[2];
// 会派(N人)は会派そのもの。
if (/^\d+人$/.test(label)) return name;
+ // 佐藤徹議員(公明)は、体調不良等による欠席・退席で列が分かれているだけで、
+ // 所属は公明党。公明の列が別にあるため、この個人列は集計しない
+ // (同じ議案を二重に数えない)。
+ if (name === "佐藤徹" && label === "公明") return "";
// 個人の(政和)は政和会に合算する(外山議員は常に、石津議員も一人でないときは政和会)。
if (label === "政和") return "政和";
// (一人)などは議員ごとに分ける。
@@ -67,11 +72,19 @@ function voteChar(cell: string): "〇" | "×" | null {
return null;
}
+export type BillType = "city" | "member" | "petition";
+
+// 議案の種別。グラフの集計と全期間一覧の分類で共通に使う。
+export function billType(cell: string): BillType {
+ if (cell.includes("sanpi-shugan")) return "petition";
+ const text = stripTags(cell);
+ if (/意見書|附帯決議|付帯決議|決議|動議|制定/.test(text)) return "member";
+ return "city";
+}
+
// 市が提出した議案か(請願・意見書・決議・条例制定・動議などの議員提出議案を除く)。
function isCityBill(cell: string): boolean {
- if (cell.includes("sanpi-shugan")) return false;
- const text = stripTags(cell);
- return !/意見書|決議|動議|制定/.test(text);
+ return billType(cell) === "city";
}
function eraKey(text: string): number | null {
@@ -141,7 +154,7 @@ export function computeSanpiStats(): SanpiChartRow[] {
for (const rule of MEMBER_RULES) {
const fromKey = eraKey(rule.from);
const untilKey = rule.until ? eraKey(rule.until) : Infinity;
- if (fromKey == null || sessionKey < fromKey || sessionKey > untilKey) continue;
+ if (fromKey == null || untilKey == null || sessionKey < fromKey || sessionKey > untilKey) continue;
if (groups.includes(rule.name)) continue;
const gi = groups.indexOf(rule.group);
if (gi < 0) continue;
@@ -174,3 +187,92 @@ export function computeSanpiStats(): SanpiChartRow[] {
};
});
}
+
+export interface SanpiBillRow {
+ /** 年度(例: 令和8年度)。 */
+ year: string;
+ /** 年度別ページへのリンク。 */
+ yearHref: string;
+ /** 定例会(例: 令和8年9月定例会)。 */
+ session: string;
+ /** 議案名(HTMLを除いたもの)。 */
+ title: string;
+ /** 議決結果(例: 可決、否決、採択、不採択)。 */
+ result: string;
+ /** 安竹洋平の賛否。—は表決に加わらない(議長・欠席・退場)。 */
+ me: "〇" | "×" | "—";
+ /** 市側の議案・議員提出の議案・請願の別。 */
+ type: BillType;
+}
+
+const YEAR_LABELS: Record<string, string> = {
+ r1d: "令和元年度",
+ r2d: "令和2年度",
+ r3d: "令和3年度",
+ r4d: "令和4年度",
+ r5d: "令和5年度",
+ r6d: "令和6年度",
+ r7d: "令和7年度",
+ r8d: "令和8年度",
+};
+
+/**
+ * 全期間の議案を、市側の議案・議員提出の議案・請願に分類して返す。
+ * 年度別ページの表をそのまま読み、安竹洋平の列だけを取り出す。
+ */
+export function computeSanpiBills(): SanpiBillRow[] {
+ const sources = import.meta.glob("/src/content/docs/sanpi/r*.mdx", {
+ query: "?raw",
+ import: "default",
+ eager: true,
+ }) as Record<string, string>;
+
+ const rows: { key: string; row: SanpiBillRow }[] = [];
+
+ for (const [path, source] of Object.entries(sources)) {
+ const key = path.match(/(r\d+d)\.mdx$/)?.[1] ?? "";
+ const year = YEAR_LABELS[key] ?? "";
+ const yearHref = key ? `/sanpi/${key}/` : "";
+ let session = "";
+ let header: string[] | null = null;
+ let meIndex = -1;
+
+ for (const line of source.split(/\r?\n/)) {
+ if (line.startsWith("## ")) {
+ session = line.slice(3).trim();
+ header = null;
+ meIndex = -1;
+ continue;
+ }
+ if (!line.startsWith("|")) {
+ header = null;
+ meIndex = -1;
+ continue;
+ }
+ const cells = splitRow(line);
+ if (isSeparatorRow(cells)) continue;
+ if (header === null) {
+ header = cells;
+ meIndex = header.findIndex((h) => groupLabel(h) === ME_NAME);
+ continue;
+ }
+ if (meIndex < 0) continue;
+ const me = voteChar(cells[meIndex] ?? "") ?? "—";
+ rows.push({
+ key,
+ row: {
+ year,
+ yearHref,
+ session,
+ title: stripTags(cells[0] ?? "").trim(),
+ result: stripTags(cells[1] ?? "").trim(),
+ me,
+ type: billType(cells[0] ?? ""),
+ },
+ });
+ }
+ }
+
+ rows.sort((a, b) => a.key.localeCompare(b.key));
+ return rows.map(({ row }) => row);
+}