ChenyuRabbitLove commited on
Commit
115ff47
1 Parent(s): cabe12b

feat: add completion reward

Browse files
app.py CHANGED
@@ -13,12 +13,20 @@ from utils.utils import (
13
  save_latest_player_data,
14
  render_finished,
15
  )
 
 
 
 
 
 
 
 
 
16
 
17
  seafoam = Seafoam()
18
 
19
 
20
  def get_player_info(player_backend_user_id):
21
-
22
  try:
23
  with open("latest_player_data.json", "r", encoding="utf-8") as file:
24
  player_info = json.load(file)
@@ -27,7 +35,7 @@ def get_player_info(player_backend_user_id):
27
  save_latest_player_data()
28
  with open("latest_player_data.json", "r", encoding="utf-8") as file:
29
  player_info = json.load(file)
30
-
31
  if player_backend_user_id in player_info:
32
  return player_info[player_backend_user_id]
33
  else:
@@ -43,12 +51,32 @@ def get_player_info(player_backend_user_id):
43
 
44
  return new_player.to_dict()
45
 
 
46
  def create_new_player_activity():
47
  return PlayerActivity()
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  # start of gradio interface
50
  with gr.Blocks(theme=seafoam, css=get_content("css/style.css")) as demo:
51
  player_info = gr.State()
 
 
52
  player_activity_tracker = gr.State(create_new_player_activity)
53
 
54
  with gr.Row():
@@ -87,15 +115,15 @@ with gr.Blocks(theme=seafoam, css=get_content("css/style.css")) as demo:
87
 
88
  with gr.Column(scale=1):
89
  adventure_description = gr.Markdown(
90
- "# 冒險階段", elem_id="adventure_description"
91
- )
92
  adventure = gr.Slider(
93
- value=0,
94
- show_label=False,
95
- interactive=False,
96
- elem_id="adventure_slider",
97
- info="",
98
- )
99
  achievements_description = gr.Markdown(
100
  "# 達成成就", elem_id="achievements_description"
101
  )
@@ -124,6 +152,146 @@ with gr.Blocks(theme=seafoam, css=get_content("css/style.css")) as demo:
124
  pull_newest_player_data = gr.Textbox("", visible=False)
125
  update_status = gr.Textbox("", visible=False)
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  # actions when player login
128
  # define args
129
  send_player_login_info = dict(
@@ -133,10 +301,14 @@ with gr.Blocks(theme=seafoam, css=get_content("css/style.css")) as demo:
133
  )
134
 
135
  player_info_query_btn.click(get_player_info, player_backend_id, player_info).then(
 
 
136
  render_player_data,
137
  player_info,
138
  [avatar, pet_gallery, badge_gallery, adventure_log, achievements, adventure],
139
- ).then(**send_player_login_info)
 
 
140
 
141
  pull_newest_player_data.submit(
142
  save_latest_player_data,
@@ -145,5 +317,186 @@ with gr.Blocks(theme=seafoam, css=get_content("css/style.css")) as demo:
145
  api_name="pull_newest_player_data",
146
  )
147
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  if __name__ == "__main__":
149
  demo.launch()
 
13
  save_latest_player_data,
14
  render_finished,
15
  )
16
+ from utils.completion_reward import CompletionReward
17
+ from utils.completion_reward_utils import (
18
+ get_llm_response,
19
+ set_player_name,
20
+ set_player_selected_character,
21
+ create_certificate,
22
+ complete_reward,
23
+ check_is_in_completion_reward,
24
+ )
25
 
26
  seafoam = Seafoam()
27
 
28
 
29
  def get_player_info(player_backend_user_id):
 
30
  try:
31
  with open("latest_player_data.json", "r", encoding="utf-8") as file:
32
  player_info = json.load(file)
 
35
  save_latest_player_data()
36
  with open("latest_player_data.json", "r", encoding="utf-8") as file:
37
  player_info = json.load(file)
38
+
39
  if player_backend_user_id in player_info:
40
  return player_info[player_backend_user_id]
41
  else:
 
51
 
52
  return new_player.to_dict()
53
 
54
+
55
  def create_new_player_activity():
56
  return PlayerActivity()
57
 
58
+
59
+ def get_player_logs(player_backend_user_id):
60
+ with open("processed_adventure_logs.json") as file:
61
+ player_logs = json.load(file)
62
+ try:
63
+ player_logs = player_logs[player_backend_user_id]
64
+ return player_logs
65
+ except KeyError:
66
+ logging.error(
67
+ f"Player {player_backend_user_id} not found in processed_player_adventure_logs.json"
68
+ )
69
+
70
+
71
+ def init_reward():
72
+ return CompletionReward()
73
+
74
+
75
  # start of gradio interface
76
  with gr.Blocks(theme=seafoam, css=get_content("css/style.css")) as demo:
77
  player_info = gr.State()
78
+ player_logs = gr.State()
79
+ completion_reward = gr.State(init_reward)
80
  player_activity_tracker = gr.State(create_new_player_activity)
81
 
82
  with gr.Row():
 
115
 
116
  with gr.Column(scale=1):
117
  adventure_description = gr.Markdown(
118
+ "# 冒險階段", elem_id="adventure_description"
119
+ )
120
  adventure = gr.Slider(
121
+ value=0,
122
+ show_label=False,
123
+ interactive=False,
124
+ elem_id="adventure_slider",
125
+ info="",
126
+ )
127
  achievements_description = gr.Markdown(
128
  "# 達成成就", elem_id="achievements_description"
129
  )
 
152
  pull_newest_player_data = gr.Textbox("", visible=False)
153
  update_status = gr.Textbox("", visible=False)
154
 
155
+ with gr.Tab("完賽獎勵"):
156
+ with gr.Row():
157
+ start_make_reward = gr.Button(
158
+ "開始製作完賽獎勵!", visible=True, elem_id="start_make_reward"
159
+ )
160
+
161
+ with gr.Row():
162
+ not_participate = gr.Markdown(
163
+ "# 同學並未參與 2023 的星空探險隊唷!", visible=False, elem_id="not_participate"
164
+ )
165
+
166
+ with gr.Row():
167
+ not_start = gr.Markdown(
168
+ "# 完賽獎勵還沒有開放申請,但是就快了,請敬請期待!", visible=False, elem_id="not_start"
169
+ )
170
+
171
+ with gr.Row():
172
+ already_issued = gr.Image(visible=False)
173
+
174
+ with gr.Row():
175
+ player_name_title = gr.Markdown(
176
+ "# 選擇玩家暱稱", visible=False, elem_id="player_name_title"
177
+ )
178
+
179
+ with gr.Row():
180
+ player_name = gr.Textbox(
181
+ label="玩家暱稱",
182
+ info="請輸入想要用在完賽獎勵上的玩家暱稱,上限為 10 字,獎勵發送後無法更改也無法補發,還請同學們謹慎填寫。",
183
+ interactive=True,
184
+ elem_id="player_name",
185
+ visible=False,
186
+ )
187
+
188
+ with gr.Row():
189
+ confirm_player_name = gr.Button(
190
+ "確認暱稱", elem_id="confirm_player_name", visible=False
191
+ )
192
+ cancel_player_name = gr.Button(
193
+ "取消", elem_id="cancel_player_name", visible=False
194
+ )
195
+
196
+ with gr.Row():
197
+ player_name_next_step = gr.Button(
198
+ "下一步", visible=False, elem_id="player_name_next_step"
199
+ )
200
+
201
+ with gr.Row():
202
+ story_title = gr.Markdown("# 選擇冒險故事", visible=False, elem_id="story_title")
203
+
204
+ with gr.Row():
205
+ story_description = gr.Markdown(
206
+ "有五位星際夥伴來為你撰寫專屬於你的冒險故事,每位星際夥伴都會得到同學冒險週記的內容,並寫成一段故事,請同學選擇自己最喜歡的一段故事��這段故事將會被用來印在獎狀上<br><br>小叮嚀:請同學選擇喜歡的故事內容,而不是星際夥伴唷!星際夥伴可是不會和同學們一起冒險的!最後,撰寫故事的過程大概需要幾分鐘的時間,請同學耐心等待",
207
+ visible=False,
208
+ elem_id="story_description",
209
+ )
210
+
211
+ with gr.Row():
212
+ openai_description = gr.Markdown(
213
+ "# 露米娜", elem_id="openai_description", visible=False
214
+ )
215
+ aws_description = gr.Markdown(
216
+ "# 索拉拉", elem_id="aws_description", visible=False
217
+ )
218
+ google_description = gr.Markdown(
219
+ "# 薇丹特", elem_id="google_description", visible=False
220
+ )
221
+ mtk_description = gr.Markdown(
222
+ "# 蔚藍", elem_id="mtk_description", visible=False
223
+ )
224
+
225
+ with gr.Row():
226
+ openai_img = gr.Image(
227
+ "medias/lumina.png",
228
+ visible=False,
229
+ elem_id="openai_img",
230
+ interactive=False,
231
+ show_download_button=False,
232
+ )
233
+ aws_img = gr.Image(
234
+ "medias/solara.png",
235
+ visible=False,
236
+ elem_id="aws_img",
237
+ interactive=False,
238
+ show_download_button=False,
239
+ )
240
+ google_img = gr.Image(
241
+ "medias/verdant.png",
242
+ visible=False,
243
+ elem_id="google_img",
244
+ interactive=False,
245
+ show_download_button=False,
246
+ )
247
+ mtk_img = gr.Image(
248
+ "medias/azure.png",
249
+ visible=False,
250
+ elem_id="mtk_img",
251
+ interactive=False,
252
+ show_download_button=False,
253
+ )
254
+
255
+ with gr.Row():
256
+ start_generate_story = gr.Button(
257
+ "開始寫作故事", visible=False, elem_id="start_generate_story"
258
+ )
259
+
260
+ with gr.Row():
261
+ bot1 = gr.Chatbot(visible=False)
262
+ bot2 = gr.Chatbot(visible=False)
263
+ bot3 = gr.Chatbot(visible=False)
264
+ bot4 = gr.Chatbot(visible=False)
265
+
266
+ with gr.Row():
267
+ select_story = gr.Radio(
268
+ ["露米娜", "索拉拉", "薇丹特", "蔚藍"],
269
+ interactive=True,
270
+ label="選擇故事",
271
+ visible=False,
272
+ elem_id="select_story",
273
+ )
274
+
275
+ with gr.Row():
276
+ confirm_story = gr.Button("確認故事", visible=False, elem_id="confirm_story")
277
+ cancel_story = gr.Button("取消", visible=False, elem_id="cancel_story")
278
+
279
+ with gr.Row():
280
+ start_generate_certificate = gr.Button(
281
+ "開始製作完賽獎勵!", visible=False, elem_id="start_generate_certificate"
282
+ )
283
+ processing = gr.Button(
284
+ "製作中...",
285
+ visible=False,
286
+ elem_id="processing",
287
+ size="lg",
288
+ interactive=False,
289
+ )
290
+ complete = gr.Markdown("# 完成!", visible=False, elem_id="complete")
291
+
292
+ with gr.Row():
293
+ reward_result = gr.Image(visible=False)
294
+
295
  # actions when player login
296
  # define args
297
  send_player_login_info = dict(
 
301
  )
302
 
303
  player_info_query_btn.click(get_player_info, player_backend_id, player_info).then(
304
+ get_player_logs, player_backend_id, player_logs
305
+ ).then(
306
  render_player_data,
307
  player_info,
308
  [avatar, pet_gallery, badge_gallery, adventure_log, achievements, adventure],
309
+ ).then(
310
+ **send_player_login_info
311
+ )
312
 
313
  pull_newest_player_data.submit(
314
  save_latest_player_data,
 
317
  api_name="pull_newest_player_data",
318
  )
319
 
320
+ def create_visibility_updates(visible, count):
321
+ return tuple(gr.update(visible=visible) for _ in range(count))
322
+
323
+ start_make_reward.click(
324
+ check_is_in_completion_reward,
325
+ player_backend_id,
326
+ [
327
+ start_make_reward,
328
+ player_name_title,
329
+ player_name,
330
+ confirm_player_name,
331
+ not_participate,
332
+ not_start,
333
+ already_issued,
334
+ ],
335
+ queue=False,
336
+ )
337
+
338
+ set_player_name_args = dict(
339
+ fn=set_player_name,
340
+ inputs=[completion_reward, player_name, player_backend_id],
341
+ outputs=None,
342
+ queue=False,
343
+ )
344
+
345
+ confirm_player_name.click(
346
+ lambda: (gr.update(interactive=False), gr.update(visible=False)),
347
+ None,
348
+ [player_name, confirm_player_name],
349
+ queue=False,
350
+ ).then(
351
+ lambda: create_visibility_updates(True, 2),
352
+ None,
353
+ [cancel_player_name, player_name_next_step],
354
+ queue=False,
355
+ ).then(
356
+ **set_player_name_args
357
+ )
358
+
359
+ cancel_player_name.click(
360
+ lambda: (gr.update(interactive=True), gr.update(visible=True)),
361
+ None,
362
+ [player_name, confirm_player_name],
363
+ queue=False,
364
+ ).then(
365
+ lambda: create_visibility_updates(False, 2),
366
+ None,
367
+ [cancel_player_name, player_name_next_step],
368
+ queue=False,
369
+ )
370
+
371
+ player_name_next_step.click(
372
+ lambda: create_visibility_updates(False, 5),
373
+ None,
374
+ [
375
+ player_name,
376
+ player_name_next_step,
377
+ confirm_player_name,
378
+ player_name_title,
379
+ cancel_player_name,
380
+ ],
381
+ queue=False,
382
+ ).then(
383
+ lambda: create_visibility_updates(True, 11),
384
+ None,
385
+ [
386
+ openai_img,
387
+ aws_img,
388
+ google_img,
389
+ mtk_img,
390
+ story_title,
391
+ story_description,
392
+ openai_description,
393
+ aws_description,
394
+ google_description,
395
+ mtk_description,
396
+ start_generate_story,
397
+ ],
398
+ queue=False,
399
+ )
400
+
401
+ get_llm_response_args = dict(
402
+ fn=get_llm_response,
403
+ inputs=[completion_reward, player_logs],
404
+ outputs=[bot1, bot2, bot3, bot4],
405
+ queue=False,
406
+ )
407
+
408
+ start_generate_story.click(
409
+ lambda: gr.update(visible=False), None, start_generate_story, queue=False
410
+ ).then(
411
+ lambda: create_visibility_updates(True, 4),
412
+ None,
413
+ [bot1, bot2, bot3, bot4],
414
+ queue=False,
415
+ ).then(
416
+ **get_llm_response_args
417
+ ).then(
418
+ lambda: gr.update(visible=True), None, [select_story], queue=False
419
+ )
420
+
421
+ select_story.select(
422
+ lambda: gr.update(visible=True), None, confirm_story, queue=False
423
+ )
424
+
425
+ set_player_selected_character_args = dict(
426
+ fn=set_player_selected_character,
427
+ inputs=[completion_reward, select_story],
428
+ outputs=None,
429
+ queue=False,
430
+ )
431
+
432
+ confirm_story.click(
433
+ lambda: gr.update(interactive=False), None, [select_story], queue=False
434
+ ).then(lambda: gr.update(visible=False), None, [confirm_story], queue=False).then(
435
+ lambda: (gr.update(visible=True), gr.update(visible=True)),
436
+ None,
437
+ [start_generate_certificate, cancel_story],
438
+ queue=False,
439
+ ).then(
440
+ **set_player_selected_character_args
441
+ )
442
+
443
+ cancel_story.click(
444
+ lambda: gr.update(interactive=True), None, [select_story], queue=False
445
+ ).then(lambda: gr.update(visible=False), None, [cancel_story], queue=False).then(
446
+ lambda: (gr.update(visible=False), gr.update(visible=True)),
447
+ None,
448
+ [start_generate_certificate, confirm_story],
449
+ queue=False,
450
+ )
451
+
452
+ create_certificate_args = dict(
453
+ fn=create_certificate,
454
+ inputs=[completion_reward],
455
+ outputs=reward_result,
456
+ queue=False,
457
+ )
458
+
459
+ complete_reward_args = dict(
460
+ fn=complete_reward,
461
+ inputs=[completion_reward],
462
+ outputs=None,
463
+ queue=False,
464
+ )
465
+
466
+ start_generate_certificate.click(
467
+ lambda: create_visibility_updates(False, 18),
468
+ None,
469
+ [
470
+ openai_img,
471
+ aws_img,
472
+ google_img,
473
+ mtk_img,
474
+ story_title,
475
+ story_description,
476
+ openai_description,
477
+ aws_description,
478
+ google_description,
479
+ mtk_description,
480
+ bot1,
481
+ bot2,
482
+ bot3,
483
+ bot4,
484
+ select_story,
485
+ processing,
486
+ cancel_story,
487
+ start_generate_certificate,
488
+ ],
489
+ queue=False,
490
+ ).then(lambda: gr.update(visible=True), None, [processing], queue=False).then(
491
+ **create_certificate_args
492
+ ).then(
493
+ lambda: (gr.update(visible=True), gr.update(visible=False)),
494
+ None,
495
+ [complete, processing],
496
+ queue=False,
497
+ ).then(
498
+ **complete_reward_args
499
+ )
500
+
501
  if __name__ == "__main__":
502
  demo.launch()
css/style.css CHANGED
@@ -246,4 +246,124 @@ input[type="range"]::-ms-track {
246
  box-shadow: 2px 2px 2px 0px rgba(0,0,0,0.75) inset !important;
247
  border-radius: 10px !important; /* Rounded corners of the track */
248
  }
249
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  box-shadow: 2px 2px 2px 0px rgba(0,0,0,0.75) inset !important;
247
  border-radius: 10px !important; /* Rounded corners of the track */
248
  }
249
+
250
+
251
+ #player_name_title {
252
+ margin: 5vh auto;
253
+ }
254
+
255
+ #player_name {
256
+ padding: 6vh 6vw 10vh 6vw;
257
+ }
258
+
259
+ #player_name [data-testid="block-info"]{
260
+ font-size: 2rem;
261
+ padding: 1vh 0 1vh 0;
262
+ display: block;
263
+ color: #000;
264
+ text-align: center !important;
265
+ }
266
+
267
+ #player_name div{
268
+ color: #444;
269
+ text-align: center;
270
+ }
271
+
272
+ #player_name textarea{
273
+ border-style: solid;
274
+ border-color: rgba(244, 250, 150, 0.2);
275
+ border-width: 1px;
276
+ background: rgba(255,255,255, 0.2);
277
+ }
278
+
279
+ #story_title {
280
+ margin: 5vh auto;
281
+ }
282
+
283
+ #story_description p {
284
+ font-size: 1.3rem;
285
+ text-align: center;
286
+ width: 70%;
287
+ margin: 5vh auto;
288
+ }
289
+
290
+ #story_description {
291
+ border: #12d2ab solid 1px !important;
292
+ border-radius: 10px;
293
+ }
294
+
295
+ #start_make_reward {
296
+ background-color: #12d2ab;
297
+ margin: 20vh 10vw;
298
+ height: 30vh;
299
+ font-size: 2rem;
300
+ }
301
+
302
+ #not_participate {
303
+ margin: 20vh 10vw;
304
+ font-size: 2rem;
305
+ }
306
+
307
+ #not_start {
308
+ margin: 20vh 10vw;
309
+ font-size: 2rem;
310
+ }
311
+
312
+ #start_generate_story, #confirm_player_name, #player_name_next_step, #confirm_story, #start_generate_certificate {
313
+ background-color: #48A1DD;
314
+ }
315
+
316
+ #cancel_player_name, #cancel_story {
317
+ background-color: #eb6584;
318
+ }
319
+
320
+ #openai_img, #aws_img, #google_img, #mtk_img, #ntu_img {
321
+ border-radius: 50%;
322
+ transform: scale(0.7);
323
+ }
324
+
325
+ #openai_img [aria-label="Download"], #aws_img [aria-label="Download"], #google_img [aria-label="Download"], #mtk_img [aria-label="Download"], #ntu_img [aria-label="Download"] {
326
+ display: none;
327
+ }
328
+
329
+ [aria-label="Loading response"] {
330
+ background: rgba(0,0,0,0.1);
331
+ }
332
+
333
+ .selected {
334
+ color: #fff;
335
+ background: #12d2ab;
336
+ border: 2px solid #02b28f;
337
+ border-radius: 8px;
338
+ }
339
+
340
+ .message {
341
+ background: rgba(0,0,0,0.05);
342
+ border: None;
343
+ }
344
+
345
+ .code_wrap {
346
+ background: rgba(0,0,0,0.1);
347
+ border-radius: 10px;
348
+ padding: 1vh 1vw 1vh 1vw;
349
+ margin: 1vh 1vw 1vh 1vw;
350
+ }
351
+
352
+ #select_story {
353
+ display: flex;
354
+ justify-content: space-evenly;
355
+ }
356
+
357
+ #select_story label{
358
+ background: #48A1DD;
359
+ color: #fff;
360
+ }
361
+
362
+ #select_story input{
363
+ background-color: #2563eb;
364
+ }
365
+
366
+ #select_story [data-testid="block-info"] {
367
+ color: #000;
368
+ margin: auto;
369
+ }
data/completion_reward_issue_status.json ADDED
The diff for this file is too large to render. See raw diff
 
medias/azure.png ADDED
medias/lumina.png ADDED
medias/solara.png ADDED
medias/verdant.png ADDED
utils/completion_reward.py ADDED
@@ -0,0 +1,531 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import json
3
+ import time
4
+ import io
5
+ import os
6
+ import re
7
+ import requests
8
+ import textwrap
9
+ import random
10
+ import hashlib
11
+ from datetime import datetime
12
+ from PIL import Image, ImageDraw, ImageFilter, ImageFont
13
+
14
+ import anthropic_bedrock
15
+ import gradio as gr
16
+ from opencc import OpenCC
17
+ from openai import OpenAI
18
+ from anthropic_bedrock import AnthropicBedrock, HUMAN_PROMPT, AI_PROMPT
19
+ from google.auth.transport.requests import Request
20
+ from google.oauth2.service_account import Credentials
21
+ from google import auth
22
+ from google.cloud import bigquery
23
+ from google.cloud import storage
24
+
25
+
26
+ class CompletionReward:
27
+ def __init__(self):
28
+ self.player_backend_user_id = None
29
+ self.player_name = None
30
+ self.background_url = None
31
+ self.player_selected_character = None
32
+ self.player_selected_model = None
33
+ self.player_selected_paragraph = None
34
+ self.paragraph_openai = None
35
+ self.paragraph_aws = None
36
+ self.paragraph_google = None
37
+ self.paragraph_mtk = None
38
+ self.player_certificate_url = None
39
+ self.openai_agent = OpenAIAgent()
40
+ self.aws_agent = AWSAgent()
41
+ self.google_agent = GoogleAgent()
42
+ self.mtk_agent = MTKAgent()
43
+ self.shuffled_response_order = {}
44
+ self.paragraph_map = {
45
+ "openai": self.paragraph_openai,
46
+ "aws": self.paragraph_aws,
47
+ "google": self.paragraph_google,
48
+ "mtk": self.paragraph_mtk,
49
+ }
50
+ SERVICE_ACCOUNT_INFO = os.getenv("GBQ_TOKEN")
51
+ service_account_info_dict = json.loads(SERVICE_ACCOUNT_INFO)
52
+
53
+ creds = Credentials.from_service_account_info(service_account_info_dict)
54
+ self.gbq_client = bigquery.Client(
55
+ credentials=creds, project=service_account_info_dict["project_id"]
56
+ )
57
+ self.gcs_client = storage.Client(
58
+ credentials=creds, project=service_account_info_dict["project_id"]
59
+ )
60
+
61
+ def get_llm_response(self, player_logs):
62
+ agents_responses = {
63
+ "openai": self.openai_agent.get_story(player_logs),
64
+ "aws": self.aws_agent.get_story(player_logs),
65
+ "google": self.google_agent.get_story(player_logs),
66
+ "mtk": self.mtk_agent.get_story(player_logs),
67
+ }
68
+ self.paragraph_openai = agents_responses["openai"]
69
+ self.paragraph_aws = agents_responses["aws"]
70
+ self.paragraph_google = agents_responses["google"]
71
+ self.paragraph_mtk = agents_responses["mtk"]
72
+ response_items = list(agents_responses.items())
73
+ random.shuffle(response_items)
74
+
75
+ self.shuffled_response_order = {
76
+ str(index): agent for index, (agent, _) in enumerate(response_items)
77
+ }
78
+
79
+ shuffled_responses = tuple(response for _, response in response_items)
80
+ return (
81
+ [(None, shuffled_responses[0])],
82
+ [(None, shuffled_responses[1])],
83
+ [(None, shuffled_responses[2])],
84
+ [(None, shuffled_responses[3])],
85
+ )
86
+
87
+ def set_player_name(self, player_name, player_backend_user_id):
88
+ self.player_backend_user_id = player_backend_user_id
89
+ self.player_name = player_name
90
+
91
+ def set_background_url(self, background_url):
92
+ self.background_url = background_url
93
+
94
+ def set_player_backend_user_id(self, player_backend_user_id):
95
+ self.player_backend_user_id = player_backend_user_id
96
+
97
+ def set_player_selected_character(self, player_selected_character):
98
+ character_map = {
99
+ "露米娜": "0",
100
+ "索拉拉": "1",
101
+ "薇丹特": "2",
102
+ "蔚藍": "3",
103
+ }
104
+ self.player_selected_character = player_selected_character
105
+ self.player_selected_model = self.shuffled_response_order[
106
+ character_map[player_selected_character]
107
+ ]
108
+ self.player_selected_paragraph = self.get_paragraph_by_model(
109
+ self.player_selected_model
110
+ )
111
+
112
+ def get_paragraph_by_model(self, model):
113
+ return getattr(self, f"paragraph_{model}", None)
114
+
115
+ def create_certificate(self):
116
+ image_url = self.openai_agent.get_background()
117
+ self.set_background_url(image_url)
118
+ source_file = ImageProcessor.generate_reward(
119
+ image_url,
120
+ self.player_name,
121
+ self.player_selected_paragraph,
122
+ self.player_backend_user_id,
123
+ )
124
+
125
+ public_url = self.upload_blob_and_get_public_url(
126
+ "mes_completion_rewards", source_file, f"2023_mes/{source_file}"
127
+ )
128
+ self.player_certificate_url = public_url
129
+
130
+ return gr.Image(public_url, visible=True)
131
+
132
+ def to_dict(self):
133
+ return {
134
+ "player_backend_user_id": self.player_backend_user_id,
135
+ "player_name": self.player_name,
136
+ "background_url": self.background_url,
137
+ "player_selected_model": self.player_selected_model,
138
+ "player_selected_paragraph": self.player_selected_paragraph,
139
+ "paragraph_openai": self.paragraph_openai,
140
+ "paragraph_aws": self.paragraph_aws,
141
+ "paragraph_google": self.paragraph_google,
142
+ "paragraph_mtk": self.paragraph_mtk,
143
+ "player_certificate_url": self.player_certificate_url,
144
+ "created_at_date": datetime.now().date(),
145
+ }
146
+
147
+ def insert_data_into_bigquery(self, client, dataset_id, table_id, rows_to_insert):
148
+ table_ref = client.dataset(dataset_id).table(table_id)
149
+ table = client.get_table(table_ref)
150
+
151
+ errors = client.insert_rows(table, rows_to_insert)
152
+
153
+ if errors:
154
+ logging.info("Errors occurred while inserting rows:")
155
+ for error in errors:
156
+ print(error)
157
+ else:
158
+ logging.info(f"Inserted {len(rows_to_insert)} rows successfully.")
159
+
160
+ def complete_reward(
161
+ self,
162
+ ):
163
+ insert_row = self.to_dict()
164
+ self.insert_data_into_bigquery(
165
+ self.gbq_client, "streaming_log", "log_mes_completion_rewards", [insert_row]
166
+ )
167
+ logging.info(
168
+ f"Player {insert_row['player_backend_user_id']} rendered successfully."
169
+ )
170
+
171
+ with open("../data/completion_reward_issue_status.json") as f:
172
+ completion_reward_issue_status_dict = json.load(f)
173
+
174
+ completion_reward_issue_status_dict[
175
+ insert_row["player_backend_user_id"]
176
+ ] = self.player_certificate_url
177
+
178
+ with open("../data/completion_reward_issue_status.json", "w") as f:
179
+ json.dump(completion_reward_issue_status_dict, f)
180
+
181
+ def upload_blob_and_get_public_url(
182
+ self, bucket_name, source_file_name, destination_blob_name
183
+ ):
184
+ """Uploads a file to the bucket and makes it publicly accessible."""
185
+ # Initialize a storage client
186
+ bucket = self.gcs_client.bucket(bucket_name)
187
+ blob = bucket.blob(destination_blob_name)
188
+
189
+ # Upload the file
190
+ blob.upload_from_filename(source_file_name)
191
+
192
+ # The public URL can be used to directly access the uploaded file via HTTP
193
+ public_url = blob.public_url
194
+
195
+ logging.info(f"File {source_file_name} uploaded to {destination_blob_name}.")
196
+
197
+ return public_url
198
+
199
+
200
+ class OpenAIAgent:
201
+ def __init__(self):
202
+ self.temperature = 0.8
203
+ self.frequency_penalty = 0
204
+ self.presence_penalty = 0
205
+ self.max_tokens = 2048
206
+
207
+ def get_story(self, user_log):
208
+ system_prompt = """
209
+ 我正在舉辦一個學習型的活動,參與活動的學生為 1-9 年級,我為他們設計了一個獨特的故事機制,每天每個學生都會收到屬於自己獨特的冒險紀錄,現在我需要你協助我將這些冒險紀錄,製作成一段冒險故事,請
210
+ - 以「你」稱呼學生
211
+ - 請用 500 字以內的短篇故事
212
+ - 試著合併故事記錄成一段連貫、有吸引力的故事
213
+ - 請使用 zh_TW
214
+ 最後,I'll tip $200
215
+ """
216
+
217
+ user_log = f"""
218
+ ```{user_log}
219
+ ```
220
+ """
221
+
222
+ messages = [
223
+ {
224
+ "role": "system",
225
+ "content": f"{system_prompt}",
226
+ },
227
+ {
228
+ "role": "user",
229
+ "content": f"{user_log}",
230
+ },
231
+ ]
232
+
233
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
234
+ response = None
235
+
236
+ retry_attempts = 0
237
+ while retry_attempts < 5:
238
+ try:
239
+ response = client.chat.completions.create(
240
+ model="gpt-3.5-turbo",
241
+ messages=messages,
242
+ temperature=self.temperature,
243
+ max_tokens=self.max_tokens,
244
+ frequency_penalty=self.frequency_penalty,
245
+ presence_penalty=self.presence_penalty,
246
+ )
247
+ chinese_converter = OpenCC("s2tw")
248
+ return chinese_converter.convert(response.choices[0].message.content)
249
+
250
+ except Exception as e:
251
+ retry_attempts += 1
252
+ logging.error(f"Attempt {retry_attempts}: {e}")
253
+ time.sleep(1 * retry_attempts)
254
+
255
+ def get_background(self):
256
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
257
+ image_url = None
258
+
259
+ retry_attempts = 0
260
+ while retry_attempts < 5:
261
+ try:
262
+ response = client.images.generate(
263
+ model="dall-e-3",
264
+ prompt="Create an image in a retro Ghibli style, with a focus on a universe theme. The artwork should maintain the traditional hand-drawn animation look characteristic of Ghibli. Imagine a scene set in outer space or a fantastical cosmic environment, rich with vibrant and varied color palettes to capture the mystery and majesty of the universe. The background should be detailed, showcasing stars, planets, and nebulae, blending the Ghibli style's nostalgia and emotional depth with the awe-inspiring aspects of space. The overall feel should be timeless, merging the natural wonder of the cosmos with the storytelling and emotional resonance typical of the retro Ghibli aesthetic. Soft lighting and gentle shading should be used to enhance the dreamlike, otherworldly quality of the scene.",
265
+ size="1024x1024",
266
+ quality="standard",
267
+ n=1,
268
+ )
269
+
270
+ image_url = response.data[0].url
271
+ return image_url
272
+
273
+ except Exception as e:
274
+ retry_attempts += 1
275
+ logging.error(f"Attempt {retry_attempts}: {e}")
276
+ time.sleep(1 * retry_attempts) # exponential backoff
277
+
278
+
279
+ class AWSAgent:
280
+ def get_story(self, user_log):
281
+ system_prompt = """
282
+ 我正在舉辦一個學習型的活動,參與活動的學生為 1-9 年級,我為他們設計了一個獨特的故事機制,每天每個學生都會收到屬於自己獨特的冒險紀錄,現在我需要你協助我將這些冒險紀錄,製作成一段冒險故事,請
283
+ - 以「你」稱呼學生
284
+ - 請用 500
285
+ - 試著合併故事記錄成一段連貫、有吸引力的故事
286
+ - 請使用 zh_TW
287
+ 最後,I'll tip $200
288
+ """
289
+
290
+ user_log = f"""
291
+ ```{user_log}
292
+ ```
293
+ """
294
+ client = AnthropicBedrock(
295
+ aws_access_key=os.getenv("AWS_ACCESS_KEY"),
296
+ aws_secret_key=os.getenv("AWS_SECRET_KEY"),
297
+ aws_region="us-west-2",
298
+ )
299
+
300
+ retry_attempts = 0
301
+ while retry_attempts < 5:
302
+ try:
303
+ completion = client.completions.create(
304
+ model="anthropic.claude-v2",
305
+ max_tokens_to_sample=2048,
306
+ prompt=f"{anthropic_bedrock.HUMAN_PROMPT}{system_prompt},以下是我的故事紀錄```{user_log}``` {anthropic_bedrock.AI_PROMPT}",
307
+ )
308
+ chinese_converter = OpenCC("s2tw")
309
+ return chinese_converter.convert(completion.completion)
310
+
311
+ except Exception as e:
312
+ retry_attempts += 1
313
+ logging.error(f"Attempt {retry_attempts}: {e}")
314
+ time.sleep(1 * retry_attempts)
315
+
316
+
317
+ class GoogleAgent:
318
+ def get_story(self, user_log):
319
+ credentials, _ = auth.default()
320
+ credentials.refresh(Request())
321
+
322
+ url = f"https://us-central1-aiplatform.googleapis.com/v1/projects/junyiacademy/locations/us-central1/publishers/google/models/gemini-pro:streamGenerateContent?"
323
+
324
+ # Headers
325
+ headers = {
326
+ "Authorization": f"Bearer {credentials.token}",
327
+ "Content-Type": "application/json",
328
+ }
329
+ system_prompt = """
330
+ 我正在舉辦一個學習型的活動,參與活動的學生為 1-9 年級,我為他們設計了一個獨特的故事機制,每天每個學生都會收到屬於自己獨特的冒險紀錄,現在我需要你協助我將這些冒險紀錄,製作成一段冒險故事,請
331
+ - 以「你」稱呼學生
332
+ - 請用 500
333
+ - 試著合併故事記錄成一段連貫、有吸引力的故事
334
+ - 請使用 zh_TW
335
+ 最後,I'll tip $200
336
+ """
337
+
338
+ user_log = f"""
339
+ ```{user_log}
340
+ ```
341
+ """
342
+
343
+ # Provided payload
344
+ payload = {
345
+ "contents": [
346
+ {
347
+ "role": "USER",
348
+ "parts": {"text": f"{system_prompt}, 以下是我的冒險故事 ```{user_log}```"},
349
+ },
350
+ ],
351
+ "safety_settings": {
352
+ "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
353
+ "threshold": "BLOCK_LOW_AND_ABOVE",
354
+ },
355
+ "generation_config": {
356
+ "temperature": 0.2,
357
+ "topP": 0.8,
358
+ "topK": 40,
359
+ "maxOutputTokens": 2048,
360
+ },
361
+ }
362
+
363
+ retry_attempts = 0
364
+ while retry_attempts < 5:
365
+ try:
366
+ response = requests.post(url, headers=headers, data=json.dumps(payload))
367
+ chinese_converter = OpenCC("s2tw")
368
+
369
+ final_story = ""
370
+ for i in response.json():
371
+ final_story += i["candidates"][0]["content"]["parts"][0]["text"]
372
+ return chinese_converter.convert(final_story)
373
+
374
+ except Exception as e:
375
+ retry_attempts += 1
376
+ logging.error(f"Attempt {retry_attempts}: {e}")
377
+ time.sleep(1 * retry_attempts)
378
+
379
+
380
+ class MTKAgent:
381
+ def get_story(self, user_log):
382
+ system_prompt = """
383
+ 我正在舉辦一個學習型的活動,參與活動的學生為 1-9 年級,我為他們設計了一個獨特的故事機制,每天每個學生都會收到屬於自己獨特的冒險紀錄,現在我需要你協助我將這些冒險紀錄,製作成一段冒險故事,請
384
+ - 以「你」稱呼學生
385
+ - 請用 500
386
+ - 試著合併故事記錄成一段連貫、有吸引力的故事
387
+ - 請使用 zh_TW
388
+ """
389
+
390
+ user_log = f"""
391
+ ```{user_log}
392
+ ```
393
+ """
394
+
395
+ BASE_URL = "http://35.229.245.251:8008/v1"
396
+ TOKEN = os.getenv("MTK_TOKEN")
397
+ MODEL_NAME = "model7-c-chat"
398
+ TEMPERATURE = 1
399
+ MAX_TOKENS = 1024
400
+ TOP_P = 0
401
+ PRESENCE_PENALTY = 0
402
+ FREQUENCY_PENALTY = 0
403
+ message = f"{system_prompt}, 以下是我的冒險故事 ```{user_log}```"
404
+ url = os.path.join(BASE_URL, "chat/completions")
405
+ headers = {
406
+ "accept": "application/json",
407
+ "Authorization": f"Bearer {TOKEN}",
408
+ "Content-Type": "application/json",
409
+ }
410
+ data = {
411
+ "model": MODEL_NAME,
412
+ "messages": str(message),
413
+ "temperature": TEMPERATURE,
414
+ "n": 1,
415
+ "max_tokens": MAX_TOKENS,
416
+ "stop": "",
417
+ "top_p": TOP_P,
418
+ "logprobs": 0,
419
+ "echo": False,
420
+ "presence_penalty": PRESENCE_PENALTY,
421
+ "frequency_penalty": FREQUENCY_PENALTY,
422
+ }
423
+
424
+ retry_attempts = 0
425
+ while retry_attempts < 5:
426
+ try:
427
+ response = requests.post(
428
+ url, headers=headers, data=json.dumps(data)
429
+ ).json()
430
+ response_text = response["choices"][0]["message"]["content"]
431
+
432
+ matched_content = re.search("```(.+)", response_text, re.DOTALL)
433
+ extracted_content = (
434
+ matched_content.group(1).strip() if matched_content else None
435
+ )
436
+
437
+ chinese_converter = OpenCC("s2tw")
438
+
439
+ if extracted_content:
440
+ return chinese_converter.convert(extracted_content)
441
+ else:
442
+ return chinese_converter.convert(response_text)
443
+
444
+ except Exception as e:
445
+ retry_attempts += 1
446
+ logging.error(f"Attempt {retry_attempts}: {e}")
447
+ time.sleep(1 * retry_attempts)
448
+
449
+
450
+ class ImageProcessor:
451
+ @staticmethod
452
+ def draw_shadow(
453
+ image, box, radius, offset=(10, 10), shadow_color=(0, 0, 0, 128), blur_radius=5
454
+ ):
455
+ shadow_image = Image.new("RGBA", image.size, (0, 0, 0, 0))
456
+ shadow_draw = ImageDraw.Draw(shadow_image)
457
+ shadow_box = [
458
+ box[0] + offset[0],
459
+ box[1] + offset[1],
460
+ box[2] + offset[0],
461
+ box[3] + offset[1],
462
+ ]
463
+ shadow_draw.rounded_rectangle(shadow_box, fill=shadow_color, radius=radius)
464
+ shadow_image = shadow_image.filter(ImageFilter.GaussianBlur(blur_radius))
465
+ image.paste(shadow_image, (0, 0), shadow_image)
466
+
467
+ @staticmethod
468
+ def generate_reward(url, player_name, paragraph, player_backend_user_id):
469
+ retry_attempts = 0
470
+ while retry_attempts < 5:
471
+ try:
472
+ response = requests.get(url)
473
+ break
474
+ except requests.RequestException as e:
475
+ retry_attempts += 1
476
+ logging.error(f"Attempt {retry_attempts}: {e}")
477
+ time.sleep(1 * retry_attempts) # exponential backoff
478
+
479
+ image_bytes = io.BytesIO(response.content)
480
+ img = Image.open(image_bytes)
481
+
482
+ tmp_img = Image.new("RGBA", img.size, (0, 0, 0, 0))
483
+ draw = ImageDraw.Draw(tmp_img)
484
+
485
+ # Draw the box
486
+ left, right = 50, img.width - 50
487
+ box_height = 500
488
+ top = (img.height - box_height) // 2
489
+ bottom = (img.height + box_height) // 2
490
+ border_radius = 20
491
+
492
+ # Draw the rounded rectangle
493
+ fill_color = (255, 255, 255, 220)
494
+ draw.rounded_rectangle(
495
+ [left, top, right, bottom],
496
+ fill=fill_color,
497
+ outline=None,
498
+ radius=border_radius,
499
+ )
500
+
501
+ img.paste(Image.alpha_composite(img.convert("RGBA"), tmp_img), (0, 0), tmp_img)
502
+
503
+ draw = ImageDraw.Draw(img)
504
+
505
+ # Draw the text
506
+ title_font = ImageFont.truetype("NotoSansTC-Bold.ttf", 36)
507
+ body_font = ImageFont.truetype("NotoSansTC-Light.ttf", 12)
508
+
509
+ # Title text
510
+ title = f"光束守護者 - {player_name} 的冒險故事"
511
+ title_x, title_y = left + 20, top + 20 # Adjust padding as needed
512
+ draw.text((title_x, title_y), title, font=title_font, fill="black")
513
+
514
+ # Paragraph text with newlines
515
+ body_x, body_y = left + 20, title_y + 60 # Adjust position as needed
516
+
517
+ for line in paragraph.split("\n"):
518
+ wrapped_lines = textwrap.wrap(line, width=55)
519
+ for wrapped_line in wrapped_lines:
520
+ draw.text((body_x, body_y), wrapped_line, font=body_font, fill="black")
521
+ body_y += 30
522
+
523
+ # Save the image with the text
524
+
525
+ def get_md5_hash(text):
526
+ return hashlib.md5(text.encode("utf-8")).hexdigest()
527
+
528
+ updated_image_path = f"certificate_{get_md5_hash(player_backend_user_id)}.png"
529
+ img.save(updated_image_path)
530
+
531
+ return updated_image_path
utils/completion_reward_utils.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ import gradio as gr
4
+
5
+
6
+ def get_llm_response(completion_reward, *args):
7
+ return completion_reward.get_llm_response(*args)
8
+
9
+
10
+ def set_player_name(completion_reward, *args):
11
+ return completion_reward.set_player_name(*args)
12
+
13
+
14
+ def display_class_info(completion_reward, *args):
15
+ return completion_reward.display_class_info(*args)
16
+
17
+
18
+ def set_player_selected_character(completion_reward, *args):
19
+ return completion_reward.set_player_selected_character(*args)
20
+
21
+
22
+ def create_certificate(completion_reward, *args):
23
+ return completion_reward.create_certificate(*args)
24
+
25
+
26
+ def complete_reward(completion_reward, *args):
27
+ return completion_reward.complete_reward(*args)
28
+
29
+
30
+ def check_is_in_completion_reward(player_backend_user_id):
31
+ with open("../data/completion_reward_issue_status.json") as f:
32
+ completion_reward_issue_status_dict = json.load(f)
33
+
34
+ if player_backend_user_id in completion_reward_issue_status_dict:
35
+ preview_list = ["[email protected]", "http://googleid.junyiacademy.org/111939868185365078143"]
36
+ if player_backend_user_id in preview_list:
37
+ value = completion_reward_issue_status_dict[player_backend_user_id]
38
+ if value == "not_issued":
39
+ return (
40
+ gr.update(visible=False),
41
+ gr.update(visible=True),
42
+ gr.update(visible=True),
43
+ gr.update(visible=True),
44
+ gr.update(visible=False),
45
+ gr.update(visible=False),
46
+ gr.update(visible=False),
47
+ )
48
+ else:
49
+ return (
50
+ gr.update(visible=False),
51
+ gr.update(visible=False),
52
+ gr.update(visible=False),
53
+ gr.update(visible=False),
54
+ gr.update(visible=False),
55
+ gr.update(visible=False),
56
+ gr.Image(value, visible=True),
57
+ )
58
+ else:
59
+ return (
60
+ gr.update(visible=False),
61
+ gr.update(visible=False),
62
+ gr.update(visible=False),
63
+ gr.update(visible=False),
64
+ gr.update(visible=False),
65
+ gr.update(visible=True),
66
+ gr.update(visible=False),
67
+ )
68
+
69
+ else:
70
+ return (
71
+ gr.update(visible=False),
72
+ gr.update(visible=False),
73
+ gr.update(visible=False),
74
+ gr.update(visible=False),
75
+ gr.update(visible=True),
76
+ gr.update(visible=False),
77
+ )
utils/mes_player_activity_model.py CHANGED
@@ -1,20 +1,19 @@
1
  from datetime import datetime
2
 
3
- class PlayerActivity():
 
4
  def __init__(self) -> None:
5
  self.player_backend_user_id = None
6
  self.login_timestamp = datetime.now()
7
  self.rendered_timestamp = None
8
 
9
  def render_finished(self, player_info):
10
- self.player_backend_user_id = player_info['player_backend_user_id']
11
  self.rendered_timestamp = datetime.now()
12
 
13
  def to_dict(self):
14
  return {
15
- 'player_backend_user_id': self.player_backend_user_id,
16
- 'login_timestamp': self.login_timestamp,
17
- 'rendered_timestamp': self.rendered_timestamp
18
  }
19
-
20
-
 
1
  from datetime import datetime
2
 
3
+
4
+ class PlayerActivity:
5
  def __init__(self) -> None:
6
  self.player_backend_user_id = None
7
  self.login_timestamp = datetime.now()
8
  self.rendered_timestamp = None
9
 
10
  def render_finished(self, player_info):
11
+ self.player_backend_user_id = player_info["player_backend_user_id"]
12
  self.rendered_timestamp = datetime.now()
13
 
14
  def to_dict(self):
15
  return {
16
+ "player_backend_user_id": self.player_backend_user_id,
17
+ "login_timestamp": self.login_timestamp,
18
+ "rendered_timestamp": self.rendered_timestamp,
19
  }
 
 
utils/mes_player_model.py CHANGED
@@ -35,9 +35,7 @@ class Player:
35
  # and the rewards_status is not yet initialized
36
  if init:
37
  self.assign_group()
38
- self.add_adventure_log(
39
- "你並未參與這次與狐貍貓的冒險,請在下次活動中參與冒險吧!"
40
- )
41
  if available_achievements is not None:
42
  self.rewards_status.update(
43
  {
 
35
  # and the rewards_status is not yet initialized
36
  if init:
37
  self.assign_group()
38
+ self.add_adventure_log("你並未參與這次與狐貍貓的冒險,請在下次活動中參與冒險吧!")
 
 
39
  if available_achievements is not None:
40
  self.rewards_status.update(
41
  {
utils/utils.py CHANGED
@@ -207,6 +207,7 @@ def render_player_data(player_info: gr.State):
207
  current_story,
208
  )
209
 
 
210
  def insert_data_into_bigquery(client, dataset_id, table_id, rows_to_insert):
211
  # Specify the destination table
212
  table_ref = client.dataset(dataset_id).table(table_id)
@@ -223,9 +224,13 @@ def insert_data_into_bigquery(client, dataset_id, table_id, rows_to_insert):
223
  else:
224
  logging.info(f"Inserted {len(rows_to_insert)} rows successfully.")
225
 
 
226
  def render_finished(player_activity, *args):
227
  player_activity.render_finished(*args)
228
  insert_row = player_activity.to_dict()
229
- insert_data_into_bigquery(client, 'streaming_log', 'log_mes_player_login_activity', [insert_row])
230
- logging.info(f"Player {insert_row['player_backend_user_id']} rendered successfully.")
231
-
 
 
 
 
207
  current_story,
208
  )
209
 
210
+
211
  def insert_data_into_bigquery(client, dataset_id, table_id, rows_to_insert):
212
  # Specify the destination table
213
  table_ref = client.dataset(dataset_id).table(table_id)
 
224
  else:
225
  logging.info(f"Inserted {len(rows_to_insert)} rows successfully.")
226
 
227
+
228
  def render_finished(player_activity, *args):
229
  player_activity.render_finished(*args)
230
  insert_row = player_activity.to_dict()
231
+ insert_data_into_bigquery(
232
+ client, "streaming_log", "log_mes_player_login_activity", [insert_row]
233
+ )
234
+ logging.info(
235
+ f"Player {insert_row['player_backend_user_id']} rendered successfully."
236
+ )