linxinhua commited on
Commit
b4f12ab
·
verified ·
1 Parent(s): ce22e00

Create app.py

Browse files

create download function according to timestamp

Files changed (1) hide show
  1. app.py +270 -0
app.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ from datetime import datetime, timedelta
4
+ import io
5
+ import os
6
+ from huggingface_hub import HfApi, hf_hub_download
7
+ from typing import List, Tuple, Optional
8
+ import tempfile
9
+ import zipfile
10
+
11
+ class DataManager:
12
+ def __init__(self):
13
+ self.repo_id = "CIV3283/Data_Storage"
14
+ self.branch = "data_branch"
15
+ self.api = HfApi()
16
+
17
+ def get_available_files(self) -> Tuple[List[str], List[str]]:
18
+ """获取所有可用的feedback和query文件"""
19
+ try:
20
+ # 获取仓库中的所有文件
21
+ repo_files = self.api.list_repo_files(
22
+ repo_id=self.repo_id,
23
+ revision=self.branch
24
+ )
25
+
26
+ feedback_files = []
27
+ query_files = []
28
+
29
+ for file in repo_files:
30
+ if file.endswith('_feedback_log.csv') and 'CIV3283_Student_' in file:
31
+ feedback_files.append(file)
32
+ elif file.endswith('_query_log.csv') and 'CIV3283_Student_' in file:
33
+ query_files.append(file)
34
+
35
+ return sorted(feedback_files), sorted(query_files)
36
+
37
+ except Exception as e:
38
+ print(f"Error getting files: {e}")
39
+ return [], []
40
+
41
+ def download_and_read_csv(self, filename: str) -> pd.DataFrame:
42
+ """下载并读取CSV文件"""
43
+ try:
44
+ # 下载文件到临时位置
45
+ local_file = hf_hub_download(
46
+ repo_id=self.repo_id,
47
+ filename=filename,
48
+ revision=self.branch,
49
+ cache_dir=tempfile.gettempdir()
50
+ )
51
+
52
+ # 读取CSV
53
+ df = pd.read_csv(local_file)
54
+
55
+ # 确保timestamp列存在并转换为datetime
56
+ if 'timestamp' in df.columns:
57
+ df['timestamp'] = pd.to_datetime(df['timestamp'])
58
+
59
+ return df
60
+
61
+ except Exception as e:
62
+ print(f"Error reading {filename}: {e}")
63
+ return pd.DataFrame()
64
+
65
+ def filter_data_by_date(self, df: pd.DataFrame, start_date: str, end_date: str) -> pd.DataFrame:
66
+ """根据日期范围筛选数据"""
67
+ if df.empty or 'timestamp' not in df.columns:
68
+ return df
69
+
70
+ try:
71
+ # 转换输入的日期字符串
72
+ start_dt = pd.to_datetime(start_date)
73
+
74
+ if end_date == "现在":
75
+ end_dt = pd.Timestamp.now()
76
+ else:
77
+ end_dt = pd.to_datetime(end_date) + timedelta(days=1) - timedelta(seconds=1) # 包含整天
78
+
79
+ # 筛选数据
80
+ mask = (df['timestamp'] >= start_dt) & (df['timestamp'] <= end_dt)
81
+ return df[mask]
82
+
83
+ except Exception as e:
84
+ print(f"Error filtering data: {e}")
85
+ return df
86
+
87
+ def process_files(self, file_type: str, start_date: str, end_date: str) -> Tuple[str, str, str]:
88
+ """处理文件并生成下载内容"""
89
+ feedback_files, query_files = self.get_available_files()
90
+
91
+ result_data = []
92
+ file_info = []
93
+ total_records = 0
94
+
95
+ # 根据选择的文件类型确定要处理的文件
96
+ files_to_process = []
97
+ if file_type in ["反馈文件", "两者都要"]:
98
+ files_to_process.extend([(f, "feedback") for f in feedback_files])
99
+ if file_type in ["查询文件", "两者都要"]:
100
+ files_to_process.extend([(f, "query") for f in query_files])
101
+
102
+ # 处理每个文件
103
+ for filename, ftype in files_to_process:
104
+ df = self.download_and_read_csv(filename)
105
+ if not df.empty:
106
+ original_count = len(df)
107
+ filtered_df = self.filter_data_by_date(df, start_date, end_date)
108
+ filtered_count = len(filtered_df)
109
+
110
+ if filtered_count > 0:
111
+ result_data.append(filtered_df)
112
+ total_records += filtered_count
113
+
114
+ file_info.append(f"{filename}: {original_count}条记录 → {filtered_count}条符合条件")
115
+
116
+ # 合并所有数据
117
+ if result_data:
118
+ combined_df = pd.concat(result_data, ignore_index=True)
119
+
120
+ # 生成CSV内容
121
+ csv_buffer = io.StringIO()
122
+ combined_df.to_csv(csv_buffer, index=False, encoding='utf-8')
123
+ csv_content = csv_buffer.getvalue()
124
+
125
+ # 生成文件名
126
+ end_date_str = "现在" if end_date == "现在" else end_date
127
+ filename = f"CIV3283_数据导出_{file_type}_{start_date}_to_{end_date_str}.csv"
128
+
129
+ # 生成状态信息
130
+ status = f"✅ 成功处理 {len(files_to_process)} 个文件\n"
131
+ status += f"📊 总共找到 {total_records} 条符合条件的记录\n"
132
+ status += f"📅 时间范围: {start_date} 到 {end_date_str}\n\n"
133
+ status += "文件详情:\n" + "\n".join(file_info)
134
+
135
+ return csv_content, filename, status
136
+ else:
137
+ return "", "", "❌ 没有找到符合条件的数据"
138
+
139
+ # 初始化数据管理器
140
+ data_manager = DataManager()
141
+
142
+ def process_and_download(file_type, start_date, end_date):
143
+ """处理请求并返回下载文件"""
144
+ try:
145
+ csv_content, filename, status = data_manager.process_files(file_type, start_date, end_date)
146
+
147
+ if csv_content:
148
+ # 创建临时文件
149
+ temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False, encoding='utf-8')
150
+ temp_file.write(csv_content)
151
+ temp_file.close()
152
+
153
+ return status, temp_file.name, filename
154
+ else:
155
+ return status, None, None
156
+
157
+ except Exception as e:
158
+ return f"❌ 处理过程中出现错误: {str(e)}", None, None
159
+
160
+ def get_file_summary():
161
+ """获取文件概览"""
162
+ try:
163
+ feedback_files, query_files = data_manager.get_available_files()
164
+ summary = f"📁 当前可用文件:\n"
165
+ summary += f" 反馈文件: {len(feedback_files)} 个\n"
166
+ summary += f" 查询文件: {len(query_files)} 个\n"
167
+ summary += f" 总计: {len(feedback_files) + len(query_files)} 个文件"
168
+ return summary
169
+ except Exception as e:
170
+ return f"❌ 获取文件列表失败: {str(e)}"
171
+
172
+ # 创建Gradio界面
173
+ def create_interface():
174
+ with gr.Blocks(title="CIV3283 数据管理器", theme=gr.themes.Soft()) as demo:
175
+ gr.Markdown("# 🗃️ CIV3283 数据存储管理器")
176
+ gr.Markdown("从 Hugging Face Space 的 data_branch 分支整理和下载学生数据")
177
+
178
+ # 文件概览
179
+ with gr.Row():
180
+ file_summary = gr.Textbox(
181
+ label="📊 文件概览",
182
+ value=get_file_summary(),
183
+ interactive=False,
184
+ max_lines=5
185
+ )
186
+ refresh_btn = gr.Button("🔄 刷新文件列表", variant="secondary")
187
+
188
+ refresh_btn.click(
189
+ fn=get_file_summary,
190
+ outputs=file_summary
191
+ )
192
+
193
+ # 主要控制面板
194
+ with gr.Row():
195
+ with gr.Column(scale=1):
196
+ file_type = gr.Radio(
197
+ choices=["查询文件", "反馈文件", "两者都要"],
198
+ value="查询文件",
199
+ label="📂 选择文件类型"
200
+ )
201
+
202
+ start_date = gr.Textbox(
203
+ label="📅 开始日期",
204
+ placeholder="2025-06-16",
205
+ value="2025-06-16",
206
+ info="格式: YYYY-MM-DD"
207
+ )
208
+
209
+ end_date = gr.Textbox(
210
+ label="📅 结束日期",
211
+ placeholder="2025-06-20 或 现在",
212
+ value="现在",
213
+ info="格式: YYYY-MM-DD 或输入'现在'"
214
+ )
215
+
216
+ process_btn = gr.Button("🚀 处理并生成下载文件", variant="primary", size="lg")
217
+
218
+ with gr.Column(scale=2):
219
+ status_box = gr.Textbox(
220
+ label="📋 处理状态",
221
+ max_lines=15,
222
+ interactive=False
223
+ )
224
+
225
+ download_file = gr.File(
226
+ label="📥 下载文件",
227
+ interactive=False
228
+ )
229
+
230
+ # 使用说明
231
+ with gr.Accordion("📖 使用说明", open=False):
232
+ gr.Markdown("""
233
+ ### 功能说明
234
+ 1. **文件类型选择**: 选择要导出的数据类型
235
+ - 查询文件: 包含搜索信息和查询响应
236
+ - 反馈文件: 包含学生反馈评论
237
+ - 两者都要: 合并所有数据
238
+
239
+ 2. **日期范围**:
240
+ - 开始日期: 必须填写,格式 YYYY-MM-DD
241
+ - 结束日期: 可以是具体日期或"现在"
242
+
243
+ 3. **数据格式**:
244
+ - Query文件列: student_space, student_id, timestamp, search_info, query_and_response, thumb_feedback
245
+ - Feedback文件列: student_space, student_id, timestamp, comment
246
+
247
+ ### 注意事项
248
+ - 系统会自动发现所有可用的学生文件(01-95,跳过缺失的编号)
249
+ - 时间戳格式: 2025-06-16 08:55:33
250
+ - 导出的数据保持原始顺序,不进行时间排序
251
+ - 每次处理会显示详细的文件统计信息
252
+ """)
253
+
254
+ # 绑定处理函数
255
+ process_btn.click(
256
+ fn=process_and_download,
257
+ inputs=[file_type, start_date, end_date],
258
+ outputs=[status_box, download_file, gr.State()]
259
+ )
260
+
261
+ return demo
262
+
263
+ # 启动应用
264
+ if __name__ == "__main__":
265
+ demo = create_interface()
266
+ demo.launch(
267
+ server_name="0.0.0.0",
268
+ server_port=7860,
269
+ share=True
270
+ )