sugitora commited on
Commit
a1014eb
·
verified ·
1 Parent(s): 27b443e

Update app.R

Browse files
Files changed (1) hide show
  1. app.R +179 -115
app.R CHANGED
@@ -4,175 +4,232 @@ library(dplyr)
4
  library(readr)
5
  library(DT)
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  # データ読み込み
8
- csv_path <- "名刺データサンプル.csv"
9
- df <- read_csv(csv_path, locale = locale(encoding = "UTF-8"))
 
 
 
 
 
 
10
 
11
- # UI の定義
 
 
12
  ui <- dashboardPage(
13
- dashboardHeader(title = "📇 名刺データベース検索"),
 
14
  dashboardSidebar(
15
  sidebarMenu(
16
- menuItem("検索・閲覧", tabName = "search", icon = icon("search")),
17
- menuItem("統計情報", tabName = "stats", icon = icon("chart-bar")),
18
- menuItem("データ管理", tabName = "data_management", icon = icon("download"))
19
  ),
20
  hr(),
21
- # 検索フォーム
22
- h4("🔍 検索条件"),
23
- textInput("keyword", "キーワード検索", placeholder = "前、メール、電話など"),
24
- br(),
25
- selectInput(
26
- "company_filter",
27
- "会社名で絞り込み",
28
- choices = c("て", sort(unique(df$会社名)))
29
- ),
30
- br(),
31
- selectInput(
32
- "department_filter",
33
- "部署名で絞り込み",
34
- choices = c("全て", sort(unique(df$部署名)))
35
- ),
36
- br(),
37
- selectInput(
38
- "position_filter",
39
- "役職で絞り込み",
40
- choices = c("全て", sort(unique(df$役職)))
41
- ),
42
- br(),
43
- actionButton("search_btn", "🔎 検索を実行", class = "btn-primary btn-lg", width = "100%"),
44
- br(), br(),
45
- h5("検索結果: ", textOutput("result_count", inline = TRUE))
46
  ),
47
  dashboardBody(
 
 
 
 
 
 
 
48
  tabItems(
49
- # タブ1: 検索・閲覧
50
  tabItem(
51
  tabName = "search",
52
  fluidRow(
53
  box(
54
- title = "検索結果",
55
- width = 12,
56
- DTOutput("data_table"),
57
- downloadButton("download_csv", "📥 CSVダウンロード")
58
  )
59
  )
60
  ),
61
-
62
- # タブ2: 統計情報
63
  tabItem(
64
  tabName = "stats",
65
  fluidRow(
66
- infoBox("レコード数", nrow(df), icon = icon("database"), color = "blue"),
67
- infoBox("登録企業数", length(unique(df$会社名)), icon = icon("building"), color = "green"),
68
- infoBox("登録部署数", length(unique(df$部署名)), icon = icon("layer-group"), color = "yellow")
69
  ),
70
  fluidRow(
71
- box(
72
- title = "企業別レコード数",
73
- width = 6,
74
- tableOutput("company_stats")
75
- ),
76
- box(
77
- title = "部署別レコード数",
78
- width = 6,
79
- tableOutput("department_stats")
80
- )
81
  ),
82
  fluidRow(
83
- box(
84
- title = "役職別レコード数",
85
- width = 6,
86
- tableOutput("position_stats")
87
- ),
88
- box(
89
- title = "名刺交換日の分布",
90
- width = 6,
91
- tableOutput("date_stats")
92
- )
93
  )
94
  ),
95
-
96
- # タブ3: データ管理
97
  tabItem(
98
- tabName = "data_management",
99
  fluidRow(
100
  box(
101
- title = "データエク",
102
- width = 12,
103
- p("データベース全体をCSV形式でダウンロードできます"),
104
- downloadButton("download_all_csv", "📥 全データをダウンロード"),
105
- br(), br(),
106
- h5("ダウンロード情報:"),
107
  p(paste("総レコード数:", nrow(df))),
108
- p(paste("総列数:", ncol(df))),
109
- p(paste("ファイル形式: CSV (UTF-8)")),
110
- p(paste("最終更新:", format(Sys.time(), "%Y-%m-%d %H:%M:%S")))
111
  )
112
  )
113
  )
114
  )
115
- ),
116
- # カスタムCSS
117
- tags$head(
118
- tags$style(HTML("
119
- .btn-primary { background-color: #0275d8; }
120
- .box { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }
121
- h4, h5 { color: #333; font-weight: bold; }
122
- "))
123
  )
124
  )
125
 
126
- # Server ロジック
127
  server <- function(input, output, session) {
128
 
129
- # 検索を実行する
130
- filtered_data <- eventReactive(input$search_btn, {
131
  result <- df
132
 
133
- # キーワード検索
134
- if (input$keyword != "") {
135
  keyword <- tolower(input$keyword)
136
- search_cols <- c("会社名", "部署名", "役職", "姓", "名", "e-mail", "TEL会社", "TEL部門", "TEL直通", "携帯電話", "住所")
137
- mask <- apply(
138
- result[, search_cols],
139
- 1,
140
- function(row) any(grepl(keyword, tolower(as.character(row)), ignore.case = TRUE))
141
- )
142
- result <- result[mask, ]
 
 
143
  }
144
 
145
  # 会社名フィルタ
146
- if (input$company_filter != "全て") {
147
- result <- result %>% filter(会社名 == input$company_filter)
148
  }
149
 
150
  # 部署名フィルタ
151
- if (input$department_filter != "全て") {
152
- result <- result %>% filter(部署名 == input$department_filter)
153
  }
154
 
155
  # 役職フィルタ
156
- if (input$position_filter != "全て") {
157
- result <- result %>% filter(役職 == input$position_filter)
158
  }
159
 
160
  result
161
  })
162
 
163
- # 検索結果のテーブル表示
164
- output$data_table <- renderDT({
165
- datatable(
166
- filtered_data(),
 
 
 
 
 
 
 
 
 
 
 
167
  options = list(
168
- pageLength = 20,
 
169
  scrollX = TRUE,
170
  language = list(
171
- url = 'https://cdn.datatables.net/plug-ins/1.10.11/i18n/Japanese.json'
172
- ),
173
- columnDefs = list(
174
- list(width = '100px', targets = 0:4),
175
- list(width = '150px', targets = 5)
176
  )
177
  ),
178
  filter = "top",
@@ -182,7 +239,7 @@ server <- function(input, output, session) {
182
 
183
  # 検索結果の件数表示
184
  output$result_count <- renderText({
185
- paste0(nrow(filtered_data()), " 件")
186
  })
187
 
188
  # CSV ダウンロード(検索結果)
@@ -208,20 +265,25 @@ server <- function(input, output, session) {
208
  # 統計情報
209
  output$company_stats <- renderTable({
210
  df %>%
 
211
  group_by(会社名) %>%
212
  summarise(件数 = n(), .groups = 'drop') %>%
213
- arrange(desc(件数))
 
214
  })
215
 
216
  output$department_stats <- renderTable({
217
  df %>%
 
218
  group_by(部署名) %>%
219
  summarise(件数 = n(), .groups = 'drop') %>%
220
- arrange(desc(件数))
 
221
  })
222
 
223
  output$position_stats <- renderTable({
224
  df %>%
 
225
  group_by(役職) %>%
226
  summarise(件数 = n(), .groups = 'drop') %>%
227
  arrange(desc(件数))
@@ -229,11 +291,13 @@ server <- function(input, output, session) {
229
 
230
  output$date_stats <- renderTable({
231
  df %>%
 
232
  group_by(名刺交換日) %>%
233
  summarise(件数 = n(), .groups = 'drop') %>%
234
- arrange(desc(名刺交換日))
 
235
  })
236
  }
237
 
238
  # アプリを起動
239
- shinyApp(ui, server)
 
4
  library(readr)
5
  library(DT)
6
 
7
+ # ===== データ読み込み(複数のファイル名・エンコーディング対応) =====
8
+ load_data <- function() {
9
+ # 優先順位で読み込みを試行するファイル名リスト
10
+ file_candidates <- c(
11
+ "名刺データサンプル.csv",
12
+ "EightData_s.csv",
13
+ "EightData.csv",
14
+ "meishi.csv",
15
+ "data.csv"
16
+ )
17
+
18
+ encodings <- c("UTF-8", "Shift_JIS", "CP932")
19
+
20
+ for (file_name in file_candidates) {
21
+ if (file.exists(file_name)) {
22
+ message(paste("📂 ファイル発見:", file_name))
23
+
24
+ for (enc in encodings) {
25
+ tryCatch({
26
+ df <- read_csv(file_name, locale = locale(encoding = enc), show_col_types = FALSE)
27
+ message(paste("✅ 読み込み成功:", file_name, "エンコーディング:", enc))
28
+ return(df)
29
+ }, error = function(e) {
30
+ message(paste("❌", enc, "で失敗"))
31
+ })
32
+ }
33
+ }
34
+ }
35
+
36
+ # ファイルが見つからない場合はサンプルデータを生成
37
+ message("⚠️ CSVファイルが見つかりません。サンプルデータを生成します...")
38
+ return(create_sample_data())
39
+ }
40
+
41
+ # サンプルデータ生成関数
42
+ create_sample_data <- function() {
43
+ companies <- c("株式会社ABC", "株式会社XYZ", "テクノソリューションズ", "グローバル商事", "デジタルネットワーク")
44
+ departments <- c("営業部", "開発部", "マーケティング部", "人事部", "経理部")
45
+ positions <- c("部長", "課長", "係長", "主任", "担当")
46
+ last_names <- c("田中", "鈴木", "佐藤", "高橋", "山田", "伊藤", "渡辺", "中村", "小林", "加藤")
47
+ first_names <- c("太郎", "花子", "一郎", "二郎", "三郎", "さくら", "翔太", "美咲", "健太", "優子")
48
+
49
+ n <- 20
50
+
51
+ data.frame(
52
+ 会社名 = sample(companies, n, replace = TRUE),
53
+ 部署名 = sample(departments, n, replace = TRUE),
54
+ 役職 = sample(positions, n, replace = TRUE),
55
+ 姓 = sample(last_names, n, replace = TRUE),
56
+ 名 = sample(first_names, n, replace = TRUE),
57
+ `e-mail` = paste0(tolower(sample(last_names, n, replace = TRUE)), "@example.com"),
58
+ 郵便番号 = sprintf("%03d-%04d", sample(100:999, n, replace = TRUE), sample(1000:9999, n, replace = TRUE)),
59
+ 住所 = paste0("東京都", sample(c("千代田区", "港区", "新宿区", "渋谷区", "中央区"), n, replace = TRUE)),
60
+ TEL会社 = sprintf("03-%04d-%04d", sample(1000:9999, n, replace = TRUE), sample(1000:9999, n, replace = TRUE)),
61
+ TEL部門 = NA_character_,
62
+ TEL直通 = NA_character_,
63
+ Fax = NA_character_,
64
+ 携帯電話 = sprintf("090-%04d-%04d", sample(1000:9999, n, replace = TRUE), sample(1000:9999, n, replace = TRUE)),
65
+ URL = paste0("https://", tolower(sample(companies, n, replace = TRUE)), ".co.jp") |> gsub("株式会社", "", x = _),
66
+ 名刺交換日 = format(Sys.Date() - sample(1:365, n, replace = TRUE), "%Y/%m/%d"),
67
+ stringsAsFactors = FALSE,
68
+ check.names = FALSE
69
+ )
70
+ }
71
+
72
  # データ読み込み
73
+ df <- load_data()
74
+
75
+ # 列名の確認と調整
76
+ expected_cols <- c("会社名", "部署名", "役職", "姓", "名", "e-mail", "郵便番号", "住所",
77
+ "TEL会社", "TEL部門", "TEL直通", "Fax", "携帯電話", "URL", "名刺交換日")
78
+
79
+ # 存在する列だけ使用
80
+ available_cols <- intersect(expected_cols, colnames(df))
81
 
82
+ message(paste("📊 データ:", nrow(df), "行,", ncol(df), "列"))
83
+
84
+ # ===== UI =====
85
  ui <- dashboardPage(
86
+ skin = "blue",
87
+ dashboardHeader(title = "📇 名刺データベース"),
88
  dashboardSidebar(
89
  sidebarMenu(
90
+ menuItem("🔍 検索・閲覧", tabName = "search", icon = icon("search")),
91
+ menuItem("📊 統計情報", tabName = "stats", icon = icon("bar-chart")),
92
+ menuItem("📥 データ管理", tabName = "download", icon = icon("download"))
93
  ),
94
  hr(),
95
+ h4("検索条件", style = "padding-left: 15px;"),
96
+ textInput("keyword", "キーワード検索", placeholder = "名前、会社名、メールなど"),
97
+ selectInput("filter_company", "会社で絞り込み",
98
+ choices = c("すべて" = "", sort(unique(na.omit(df$会社名))))),
99
+ selectInput("filter_dept", "部署名で絞り込み",
100
+ choices = c("すべて" = "", sort(unique(na.omit(df$部署名))))),
101
+ selectInput("filter_position", "役職で絞り込み",
102
+ choices = c("すべて" = "", sort(unique(na.omit(df$役職))))),
103
+ actionButton("search_btn", "🔎 検索", class = "btn-primary btn-block"),
104
+ actionButton("reset_btn", "🔄 リセット", class = "btn-default btn-block"),
105
+ hr(),
106
+ h5(textOutput("result_count"), style = "padding-left: 15px; font-weight: bold;")
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  ),
108
  dashboardBody(
109
+ tags$head(
110
+ tags$style(HTML("
111
+ .content-wrapper { background-color: #f4f6f9; }
112
+ .box { border-radius: 5px; }
113
+ .dataTables_filter { display: none; }
114
+ "))
115
+ ),
116
  tabItems(
117
+ # 検索・閲覧タブ
118
  tabItem(
119
  tabName = "search",
120
  fluidRow(
121
  box(
122
+ title = "検索結果", width = 12, status = "primary", solidHeader = TRUE,
123
+ DT::dataTableOutput("data_table"),
124
+ br(),
125
+ downloadButton("download_csv", "📥 検索結果をCSVダウンロード", class = "btn-success")
126
  )
127
  )
128
  ),
129
+ # 統計情報タブ
 
130
  tabItem(
131
  tabName = "stats",
132
  fluidRow(
133
+ infoBox("レコード数", nrow(df), icon = icon("database"), color = "blue", width = 4),
134
+ infoBox("企業数", length(unique(na.omit(df$会社名))), icon = icon("building"), color = "green", width = 4),
135
+ infoBox("部署数", length(unique(na.omit(df$部署名))), icon = icon("sitemap"), color = "purple", width = 4)
136
  ),
137
  fluidRow(
138
+ box(title = "会社別レコード数 (Top 15)", width = 6, status = "info", solidHeader = TRUE,
139
+ tableOutput("company_stats")),
140
+ box(title = "部署別レコード数 (Top 15)", width = 6, status = "info", solidHeader = TRUE,
141
+ tableOutput("department_stats"))
 
 
 
 
 
 
142
  ),
143
  fluidRow(
144
+ box(title = "役職別レコード数", width = 6, status = "warning", solidHeader = TRUE,
145
+ tableOutput("position_stats")),
146
+ box(title = "名刺交換日", width = 6, status = "warning", solidHeader = TRUE,
147
+ tableOutput("date_stats"))
 
 
 
 
 
 
148
  )
149
  ),
150
+ # データ管理タブ
 
151
  tabItem(
152
+ tabName = "download",
153
  fluidRow(
154
  box(
155
+ title = "データベー全体のダウンロ", width = 12, status = "success", solidHeader = TRUE,
156
+ p("現在のデータベース全体をCSV形式でダウンロードできます。"),
157
+ downloadButton("download_all_csv", "📥 全データをCSVでダウンロード", class = "btn-primary btn-lg"),
158
+ hr(),
159
+ h4("データベース情報"),
 
160
  p(paste("総レコード数:", nrow(df))),
161
+ p(paste("��ラム数:", ncol(df))),
162
+ p(paste("カラム:", paste(colnames(df), collapse = ", ")))
 
163
  )
164
  )
165
  )
166
  )
 
 
 
 
 
 
 
 
167
  )
168
  )
169
 
170
+ # ===== Server =====
171
  server <- function(input, output, session) {
172
 
173
+ # リアクティブ: フィルタリングされたデータ
174
+ filtered_data <- reactive({
175
  result <- df
176
 
177
+ # キーワード検索(複数列対象)
178
+ if (!is.null(input$keyword) && input$keyword != "") {
179
  keyword <- tolower(input$keyword)
180
+ search_cols <- c("会社名", "部署名", "役職", "姓", "名", "e-mail", "住所")
181
+ search_cols <- intersect(search_cols, colnames(result))
182
+
183
+ result <- result %>%
184
+ filter(
185
+ Reduce(`|`, lapply(search_cols, function(col) {
186
+ grepl(keyword, tolower(as.character(.data[[col]])), fixed = TRUE)
187
+ }))
188
+ )
189
  }
190
 
191
  # 会社名フィルタ
192
+ if (!is.null(input$filter_company) && input$filter_company != "") {
193
+ result <- result %>% filter(会社名 == input$filter_company)
194
  }
195
 
196
  # 部署名フィルタ
197
+ if (!is.null(input$filter_dept) && input$filter_dept != "") {
198
+ result <- result %>% filter(部署名 == input$filter_dept)
199
  }
200
 
201
  # 役職フィルタ
202
+ if (!is.null(input$filter_position) && input$filter_position != "") {
203
+ result <- result %>% filter(役職 == input$filter_position)
204
  }
205
 
206
  result
207
  })
208
 
209
+ # リセットボタン
210
+ observeEvent(input$reset_btn, {
211
+ updateTextInput(session, "keyword", value = "")
212
+ updateSelectInput(session, "filter_company", selected = "")
213
+ updateSelectInput(session, "filter_dept", selected = "")
214
+ updateSelectInput(session, "filter_position", selected = "")
215
+ })
216
+
217
+ # データテーブル表示
218
+ output$data_table <- DT::renderDataTable({
219
+ display_cols <- c("会社名", "部署名", "役職", "姓", "名", "e-mail", "携帯電話", "名刺交換日")
220
+ display_cols <- intersect(display_cols, colnames(filtered_data()))
221
+
222
+ DT::datatable(
223
+ filtered_data()[, display_cols, drop = FALSE],
224
  options = list(
225
+ pageLength = 15,
226
+ lengthMenu = c(10, 15, 25, 50, 100),
227
  scrollX = TRUE,
228
  language = list(
229
+ lengthMenu = "_MENU_ 件表示",
230
+ search = "テーブル内検索:",
231
+ info = "_TOTAL_ 件中 _START_ ~ _END_ 件を表示",
232
+ paginate = list(previous = "前へ", `next` = "次へ")
 
233
  )
234
  ),
235
  filter = "top",
 
239
 
240
  # 検索結果の件数表示
241
  output$result_count <- renderText({
242
+ paste0("検索結果: ", nrow(filtered_data()), " 件")
243
  })
244
 
245
  # CSV ダウンロード(検索結果)
 
265
  # 統計情報
266
  output$company_stats <- renderTable({
267
  df %>%
268
+ filter(!is.na(会社名)) %>%
269
  group_by(会社名) %>%
270
  summarise(件数 = n(), .groups = 'drop') %>%
271
+ arrange(desc(件数)) %>%
272
+ head(15)
273
  })
274
 
275
  output$department_stats <- renderTable({
276
  df %>%
277
+ filter(!is.na(部署名)) %>%
278
  group_by(部署名) %>%
279
  summarise(件数 = n(), .groups = 'drop') %>%
280
+ arrange(desc(件数)) %>%
281
+ head(15)
282
  })
283
 
284
  output$position_stats <- renderTable({
285
  df %>%
286
+ filter(!is.na(役職)) %>%
287
  group_by(役職) %>%
288
  summarise(件数 = n(), .groups = 'drop') %>%
289
  arrange(desc(件数))
 
291
 
292
  output$date_stats <- renderTable({
293
  df %>%
294
+ filter(!is.na(名刺交換日)) %>%
295
  group_by(名刺交換日) %>%
296
  summarise(件数 = n(), .groups = 'drop') %>%
297
+ arrange(desc(名刺交換日)) %>%
298
+ head(15)
299
  })
300
  }
301
 
302
  # アプリを起動
303
+ shinyApp(ui, server)