Spaces:
Sleeping
Sleeping
def update_submission_data_html(submission_json):
Browse files- README.md +1 -1
- assignment_service.py +27 -3
- assignment_ui.py +122 -49
- requirements.txt +1 -1
README.md
CHANGED
@@ -4,7 +4,7 @@ emoji: 📚
|
|
4 |
colorFrom: red
|
5 |
colorTo: blue
|
6 |
sdk: gradio
|
7 |
-
sdk_version: 4.
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
---
|
|
|
4 |
colorFrom: red
|
5 |
colorTo: blue
|
6 |
sdk: gradio
|
7 |
+
sdk_version: 4.41.0
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
---
|
assignment_service.py
CHANGED
@@ -16,7 +16,7 @@ class AssignmentService:
|
|
16 |
if not self.gcs_service.check_file_exists(self.bucket_name, file_name):
|
17 |
return assignment_id
|
18 |
|
19 |
-
def create_assignment_metadata(self, assignment_type, grade, topic, introduction, description,
|
20 |
if assignment_type == "中文寫作 AI 批改":
|
21 |
metadata = {
|
22 |
"assignment_type": assignment_type,
|
@@ -24,7 +24,6 @@ class AssignmentService:
|
|
24 |
"topic": topic,
|
25 |
"introduction": introduction,
|
26 |
"description": description,
|
27 |
-
"url": url,
|
28 |
"attach_materials": attach_materials,
|
29 |
"submission_deadline": submission_deadline
|
30 |
}
|
@@ -34,17 +33,42 @@ class AssignmentService:
|
|
34 |
def create_assignment(self, user_data, assignment_type, metadata):
|
35 |
assignment_id = self.create_assignment_id()
|
36 |
timestamp_now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
new_assignment = {
|
38 |
"assignment_id": assignment_id,
|
39 |
"assigner_data": user_data,
|
40 |
"assignment_type": assignment_type,
|
41 |
"metadata": metadata,
|
|
|
42 |
"submission_ids": [],
|
43 |
"timestamp": timestamp_now
|
44 |
}
|
45 |
self.save_assignment_to_gcs(new_assignment)
|
46 |
self.save_user_assignment_to_gcs(user_data, assignment_id)
|
47 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
|
49 |
def save_assignment_to_gcs(self, assignment):
|
50 |
file_name = f"assignments/{assignment['assignment_id']}.json"
|
|
|
16 |
if not self.gcs_service.check_file_exists(self.bucket_name, file_name):
|
17 |
return assignment_id
|
18 |
|
19 |
+
def create_assignment_metadata(self, assignment_type, grade, topic, introduction, description, attach_materials, submission_deadline):
|
20 |
if assignment_type == "中文寫作 AI 批改":
|
21 |
metadata = {
|
22 |
"assignment_type": assignment_type,
|
|
|
24 |
"topic": topic,
|
25 |
"introduction": introduction,
|
26 |
"description": description,
|
|
|
27 |
"attach_materials": attach_materials,
|
28 |
"submission_deadline": submission_deadline
|
29 |
}
|
|
|
33 |
def create_assignment(self, user_data, assignment_type, metadata):
|
34 |
assignment_id = self.create_assignment_id()
|
35 |
timestamp_now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
36 |
+
host_url = "https://www.junyiacademy.org/event/jutor_write_2024/?__theme=light"
|
37 |
+
language_mapping = {
|
38 |
+
"寫作 AI 批改": "chinese",
|
39 |
+
"英文寫作 AI 批改": "english"
|
40 |
+
}
|
41 |
+
language = language_mapping.get(assignment_type, "")
|
42 |
+
|
43 |
+
assignment_url = f"{host_url}&language={language}&assignment={assignment_id}"
|
44 |
new_assignment = {
|
45 |
"assignment_id": assignment_id,
|
46 |
"assigner_data": user_data,
|
47 |
"assignment_type": assignment_type,
|
48 |
"metadata": metadata,
|
49 |
+
"assignment_url": assignment_url,
|
50 |
"submission_ids": [],
|
51 |
"timestamp": timestamp_now
|
52 |
}
|
53 |
self.save_assignment_to_gcs(new_assignment)
|
54 |
self.save_user_assignment_to_gcs(user_data, assignment_id)
|
55 |
+
return new_assignment
|
56 |
+
|
57 |
+
def get_assignment_url(self, assignment_id):
|
58 |
+
assignment_data = self.get_assignment(assignment_id)
|
59 |
+
if isinstance(assignment_data, str):
|
60 |
+
print(f"错误:无法获取作业数据。返回的数据:{assignment_data}")
|
61 |
+
return None
|
62 |
+
|
63 |
+
assignment_url = assignment_data.get("assignment_url")
|
64 |
+
if not assignment_url:
|
65 |
+
print(f"警告:作业 {assignment_id} 没有 URL")
|
66 |
+
return None
|
67 |
+
|
68 |
+
return assignment_url
|
69 |
+
|
70 |
+
def update_assignment(self, assignment_id, assignment_data):
|
71 |
+
self.gcs_service.upload_json_string(self.bucket_name, f"assignments/{assignment_id}.json", json.dumps(assignment_data))
|
72 |
|
73 |
def save_assignment_to_gcs(self, assignment):
|
74 |
file_name = f"assignments/{assignment['assignment_id']}.json"
|
assignment_ui.py
CHANGED
@@ -5,29 +5,63 @@ def create_assignment_ui(user_data, assignment_service, submission_service):
|
|
5 |
with gr.Tab("老師|建立作業"):
|
6 |
with gr.Row():
|
7 |
with gr.Column():
|
8 |
-
|
9 |
-
"
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
|
24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
assignment_service.create_assignment_metadata,
|
26 |
-
inputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
outputs=[assignment_metadata]
|
28 |
).then(
|
29 |
assignment_service.create_assignment,
|
30 |
inputs=[user_data, assignment_type, assignment_metadata],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
outputs=[assignment_id_display_teacher]
|
32 |
)
|
33 |
|
@@ -38,40 +72,66 @@ def create_assignment_ui(user_data, assignment_service, submission_service):
|
|
38 |
assignment_list = gr.Radio([], label="作業列表", interactive=True)
|
39 |
with gr.Column(scale=2):
|
40 |
assignment_data = gr.JSON(label="作業內容", visible=False)
|
41 |
-
|
42 |
with gr.Row():
|
43 |
with gr.Column(scale=1):
|
44 |
submissions_list_json = gr.JSON(visible=False)
|
45 |
submissions_list_radio = gr.Radio([], label="已提交的作業學生", interactive=True)
|
46 |
with gr.Column(scale=2):
|
47 |
submission_data_json = gr.JSON(label="提交內容", visible=False)
|
48 |
-
|
49 |
|
50 |
def init_assignment_list_data(assignment_list_value=None):
|
51 |
assignment_list = gr.update(value=assignment_list_value)
|
52 |
assignment_data = gr.update(value=None)
|
53 |
-
|
54 |
submissions_list_json = gr.update(value=None)
|
55 |
submissions_list_radio = gr.update(choices=[], value=None)
|
56 |
submission_data_json = gr.update(value=None)
|
57 |
-
|
58 |
-
return assignment_list, assignment_data,
|
59 |
|
60 |
-
def
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
"""
|
74 |
-
return gr.update(value=
|
75 |
|
76 |
def update_submissions_list_radio(submission_ids):
|
77 |
choices = []
|
@@ -82,14 +142,27 @@ def create_assignment_ui(user_data, assignment_service, submission_service):
|
|
82 |
choices.append(choice)
|
83 |
return gr.update(choices=choices)
|
84 |
|
85 |
-
def
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
"""
|
92 |
-
return gr.update(value=
|
93 |
|
94 |
get_all_assignments_button.click(
|
95 |
assignment_service.update_assignment_list,
|
@@ -98,21 +171,21 @@ def create_assignment_ui(user_data, assignment_service, submission_service):
|
|
98 |
).then(
|
99 |
fn=init_assignment_list_data,
|
100 |
inputs=[],
|
101 |
-
outputs=[assignment_list, assignment_data,
|
102 |
)
|
103 |
|
104 |
assignment_list.select(
|
105 |
fn=init_assignment_list_data,
|
106 |
inputs=[assignment_list],
|
107 |
-
outputs=[assignment_list, assignment_data,
|
108 |
).then(
|
109 |
assignment_service.get_assignment,
|
110 |
inputs=[assignment_list],
|
111 |
outputs=[assignment_data]
|
112 |
).then(
|
113 |
-
fn=
|
114 |
inputs=[assignment_data],
|
115 |
-
outputs=[
|
116 |
).then(
|
117 |
assignment_service.get_assignment_submissions,
|
118 |
inputs=[assignment_list],
|
@@ -128,9 +201,9 @@ def create_assignment_ui(user_data, assignment_service, submission_service):
|
|
128 |
inputs=[submissions_list_radio],
|
129 |
outputs=submission_data_json
|
130 |
).then(
|
131 |
-
fn=
|
132 |
inputs=[submission_data_json],
|
133 |
-
outputs=[
|
134 |
)
|
135 |
|
136 |
with gr.Tab("學生"):
|
|
|
5 |
with gr.Tab("老師|建立作業"):
|
6 |
with gr.Row():
|
7 |
with gr.Column():
|
8 |
+
with gr.Row():
|
9 |
+
gr.Markdown("### 作業資訊")
|
10 |
+
with gr.Row():
|
11 |
+
assignment_topic = gr.Textbox(label="題目(必填)")
|
12 |
+
assignment_type_list = [
|
13 |
+
"中文寫作 AI 批改",
|
14 |
+
"英文寫作 AI 批改"
|
15 |
+
]
|
16 |
+
assignment_type = gr.Radio(choices=assignment_type_list, label="選擇類型")
|
17 |
+
assignment_grade = gr.Radio(["一年級", "二年級", "三年級", "四年級", "五年級", "六年級"], label="選擇年級", visible=False)
|
18 |
+
with gr.Row():
|
19 |
+
gr.Markdown("### 內容規範")
|
20 |
+
with gr.Row():
|
21 |
+
assignment_introduction = gr.TextArea(label="寫作引文��填)")
|
22 |
+
assignment_description = gr.TextArea(label="作業說明")
|
23 |
+
with gr.Row():
|
24 |
+
gr.Markdown("### 繳交規範")
|
25 |
+
with gr.Row():
|
26 |
+
assignment_submission_deadline = gr.DateTime(label="提交截止日期")
|
27 |
+
assignment_attach_materials = gr.Textbox(label="附件參考", placeholder='[{"type": "video", "url": "link1", "description": "描述"}]')
|
28 |
+
with gr.Row():
|
29 |
+
assignment_create_button = gr.Button("建立作業")
|
30 |
+
assignment_metadata = gr.JSON(label="作業元數據", visible=False)
|
31 |
+
assignment_data = gr.JSON(label="作業數據", visible=False)
|
32 |
+
assignment_url = gr.Textbox(label="作業指派連結", interactive=False, show_copy_button=True)
|
33 |
+
assignment_id_display_teacher = gr.Textbox(label="作業 ID", interactive=False, visible=False)
|
34 |
+
|
35 |
|
36 |
+
def update_gr_assignment_url(assignment_data):
|
37 |
+
return assignment_data['assignment_url']
|
38 |
+
|
39 |
+
def update_gr_assignment_id(assignment_data):
|
40 |
+
return assignment_data['assignment_id']
|
41 |
+
|
42 |
+
assignment_create_button.click(
|
43 |
assignment_service.create_assignment_metadata,
|
44 |
+
inputs=[
|
45 |
+
assignment_type,
|
46 |
+
assignment_grade,
|
47 |
+
assignment_topic,
|
48 |
+
assignment_introduction,
|
49 |
+
assignment_description,
|
50 |
+
assignment_attach_materials,
|
51 |
+
assignment_submission_deadline
|
52 |
+
],
|
53 |
outputs=[assignment_metadata]
|
54 |
).then(
|
55 |
assignment_service.create_assignment,
|
56 |
inputs=[user_data, assignment_type, assignment_metadata],
|
57 |
+
outputs=[assignment_data]
|
58 |
+
).then(
|
59 |
+
update_gr_assignment_url,
|
60 |
+
inputs=[assignment_data],
|
61 |
+
outputs=[assignment_url]
|
62 |
+
).then(
|
63 |
+
update_gr_assignment_id,
|
64 |
+
inputs=[assignment_data],
|
65 |
outputs=[assignment_id_display_teacher]
|
66 |
)
|
67 |
|
|
|
72 |
assignment_list = gr.Radio([], label="作業列表", interactive=True)
|
73 |
with gr.Column(scale=2):
|
74 |
assignment_data = gr.JSON(label="作業內容", visible=False)
|
75 |
+
assignment_data_html = gr.HTML()
|
76 |
with gr.Row():
|
77 |
with gr.Column(scale=1):
|
78 |
submissions_list_json = gr.JSON(visible=False)
|
79 |
submissions_list_radio = gr.Radio([], label="已提交的作業學生", interactive=True)
|
80 |
with gr.Column(scale=2):
|
81 |
submission_data_json = gr.JSON(label="提交內容", visible=False)
|
82 |
+
submission_data_html = gr.HTML()
|
83 |
|
84 |
def init_assignment_list_data(assignment_list_value=None):
|
85 |
assignment_list = gr.update(value=assignment_list_value)
|
86 |
assignment_data = gr.update(value=None)
|
87 |
+
assignment_data_html = gr.update(value=None)
|
88 |
submissions_list_json = gr.update(value=None)
|
89 |
submissions_list_radio = gr.update(choices=[], value=None)
|
90 |
submission_data_json = gr.update(value=None)
|
91 |
+
submission_data_html = gr.update(value=None)
|
92 |
+
return assignment_list, assignment_data, assignment_data_html, submissions_list_json, submissions_list_radio, submission_data_json, submission_data_html
|
93 |
|
94 |
+
def update_assignment_data_html(assignment_data):
|
95 |
+
html = f"""
|
96 |
+
<div style="background-color: #f8f9fa; padding: 30px; border-radius: 15px; font-family: 'Helvetica', sans-serif;">
|
97 |
+
<h2 style="color: #007bff; font-size: 28px; margin-bottom: 20px;">✨ 作業詳情</h2>
|
98 |
+
<div style="background-color: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.05);">
|
99 |
+
<div style="margin-bottom: 20px;">
|
100 |
+
<div style="font-weight: bold; color: #6c757d; margin-bottom: 5px;">🏷️ 作業類型</div>
|
101 |
+
<div style="background-color: #f8f9fa; padding: 10px 15px; border-radius: 5px;">{assignment_data['assignment_type']}</div>
|
102 |
+
</div>
|
103 |
+
<div style="margin-bottom: 20px;">
|
104 |
+
<div style="font-weight: bold; color: #6c757d; margin-bottom: 5px;">📝 作業題目</div>
|
105 |
+
<div style="background-color: #f8f9fa; padding: 10px 15px; border-radius: 5px;">{assignment_data['metadata']['topic']}</div>
|
106 |
+
</div>
|
107 |
+
<div style="margin-bottom: 20px;">
|
108 |
+
<div style="font-weight: bold; color: #6c757d; margin-bottom: 5px;">🗓️ 日期</div>
|
109 |
+
<div style="background-color: #f8f9fa; padding: 10px 15px; border-radius: 5px;">{assignment_data['timestamp']}</div>
|
110 |
+
</div>
|
111 |
+
|
112 |
+
<div style="margin-bottom: 20px;">
|
113 |
+
<div style="font-weight: bold; color: #28a745; margin-bottom: 5px;">📖 寫作引文</div>
|
114 |
+
<div style="background-color: #f8f9fa; padding: 10px 15px; border-radius: 5px;">{assignment_data['metadata']['introduction']}</div>
|
115 |
+
</div>
|
116 |
+
|
117 |
+
<div style="margin-bottom: 20px;">
|
118 |
+
<div style="font-weight: bold; color: #ffc107; margin-bottom: 5px;">ℹ️ 作業說明</div>
|
119 |
+
<div style="background-color: #f8f9fa; padding: 10px 15px; border-radius: 5px;">{assignment_data['metadata']['description']}</div>
|
120 |
+
</div>
|
121 |
+
|
122 |
+
<div style="margin-bottom: 20px;">
|
123 |
+
<div style="font-weight: bold; color: #17a2b8; margin-bottom: 5px;">🔗 作業連結</div>
|
124 |
+
<a href="{assignment_data['assignment_url']}" style="display: inline-block; padding: 10px 15px; background-color: #007bff; color: white; text-decoration: none; border-radius: 5px; transition: background-color 0.3s;" onmouseover="this.style.backgroundColor='#0056b3'" onmouseout="this.style.backgroundColor='#007bff'">點擊前往作業</a>
|
125 |
+
</div>
|
126 |
+
|
127 |
+
<div>
|
128 |
+
<div style="font-weight: bold; color: #dc3545; margin-bottom: 5px;">📎 附加材料</div>
|
129 |
+
<div style="background-color: #f8f9fa; padding: 10px 15px; border-radius: 5px;">{assignment_data['metadata']['attach_materials']}</div>
|
130 |
+
</div>
|
131 |
+
</div>
|
132 |
+
</div>
|
133 |
"""
|
134 |
+
return gr.update(value=html)
|
135 |
|
136 |
def update_submissions_list_radio(submission_ids):
|
137 |
choices = []
|
|
|
142 |
choices.append(choice)
|
143 |
return gr.update(choices=choices)
|
144 |
|
145 |
+
def update_submission_data_html(submission_json):
|
146 |
+
html = f"""
|
147 |
+
<div style="background-color: #f8f9fa; padding: 30px; border-radius: 15px; font-family: 'Helvetica', sans-serif;">
|
148 |
+
<h2 style="color: #007bff; font-size: 28px; margin-bottom: 20px;">📝 學生回傳作業</h2>
|
149 |
+
<div style="background-color: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.05);">
|
150 |
+
<div style="margin-bottom: 20px;">
|
151 |
+
<div style="font-weight: bold; color: #6c757d; margin-bottom: 5px;">👤 學生姓名</div>
|
152 |
+
<div style="background-color: #f8f9fa; padding: 10px 15px; border-radius: 5px;">{submission_json['student_name']}</div>
|
153 |
+
</div>
|
154 |
+
<div style="margin-bottom: 20px;">
|
155 |
+
<div style="font-weight: bold; color: #28a745; margin-bottom: 5px;">📄 回傳内容</div>
|
156 |
+
<div style="background-color: #f8f9fa; padding: 10px 15px; border-radius: 5px; white-space: pre-wrap;">{submission_json['submission_data']['content']}</div>
|
157 |
+
</div>
|
158 |
+
<div>
|
159 |
+
<div style="font-weight: bold; color: #17a2b8; margin-bottom: 5px;">🕒 回傳日期</div>
|
160 |
+
<div style="background-color: #f8f9fa; padding: 10px 15px; border-radius: 5px;">{submission_json['timestamp']}</div>
|
161 |
+
</div>
|
162 |
+
</div>
|
163 |
+
</div>
|
164 |
"""
|
165 |
+
return gr.update(value=html)
|
166 |
|
167 |
get_all_assignments_button.click(
|
168 |
assignment_service.update_assignment_list,
|
|
|
171 |
).then(
|
172 |
fn=init_assignment_list_data,
|
173 |
inputs=[],
|
174 |
+
outputs=[assignment_list, assignment_data, assignment_data_html, submissions_list_json, submissions_list_radio, submission_data_json, submission_data_html]
|
175 |
)
|
176 |
|
177 |
assignment_list.select(
|
178 |
fn=init_assignment_list_data,
|
179 |
inputs=[assignment_list],
|
180 |
+
outputs=[assignment_list, assignment_data, assignment_data_html, submissions_list_json, submissions_list_radio, submission_data_json, submission_data_html]
|
181 |
).then(
|
182 |
assignment_service.get_assignment,
|
183 |
inputs=[assignment_list],
|
184 |
outputs=[assignment_data]
|
185 |
).then(
|
186 |
+
fn=update_assignment_data_html,
|
187 |
inputs=[assignment_data],
|
188 |
+
outputs=[assignment_data_html]
|
189 |
).then(
|
190 |
assignment_service.get_assignment_submissions,
|
191 |
inputs=[assignment_list],
|
|
|
201 |
inputs=[submissions_list_radio],
|
202 |
outputs=submission_data_json
|
203 |
).then(
|
204 |
+
fn=update_submission_data_html,
|
205 |
inputs=[submission_data_json],
|
206 |
+
outputs=[submission_data_html]
|
207 |
)
|
208 |
|
209 |
with gr.Tab("學生"):
|
requirements.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
gradio==4.
|
2 |
openai>=1.16.2
|
3 |
python-docx
|
4 |
google-api-python-client
|
|
|
1 |
+
gradio==4.41.0
|
2 |
openai>=1.16.2
|
3 |
python-docx
|
4 |
google-api-python-client
|