Spaces:
Running
on
Zero
Running
on
Zero
tes
Browse files- mysite/routers/fastapi.py +0 -363
mysite/routers/fastapi.py
CHANGED
@@ -76,366 +76,3 @@ def setup_webhook_routes(app: FastAPI):
|
|
76 |
from polls.routers import register_routers
|
77 |
|
78 |
register_routers(app)
|
79 |
-
"""
|
80 |
-
@app.post("/webhook")
|
81 |
-
async def webhook(request: Request):
|
82 |
-
#return
|
83 |
-
#logger.info("[Start] ====== LINE webhook ======")
|
84 |
-
body = await request.body()
|
85 |
-
received_headers = dict(request.headers)
|
86 |
-
body_str = body.decode("utf-8")
|
87 |
-
logger.info("Received Body: %s", body_str)
|
88 |
-
body_json = json.loads(body_str)
|
89 |
-
events = body_json.get("events", [])
|
90 |
-
|
91 |
-
webhook_url = os.getenv("chat_url")
|
92 |
-
token = os.getenv("token")
|
93 |
-
ChannelAccessToken = os.getenv('ChannelAccessToken')
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
thread_name=""
|
99 |
-
#return
|
100 |
-
#url = github(token,foldername)
|
101 |
-
try:
|
102 |
-
DEBUG=0
|
103 |
-
for event in events:
|
104 |
-
if event["type"] == "message" and event["message"]["type"] == "text":
|
105 |
-
user_id = event["source"]["userId"]
|
106 |
-
text = event["message"]["text"]
|
107 |
-
user_name,thmbnail = get_user_profile(user_id,ChannelAccessToken)
|
108 |
-
|
109 |
-
logger.info("Received Headers: %s", user_name)
|
110 |
-
logger.info("Received Headers: %s", thmbnail)
|
111 |
-
|
112 |
-
#logger.info("------------------------------------------")
|
113 |
-
first_line = text.split('\n')[0]
|
114 |
-
#logger.info(f"User ID: {user_id}, Text: {text}")
|
115 |
-
prompt = """
|
116 |
-
1, Q&Aのテーブルを作成してください DBはpostgress pk はPostgresのAutoIncrementの serialでの自動追加
|
117 |
-
2, 質問が来た際には、まず質問に対しての答えを過去のデータから探します
|
118 |
-
3, Q&Aから役割を作成します
|
119 |
-
質問に対しての答えを出す、シナリオを考える
|
120 |
-
4, 実際にテストして正しい答えがでるか確認
|
121 |
-
5, 出ない場合は再度作成しなおします
|
122 |
-
1から6を繰り返し、答えが出たプロンプトを登録します
|
123 |
-
7, 成功した場合それを保存します
|
124 |
-
8, 同じ質問が来たら質問別にプロンプトを変更します
|
125 |
-
9, 上記をラインの質問に内部の方が納得いくまで、日々修正していきます
|
126 |
-
"""
|
127 |
-
prompt2 = f"""
|
128 |
-
# 返信について日本語で必ず答えて下さい
|
129 |
-
# 役割
|
130 |
-
あなたはリファスタという会社のアシスタントです
|
131 |
-
金、ダイヤモンド、商品を売りにきた顧客います
|
132 |
-
売りに来た顧客の質問内容は {text}
|
133 |
-
この質問を買取店の査定人に対して、理解がしやすい わかりやすい質問に変更してください
|
134 |
-
|
135 |
-
会社にはデータベースがあり質問内容から、商品を検索するSQLを作成してください
|
136 |
-
必要なテーブルのCreate文も作成してして下さい
|
137 |
-
pk は postgress なので 自動連番のserialにして下さい
|
138 |
-
質問の内容をそのテーブルにいれるインサート文も作成してくさい
|
139 |
-
|
140 |
-
ほかに、良い提案があればして下さい。こうしたらもっと、良くなるよなど。
|
141 |
-
## リファスタの住所
|
142 |
-
〒170-0013 東京都豊島区東池袋1丁目25−14 アルファビルディング 4F
|
143 |
-
## 買取ダイヤテンプレート
|
144 |
-
- price,
|
145 |
-
- carat,
|
146 |
-
- cut,
|
147 |
-
- color,
|
148 |
-
- clarity,
|
149 |
-
- depth,
|
150 |
-
- diamondprice.table,
|
151 |
-
- x,
|
152 |
-
- y,
|
153 |
-
- z
|
154 |
-
## 買取ブランドテンプレート
|
155 |
-
・ブランド名:
|
156 |
-
・モデル名:
|
157 |
-
・型番や品番:
|
158 |
-
・購入店:
|
159 |
-
・購入時期:
|
160 |
-
・購入金額:
|
161 |
-
・付属品:
|
162 |
-
・コンディション:
|
163 |
-
(10段階評価厳しめ)
|
164 |
-
・貴金属品位:
|
165 |
-
・貴金属重量:
|
166 |
-
(キッチンスケールでも(sparkle))
|
167 |
-
・ダイヤや宝石の鑑定書はお写真で!
|
168 |
-
・イニシャル:あり なし
|
169 |
-
## リファスタのサイト
|
170 |
-
(monitor)24h対応事前査定
|
171 |
-
https://kinkaimasu.jp/estimate/?openExternalBrowser=1&utm_source=LINE
|
172 |
-
|
173 |
-
(open book)買取システムナビ
|
174 |
-
https://kinkaimasu.jp/system/?openExternalBrowser=1&utm_source=LINE
|
175 |
-
|
176 |
-
(car)店舗アクセス
|
177 |
-
https://goo.gl/veQZ03
|
178 |
-
|
179 |
-
(?)よくある質問
|
180 |
-
https://kinkaimasu.jp/faq/?openExternalBrowser=1&utm_source=LINE"
|
181 |
-
User,hibiki,2024/06/16,21:53:47,"まだ買取をするか未定ですが、
|
182 |
-
一度査定をよろしくお願いします。"
|
183 |
-
Account,応答メッセージ,2024/06/16,21:53:47,"(clock)ただ今対応時間外(clock)
|
184 |
-
営業時間:11:00~20:00
|
185 |
-
※年中無休
|
186 |
-
翌営業日に順次対応致しますので、お写真や情報はいつでもお送りください(moon wink)
|
187 |
-
|
188 |
-
|
189 |
-
(monitor)24h対応事前査定
|
190 |
-
https://kinkaimasu.jp/estimate/?openExternalBrowser=1&utm_source=LINE
|
191 |
-
|
192 |
-
(open book)買取システムナビ
|
193 |
-
https://kinkaimasu.jp/system/?openExternalBrowser=1&utm_source=LINE
|
194 |
-
|
195 |
-
(car)店舗アクセス
|
196 |
-
https://goo.gl/veQZ03
|
197 |
-
|
198 |
-
(?)よくある質問
|
199 |
-
https://kinkaimasu.jp/faq/?openExternalBrowser=1&utm_source=LINE"
|
200 |
-
## サービス
|
201 |
-
## フリーダイヤル
|
202 |
-
お気軽にお電話くださいませ(sparkle)
|
203 |
-
10:30〜20:00 年中無休
|
204 |
-
|
205 |
-
オンライン買取も受付中
|
206 |
-
https://kinkaimasu.jp/online-promise/?openExternalBrowser=1&utm_source=LINE
|
207 |
-
|
208 |
-
## (smartphone) 電話番号
|
209 |
-
0120-954-679
|
210 |
-
|
211 |
-
## (LINE messenger) LINE通話
|
212 |
-
https://lin.ee/c6inM4V
|
213 |
-
|
214 |
-
"""
|
215 |
-
# 査定用のプロンプト
|
216 |
-
promps,prompt_res = prompt_genalate("返信は日本語で答えて下さい "+text,prompt2)
|
217 |
-
|
218 |
-
#test_set_lide(text,"a1")
|
219 |
-
#no_process_file(text, "ai")
|
220 |
-
#\r\m
|
221 |
-
#########################################################################
|
222 |
-
#user_name,thmbnail#
|
223 |
-
title = f""" {user_name}様から下記の質問があります"""
|
224 |
-
subtitle = f"""<b>ユーザーID</b> {user_id}\r\n <b>質問内容</b>\r\n {text}"""
|
225 |
-
link_text = "\r\n<b>チャットボット設定用シート</b>\r\n シート用のアプリはチャットから\r\n @リファペディア\r\n と打ち込むと開きます"
|
226 |
-
link_url = "https://docs.google.com/spreadsheets/d/13pqP-Ywo5eRlZBsYX2m3ChARG38EoIYOowFd3cWij1c/edit?gid=283940886#gid=283940886"
|
227 |
-
#test_set_lide(subtitle, text)
|
228 |
-
thread_name = send_google_chat_card(webhook_url, title, subtitle, link_text, link_url,thmbnail)
|
229 |
-
from command.n8n import post_data
|
230 |
-
import time
|
231 |
-
n8nurl = os.getenv("n8nhook")
|
232 |
-
post_data(n8nurl,text,thread_name)
|
233 |
-
time.sleep(10)
|
234 |
-
#########################################################################
|
235 |
-
title = f""" プロンプト作成 {promps}"""
|
236 |
-
subtitle = f"""userid {user_id}\r\n chatid {thread_name}\r\n{prompt_res}"""
|
237 |
-
link_text = "データを確認する"
|
238 |
-
link_url = "https://kenken999-php.hf.space/diamondprice_list.php"
|
239 |
-
#test_set_lide(subtitle, text)
|
240 |
-
if DEBUG==0:
|
241 |
-
thread_name = send_google_chat_card_thread(webhook_url, title, subtitle, link_text, link_url,thread_name)
|
242 |
-
|
243 |
-
#thread_name = send_google_chat_card_thread(webhook_url, title, subtitle, link_text, link_url,thread_name)
|
244 |
-
#return
|
245 |
-
#test case
|
246 |
-
#########################################################################
|
247 |
-
first_line = text.split('\n')[0]
|
248 |
-
#test_prompt
|
249 |
-
res = test_prompt("返信は必ず日本語でして下さい \r\n"+prompt_res,text)
|
250 |
-
if DEBUG==0:
|
251 |
-
thread_name = send_google_chat_card_thread(webhook_url, "プロンプトテスト "+first_line, str(res), link_text, link_url,thread_name)
|
252 |
-
#thread_name = send_google_chat_card_thread(webhook_url, title, subtitle, link_text, link_url,thread_name)
|
253 |
-
now = datetime.now()
|
254 |
-
yyyymmddhis = now.strftime('%Y%m%d%H%M%S')
|
255 |
-
|
256 |
-
prompt_for_create_system = """
|
257 |
-
下記の質問に対応するコードをdjangoでアプリを作成 プロジェクトはいりません
|
258 |
-
fastapiでrouter部分を作成 組み込みはメイン部分でします
|
259 |
-
フロントエンドをgradioで作成
|
260 |
-
#google apps script frontend
|
261 |
-
googleappsscript doGet でのgradioの表示処理を作成 google.script.runで関数は呼び出し
|
262 |
-
#google apps script backend
|
263 |
-
frontendからの呼び出し用のバックエンドスクリプト
|
264 |
-
仕様書の作成
|
265 |
-
PlantUMLでシーケンス図の作成
|
266 |
-
Markdownでのプログラム殺名
|
267 |
-
#下記参考にAPIも作成しておいて
|
268 |
-
action insert list edit update でCRUDがかわる
|
269 |
-
同じようにGASのAPIも作成しておいて
|
270 |
-
|
271 |
-
def create_vector():
|
272 |
-
inputs = tokenizer(result, return_tensors="pt", max_length=512, truncation=True)
|
273 |
-
outputs = model(**inputs)
|
274 |
-
# [CLS]トークンの出力を取得
|
275 |
-
embeddings = outputs.last_hidden_state[:,0,:].squeeze().detach().cpu().numpy().tolist()
|
276 |
-
print(embeddings)
|
277 |
-
import requests
|
278 |
-
|
279 |
-
url = "https://kenken999-php.hf.space/api/v1.php"
|
280 |
-
|
281 |
-
payload = "model_name={embeddings}&vector_text={result}&table=products&action=insert""
|
282 |
-
headers = {
|
283 |
-
'X-Auth-Token': 'admin',
|
284 |
-
'Content-Type': 'application/x-www-form-urlencoded',
|
285 |
-
'Cookie': 'runnerSession=muvclb78zpsdjbm7y9c3; pD1lszvk6ratOZhmmgvkp=13767810ebf0782b0b51bf72dedb63b3'
|
286 |
-
}
|
287 |
-
|
288 |
-
response = requests.request("POST", url, headers=headers, data=payload)
|
289 |
-
|
290 |
-
print(response.text)
|
291 |
-
return True
|
292 |
-
|
293 |
-
下記の質問 作成対応内容
|
294 |
-
|
295 |
-
"""
|
296 |
-
#########################################################################
|
297 |
-
## excute create program
|
298 |
-
res_no_process = no_process_file(prompt_for_create_system+res, "gpt_enginner"+ yyyymmddhis,thread_name)
|
299 |
-
# execute open interpreter
|
300 |
-
#########################################################################
|
301 |
-
full_response,history = chat_with_interpreter_no_stream(prompt_for_create_system+"\r\n"+res)
|
302 |
-
if DEBUG==1:
|
303 |
-
thread_name = send_google_chat_card_thread(webhook_url, f"自動設定開始 {res}", str(full_response), link_text, link_url,thread_name)
|
304 |
-
#########################################################################
|
305 |
-
#ダイヤ金額計算
|
306 |
-
from babyagi.classesa.diamond import calculate
|
307 |
-
title = f""" ダイヤ予測計算の実行 類似5件表示 {text}
|
308 |
-
id,price,carat, cut, color, clarity, depth, diamondprice.table, x, y, z 類似度"""
|
309 |
-
res_calculate = calculate(text)
|
310 |
-
subtitle = res_calculate
|
311 |
-
link_text = "データを確認する"
|
312 |
-
link_url = "https://kenken999-php.hf.space/diamondprice_list.php"
|
313 |
-
#test_set_lide(subtitle, text)
|
314 |
-
if DEBUG==0:
|
315 |
-
thread_name = send_google_chat_card_thread(webhook_url, title, subtitle, link_text, link_url,thread_name)
|
316 |
-
#########################################################################
|
317 |
-
from babyagi.babyagi import completion
|
318 |
-
#import tempfile
|
319 |
-
text = text.replace("\r\n","")
|
320 |
-
# コマンドを構築
|
321 |
-
command = f"""make runbabyagi "{text}に対して、より良いチャットボットでのQAプランデータ設定の提案を日本語で作成してください" {thread_name}"""
|
322 |
-
|
323 |
-
with open('/home/user/app/babyagi/prompt.txt', 'w') as file:
|
324 |
-
file.write(f"""{text}の質問 についてチャットボットでよりよく対応するプランを日本語で作成して""")
|
325 |
-
|
326 |
-
#
|
327 |
-
proc = subprocess.Popen(
|
328 |
-
["make", "runbabyagi", thread_name],
|
329 |
-
stdin=subprocess.PIPE,
|
330 |
-
stdout=subprocess.PIPE,
|
331 |
-
stderr=subprocess.PIPE,
|
332 |
-
text=True,
|
333 |
-
)
|
334 |
-
#await asyncio.sleep(30)
|
335 |
-
"""
|
336 |
-
proc = subprocess.Popen(
|
337 |
-
command,
|
338 |
-
stdin=subprocess.PIPE,
|
339 |
-
stdout=subprocess.PIPE,
|
340 |
-
stderr=subprocess.PIPE,
|
341 |
-
text=True,
|
342 |
-
shell=True
|
343 |
-
)
|
344 |
-
"""
|
345 |
-
|
346 |
-
|
347 |
-
#prompt_res_agi = completion("日本語で下記のプランを考えて "+res)
|
348 |
-
#thread_name = send_google_chat_card_thread(webhook_url, title, prompt_res_agi, link_text, link_url,thread_name)
|
349 |
-
#title = f""" タスク作成 再度考える {promps}"""
|
350 |
-
#subtitle = prompt_res_agi
|
351 |
-
#link_text = "データを確認する"
|
352 |
-
#link_url = "https://kenken999-php.hf.space/diamondprice_list.php"
|
353 |
-
#thread_name = send_google_chat_card_thread(webhook_url, title, prompt_res_agi, link_text, link_url,thread_name)
|
354 |
-
|
355 |
-
#send_google_chat_card(webhook_url, title, subtitle, link_text, link_url)
|
356 |
-
|
357 |
-
#return
|
358 |
-
#return
|
359 |
-
#return
|
360 |
-
"""
|
361 |
-
for event in events:
|
362 |
-
if event["type"] == "message" and event["message"]["type"] == "text":
|
363 |
-
user_id = event["source"]["userId"]
|
364 |
-
text = event["message"]["text"]
|
365 |
-
logger.info(event)
|
366 |
-
logger.info(f"User ID: {user_id}, Text: {text}")
|
367 |
-
now = datetime.now().strftime("%Y%m%d%H%M%S")
|
368 |
-
title = text[:10]
|
369 |
-
user_id_with_timestamp = title#f"{now}_{title}_{user_id}"
|
370 |
-
no_process_file(text, user_id_with_timestamp)
|
371 |
-
#db登録
|
372 |
-
test_set_lide(text, user_id_with_timestamp)
|
373 |
-
"""
|
374 |
-
logger.info("Received Headers: %s", received_headers)
|
375 |
-
logger.info("Received Body: %s", body.decode("utf-8"))
|
376 |
-
###############################################################################
|
377 |
-
#send to appsheet
|
378 |
-
get_senario("user_id",str(body))
|
379 |
-
#apps script send
|
380 |
-
headers = {
|
381 |
-
"Content-Type": "application/json",
|
382 |
-
}
|
383 |
-
logger.info("Received Body: %s", "send data to appsheet ")
|
384 |
-
#response = requests.post(os.getenv("WEBHOOK_URL"), headers=headers, data=body)
|
385 |
-
# check signature
|
386 |
-
line_signature = received_headers.get("x-line-signature")
|
387 |
-
logger.info("Received Body: %s", "start send messages ")
|
388 |
-
#headers = {
|
389 |
-
# "Content-Type": "application/json",
|
390 |
-
# "X-Line-Signature": line_signature,
|
391 |
-
# "Authorization": f"Bearer {os.getenv('ChannelAccessToken')}",
|
392 |
-
# }
|
393 |
-
#r#esponse = requests.post(os.getenv("WEBHOOK_URL"), headers=headers, data=body)
|
394 |
-
if not line_signature:
|
395 |
-
raise HTTPException(status_code=400, detail="X-Line-Signature header is missing.")
|
396 |
-
|
397 |
-
if not validate_signature(body.decode("utf-8"), line_signature, os.getenv("ChannelSecret")):
|
398 |
-
raise HTTPException(status_code=400, detail="Invalid signature.")
|
399 |
-
|
400 |
-
if not os.getenv("WEBHOOK_URL") or not os.getenv("WEBHOOK_URL").startswith("https://"):
|
401 |
-
raise HTTPException(status_code=400, detail="Invalid webhook URL")
|
402 |
-
|
403 |
-
headers = {
|
404 |
-
"Content-Type": "application/json",
|
405 |
-
"X-Line-Signature": line_signature,
|
406 |
-
"Authorization": f"Bearer {os.getenv('ChannelAccessToken')}",
|
407 |
-
}
|
408 |
-
|
409 |
-
logger.info("Forwarding to URL: %s", os.getenv("WEBHOOK_URL"))
|
410 |
-
logger.info("Forwarding Headers: %s", headers)
|
411 |
-
logger.info("Forwarding Body: %s", body.decode("utf-8"))
|
412 |
-
|
413 |
-
#response = requests.post(os.getenv("WEBHOOK_URL"), headers=headers, data=body)
|
414 |
-
responses = requests.post(os.getenv("WEBHOOK_GAS"), headers=headers, data=body)
|
415 |
-
logger.info("Response Code: %s", response.status_code)
|
416 |
-
logger.info("Response Content: %s", response.text)
|
417 |
-
logger.info("Response Headers: %s", response.headers)
|
418 |
-
|
419 |
-
return {"status": "success", "response_content": response.text}#, response.status_code
|
420 |
-
|
421 |
-
except Exception as e:
|
422 |
-
error_file = os.path.basename(__file__) # ファイル名を取得
|
423 |
-
error_line = sys._getframe(1).f_lineno # 行番号を取得
|
424 |
-
print(f"Error occurred at file {error_file} on line {error_line}: {str(e)}")
|
425 |
-
#エラー内容の分析
|
426 |
-
promps,res = prompt_genalate(str(e))
|
427 |
-
#test_set_lide(text,"a1")
|
428 |
-
#no_process_file(text, "ai")
|
429 |
-
custormer_supportpage = "\r\n カスタマーサポートはこちらから \r\n https://bpmboxesscom-46463613.hubspotpagebuilder.com/ja \r\n "
|
430 |
-
title = f"""エラーが起こりました -+errer file is {error_file} error line is {error_line} {str(e)} 自動修復の開始 """
|
431 |
-
subtitle = custormer_supportpage+res
|
432 |
-
link_text = "test"
|
433 |
-
link_url = "url"
|
434 |
-
#test_set_lide(subtitle, text)
|
435 |
-
logger.error(res)
|
436 |
-
#send error to google chat
|
437 |
-
send_google_chat_card_thread(webhook_url, title, subtitle, link_text, link_url,thread_name)
|
438 |
-
logger.error("Error: %s", str(e))
|
439 |
-
#raise するとシステムとまるのでアンコメント
|
440 |
-
#raise HTTPException(status_code=500, detail=str(e))
|
441 |
-
return {"status": "success", "response_content": str(e)}#, response.status_code
|
|
|
76 |
from polls.routers import register_routers
|
77 |
|
78 |
register_routers(app)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|