OnlyBiggg commited on
Commit
df37f6e
·
1 Parent(s): 724c9dc

refactor code

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. app/__pycache__/__init__.cpython-310.pyc +0 -0
  2. app/__pycache__/router.cpython-310.pyc +0 -0
  3. app/ai/service.py +102 -0
  4. app/ai/weights/electra/config.json +53 -0
  5. app/ai/weights/electra/model_optimized.onnx +0 -0
  6. app/ai/weights/electra/ort_config.json +37 -0
  7. app/ai/weights/electra/special_tokens_map.json +37 -0
  8. app/ai/weights/electra/tokenizer.json +0 -0
  9. app/ai/weights/electra/tokenizer_config.json +65 -0
  10. app/ai/weights/electra/vocab.txt +0 -0
  11. app/{dialogflow/api → api}/__init__.py +0 -0
  12. app/api/router.py +24 -0
  13. app/{dialogflow/api → api}/v1/__init__.py +0 -0
  14. app/api/v1/dialogflow.py +1247 -0
  15. app/api/v1/location.py +58 -0
  16. app/api/v1/time.py +63 -0
  17. app/api/v1/trip.py +277 -0
  18. app/api/v1/user.py +60 -0
  19. app/client/futabus_client.py +210 -0
  20. app/dependencies/containers.py +50 -0
  21. app/dialogflow/api/__pycache__/__init__.cpython-310.pyc +0 -0
  22. app/dialogflow/api/__pycache__/router.cpython-310.pyc +0 -0
  23. app/dialogflow/api/__pycache__/routes.cpython-39.pyc +0 -0
  24. app/dialogflow/api/router.py +0 -12
  25. app/dialogflow/api/v1/__pycache__/__init__.cpython-310.pyc +0 -0
  26. app/dialogflow/api/v1/__pycache__/dialogflow.cpython-310.pyc +0 -0
  27. app/dialogflow/api/v1/dialogflow.py +0 -1059
  28. app/dialogflow/services/__init__.py +0 -0
  29. app/dialogflow/services/__pycache__/__init__.cpython-310.pyc +0 -0
  30. app/dialogflow/services/__pycache__/api.cpython-39.pyc +0 -0
  31. app/dialogflow/services/__pycache__/dialog_service.cpython-310.pyc +0 -0
  32. app/dialogflow/services/__pycache__/external_api.cpython-39.pyc +0 -0
  33. app/dialogflow/services/__pycache__/origin_codes.cpython-310.pyc +0 -0
  34. app/dialogflow/services/dialog_service.py +0 -432
  35. app/dialogflow/services/origin_codes.py +0 -14
  36. app/dto/request.py +96 -0
  37. app/dto/response.py +148 -0
  38. app/dto/seat.py +17 -0
  39. app/dto/ticket.py +80 -0
  40. app/dto/trip.py +136 -0
  41. app/dto/user.py +7 -0
  42. app/ner/services/ner.py +47 -24
  43. app/ner/utils/__init__.py +0 -0
  44. app/repositories/trip_repository.py +88 -0
  45. app/resolvers/location_resolver.py +27 -0
  46. app/resolvers/origin_code.py +37 -0
  47. app/router.py +0 -9
  48. app/{dialogflow/schemas → services}/__init__.py +0 -0
  49. app/services/dialog_service.py +537 -0
  50. app/services/helper.py +97 -0
app/__pycache__/__init__.cpython-310.pyc CHANGED
Binary files a/app/__pycache__/__init__.cpython-310.pyc and b/app/__pycache__/__init__.cpython-310.pyc differ
 
app/__pycache__/router.cpython-310.pyc CHANGED
Binary files a/app/__pycache__/router.cpython-310.pyc and b/app/__pycache__/router.cpython-310.pyc differ
 
app/ai/service.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from loguru import logger
2
+ from ...core.conf import settings
3
+
4
+
5
+ class ElectraModel:
6
+ def __init__(self, model_dir: str = settings.NER_MODEL_DIR):
7
+ self.model_dir = model_dir
8
+ self.model = None
9
+ self.tokenizer = None
10
+ self.pipeline = None
11
+ self.load_model()
12
+
13
+ def load_model(self):
14
+ from transformers import AutoTokenizer
15
+ from optimum.onnxruntime import ORTModelForTokenClassification
16
+ from optimum.pipelines import pipeline
17
+
18
+ self.tokenizer = AutoTokenizer.from_pretrained(
19
+ self.model_dir, truncation=settings.TRUNCATE, max_length=settings.MAX_LENGTH
20
+ )
21
+ self.model = ORTModelForTokenClassification.from_pretrained(self.model_dir)
22
+ self.pipeline = pipeline(
23
+ task=settings.TASK_NAME,
24
+ model=self.model,
25
+ tokenizer=self.tokenizer,
26
+ device=settings.DEVICE,
27
+ )
28
+ logger.info(f"Model loaded from {self.model_dir}")
29
+
30
+ async def predict(self, text: str, entity_tag: str = None):
31
+
32
+ if not text:
33
+ return None
34
+
35
+ if self.pipeline is None:
36
+ raise ValueError("Model not loaded. Please call load_model() first.")
37
+
38
+ pred = self.pipeline(text)
39
+
40
+ if entity_tag:
41
+ return self.extract_entities(pred, entity_tag)
42
+ return pred
43
+
44
+ def extract_entities(
45
+ self, result_pred: list[dict[str, any]], entity: str
46
+ ) -> list[str]:
47
+ if self.pipeline is None:
48
+ raise ValueError("Model not loaded. Please call load_model() first.")
49
+ B_ENTITY = f"B-{entity}"
50
+ I_ENTITY = f"I-{entity}"
51
+
52
+ extracted_entities = []
53
+ current_entity_tokens = []
54
+
55
+ for item in result_pred:
56
+ word = item["word"]
57
+ entity_tag = item["entity"]
58
+
59
+ if entity_tag == B_ENTITY:
60
+ if current_entity_tokens:
61
+ extracted_entities.append(
62
+ self._combine_token(current_entity_tokens)
63
+ )
64
+ current_entity_tokens = [word]
65
+ elif entity_tag == I_ENTITY and current_entity_tokens:
66
+ current_entity_tokens.append(word)
67
+ else:
68
+ if current_entity_tokens:
69
+ extracted_entities.append(
70
+ self._combine_token(current_entity_tokens)
71
+ )
72
+ current_entity_tokens = []
73
+
74
+ if current_entity_tokens:
75
+ extracted_entities.append(self._combine_token(current_entity_tokens))
76
+
77
+ return extracted_entities
78
+
79
+ def _combine_token(self, tokens: list[str]) -> str:
80
+ """Combines tokens into a single string, removing leading hashtags from the first token if present.
81
+ Args:
82
+ tokens (list[str]): List of tokens to combine.
83
+
84
+ Returns:
85
+ str: Combined string of tokens.
86
+ """
87
+ if not tokens:
88
+ return ""
89
+
90
+ words = []
91
+
92
+ for token in tokens:
93
+ if token.strip("#") != token:
94
+ clean_token = token.strip("#")
95
+ if words:
96
+ words[-1] += clean_token
97
+ else:
98
+ words.append(clean_token)
99
+ else:
100
+ words.append(token)
101
+
102
+ return " ".join(words)
app/ai/weights/electra/config.json ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_name_or_path": "NlpHUST/ner-vietnamese-electra-base",
3
+ "architectures": [
4
+ "ElectraForTokenClassification"
5
+ ],
6
+ "attention_probs_dropout_prob": 0.1,
7
+ "classifier_dropout": null,
8
+ "embedding_size": 768,
9
+ "finetuning_task": "ner",
10
+ "hidden_act": "gelu",
11
+ "hidden_dropout_prob": 0.1,
12
+ "hidden_size": 768,
13
+ "id2label": {
14
+ "0": "B-LOCATION",
15
+ "1": "B-MISCELLANEOUS",
16
+ "2": "B-ORGANIZATION",
17
+ "3": "B-PERSON",
18
+ "4": "I-LOCATION",
19
+ "5": "I-MISCELLANEOUS",
20
+ "6": "I-ORGANIZATION",
21
+ "7": "I-PERSON",
22
+ "8": "O"
23
+ },
24
+ "initializer_range": 0.02,
25
+ "intermediate_size": 3072,
26
+ "label2id": {
27
+ "B-LOCATION": 0,
28
+ "B-MISCELLANEOUS": 1,
29
+ "B-ORGANIZATION": 2,
30
+ "B-PERSON": 3,
31
+ "I-LOCATION": 4,
32
+ "I-MISCELLANEOUS": 5,
33
+ "I-ORGANIZATION": 6,
34
+ "I-PERSON": 7,
35
+ "O": 8
36
+ },
37
+ "layer_norm_eps": 1e-12,
38
+ "max_position_embeddings": 512,
39
+ "model_type": "electra",
40
+ "num_attention_heads": 12,
41
+ "num_hidden_layers": 12,
42
+ "pad_token_id": 0,
43
+ "position_embedding_type": "absolute",
44
+ "summary_activation": "gelu",
45
+ "summary_last_dropout": 0.1,
46
+ "summary_type": "first",
47
+ "summary_use_proj": true,
48
+ "torch_dtype": "float32",
49
+ "transformers_version": "4.48.3",
50
+ "type_vocab_size": 2,
51
+ "use_cache": true,
52
+ "vocab_size": 62000
53
+ }
app/ai/weights/electra/model_optimized.onnx ADDED
Binary file (134 Bytes). View file
 
app/ai/weights/electra/ort_config.json ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "one_external_file": true,
3
+ "opset": null,
4
+ "optimization": {
5
+ "disable_attention": null,
6
+ "disable_attention_fusion": false,
7
+ "disable_bias_gelu": null,
8
+ "disable_bias_gelu_fusion": false,
9
+ "disable_bias_skip_layer_norm": null,
10
+ "disable_bias_skip_layer_norm_fusion": false,
11
+ "disable_embed_layer_norm": true,
12
+ "disable_embed_layer_norm_fusion": true,
13
+ "disable_gelu": null,
14
+ "disable_gelu_fusion": false,
15
+ "disable_group_norm_fusion": true,
16
+ "disable_layer_norm": null,
17
+ "disable_layer_norm_fusion": false,
18
+ "disable_packed_kv": true,
19
+ "disable_rotary_embeddings": false,
20
+ "disable_shape_inference": false,
21
+ "disable_skip_layer_norm": null,
22
+ "disable_skip_layer_norm_fusion": false,
23
+ "enable_gelu_approximation": true,
24
+ "enable_gemm_fast_gelu_fusion": false,
25
+ "enable_transformers_specific_optimizations": true,
26
+ "fp16": false,
27
+ "no_attention_mask": false,
28
+ "optimization_level": 2,
29
+ "optimize_for_gpu": false,
30
+ "optimize_with_onnxruntime_only": null,
31
+ "use_mask_index": false,
32
+ "use_multi_head_attention": false,
33
+ "use_raw_attention_mask": false
34
+ },
35
+ "quantization": {},
36
+ "use_external_data_format": false
37
+ }
app/ai/weights/electra/special_tokens_map.json ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cls_token": {
3
+ "content": "[CLS]",
4
+ "lstrip": false,
5
+ "normalized": false,
6
+ "rstrip": false,
7
+ "single_word": false
8
+ },
9
+ "mask_token": {
10
+ "content": "[MASK]",
11
+ "lstrip": false,
12
+ "normalized": false,
13
+ "rstrip": false,
14
+ "single_word": false
15
+ },
16
+ "pad_token": {
17
+ "content": "[PAD]",
18
+ "lstrip": false,
19
+ "normalized": false,
20
+ "rstrip": false,
21
+ "single_word": false
22
+ },
23
+ "sep_token": {
24
+ "content": "[SEP]",
25
+ "lstrip": false,
26
+ "normalized": false,
27
+ "rstrip": false,
28
+ "single_word": false
29
+ },
30
+ "unk_token": {
31
+ "content": "[UNK]",
32
+ "lstrip": false,
33
+ "normalized": false,
34
+ "rstrip": false,
35
+ "single_word": false
36
+ }
37
+ }
app/ai/weights/electra/tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
app/ai/weights/electra/tokenizer_config.json ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "added_tokens_decoder": {
3
+ "0": {
4
+ "content": "[PAD]",
5
+ "lstrip": false,
6
+ "normalized": false,
7
+ "rstrip": false,
8
+ "single_word": false,
9
+ "special": true
10
+ },
11
+ "1": {
12
+ "content": "[UNK]",
13
+ "lstrip": false,
14
+ "normalized": false,
15
+ "rstrip": false,
16
+ "single_word": false,
17
+ "special": true
18
+ },
19
+ "2": {
20
+ "content": "[CLS]",
21
+ "lstrip": false,
22
+ "normalized": false,
23
+ "rstrip": false,
24
+ "single_word": false,
25
+ "special": true
26
+ },
27
+ "3": {
28
+ "content": "[SEP]",
29
+ "lstrip": false,
30
+ "normalized": false,
31
+ "rstrip": false,
32
+ "single_word": false,
33
+ "special": true
34
+ },
35
+ "4": {
36
+ "content": "[MASK]",
37
+ "lstrip": false,
38
+ "normalized": false,
39
+ "rstrip": false,
40
+ "single_word": false,
41
+ "special": true
42
+ }
43
+ },
44
+ "clean_up_tokenization_spaces": false,
45
+ "cls_token": "[CLS]",
46
+ "do_basic_tokenize": true,
47
+ "do_lower_case": false,
48
+ "extra_special_tokens": {},
49
+ "mask_token": "[MASK]",
50
+ "max_length": 256,
51
+ "model_max_length": 1000000000000000019884624838656,
52
+ "never_split": null,
53
+ "pad_to_multiple_of": null,
54
+ "pad_token": "[PAD]",
55
+ "pad_token_type_id": 0,
56
+ "padding_side": "right",
57
+ "sep_token": "[SEP]",
58
+ "stride": 0,
59
+ "strip_accents": null,
60
+ "tokenize_chinese_chars": true,
61
+ "tokenizer_class": "ElectraTokenizer",
62
+ "truncation_side": "right",
63
+ "truncation_strategy": "longest_first",
64
+ "unk_token": "[UNK]"
65
+ }
app/ai/weights/electra/vocab.txt ADDED
The diff for this file is too large to render. See raw diff
 
app/{dialogflow/api → api}/__init__.py RENAMED
File without changes
app/api/router.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ from fastapi import APIRouter
4
+
5
+ # from app.api.v1.dialogflow import router as v1_dialogflow
6
+ from app.api.v1 import trip, time, user, location
7
+ from core.conf import settings
8
+
9
+ # from utils.life_span import lifespan
10
+
11
+ router = APIRouter(prefix=settings.FASTAPI_API_V1_PATH)
12
+
13
+ router.include_router(trip.router)
14
+ router.include_router(time.router)
15
+ router.include_router(user.router)
16
+ router.include_router(location.router)
17
+
18
+
19
+ @router.get("/")
20
+ def get_api_v1_root():
21
+ """
22
+ Root endpoint for API v1.
23
+ """
24
+ return {"message": "Welcome to the API v1", "version": settings.FASTAPI_VERSION}
app/{dialogflow/api → api}/v1/__init__.py RENAMED
File without changes
app/api/v1/dialogflow.py ADDED
@@ -0,0 +1,1247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # from typing import Dict, List
2
+ # from fastapi import FastAPI, APIRouter, HTTPException, Request, Response # type: ignore
3
+ # from fastapi.responses import JSONResponse, RedirectResponse, HTMLResponse # type: ignore
4
+ # from datetime import datetime, timedelta
5
+ # from fastapi.templating import Jinja2Templates
6
+ # from app.services.dialog_service import dialog_service
7
+ # from app.ner.services.ner import NER
8
+ # from utils.format_data_dialog import (
9
+ # extra_time_dialogflow,
10
+ # get_weekday_name,
11
+ # find_surrounding_times,
12
+ # )
13
+
14
+ # from common.external.external_api import api
15
+ # from app.services.origin_codes import get_origin_id_and_code
16
+ # from app.dto.response import DialogFlowResponseAPI
17
+
18
+ # router = APIRouter()
19
+
20
+ # templates = Jinja2Templates(directory="templates")
21
+
22
+
23
+ # @router.post("/search/origin-city/from/office") ##
24
+ # async def search_origin_office(request: Request):
25
+ # body = await request.json()
26
+
27
+ # session_info = body.get("sessionInfo", {})
28
+ # parameters = session_info.get("parameters")
29
+
30
+ # raw_departure_city = parameters.get("departure_city", None)
31
+ # origin_office = parameters.get("origin_office")
32
+ # departure_city = None
33
+ # if origin_office and raw_departure_city is None:
34
+ # departure_city = await dialog_service.get_origin_city_from_office(origin_office)
35
+ # parameters = {"departure_city": departure_city}
36
+ # return DialogFlowResponseAPI(parameters=parameters)
37
+
38
+
39
+ # @router.post("/search/destination-city/from/office") ##
40
+ # async def search_destination_office(request: Request):
41
+ # body = await request.json()
42
+ # session_info = body.get("sessionInfo", {})
43
+ # parameters = session_info.get("parameters")
44
+
45
+ # raw_destination_city = parameters.get("destination_city", None)
46
+ # dest_office = parameters.get("dest_office")
47
+
48
+ # destination_city = None
49
+ # if dest_office and raw_destination_city is None:
50
+ # destination_city = await dialog_service.get_destination_city_from_office(
51
+ # dest_office
52
+ # )
53
+
54
+ # parameters = {"destination_city": destination_city}
55
+ # return DialogFlowResponseAPI(parameters=parameters)
56
+
57
+
58
+ # @router.post("/info/confirm")
59
+ # async def confirm(request: Request):
60
+ # body = await request.json()
61
+ # session_info = body.get("sessionInfo", {})
62
+ # parameters = (
63
+ # session_info.get("parameters", {})
64
+ # if isinstance(session_info.get("parameters"), dict)
65
+ # else {}
66
+ # )
67
+ # raw_date = parameters.get("date")
68
+ # departure_city = parameters.get("departure_city")
69
+ # destination_city = parameters.get("destination_city")
70
+ # origin_office = parameters.get("origin_office")
71
+ # dest_office = parameters.get("dest_office")
72
+ # raw_ticket_number = parameters.get("ticket_number")
73
+ # time = parameters.get("time_select")
74
+
75
+ # time = extra_time_dialogflow(time)
76
+ # parameters = {}
77
+
78
+ # if isinstance(time, list):
79
+ # parameters["is_time_ambiguous"] = True
80
+ # return DialogFlowResponseAPI(parameters=parameters)
81
+
82
+ # ticket_number = int(raw_ticket_number) if raw_ticket_number else 1
83
+ # date = dialog_service.to_datetime_from_Dialogflow(raw_date)
84
+ # date, week_day = get_weekday_name(date)
85
+ # text = [
86
+ # f"""**Thời gian:** {time} - {date} - {week_day}\n**Số vé:** {ticket_number}"""
87
+ # ]
88
+ # temp = ""
89
+ # if origin_office and dest_office:
90
+ # temp = f"""**Điểm đi:** {origin_office}\n**Điểm đến:** {dest_office}\n"""
91
+ # elif origin_office and destination_city:
92
+ # temp = f"""**Điểm đi:** {origin_office}\n**Điểm đến:** {destination_city}\n"""
93
+ # elif dest_office and departure_city:
94
+ # temp = f"""**Điểm đi:** {departure_city}\n**Điểm đến:** {dest_office}\n"""
95
+ # elif departure_city and destination_city:
96
+ # temp = f"""**Điểm đi:** {departure_city}\n**Điểm đến:** {destination_city}\n"""
97
+ # text[0] = temp + text[0]
98
+ # payload = {
99
+ # "richContent": [
100
+ # [
101
+ # {
102
+ # "type": "chips",
103
+ # "options": [{"text": "Tìm chuyến xe"}, {"text": "Không, cảm ơn"}],
104
+ # }
105
+ # ]
106
+ # ]
107
+ # }
108
+ # return DialogFlowResponseAPI(text=text, payload=payload)
109
+
110
+
111
+ # @router.post("/routes")
112
+ # async def route(request: Request):
113
+ # body = await request.json()
114
+ # session_info = body.get("sessionInfo", {})
115
+ # parameters = session_info.get("parameters")
116
+
117
+ # origin_office = parameters.get("origin_office")
118
+ # dest_office = parameters.get("dest_office")
119
+
120
+ # raw_departure_city, raw_destination_city, raw_ticket_number, raw_date, _ = (
121
+ # dialog_service.get_param_from_dialogflow(body)
122
+ # )
123
+
124
+ # ticket_count = int(raw_ticket_number) if raw_ticket_number else 1
125
+
126
+ # if raw_date is None:
127
+ # from_time, to_time = dialog_service.process_dates_to_timestamp()
128
+ # date = datetime.today().date().strftime("%m-%d-%Y")
129
+ # else:
130
+ # date = raw_date.strftime("%m-%d-%Y")
131
+ # from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date)
132
+
133
+ # origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = (
134
+ # None,
135
+ # None,
136
+ # None,
137
+ # None,
138
+ # None,
139
+ # None,
140
+ # )
141
+ # if origin_office:
142
+ # origin_id, origin_code = (
143
+ # await dialog_service.find_id_and_code_provine_by_name_office(origin_office)
144
+ # )
145
+ # origin_ids = await dialog_service.find_id_office_by_name_office(origin_office)
146
+ # elif raw_departure_city:
147
+ # origin_id, origin_code = get_origin_id_and_code(raw_departure_city)
148
+ # if dest_office:
149
+ # dest_id, dest_code = (
150
+ # await dialog_service.find_id_and_code_provine_by_name_office(dest_office)
151
+ # )
152
+ # dest_ids = await dialog_service.find_id_office_by_name_office(dest_office)
153
+ # elif raw_destination_city:
154
+ # dest_id, dest_code = get_origin_id_and_code(raw_destination_city)
155
+ # route_ids = await dialog_service.search_all_route_ids(
156
+ # origin_code=origin_code,
157
+ # from_id=origin_id,
158
+ # orign_ids=origin_ids,
159
+ # dest_code=dest_code,
160
+ # to_id=dest_id,
161
+ # dest_ids=dest_ids,
162
+ # )
163
+
164
+ # payload = {
165
+ # "from_time": from_time,
166
+ # "to_time": to_time,
167
+ # "route_ids": route_ids,
168
+ # "ticket_count": 1,
169
+ # "sort_by": ["price", "departure_time"],
170
+ # }
171
+ # try:
172
+ # respone = await api.post("/search/trips", payload=payload)
173
+ # data = respone["data"]["items"]
174
+ # total = respone["data"]["total"]
175
+ # if total > 0:
176
+ # price = data[0]["price"]
177
+ # list_raw_departure_times = sorted(
178
+ # list(set([trip["raw_departure_time"] for trip in data]))
179
+ # )
180
+ # list_raw_seat_type = list(set([trip["seat_type_name"] for trip in data]))
181
+ # seat_type_trip_string = (
182
+ # "**" + "** | **".join(map(str, list_raw_seat_type)) + "**"
183
+ # )
184
+ # schedule_time_trip = (
185
+ # "**" + "** | **".join(map(str, list_raw_departure_times)) + "**"
186
+ # )
187
+ # duration = data[0]["duration"]
188
+ # link = f"https://stag.futabus.vn/dat-ve?from={departure_code}&fromTime={date}&isReturn=false&ticketCount={ticket_count}&to={destination_code}&toTime="
189
+ # text = [
190
+ # f"Tuyến xe **{raw_departure_city}** - **{raw_destination_city}**\n \
191
+ # Loại xe: {seat_type_trip_string} \n \
192
+ # Thời gian hành trình: {duration} giờ \n \
193
+ # Giá vé: **{price}** VND"
194
+ # ]
195
+ # payload = {
196
+ # "richContent": [
197
+ # [
198
+ # {
199
+ # "type": "chips",
200
+ # "options": [
201
+ # {"text": "Tìm chuyến xe"},
202
+ # {"text": "Xem lịch trình khác"},
203
+ # {"text": "Không, cảm ơn"},
204
+ # ],
205
+ # }
206
+ # ]
207
+ # ]
208
+ # }
209
+
210
+ # return DialogFlowResponseAPI(text=text, payload=payload)
211
+
212
+ # text = [
213
+ # f"Hệ thống không tìm thấy tuyến xe **{raw_departure_city}** - **{raw_destination_city}**.\n Quý khách vui lòng thử lại với lộ trình khác hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
214
+ # ]
215
+ # payload = {
216
+ # "richContent": [
217
+ # [
218
+ # {
219
+ # "type": "chips",
220
+ # "options": [
221
+ # {"text": "Xem lịch trình khác"},
222
+ # {"text": "Không, cảm ơn"},
223
+ # ],
224
+ # }
225
+ # ]
226
+ # ]
227
+ # }
228
+ # return DialogFlowResponseAPI(text=text, payload=payload)
229
+ # except Exception as e:
230
+ # return DialogFlowResponseAPI(
231
+ # text=[
232
+ # "Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
233
+ # ]
234
+ # )
235
+
236
+
237
+ # @router.post("/price")
238
+ # async def price(request: Request):
239
+ # body = await request.json()
240
+ # session_info = body.get("sessionInfo", {})
241
+ # parameters = session_info.get("parameters")
242
+
243
+ # raw_departure_city, raw_destination_city, _, raw_date, _ = (
244
+ # dialog_service.get_param_from_dialogflow(body)
245
+ # )
246
+
247
+ # if raw_date is None:
248
+ # from_time, to_time = dialog_service.process_dates_to_timestamp()
249
+ # from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date)
250
+
251
+ # origin_office = parameters.get("origin_office")
252
+ # dest_office = parameters.get("dest_office")
253
+
254
+ # origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = (
255
+ # None,
256
+ # None,
257
+ # None,
258
+ # None,
259
+ # None,
260
+ # None,
261
+ # )
262
+ # if origin_office:
263
+ # origin_id, origin_code = (
264
+ # await dialog_service.find_id_and_code_provine_by_name_office(origin_office)
265
+ # )
266
+ # origin_ids = await dialog_service.find_id_office_by_name_office(origin_office)
267
+ # elif raw_departure_city:
268
+ # origin_id, origin_code = get_origin_id_and_code(raw_departure_city)
269
+ # if dest_office:
270
+ # dest_id, dest_code = (
271
+ # await dialog_service.find_id_and_code_provine_by_name_office(dest_office)
272
+ # )
273
+ # dest_ids = await dialog_service.find_id_office_by_name_office(dest_office)
274
+ # elif raw_destination_city:
275
+ # dest_id, dest_code = get_origin_id_and_code(raw_destination_city)
276
+ # route_ids = await dialog_service.search_all_route_ids(
277
+ # origin_code=origin_code,
278
+ # from_id=origin_id,
279
+ # orign_ids=origin_ids,
280
+ # dest_code=dest_code,
281
+ # to_id=dest_id,
282
+ # dest_ids=dest_ids,
283
+ # )
284
+
285
+ # payload = {
286
+ # "from_time": from_time,
287
+ # "to_time": to_time,
288
+ # "route_ids": route_ids,
289
+ # "ticket_count": 1,
290
+ # "sort_by": ["price", "departure_time"],
291
+ # }
292
+ # try:
293
+ # respone = await api.post("/search/trips", payload=payload)
294
+ # total = respone["data"]["total"]
295
+ # if total > 0:
296
+ # price = respone["data"]["items"][0]["price"]
297
+ # text = [
298
+ # f"Tuyến xe **{raw_departure_city}** - **{raw_destination_city}**\n"
299
+ # f"Giá vé: **{price}** VND.\n \
300
+ # Bạn có muốn đặt vé không?"
301
+ # ]
302
+ # payload = {
303
+ # "richContent": [
304
+ # [
305
+ # {
306
+ # "type": "chips",
307
+ # "options": [
308
+ # {"text": "Có, tôi muốn đặt vé"},
309
+ # {"text": "Xem giá vé tuyến xe khác"},
310
+ # {"text": "Không, cảm ơn"},
311
+ # ],
312
+ # }
313
+ # ]
314
+ # ]
315
+ # }
316
+ # return DialogFlowResponseAPI(text=text, payload=payload)
317
+ # text = [
318
+ # f"Hệ thống không tìm thấy tuyến xe **{raw_departure_city}** - **{raw_destination_city}**.\n Quý khách vui lòng thử lại với lộ trình khác hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
319
+ # ]
320
+ # payload = {
321
+ # "richContent": [
322
+ # [
323
+ # {
324
+ # "type": "chips",
325
+ # "options": [
326
+ # {"text": "Xem giá vé lịch trình khác"},
327
+ # {"text": "Không, cảm ơn"},
328
+ # ],
329
+ # }
330
+ # ]
331
+ # ]
332
+ # }
333
+
334
+ # return DialogFlowResponseAPI(text=text, payload=payload)
335
+ # except:
336
+ # return DialogFlowResponseAPI(
337
+ # text=[
338
+ # "Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
339
+ # ]
340
+ # )
341
+
342
+
343
+ # @router.post("/trip/list") ##
344
+ # async def get_trip_list(request: Request) -> Response:
345
+ # body = await request.json()
346
+ # session_info = body.get("sessionInfo", {})
347
+ # parameters = session_info.get("parameters")
348
+
349
+ # raw_departure_city, raw_destination_city, raw_ticket_number, raw_date, _ = (
350
+ # dialog_service.get_param_from_dialogflow(body)
351
+ # )
352
+
353
+ # origin_office = parameters.get("origin_office")
354
+ # dest_office = parameters.get("dest_office")
355
+ # time = parameters.get("time_select")
356
+
357
+ # time = extra_time_dialogflow(time)
358
+
359
+ # if isinstance(time, list):
360
+ # parameters = {"is_time_ambiguous": True}
361
+ # return DialogFlowResponseAPI(parameters=parameters)
362
+
363
+ # from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date)
364
+
365
+ # ticket_count = int(raw_ticket_number) if raw_ticket_number else 1
366
+
367
+ # origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = (
368
+ # None,
369
+ # None,
370
+ # None,
371
+ # None,
372
+ # None,
373
+ # None,
374
+ # )
375
+ # if origin_office:
376
+ # origin_id, origin_code = (
377
+ # await dialog_service.find_id_and_code_provine_by_name_office(origin_office)
378
+ # )
379
+ # origin_ids = await dialog_service.find_id_office_by_name_office(origin_office)
380
+ # elif raw_departure_city:
381
+ # origin_id, origin_code = get_origin_id_and_code(raw_departure_city)
382
+ # if dest_office:
383
+ # dest_id, dest_code = (
384
+ # await dialog_service.find_id_and_code_provine_by_name_office(dest_office)
385
+ # )
386
+ # dest_ids = await dialog_service.find_id_office_by_name_office(dest_office)
387
+ # elif raw_destination_city:
388
+ # dest_id, dest_code = get_origin_id_and_code(raw_destination_city)
389
+ # route_ids = await dialog_service.search_all_route_ids(
390
+ # origin_code=origin_code,
391
+ # from_id=origin_id,
392
+ # orign_ids=origin_ids,
393
+ # dest_code=dest_code,
394
+ # to_id=dest_id,
395
+ # dest_ids=dest_ids,
396
+ # )
397
+
398
+ # try:
399
+ # data = await dialog_service.search_trip(
400
+ # from_time, to_time, route_ids, ticket_count
401
+ # )
402
+ # if len(data) > 0:
403
+
404
+ # if origin_office and dest_office:
405
+ # trip_by_time_office = dialog_service.get_trip_by_time_and_office_id(
406
+ # data, time, origin_ids, dest_ids
407
+ # )
408
+ # # Nếu có chuyến xe theo thời gian và văn phòng chỉ định
409
+ # if trip_by_time_office:
410
+ # parameters = {"is_valid_trip": True, "trip": trip_by_time_office}
411
+ # return DialogFlowResponseAPI(parameters=parameters)
412
+
413
+ # # Nếu không có chuyến xe theo thời gian và văn phòng chỉ định
414
+ # # Danh sách chuyến xe khớp với văn phòng đón hoặc văn phòng trả
415
+ # # data_by_office = dialog_service.get_all_trip_by_office(data, origin_ids, dest_ids)
416
+ # # elif origin_office:
417
+ # # data_by_office = dialog_service.get_all_trip_by_office(data,origin_id=origin_ids)
418
+ # # elif dest_office:
419
+ # # data_by_office = dialog_service.get_all_trip_by_office(data, dest_id=dest_ids)
420
+ # # else:
421
+
422
+ # data_by_office = data
423
+ # # Tìm 4 chuyến xe gần thời gian chỉ định nhất
424
+ # trip_surrounding_time = dialog_service.get_surrounding_trip(
425
+ # data_by_office, time, num_trip=4
426
+ # )
427
+ # trip_dialogflow = []
428
+
429
+ # for trip in trip_surrounding_time:
430
+ # if ticket_count <= trip["empty_seat_quantity"]:
431
+ # trip_dialogflow.append(
432
+ # {
433
+ # "trip_id": trip["id"],
434
+ # "route": f'{trip["raw_departure_time"]} | {trip["route"]["origin_hub_name"]} => {trip["route"]["dest_hub_name"]}',
435
+ # }
436
+ # )
437
+ # text = [
438
+ # "Quý khách vui lòng lựa chọn chuyến xe\n"
439
+ # + "\n".join(
440
+ # f'{i+1}. {trip["route"]}' for i, trip in enumerate(trip_dialogflow)
441
+ # )
442
+ # ]
443
+ # payload = {
444
+ # "richContent": [
445
+ # [
446
+ # {
447
+ # "type": "chips",
448
+ # "options": [
449
+ # {"text": trip["route"]} for trip in (trip_dialogflow)
450
+ # ],
451
+ # }
452
+ # ]
453
+ # ]
454
+ # }
455
+ # parameters = {
456
+ # "trip_select": trip_dialogflow,
457
+ # "trips": trip_surrounding_time,
458
+ # }
459
+ # return DialogFlowResponseAPI(
460
+ # text=text, payload=payload, parameters=parameters
461
+ # )
462
+
463
+ # text = [
464
+ # f"Hệ thống không tìm thấy tuyến xe **{raw_departure_city}** - **{raw_destination_city}**.\n Quý khách vui lòng thử lại với lộ trình khác hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
465
+ # ]
466
+ # payload = {
467
+ # "richContent": [
468
+ # [
469
+ # {
470
+ # "type": "chips",
471
+ # "options": [
472
+ # {"text": "Xem tuyến xe khác"},
473
+ # {"text": "Không, cảm ơn"},
474
+ # ],
475
+ # }
476
+ # ]
477
+ # ]
478
+ # }
479
+
480
+ # return DialogFlowResponseAPI(text=text, payload=payload)
481
+ # except Exception as e:
482
+ # print(e)
483
+ # return JSONResponse(
484
+ # {
485
+ # "fulfillment_response": {
486
+ # "messages": [
487
+ # {
488
+ # "text": {
489
+ # "text": [
490
+ # "Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
491
+ # ]
492
+ # }
493
+ # }
494
+ # ]
495
+ # },
496
+ # }
497
+ # )
498
+
499
+
500
+ # # Không xài
501
+ # @router.post("/trip/route/list")
502
+ # async def booking_trip(request: Request) -> Response:
503
+ # body = await request.json()
504
+ # session_info = body.get("sessionInfo", {})
505
+ # parameters = session_info.get("parameters")
506
+
507
+ # (
508
+ # raw_departure_city,
509
+ # raw_destination_city,
510
+ # raw_ticket_number,
511
+ # raw_date,
512
+ # raw_time_of_day,
513
+ # ) = dialog_service.get_param_from_dialogflow(body)
514
+
515
+ # origin_office = parameters.get("origin_office")
516
+ # dest_office = parameters.get("dest_office")
517
+
518
+ # from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date)
519
+
520
+ # ticket_count = int(raw_ticket_number) if raw_ticket_number else 1
521
+
522
+ # origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = (
523
+ # None,
524
+ # None,
525
+ # None,
526
+ # None,
527
+ # None,
528
+ # None,
529
+ # )
530
+ # if origin_office:
531
+ # origin_id, origin_code = (
532
+ # await dialog_service.find_id_and_code_provine_by_name_office(origin_office)
533
+ # )
534
+ # origin_ids = await dialog_service.find_id_office_by_name_office(origin_office)
535
+ # elif raw_departure_city:
536
+ # origin_id, origin_code = get_origin_id_and_code(raw_departure_city)
537
+ # if dest_office:
538
+ # dest_id, dest_code = (
539
+ # await dialog_service.find_id_and_code_provine_by_name_office(dest_office)
540
+ # )
541
+ # dest_ids = await dialog_service.find_id_office_by_name_office(dest_office)
542
+ # elif raw_destination_city:
543
+ # dest_id, dest_code = get_origin_id_and_code(raw_destination_city)
544
+ # route_ids = await dialog_service.search_all_route_ids(
545
+ # origin_code=origin_code,
546
+ # from_id=origin_id,
547
+ # orign_ids=origin_ids,
548
+ # dest_code=dest_code,
549
+ # to_id=dest_id,
550
+ # dest_ids=dest_ids,
551
+ # )
552
+ # try:
553
+ # data = await dialog_service.search_trip(
554
+ # from_time, to_time, route_ids, ticket_count
555
+ # )
556
+ # if len(data) > 0:
557
+ # trips = []
558
+ # routes_name = []
559
+ # for trip in data:
560
+ # if ticket_count <= trip["empty_seat_quantity"]:
561
+ # route = f"{trip['route']['origin_hub_name']} => {trip['route']['dest_hub_name']}"
562
+ # if trip["route"] and route not in routes_name:
563
+ # routes_name.append(
564
+ # f"{trip['route']['origin_hub_name']} => {trip['route']['dest_hub_name']}"
565
+ # )
566
+ # trips.append(
567
+ # {
568
+ # "route_name": f"{trip['route']['origin_hub_name']} => {trip['route']['dest_hub_name']}",
569
+ # "route_id": trip["route_id"],
570
+ # "id": trip["id"],
571
+ # "departure_date": trip["raw_departure_date"],
572
+ # "departure_time": trip["raw_departure_time"],
573
+ # "kind": trip["seat_type_name"],
574
+ # "way_id": trip["way_id"],
575
+ # }
576
+ # )
577
+ # text = [
578
+ # "Quý khách vui lòng lựa chọn chuyến xe\n"
579
+ # + "\n".join(f"{i+1}. {name}" for i, name in enumerate(routes_name))
580
+ # ]
581
+ # payload = {
582
+ # "richContent": [
583
+ # [
584
+ # {
585
+ # "type": "chips",
586
+ # "options": [{"text": name} for name in (routes_name)],
587
+ # }
588
+ # ]
589
+ # ]
590
+ # }
591
+ # parameters = {"trip_list": trips, "routes_name": routes_name}
592
+ # return DialogFlowResponseAPI(
593
+ # text=text, payload=payload, parameters=parameters
594
+ # )
595
+
596
+ # text = [
597
+ # f"Hệ thống không tìm thấy tuyến xe **{raw_departure_city}** - **{raw_destination_city}**.\n Quý khách vui lòng thử lại với lộ trình khác hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
598
+ # ]
599
+ # payload = {
600
+ # "richContent": [
601
+ # [
602
+ # {
603
+ # "type": "chips",
604
+ # "options": [
605
+ # {"text": "Xem tuyến xe khác"},
606
+ # {"text": "Không, cảm ơn"},
607
+ # ],
608
+ # }
609
+ # ]
610
+ # ]
611
+ # }
612
+
613
+ # return DialogFlowResponseAPI(text=text, payload=payload)
614
+ # except Exception as e:
615
+ # print(e)
616
+ # return JSONResponse(
617
+ # {
618
+ # "fulfillment_response": {
619
+ # "messages": [
620
+ # {
621
+ # "text": {
622
+ # "text": [
623
+ # "Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
624
+ # ]
625
+ # }
626
+ # }
627
+ # ]
628
+ # },
629
+ # }
630
+ # )
631
+
632
+
633
+ # @router.post("/trip/check-trip")
634
+ # async def is_valid_select_trip(request: Request) -> Response:
635
+ # body = await request.json()
636
+ # raw_input = body.get("text", "")
637
+ # session_info = body.get("sessionInfo", {})
638
+ # parameters = session_info.get("parameters")
639
+ # trip_select: list[dict[str, any]] = parameters.get("trip_select")
640
+ # trips: list[dict[str, any]] = parameters.get("trips")
641
+
642
+ # if trip_select is None and trips is None:
643
+ # return DialogFlowResponseAPI()
644
+
645
+ # if raw_input:
646
+ # raw_input = raw_input.strip()
647
+ # for item in trip_select:
648
+ # if item["route"] == raw_input:
649
+ # id = int(item["trip_id"])
650
+ # trip = dialog_service.get_trip_by_id(id, trips)
651
+ # parameters = {"is_valid_trip": True, "trip": trip}
652
+ # return DialogFlowResponseAPI(parameters=parameters)
653
+
654
+ # parameters = {
655
+ # "is_valid_trip": False,
656
+ # }
657
+ # return DialogFlowResponseAPI(parameters=parameters)
658
+
659
+
660
+ # @router.post("/trip/check-time-ambiguous") #
661
+ # async def check_time_ambiguous(request: Request) -> Response:
662
+ # try:
663
+ # body = await request.json()
664
+ # session_info = body.get("sessionInfo", {})
665
+ # parameters = session_info.get("parameters")
666
+ # time = parameters.get("time_select")
667
+
668
+ # parameters = {}
669
+
670
+ # time = extra_time_dialogflow(time)
671
+
672
+ # if isinstance(time, list) or time is None:
673
+ # parameters["is_time_ambiguous"] = True
674
+ # else:
675
+ # parameters["is_time_ambiguous"] = None
676
+
677
+ # return DialogFlowResponseAPI(parameters=parameters)
678
+
679
+ # except Exception as e:
680
+ # print(e)
681
+ # return DialogFlowResponseAPI(
682
+ # text=[
683
+ # "Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
684
+ # ]
685
+ # )
686
+
687
+
688
+ # @router.post("/trip/select-time-ambiguous") #
689
+ # async def select_time(request: Request) -> Response:
690
+ # try:
691
+ # body = await request.json()
692
+ # session_info = body.get("sessionInfo", {})
693
+ # parameters = session_info.get("parameters")
694
+ # time = parameters.get("time_select")
695
+
696
+ # parameters = {}
697
+
698
+ # time = extra_time_dialogflow(time)
699
+
700
+ # text = []
701
+
702
+ # if time is None:
703
+ # text = ["Quý khách vui lòng chỉ định thời gian đi cụ thể trong ngày?"]
704
+
705
+ # elif isinstance(time, list):
706
+ # text = [f"Quý khách dự định đi vào lúc"]
707
+ # payload = {
708
+ # "richContent": [
709
+ # [{"type": "chips", "options": [{"text": item} for item in (time)]}]
710
+ # ]
711
+ # }
712
+ # else:
713
+ # parameters["is_time_ambiguous"] = None
714
+
715
+ # return DialogFlowResponseAPI(text=text, payload=payload, parameters=parameters)
716
+
717
+ # except Exception as e:
718
+ # print(e)
719
+ # return DialogFlowResponseAPI(
720
+ # text=[
721
+ # "Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
722
+ # ]
723
+ # )
724
+
725
+
726
+ # # Không xài
727
+ # @router.post("/trip/time-trip-list")
728
+ # async def time_trip(request: Request) -> Response:
729
+ # try:
730
+ # body = await request.json()
731
+ # session_info = body.get("sessionInfo", {})
732
+ # parameters = session_info.get("parameters")
733
+ # trip_list: list[Dict[str, any]] = parameters.get("trip_list", [])
734
+ # raw_route_id = parameters.get("route_id")
735
+ # route_name = parameters.get("route_name", "")
736
+ # raw_time = parameters.get("time_select")
737
+ # time_list = parameters.get("time_list", [])
738
+ # is_has_time = parameters.get("is_has_time", False)
739
+
740
+ # route_id = int(raw_route_id) if raw_route_id else None
741
+
742
+ # text = []
743
+ # quick_time_reply = []
744
+ # parameters = {}
745
+
746
+ # if raw_time:
747
+ # time = extra_time_dialogflow(raw_time)
748
+ # quick_time_reply = time_list
749
+
750
+ # if isinstance(time, list):
751
+ # parameters["time_select"] = None
752
+ # parameters["is_time_ambiguous"] = True
753
+ # text = [f"Quý khách dự định đi vào lúc"]
754
+ # payload = {
755
+ # "richContent": [
756
+ # [
757
+ # {
758
+ # "type": "chips",
759
+ # "options": [{"text": item} for item in (time)],
760
+ # }
761
+ # ]
762
+ # ]
763
+ # }
764
+ # return DialogFlowResponseAPI(
765
+ # text=text, payload=payload, parameters=parameters
766
+ # )
767
+ # else:
768
+ # if is_has_time:
769
+ # text = [
770
+ # f"Quý khách lựa chọn thời gian chuyến {route_name}\n"
771
+ # + " | ".join([item["time"] for item in quick_time_reply])
772
+ # ]
773
+ # parameters["is_has_time"] = None
774
+ # else:
775
+ # parameters["is_has_time"] = True
776
+ # return DialogFlowResponseAPI(text=text, parameters=parameters)
777
+ # else:
778
+ # for trip in trip_list:
779
+ # if (trip["route_id"]) == route_id:
780
+ # time_list.append(
781
+ # {"time": trip["departure_time"], "trip_id": trip["id"]}
782
+ # )
783
+ # quick_time_reply = time_list
784
+ # text = [
785
+ # f"Quý khách lựa chọn thời gian chuyến {route_name}\n"
786
+ # + " | ".join([item["time"] for item in quick_time_reply])
787
+ # ]
788
+
789
+ # parameters["time_list"] = time_list
790
+ # payload = {
791
+ # "richContent": [
792
+ # [
793
+ # {
794
+ # "type": "chips",
795
+ # "options": [
796
+ # {"text": time["time"]} for time in (quick_time_reply)
797
+ # ],
798
+ # }
799
+ # ]
800
+ # ]
801
+ # }
802
+ # return DialogFlowResponseAPI(text=text, payload=payload, parameters=parameters)
803
+ # except Exception as e:
804
+ # print(e)
805
+ # return DialogFlowResponseAPI(
806
+ # text=[
807
+ # "Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
808
+ # ]
809
+ # )
810
+
811
+
812
+ # # Không xài
813
+ # @router.post("/trip/check-time-select")
814
+ # async def is_valid_select_time(request: Request) -> Response:
815
+ # try:
816
+ # body = await request.json()
817
+ # session_info = body.get("sessionInfo", {})
818
+ # parameters = session_info.get("parameters")
819
+ # trip_list: list[Dict[str, any]] = parameters.get("trip_list", [])
820
+ # raw_route_id = parameters.get("route_id")
821
+
822
+ # (
823
+ # raw_departure_city,
824
+ # raw_destination_city,
825
+ # raw_ticket_number,
826
+ # raw_date,
827
+ # raw_time_of_day,
828
+ # ) = dialog_service.get_param_from_dialogflow(body)
829
+ # route_id = int(raw_route_id) if raw_route_id else None
830
+
831
+ # from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date)
832
+ # ticket_count = int(raw_ticket_number) if raw_ticket_number else 1
833
+
834
+ # origin_office = parameters.get("origin_office")
835
+ # dest_office = parameters.get("dest_office")
836
+
837
+ # origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = (
838
+ # None,
839
+ # None,
840
+ # None,
841
+ # None,
842
+ # None,
843
+ # None,
844
+ # )
845
+ # if origin_office:
846
+ # origin_id, origin_code = (
847
+ # await dialog_service.find_id_and_code_provine_by_name_office(
848
+ # origin_office
849
+ # )
850
+ # )
851
+ # origin_ids = await dialog_service.find_id_office_by_name_office(
852
+ # origin_office
853
+ # )
854
+ # elif raw_departure_city:
855
+ # origin_id, origin_code = get_origin_id_and_code(raw_departure_city)
856
+ # if dest_office:
857
+ # dest_id, dest_code = (
858
+ # await dialog_service.find_id_and_code_provine_by_name_office(
859
+ # dest_office
860
+ # )
861
+ # )
862
+ # dest_ids = await dialog_service.find_id_office_by_name_office(dest_office)
863
+ # elif raw_destination_city:
864
+ # dest_id, dest_code = get_origin_id_and_code(raw_destination_city)
865
+ # route_ids = await dialog_service.search_all_route_ids(
866
+ # origin_code=origin_code,
867
+ # from_id=origin_id,
868
+ # orign_ids=origin_ids,
869
+ # dest_code=dest_code,
870
+ # to_id=dest_id,
871
+ # dest_ids=dest_ids,
872
+ # )
873
+
874
+ # time_list: list[Dict[str, any]] = parameters.get("time_list", [])
875
+ # time_select = parameters.get("time_select")
876
+ # route_name = parameters.get("route_name")
877
+ # is_has_time = parameters.get("is_has_time")
878
+
879
+ # if len(time_list) == 0:
880
+ # for trip in trip_list:
881
+ # if (trip["route_id"]) == route_id:
882
+ # time_list.append(
883
+ # {"time": trip["departure_time"], "trip_id": trip["id"]}
884
+ # )
885
+
886
+ # if time_select:
887
+ # time_select = extra_time_dialogflow(time_select)
888
+ # for time in time_list:
889
+ # if time_select == time["time"]:
890
+ # id = int(time["trip_id"])
891
+ # trip = await dialog_service.search_trip_by_id(
892
+ # id, from_time, to_time, route_ids, ticket_count
893
+ # )
894
+ # departure_date = trip["raw_departure_date"]
895
+ # parameters = {
896
+ # "is_valid_time": True,
897
+ # "departure_time": time_select,
898
+ # "departure_date": departure_date,
899
+ # "trip": trip,
900
+ # }
901
+ # text = [
902
+ # f"Quý khách chọn chuyến **{time_select}** | **{route_name}**"
903
+ # ]
904
+ # return DialogFlowResponseAPI(text=text, parameters=parameters)
905
+ # if is_has_time:
906
+ # time_list = find_surrounding_times(time_list, time_select)
907
+ # parameters = {"time_list": time_list, "is_valid_time": False}
908
+
909
+ # return DialogFlowResponseAPI(parameters=parameters)
910
+
911
+ # parameters = {"time_select": None, "is_valid_time": False}
912
+ # text = []
913
+ # return DialogFlowResponseAPI(text=text, parameters=parameters)
914
+ # except Exception as e:
915
+ # print(e)
916
+ # return DialogFlowResponseAPI(
917
+ # text=[
918
+ # "Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
919
+ # ]
920
+ # )
921
+
922
+
923
+ # @router.post("/trip/seats") #
924
+ # async def seats_trip(request: Request) -> Response:
925
+ # try:
926
+ # body = await request.json()
927
+ # session_info = body.get("sessionInfo", {})
928
+ # parameters = session_info.get("parameters")
929
+
930
+ # trip = parameters.get("trip", None)
931
+ # route_id = int(trip.get("route_id")) if trip.get("route_id") else None
932
+ # trip_id = int(trip.get("id")) if trip.get("id") else None
933
+
934
+ # departure_date: str = trip.get("raw_departure_date")
935
+ # departure_time: str = trip.get("raw_departure_time")
936
+ # kind: str = trip.get("seat_type_name")
937
+
938
+ # seats = await dialog_service.seats_trip(
939
+ # route_id, trip_id, departure_date, departure_time, kind
940
+ # )
941
+ # seats_empty = [seat for seat in seats if seat["bookStatus"] == 0]
942
+ # seats_empty.sort(key=lambda x: x["chair"])
943
+ # text = ["Quý khách vui lòng chọn ghế"]
944
+ # payload = {}
945
+ # if seats_empty:
946
+ # payload = {
947
+ # "richContent": [
948
+ # [
949
+ # {
950
+ # "type": "chips",
951
+ # "options": [
952
+ # {"text": seat["chair"]} for seat in (seats_empty)
953
+ # ],
954
+ # }
955
+ # ]
956
+ # ]
957
+ # }
958
+ # else:
959
+ # text = ["Không còn ghế trống. Quý khách vui lòng lựa chọn chuyến khác"]
960
+ # parameters = {"seat_list": seats}
961
+ # return DialogFlowResponseAPI(text=text, payload=payload, parameters=parameters)
962
+ # except Exception as e:
963
+ # print(e)
964
+ # return DialogFlowResponseAPI(
965
+ # text=[
966
+ # "Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
967
+ # ]
968
+ # )
969
+
970
+
971
+ # @router.post("/trip/check-seat-select") #
972
+ # async def is_valid_select_seat(request: Request) -> Response:
973
+ # try:
974
+ # body = await request.json()
975
+ # session_info = body.get("sessionInfo", {})
976
+ # parameters = session_info.get("parameters")
977
+
978
+ # trip: list[dict[str, any]] = parameters.get("trip", None)
979
+
980
+ # route_id = int(trip.get("route_id")) if trip.get("route_id") else None
981
+
982
+ # trip_id = int(trip.get("id")) if trip.get("id") else None
983
+
984
+ # departure_date: str = trip.get("raw_departure_date")
985
+
986
+ # departure_time: str = trip.get("raw_departure_time")
987
+
988
+ # kind: str = trip.get("seat_type_name")
989
+
990
+ # seat: str = parameters.get("seat")
991
+
992
+ # is_valid = await dialog_service.is_valid_select_seat(
993
+ # seat, route_id, trip_id, departure_date, departure_time, kind
994
+ # )
995
+ # if is_valid:
996
+ # parameters = {"is_valid_seat": True, "seat": seat}
997
+ # text = [f"Quý khách chọn ghế **{seat.upper()}**"]
998
+ # else:
999
+ # parameters = {"is_valid_seat": False, "seat": None}
1000
+ # text = [
1001
+ # f"Ghế **{seat.upper()}** không hợp lệ. Quý khách vui lòng chọn ghế khác"
1002
+ # ]
1003
+ # return DialogFlowResponseAPI(text=text, parameters=parameters)
1004
+
1005
+ # except Exception as e:
1006
+ # return DialogFlowResponseAPI(
1007
+ # text=[
1008
+ # "Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."
1009
+ # ]
1010
+ # )
1011
+
1012
+
1013
+ # @router.post("/trip/check-exist-user-info") #
1014
+ # async def check_exist_user_info(request: Request) -> Response:
1015
+ # body = await request.json()
1016
+ # session_info = body.get("sessionInfo", {})
1017
+ # parameters = session_info.get("parameters")
1018
+
1019
+ # is_exist_user_info = await dialog_service.check_exist_user_info()
1020
+
1021
+ # user_info = {}
1022
+
1023
+ # if is_exist_user_info:
1024
+ # user_info = await dialog_service.get_user_info()
1025
+
1026
+ # user_name = user_info.get("user_name")
1027
+ # phone_number = user_info.get("phone_number")
1028
+ # email = user_info.get("email")
1029
+
1030
+ # parameters = {
1031
+ # "is_user_exist": is_exist_user_info,
1032
+ # "user_name": user_name,
1033
+ # "phone_number": phone_number,
1034
+ # "email": email,
1035
+ # }
1036
+
1037
+ # return DialogFlowResponseAPI(parameters=parameters)
1038
+
1039
+
1040
+ # @router.post("/trip/extract-user-name") #
1041
+ # async def get_user_name(request: Request) -> Response:
1042
+ # body = await request.json()
1043
+ # session_info = body.get("sessionInfo", {})
1044
+ # parameters = session_info.get("parameters")
1045
+
1046
+ # raw_text_user_name = body.get("text", "")
1047
+
1048
+ # ner: NER = request.app.state.ner
1049
+
1050
+ # user_name = await dialog_service.extract_user_name(text=raw_text_user_name, ner=ner)
1051
+
1052
+ # parameters = {"user_name": user_name}
1053
+
1054
+ # return DialogFlowResponseAPI(parameters=parameters)
1055
+
1056
+
1057
+ # @router.post("/trip/stop/pickup") #
1058
+ # async def pickup(request: Request) -> Response:
1059
+ # body = await request.json()
1060
+ # session_info = body.get("sessionInfo", {})
1061
+ # parameters = session_info.get("parameters")
1062
+
1063
+ # trip: list[dict[str, any]] = parameters.get("trip", {})
1064
+
1065
+ # route_id = int(trip.get("route_id")) if trip.get("route_id") else None
1066
+
1067
+ # way_id = int(trip.get("way_id")) if trip.get("way_id") else None
1068
+
1069
+ # pickup_list = await dialog_service.pickup_list(route_id, way_id)
1070
+
1071
+ # text = ["Quý khách vui lòng chọn điểm đón"]
1072
+ # payload = {
1073
+ # "richContent": [
1074
+ # [
1075
+ # {
1076
+ # "type": "chips",
1077
+ # "options": [{"text": pickup["name"]} for pickup in (pickup_list)],
1078
+ # }
1079
+ # ]
1080
+ # ]
1081
+ # }
1082
+ # parameters = {"pickup_list": pickup_list}
1083
+
1084
+ # return DialogFlowResponseAPI(text=text, payload=payload, parameters=parameters)
1085
+
1086
+
1087
+ # @router.post("/trip/check-pickup-select")
1088
+ # async def is_valid_select_pickup(request: Request) -> Response:
1089
+ # body = await request.json()
1090
+ # session_info = body.get("sessionInfo", {})
1091
+ # parameters = session_info.get("parameters")
1092
+
1093
+ # trip: list[dict[str, any]] = parameters.get("trip", {})
1094
+
1095
+ # route_id = int(trip.get("route_id")) if trip.get("route_id") else None
1096
+
1097
+ # way_id = int(trip.get("way_id")) if trip.get("way_id") else None
1098
+
1099
+ # raw_input = body.get("text", "")
1100
+ # pickup = raw_input.strip()
1101
+
1102
+ # is_valid = await dialog_service.is_valid_pickup(pickup, route_id, way_id)
1103
+
1104
+ # if is_valid:
1105
+ # parameters = {"is_valid_pickup": True, "pick_up": pickup}
1106
+ # text = [f"Quý khách chọn điểm đón **{pickup}**"]
1107
+ # else:
1108
+ # parameters = {
1109
+ # "is_valid_pickup": False,
1110
+ # }
1111
+ # # text = [f"Điểm đón không hợp lệ. Quý khách vui lòng chọn điểm đón khác"]
1112
+ # text = []
1113
+
1114
+ # return DialogFlowResponseAPI(text=text, parameters=parameters)
1115
+
1116
+
1117
+ # @router.post("/trip/stop/dropoff")
1118
+ # async def dropoff(request: Request) -> Response:
1119
+ # body = await request.json()
1120
+ # session_info = body.get("sessionInfo", {})
1121
+ # parameters = session_info.get("parameters")
1122
+
1123
+ # trip: list[dict[str, any]] = parameters.get("trip", {})
1124
+
1125
+ # route_id = int(trip.get("route_id")) if trip.get("route_id") else None
1126
+
1127
+ # way_id = int(trip.get("way_id")) if trip.get("way_id") else None
1128
+
1129
+ # dropoff_list = await dialog_service.dropoff_list(route_id, way_id)
1130
+
1131
+ # text = ["Quý khách vui lòng chọn điểm trả khách"]
1132
+ # payload = {
1133
+ # "richContent": [
1134
+ # [
1135
+ # {
1136
+ # "type": "chips",
1137
+ # "options": [
1138
+ # {"text": dropoff["name"]} for dropoff in (dropoff_list)
1139
+ # ],
1140
+ # }
1141
+ # ]
1142
+ # ]
1143
+ # }
1144
+ # parameters = {"dropoff_list": dropoff_list}
1145
+
1146
+ # return DialogFlowResponseAPI(text=text, payload=payload, parameters=parameters)
1147
+
1148
+
1149
+ # @router.post("/trip/check-dropoff-select")
1150
+ # async def is_valid_select_dropoff(request: Request) -> Response:
1151
+ # body = await request.json()
1152
+ # session_info = body.get("sessionInfo", {})
1153
+ # parameters = session_info.get("parameters")
1154
+
1155
+ # trip: list[dict[str, any]] = parameters.get("trip", {})
1156
+
1157
+ # route_id = int(trip.get("route_id")) if trip.get("route_id") else None
1158
+
1159
+ # way_id = int(trip.get("way_id")) if trip.get("way_id") else None
1160
+
1161
+ # raw_input = body.get("text", "")
1162
+ # dropoff = raw_input.strip()
1163
+
1164
+ # is_valid = await dialog_service.is_valid_dropoff(dropoff, route_id, way_id)
1165
+
1166
+ # if is_valid:
1167
+ # parameters = {"is_valid_dropoff": True, "drop_off": dropoff}
1168
+ # text = [f"Quý khách chọn điểm trả khách **{dropoff}**"]
1169
+ # else:
1170
+ # parameters = {
1171
+ # "is_valid_dropoff": False,
1172
+ # }
1173
+ # # text = [f"Điểm trả khách không hợp lệ. Quý khách vui lòng chọn điểm trả khách khác"]
1174
+ # text = []
1175
+
1176
+ # return DialogFlowResponseAPI(text=text, parameters=parameters)
1177
+
1178
+
1179
+ # @router.post("/ticket/info")
1180
+ # async def response_ticket_info(request: Request) -> Response:
1181
+ # body = await request.json()
1182
+ # session_info = body.get("sessionInfo", {})
1183
+ # parameters = session_info.get("parameters")
1184
+
1185
+ # raw_user_name = parameters.get("user_name")
1186
+ # user_name = raw_user_name["name"] if raw_user_name else None
1187
+
1188
+ # phone_number = parameters.get("phone_number")
1189
+
1190
+ # email = parameters.get("email")
1191
+
1192
+ # seat: str = parameters.get("seat")
1193
+
1194
+ # pickup = parameters.get("pick_up")
1195
+
1196
+ # dropoff = parameters.get("drop_off")
1197
+
1198
+ # trip = parameters.get("trip", {})
1199
+
1200
+ # route_name = trip["route"]["name"]
1201
+
1202
+ # time = trip["raw_departure_time"]
1203
+
1204
+ # date = trip["raw_departure_date"]
1205
+
1206
+ # price = int(trip.get("price")) if trip.get("price") else None
1207
+
1208
+ # text = [
1209
+ # f" \
1210
+ # **Thông tin hành khách**\n\
1211
+ # **Họ và tên** {user_name} \n\
1212
+ # **Số điện thoại** {phone_number}\n\
1213
+ # **Email** {email} \n\
1214
+ # **Thông tin lượt đi**\n\
1215
+ # **Tuyến xe** {route_name} \n\
1216
+ # **Thời gian xuất bến** {time} {date} \n\
1217
+ # **Số ghế** {seat.upper()} \n\
1218
+ # **Điểm lên xe** {pickup} \n\
1219
+ # **Điểm trả khách** {dropoff} \n\
1220
+ # **Tổng tiền lượt đi** {price} VND \
1221
+ # "
1222
+ # ]
1223
+
1224
+ # payload = {
1225
+ # "richContent": [
1226
+ # [
1227
+ # {
1228
+ # "type": "chips",
1229
+ # "options": [{"text": "Đặt vé"}, {"text": "Không, cảm ơn"}],
1230
+ # }
1231
+ # ]
1232
+ # ]
1233
+ # }
1234
+
1235
+ # return DialogFlowResponseAPI(text=text, payload=payload)
1236
+
1237
+
1238
+ # @router.get("/")
1239
+ # def home():
1240
+ # return "Hello World!"
1241
+
1242
+
1243
+ # @router.get("/chatbot", response_class=HTMLResponse)
1244
+ # async def index(request: Request):
1245
+ # return templates.TemplateResponse(
1246
+ # "index.html", {"request": request, "title": "Dawi Chatbot"}
1247
+ # )
app/api/v1/location.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from loguru import logger
2
+ from fastapi import APIRouter, Depends, HTTPException, Request
3
+ from dependency_injector.wiring import inject, Provide
4
+
5
+
6
+ from app.dependencies.containers import Container
7
+ from app.dto.request import LocationRequest
8
+ from app.dto.response import DialogFlowResponseAPI
9
+ from app.services.location_service import LocationService
10
+
11
+
12
+ router = APIRouter(prefix="/location", tags=["Location"])
13
+
14
+
15
+ @router.post("/origin-city-from-office")
16
+ @inject
17
+ async def get_origin_city_from_office(
18
+ request: Request,
19
+ service: LocationService = Depends(Provide[Container.location_service]),
20
+ ):
21
+ body = await request.json()
22
+ params = body.get("sessionInfo", {}).get("parameters", {})
23
+ try:
24
+ data = LocationRequest(**params)
25
+ except Exception:
26
+ logger.error("Invalid location parameters", exc_info=True)
27
+ raise HTTPException(status_code=400, detail="Invalid location parameters")
28
+
29
+ if data.origin_office and not data.departure_city:
30
+ _, _, city = await service.get_city_by_office(
31
+ office_name=data.origin_office, role="origin"
32
+ )
33
+ return DialogFlowResponseAPI(parameters={"departure_city": city})
34
+
35
+ return DialogFlowResponseAPI(parameters={"departure_city": data.departure_city})
36
+
37
+
38
+ @router.post("/dest-city-from-office")
39
+ @inject
40
+ async def get_dest_city_from_office(
41
+ request: Request,
42
+ service: LocationService = Depends(Provide[Container.location_service]),
43
+ ):
44
+ body = await request.json()
45
+ params = body.get("sessionInfo", {}).get("parameters", {})
46
+ try:
47
+ data = LocationRequest.model_validate(params)
48
+ except Exception:
49
+ logger.error("Invalid location parameters", exc_info=True)
50
+ raise HTTPException(status_code=400, detail="Invalid location parameters")
51
+
52
+ if data.dest_office and not data.destination_city:
53
+ city = await service.get_city_by_office(
54
+ office_name=data.dest_office, role="dest"
55
+ )
56
+ return DialogFlowResponseAPI(parameters={"destination_city": city})
57
+
58
+ return DialogFlowResponseAPI(parameters={"destination_city": data.destination_city})
app/api/v1/time.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from loguru import logger
2
+
3
+ from fastapi import APIRouter, Depends, HTTPException, Request
4
+ from dependency_injector.wiring import inject, Provide
5
+
6
+
7
+ from app.dependencies.containers import Container
8
+ from app.dto.request import DialogflowWebhookRequest, TimeRequest
9
+ from app.dto.response import (
10
+ DialogServiceResult,
11
+ DialogflowResponseBuilder,
12
+ )
13
+ from app.services.time_service import TimeService
14
+
15
+
16
+ router = APIRouter(prefix="/time", tags=["Time"])
17
+
18
+
19
+ @router.post("/check-ambiguity")
20
+ @inject
21
+ async def check_time_ambiguity(
22
+ request: Request, service: TimeService = Depends(Provide[Container.time_service])
23
+ ):
24
+ body = await request.json()
25
+ body = DialogflowWebhookRequest(**body)
26
+ params = body.get_parameters()
27
+ time = TimeRequest(**params)
28
+ if not time:
29
+ logger.error("Time selection is empty")
30
+ raise HTTPException(status_code=400, detail="Time selection is required")
31
+
32
+ is_ambiguous = service.check_time_ambiguity(time=time.time_select)
33
+
34
+ return (
35
+ DialogflowResponseBuilder()
36
+ .set_parameters({"is_time_ambiguous": is_ambiguous})
37
+ .build()
38
+ )
39
+
40
+
41
+ @router.post("/ambiguity-selection")
42
+ @inject
43
+ async def time_ambiguity_selection(
44
+ request: Request, service: TimeService = Depends(Provide[Container.time_service])
45
+ ):
46
+ body = await request.json()
47
+ body = DialogflowWebhookRequest(**body)
48
+ params = body.get_parameters()
49
+
50
+ time = params.get("time_select")
51
+ if not time:
52
+ logger.error("Time selection is required")
53
+ raise HTTPException(status_code=400, detail="Time selection is required")
54
+
55
+ rs: DialogServiceResult = service.get_time_ambiguous_selection(time)
56
+
57
+ return (
58
+ DialogflowResponseBuilder()
59
+ .add_text(rs.text)
60
+ .add_choice_chips(options=rs.options)
61
+ .set_parameters(rs.parameters)
62
+ .build()
63
+ )
app/api/v1/trip.py ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Annotated
2
+ from loguru import logger
3
+ from fastapi import APIRouter, Depends, HTTPException, Request
4
+ from dependency_injector.wiring import inject, Provide
5
+
6
+ from app.dependencies.containers import Container
7
+ from app.dto.request import (
8
+ DialogflowWebhookRequest,
9
+ SeatQueryRequest,
10
+ StopRequest,
11
+ TicketRequest,
12
+ TripQueryRequest,
13
+ )
14
+ from app.dto.response import (
15
+ DialogServiceResult,
16
+ DialogflowResponseBuilder,
17
+ )
18
+ from app.services.trip_service import TripService
19
+
20
+
21
+ router = APIRouter(prefix="/trip", tags=["Trip"])
22
+
23
+
24
+ @router.post("/option")
25
+ @inject
26
+ async def trips_option(
27
+ request: Request, service: TripService = Depends(Provide[Container.trip_service])
28
+ ):
29
+ body = await request.json()
30
+ params = body.get("sessionInfo", {}).get("parameters", {})
31
+ try:
32
+ data = TripQueryRequest(**params)
33
+ except Exception as e:
34
+ raise HTTPException(status_code=400, detail="Invalid location parameters")
35
+
36
+ rs: DialogServiceResult = await service.get_trips_option(data)
37
+
38
+ return (
39
+ DialogflowResponseBuilder()
40
+ .add_text(rs.text)
41
+ .add_choice_chips(options=rs.options, key="route")
42
+ .set_parameters(rs.parameters)
43
+ .build()
44
+ )
45
+
46
+
47
+ @router.post("/confirm-info")
48
+ @inject
49
+ async def confirm(
50
+ request: Request, service: TripService = Depends(Provide[Container.trip_service])
51
+ ):
52
+ body = await request.json()
53
+ body = DialogflowWebhookRequest(**body)
54
+ parameters = body.get_parameters()
55
+ try:
56
+ data = TripQueryRequest(**parameters)
57
+ except Exception as e:
58
+ logger.error(f"Error parsing trip parameters {e}", exc_info=True)
59
+ raise HTTPException(status_code=400, detail="Invalid trip query parameters")
60
+
61
+ rs: DialogServiceResult = service.confirm_trip_search_input(data=data)
62
+
63
+ return (
64
+ DialogflowResponseBuilder()
65
+ .add_text(rs.text)
66
+ .add_choice_chips(options=rs.options)
67
+ .build()
68
+ )
69
+
70
+
71
+ @router.post("/validate-trip-selection")
72
+ @inject
73
+ async def validate_trip_selection(
74
+ request: Request, service: TripService = Depends(Provide[Container.trip_service])
75
+ ):
76
+ body = await request.json()
77
+ body = DialogflowWebhookRequest(**body)
78
+ params = body.get_parameters()
79
+ try:
80
+ data = TripQueryRequest(**params)
81
+ except Exception as e:
82
+ raise HTTPException(status_code=400, detail="Invalid trip selection parameters")
83
+
84
+ rs: DialogServiceResult = await service.validate_trip_selection(
85
+ id_selected=data.id_selected, data=data
86
+ )
87
+ return DialogflowResponseBuilder().set_parameters(rs.parameters).build()
88
+
89
+
90
+ @router.post("/available-seat")
91
+ @inject
92
+ async def fetch_available_seat(
93
+ request: Request, service: TripService = Depends(Provide[Container.trip_service])
94
+ ):
95
+ body = await request.json()
96
+ body = DialogflowWebhookRequest(**body)
97
+ params = body.get_parameters()
98
+
99
+ try:
100
+ logger.info(f"Received parameters: {params}")
101
+ data = SeatQueryRequest(**params)
102
+
103
+ except Exception as e:
104
+ logger.error(f"Error parsing trip parameters {e}", exc_info=True)
105
+ raise HTTPException(status_code=400, detail="Invalid trip query parameters")
106
+
107
+ rs: DialogServiceResult = await service.get_available_seat(data.trip_data)
108
+ return (
109
+ DialogflowResponseBuilder()
110
+ .add_text(rs.text)
111
+ .add_choice_chips(options=rs.options, key="chair")
112
+ .set_parameters(rs.parameters)
113
+ .build()
114
+ )
115
+
116
+
117
+ @router.post("/validate-seat-selection")
118
+ @inject
119
+ async def validate_seat_selection(
120
+ request: Request, service: TripService = Depends(Provide[Container.trip_service])
121
+ ):
122
+ body = await request.json()
123
+ body = DialogflowWebhookRequest(**body)
124
+ params = body.get_parameters()
125
+
126
+ try:
127
+ data = SeatQueryRequest(**params)
128
+ except Exception as e:
129
+ logger.error("Error parsing trip parameters", exc_info=True)
130
+ raise HTTPException(status_code=400, detail="Invalid trip query parameters")
131
+
132
+ rs: DialogServiceResult = await service.validate_seat_selection(
133
+ selected=data.seat, data=data.trip_data
134
+ )
135
+ return (
136
+ DialogflowResponseBuilder()
137
+ .add_text(rs.text)
138
+ .set_parameters(rs.parameters)
139
+ .build()
140
+ )
141
+
142
+
143
+ @router.post("/pick-up")
144
+ @inject
145
+ async def pick_up_stop(
146
+ request: Request, service: TripService = Depends(Provide[Container.trip_service])
147
+ ):
148
+ try:
149
+ body = await request.json()
150
+ body = DialogflowWebhookRequest(**body)
151
+ params = body.get_parameters()
152
+ data = StopRequest(**params)
153
+ except Exception as e:
154
+ logger.error(f"Error parsing trip parameters {e}", exc_info=True)
155
+ raise HTTPException(status_code=400, detail="Invalid trip query parameters")
156
+
157
+ rs: DialogServiceResult = await service.get_pick_up_points(
158
+ route_id=data.trip_data.route_id, way_id=data.trip_data.way_id
159
+ )
160
+ return (
161
+ DialogflowResponseBuilder()
162
+ .add_text(rs.text)
163
+ .add_choice_chips(options=rs.options, key="name")
164
+ .set_parameters(rs.parameters)
165
+ .build()
166
+ )
167
+
168
+
169
+ @router.post("/pick-up/validate-selection")
170
+ @inject
171
+ async def validate_pick_up_selection(
172
+ request: Request, service: TripService = Depends(Provide[Container.trip_service])
173
+ ):
174
+ body = await request.json()
175
+ body = DialogflowWebhookRequest(**body)
176
+ params = body.get_parameters()
177
+
178
+ try:
179
+ data = StopRequest(**params)
180
+ except Exception as e:
181
+ logger.error("Error parsing trip parameters", exc_info=True)
182
+ raise HTTPException(status_code=400, detail="Invalid trip query parameters")
183
+
184
+ selected = body.get_text()
185
+ rs: DialogServiceResult = await service.validate_stop_selection(
186
+ id_selected=data.id_selected, data=data.trip_data, role="pick-up"
187
+ )
188
+
189
+ return (
190
+ DialogflowResponseBuilder()
191
+ .add_text(rs.text)
192
+ .add_choice_chips(options=rs.options)
193
+ .set_parameters(rs.parameters)
194
+ .build()
195
+ )
196
+
197
+
198
+ @router.post("/drop-off")
199
+ @inject
200
+ async def drop_off_stop(
201
+ request: Request, service: TripService = Depends(Provide[Container.trip_service])
202
+ ):
203
+ body = await request.json()
204
+ body = DialogflowWebhookRequest(**body)
205
+ params = body.get_parameters()
206
+
207
+ try:
208
+ data = StopRequest(**params)
209
+ except Exception as e:
210
+ logger.error("Error parsing trip parameters", exc_info=True)
211
+ raise HTTPException(status_code=400, detail="Invalid trip query parameters")
212
+
213
+ rs: DialogServiceResult = await service.get_drop_off_points(
214
+ route_id=data.trip_data.route_id, way_id=data.trip_data.way_id
215
+ )
216
+ logger.info(f"Drop-off points: {rs.options}")
217
+
218
+ return (
219
+ DialogflowResponseBuilder()
220
+ .add_text(rs.text)
221
+ .add_choice_chips(options=rs.options, key="name")
222
+ .set_parameters(rs.parameters)
223
+ .build()
224
+ )
225
+
226
+
227
+ @router.post("/drop-off/validate-selection")
228
+ @inject
229
+ async def validate_drop_off_selection(
230
+ request: Request, service: TripService = Depends(Provide[Container.trip_service])
231
+ ):
232
+ body = await request.json()
233
+ body = DialogflowWebhookRequest(**body)
234
+ params = body.get_parameters()
235
+
236
+ try:
237
+ data = StopRequest(**params)
238
+ except Exception as e:
239
+ logger.error("Error parsing trip parameters", exc_info=True)
240
+ raise HTTPException(status_code=400, detail="Invalid trip query parameters")
241
+
242
+ rs: DialogServiceResult = await service.validate_stop_selection(
243
+ id_selected=data.id_selected, data=data.trip_data, role="drop-off"
244
+ )
245
+
246
+ return (
247
+ DialogflowResponseBuilder()
248
+ .add_text(rs.text)
249
+ .add_choice_chips(options=rs.options)
250
+ .set_parameters(rs.parameters)
251
+ .build()
252
+ )
253
+
254
+
255
+ @router.post("/ticket")
256
+ @inject
257
+ async def show_ticket(
258
+ request: Request, service: TripService = Depends(Provide[Container.trip_service])
259
+ ):
260
+ body = await request.json()
261
+ body = DialogflowWebhookRequest(**body)
262
+ params = body.get_parameters()
263
+
264
+ try:
265
+ data = TicketRequest(**params)
266
+ except Exception as e:
267
+ logger.error("Error parsing trip parameters", exc_info=True)
268
+ raise HTTPException(status_code=400, detail="Invalid trip query parameters")
269
+
270
+ rs: DialogServiceResult = await service.make_ticket_summary(data)
271
+
272
+ return (
273
+ DialogflowResponseBuilder()
274
+ .add_text(rs.text)
275
+ .set_parameters(rs.parameters)
276
+ .build()
277
+ )
app/api/v1/user.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi.params import Depends
2
+ from loguru import logger
3
+ from typing_extensions import Annotated
4
+ from fastapi import APIRouter, Request
5
+ from dependency_injector.wiring import inject, Provide
6
+
7
+
8
+ from app.dependencies.containers import Container
9
+ from app.dto.request import DialogflowWebhookRequest
10
+ from app.dto.response import DialogflowResponseBuilder
11
+ from app.dto.user import UserDTO
12
+ from app.services.user_service import UserService
13
+ from app.ner.services.ner import NER
14
+
15
+
16
+ router = APIRouter(prefix="/user", tags=["User"])
17
+
18
+
19
+ def get_ner_model(request: Request) -> NER:
20
+ """
21
+ Dependency to get the NER model.
22
+ This can be used to inject the NER model into the endpoint.
23
+ """
24
+ return request.app.state.ner
25
+
26
+
27
+ @router.post("/info")
28
+ @inject
29
+ async def get_user_info(
30
+ request: Request, service: UserService = Depends(Provide[Container.user_service])
31
+ ):
32
+ body = await request.json()
33
+ body = DialogflowWebhookRequest(**body)
34
+ params = body.get_parameters()
35
+
36
+ user: UserDTO = await service.get_user()
37
+ user_exists: bool = user is not None
38
+
39
+ parameters = {
40
+ "is_user_exist": user_exists,
41
+ "user_name": user.user_name if user_exists else None,
42
+ "phone_number": user.phone_number if user_exists else None,
43
+ "email": user.email if user_exists else None,
44
+ }
45
+ return DialogflowResponseBuilder().set_parameters(parameters).build()
46
+
47
+
48
+ @router.post("/extract-name")
49
+ @inject
50
+ async def extract_user_name(
51
+ request: Request, service: UserService = Depends(Provide[Container.user_service])
52
+ ):
53
+ body = await request.json()
54
+ body = DialogflowWebhookRequest(**body)
55
+
56
+ text = body.get_text()
57
+
58
+ user_name = await service.extract_user_name(text)
59
+
60
+ return DialogflowResponseBuilder().set_parameters({"user_name": user_name}).build()
app/client/futabus_client.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from app.dto.trip import TripDTO
3
+ from common.external.external_api import api
4
+ from datetime import datetime
5
+
6
+ from core.conf import settings
7
+
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class FutabusClient:
13
+ @staticmethod
14
+ async def get_pickup_points(
15
+ origin: str | None = None, dest: str | None = None
16
+ ) -> dict:
17
+ """
18
+ Gọi API để lấy metadata điểm đón/trả.
19
+
20
+ Args:
21
+ origin (str): tên văn phòng đi
22
+ dest (str): tên văn phòng đến
23
+
24
+ Returns:
25
+ dict: dữ liệu metadata chứa các điểm đón/trả
26
+ """
27
+ params = {
28
+ "origin": origin,
29
+ "dest": dest,
30
+ "session_id": str(int(datetime.now().timestamp())),
31
+ }
32
+ try:
33
+ response = await api.get(
34
+ "/search/metadata/pickup-points",
35
+ params={k: v for k, v in params.items() if v is not None},
36
+ )
37
+ if response.get("status") == 200:
38
+ logger.debug(f"Fetched pickup points for: origin={origin}, dest={dest}")
39
+ return response.get("data", {})
40
+ logger.warning(
41
+ f"Unexpected response status: {response.get('status')} - origin={origin}, dest={dest}"
42
+ )
43
+ return {}
44
+ except Exception as e:
45
+ logger.exception(
46
+ f"[FutabusClient] Error fetching pickup points for origin={origin}, dest={dest}: {e}"
47
+ )
48
+ return {}
49
+
50
+ @staticmethod
51
+ async def get_route_ids_by_metadata(
52
+ origin_code: str | None = None,
53
+ from_id: int | None = None,
54
+ origin_ids: int | list[int] | None = None,
55
+ dest_code: str | None = None,
56
+ to_id: int | None = None,
57
+ dest_ids: int | list[int] | None = None,
58
+ ) -> list[int]:
59
+ """
60
+ Gọi API `/metadata/office/routes` để lấy routeId theo ID/code văn phòng hoặc tỉnh.
61
+ """
62
+ params = {
63
+ k: v
64
+ for k, v in {
65
+ "OriginCode": origin_code,
66
+ "FromId": from_id,
67
+ "OriginIds": origin_ids,
68
+ "DestCode": dest_code,
69
+ "ToId": to_id,
70
+ "DestIds": dest_ids,
71
+ }.items()
72
+ if v is not None
73
+ }
74
+
75
+ try:
76
+ response = await api.get("/metadata/office/routes", params=params)
77
+ if isinstance(response, list):
78
+ return [item.get("routeId") for item in response if "routeId" in item]
79
+ except Exception as e:
80
+ logger.exception("Error fetching route ids by metadata")
81
+ return []
82
+
83
+ @staticmethod
84
+ async def get_route_ids_by_code(origin_code: str, dest_code: str) -> list:
85
+ """
86
+ Gọi API `/booking/api/v2/routes/from/{origin}/to/{dest}` để lấy danh sách route khi metadata không có.
87
+ """
88
+ try:
89
+ endpoint = f"/booking/api/v2/routes/from/{origin_code}/to/{dest_code}"
90
+ response = await api.get(endpoint)
91
+ if response.get("Status") == 200 and response.get("Data"):
92
+ data = response.get("Data", [])
93
+ route_ids = [item.get("Id", None) for item in data]
94
+ return route_ids
95
+ return []
96
+ except Exception as e:
97
+ logger.exception("Error fetching route ids by province codes")
98
+ return []
99
+
100
+ @staticmethod
101
+ async def search_trips(
102
+ from_time: int, to_time: int, route_ids: list, ticket_count: int = 1
103
+ ) -> list[dict[str, any]]:
104
+ """
105
+ Gọi external API để tìm chuyến xe theo danh sách route_id và thời gian.
106
+
107
+ Args:
108
+ from_time (int): Mốc thời gian bắt đầu (timestamp in ms)
109
+ to_time (int): Mốc thời gian kết thúc (timestamp in ms)
110
+ route_ids (list[int]): Danh sách route ID để lọc chuyến
111
+ ticket_count (int): Số lượng vé cần đặt (default = 1)
112
+
113
+ Returns:
114
+ list[dict[str, any]]: Danh sách chuyến xe phù hợp
115
+ """
116
+ if not route_ids:
117
+ return []
118
+
119
+ payload = {
120
+ "channel": "web_client",
121
+ "size": 300,
122
+ "only_online_trip": True,
123
+ "from_time": from_time,
124
+ "to_time": to_time,
125
+ "route_ids": route_ids,
126
+ "ticket_count": int(ticket_count),
127
+ "sort_by": ["price", "departure_time"],
128
+ }
129
+ logger.info(f"Searching trips with payload: {payload}")
130
+
131
+ try:
132
+ response = await api.post("/search/trips", payload=payload)
133
+ if response.get("status") == 200:
134
+ data = response.get("data", {})
135
+ if data.get("total", 0) > 0:
136
+ return data.get("items", [])
137
+ logger.warning(
138
+ f"Unexpected response status: {response.get('status')} - No trips found"
139
+ )
140
+ return []
141
+ except Exception as e:
142
+ logger.exception("Failed to fetch trips from external API")
143
+ return []
144
+
145
+ @staticmethod
146
+ async def get_seat_trip(trip: TripDTO) -> list[dict]:
147
+ """
148
+ Gọi API để lấy thông tin ghế của chuyến xe.
149
+
150
+ Args:
151
+ trip (TripDTO): Thông tin chuyến xe cần lấy ghế.
152
+
153
+ Returns:
154
+ list[dict]: Danh sách ghế của chuyến xe.
155
+ """
156
+ params = {
157
+ k: v
158
+ for k, v in {
159
+ "departureDate": trip.raw_departure_date,
160
+ "departureTime": trip.raw_departure_time,
161
+ "kind": trip.seat_type_name,
162
+ }.items()
163
+ if v is not None
164
+ }
165
+ logger.info(f"Parameters for fetching seats: {params}")
166
+ logger.debug(f"Parameters for fetching seats: {params}")
167
+ try:
168
+ rs = await api.get(
169
+ api_base=settings.API_BASE_URL_VATO,
170
+ # api_base="https://api-busline.vato.vn/api/buslines/futa/booking",
171
+ endpoint=f"/seats/{trip.route_id}/{trip.id}",
172
+ params=params,
173
+ )
174
+
175
+ return rs.get("data", [])
176
+ except Exception as e:
177
+ logger.exception(f"Error fetching seats for trip {trip.id}: {e}")
178
+ return []
179
+
180
+ @staticmethod
181
+ async def get_stop_by_route_and_way(route_id: int, way_id: int) -> list:
182
+ """
183
+ Lấy danh sách các điểm dừng của chuyến xe theo route_id và way_id.
184
+
185
+ Args:
186
+ route_id (int): ID của tuyến xe.
187
+ way_id (int): ID của lộ trình.
188
+
189
+ Returns:
190
+ list[dict]: Danh sách các điểm dừng.
191
+ """
192
+ try:
193
+ params = {
194
+ k: v
195
+ for k, v in {
196
+ "wayId": way_id,
197
+ }.items()
198
+ if v is not None
199
+ }
200
+ rs = await api.get(
201
+ api_base=settings.API_BASE_URL_VATO,
202
+ endpoint=f"/stops/{route_id}",
203
+ params=params,
204
+ )
205
+ return rs.get("data", [])
206
+ except Exception as e:
207
+ logger.exception(
208
+ f"Error fetching stops for route {route_id}, way {way_id}: {e}"
209
+ )
210
+ return []
app/dependencies/containers.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dependency_injector import containers, providers
2
+
3
+ from app.client.futabus_client import FutabusClient
4
+ from app.repositories.trip_repository import TripRepository
5
+ from app.resolvers.location_resolver import LocationResolver
6
+ from app.services.location_service import LocationService
7
+ from app.services.time_service import TimeService
8
+ from app.services.trip_service import TripService
9
+ from app.services.user_service import UserService
10
+ from app.ner.services.ner import NER
11
+
12
+
13
+ class Container(containers.DeclarativeContainer):
14
+ wiring_config = containers.WiringConfiguration(
15
+ modules=[
16
+ "app.api.v1.trip",
17
+ "app.api.v1.user",
18
+ "app.api.v1.location",
19
+ "app.api.v1.time",
20
+ ]
21
+ )
22
+
23
+ ner_model = providers.Singleton(NER)
24
+
25
+ futabus_client = providers.Singleton(FutabusClient)
26
+
27
+ location_service = providers.Factory(
28
+ LocationService,
29
+ client=futabus_client,
30
+ )
31
+
32
+ location_resolver = providers.Factory(
33
+ LocationResolver, location_service=location_service
34
+ )
35
+ # NER model (singleton nếu model lớn)
36
+
37
+ trip_repository = providers.Factory(TripRepository, client=futabus_client)
38
+
39
+ trip_service = providers.Factory(
40
+ TripService,
41
+ trip_repository=trip_repository,
42
+ futabus_client=futabus_client,
43
+ location_resolver=location_resolver,
44
+ )
45
+
46
+ time_service = providers.Factory(
47
+ TimeService,
48
+ )
49
+
50
+ user_service = providers.Factory(UserService, model=ner_model)
app/dialogflow/api/__pycache__/__init__.cpython-310.pyc DELETED
Binary file (176 Bytes)
 
app/dialogflow/api/__pycache__/router.cpython-310.pyc DELETED
Binary file (416 Bytes)
 
app/dialogflow/api/__pycache__/routes.cpython-39.pyc DELETED
Binary file (9.23 kB)
 
app/dialogflow/api/router.py DELETED
@@ -1,12 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- from fastapi import APIRouter
4
-
5
- from app.dialogflow.api.v1.dialogflow import router as dialogflow_router
6
- from core.conf import settings
7
-
8
- from utils.life_span import lifespan
9
-
10
- v1 = APIRouter(prefix=settings.FASTAPI_API_V1_PATH, lifespan=lifespan)
11
-
12
- v1.include_router(dialogflow_router)
 
 
 
 
 
 
 
 
 
 
 
 
 
app/dialogflow/api/v1/__pycache__/__init__.cpython-310.pyc DELETED
Binary file (179 Bytes)
 
app/dialogflow/api/v1/__pycache__/dialogflow.cpython-310.pyc DELETED
Binary file (9.93 kB)
 
app/dialogflow/api/v1/dialogflow.py DELETED
@@ -1,1059 +0,0 @@
1
- from typing import Dict, List
2
- from fastapi import FastAPI, APIRouter, HTTPException, Request, Response # type: ignore
3
- from fastapi.responses import JSONResponse, RedirectResponse, HTMLResponse # type: ignore
4
- from datetime import datetime, timedelta
5
- from fastapi.templating import Jinja2Templates
6
- from app.dialogflow.services.dialog_service import dialog_service
7
- from app.ner.services.ner import NER
8
- from utils.format_data_dialog import extra_time_dialogflow, get_weekday_name, find_surrounding_times
9
-
10
- from common.external.external_api import api
11
- from app.dialogflow.services.origin_codes import origin_codes, get_origin_id_and_code
12
- from common.response.respone_dialogflow import DialogFlowResponseAPI
13
- router = APIRouter()
14
-
15
- templates = Jinja2Templates(directory="templates")
16
-
17
-
18
- @router.post('/search/origin-city/from/office')
19
- async def search_origin_office(request: Request):
20
- body = await request.json()
21
-
22
- session_info = body.get("sessionInfo", {})
23
- parameters = session_info.get("parameters")
24
-
25
- raw_departure_city = parameters.get("departure_city", None)
26
- origin_office = parameters.get("origin_office")
27
- departure_city = None
28
- if origin_office and raw_departure_city is None:
29
- departure_city = await dialog_service.get_origin_city_from_office(origin_office)
30
- parameters = {
31
- "departure_city": departure_city
32
- }
33
- return DialogFlowResponseAPI(parameters=parameters)
34
-
35
- @router.post('/search/destination-city/from/office')
36
- async def search_destination_office(request: Request):
37
- body = await request.json()
38
- session_info = body.get("sessionInfo", {})
39
- parameters = session_info.get("parameters")
40
-
41
- raw_destination_city = parameters.get("destination_city", None)
42
- dest_office = parameters.get("dest_office")
43
-
44
- destination_city = None
45
- if dest_office and raw_destination_city is None:
46
- destination_city = await dialog_service.get_destination_city_from_office(dest_office)
47
-
48
- parameters = {
49
- "destination_city": destination_city
50
- }
51
- return DialogFlowResponseAPI(parameters=parameters)
52
- @router.post('/info/confirm')
53
- async def confirm(request: Request):
54
- body = await request.json()
55
- session_info = body.get("sessionInfo", {})
56
- parameters = session_info.get("parameters", {}) if isinstance(session_info.get("parameters"), dict) else {}
57
- raw_date = parameters.get("date")
58
- departure_city = parameters.get("departure_city")
59
- destination_city = parameters.get("destination_city")
60
- origin_office = parameters.get("origin_office")
61
- dest_office = parameters.get("dest_office")
62
- raw_ticket_number = parameters.get("ticket_number")
63
- time = parameters.get("time-select")
64
-
65
- time = extra_time_dialogflow(time)
66
- parameters = {}
67
-
68
- if isinstance(time, list):
69
- parameters["is_time_ambiguous"] = True
70
- return DialogFlowResponseAPI(parameters=parameters)
71
-
72
- ticket_number = int(raw_ticket_number) if raw_ticket_number else 1
73
- date = dialog_service.to_datetime_from_Dialogflow(raw_date)
74
- date, week_day = get_weekday_name(date)
75
- text = [f"""**Thời gian:** {time} - {date} - {week_day}\n**Số vé:** {ticket_number}"""]
76
- temp = ""
77
- if origin_office and dest_office:
78
- temp = f"""**Điểm đi:** {origin_office}\n**Điểm đến:** {dest_office}\n"""
79
- elif origin_office and destination_city:
80
- temp = f"""**Điểm đi:** {origin_office}\n**Điểm đến:** {destination_city}\n"""
81
- elif dest_office and departure_city:
82
- temp = f"""**Điểm đi:** {departure_city}\n**Điểm đến:** {dest_office}\n"""
83
- elif departure_city and destination_city:
84
- temp = f"""**Điểm đi:** {departure_city}\n**Điểm đến:** {destination_city}\n"""
85
- text[0] = temp + text[0]
86
- payload = {
87
- "richContent": [
88
- [
89
- {
90
- "type": "chips",
91
- "options": [
92
- {"text": "Tìm chuyến xe"},
93
- {"text": "Không, cảm ơn"}
94
- ]
95
- }
96
- ]
97
- ]
98
- }
99
- return DialogFlowResponseAPI(text=text, payload=payload)
100
- @router.post('/routes')
101
- async def route(request: Request):
102
- body = await request.json()
103
- session_info = body.get("sessionInfo", {})
104
- parameters = session_info.get("parameters")
105
-
106
- origin_office = parameters.get("origin_office")
107
- dest_office = parameters.get("dest_office")
108
-
109
- raw_departure_city, raw_destination_city, raw_ticket_number , raw_date, _ = dialog_service.get_param_from_dialogflow(body)
110
-
111
- ticket_count = int(raw_ticket_number) if raw_ticket_number else 1
112
-
113
- if raw_date is None:
114
- from_time, to_time = dialog_service.process_dates_to_timestamp()
115
- date = datetime.today().date().strftime('%m-%d-%Y')
116
- else:
117
- date = raw_date.strftime('%m-%d-%Y')
118
- from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date)
119
-
120
- origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = None, None, None, None, None, None
121
- if origin_office:
122
- origin_id, origin_code = await dialog_service.find_id_and_code_provine_by_name_office(origin_office)
123
- origin_ids = await dialog_service.find_id_office_by_name_office(origin_office)
124
- elif raw_departure_city:
125
- origin_id, origin_code = get_origin_id_and_code(raw_departure_city)
126
- if dest_office:
127
- dest_id, dest_code = await dialog_service.find_id_and_code_provine_by_name_office(dest_office)
128
- dest_ids = await dialog_service.find_id_office_by_name_office(dest_office)
129
- elif raw_destination_city:
130
- dest_id, dest_code = get_origin_id_and_code(raw_destination_city)
131
- route_ids = await dialog_service.search_all_route_ids(origin_code=origin_code, from_id=origin_id, orign_ids=origin_ids, dest_code=dest_code, to_id=dest_id, dest_ids=dest_ids)
132
-
133
- payload = {
134
- "from_time": from_time,
135
- "to_time": to_time,
136
- "route_ids": route_ids,
137
- "ticket_count": 1,
138
- "sort_by": ["price", "departure_time"]
139
- }
140
- try:
141
- respone = await api.post("/search/trips", payload=payload)
142
- data = respone["data"]["items"]
143
- total = respone["data"]["total"]
144
- if total > 0:
145
- price = data[0]["price"]
146
- list_raw_departure_times = sorted(list(set([ trip["raw_departure_time"] for trip in data])))
147
- list_raw_seat_type = list(set([ trip["seat_type_name"] for trip in data]))
148
- seat_type_trip_string = "**" + "** | **".join(map(str, list_raw_seat_type)) + "**"
149
- schedule_time_trip = "**" + "** | **".join(map(str, list_raw_departure_times)) + "**"
150
- duration = data[0]["duration"]
151
- link = f'https://stag.futabus.vn/dat-ve?from={departure_code}&fromTime={date}&isReturn=false&ticketCount={ticket_count}&to={destination_code}&toTime='
152
- text = [f"Tuyến xe **{raw_departure_city}** - **{raw_destination_city}**\n \
153
- Loại xe: {seat_type_trip_string} \n \
154
- Thời gian hành trình: {duration} giờ \n \
155
- Giá vé: **{price}** VND"]
156
- payload = {
157
- "richContent": [
158
- [
159
- {
160
- "type": "chips",
161
- "options": [
162
- {"text": "Tìm chuyến xe"},
163
- {"text": "Xem lịch trình khác"},
164
- {"text": "Không, cảm ơn"}
165
- ]
166
- }
167
- ]
168
- ]
169
- }
170
-
171
- return DialogFlowResponseAPI(text=text, payload=payload)
172
-
173
- text = [f"Hệ thống không tìm thấy tuyến xe **{raw_departure_city}** - **{raw_destination_city}**.\n Quý khách vui lòng thử lại với lộ trình khác hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."]
174
- payload = {
175
- "richContent": [
176
- [
177
- {
178
- "type": "chips",
179
- "options": [
180
- {"text": "Xem lịch trình khác"},
181
- {"text": "Không, cảm ơn"}
182
- ]
183
- }
184
- ]
185
- ]
186
- }
187
- return DialogFlowResponseAPI(text=text, payload=payload)
188
- except Exception as e:
189
- return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."])
190
-
191
-
192
- @router.post('/price')
193
- async def price(request: Request):
194
- body = await request.json()
195
- session_info = body.get("sessionInfo", {})
196
- parameters = session_info.get("parameters")
197
-
198
- raw_departure_city, raw_destination_city, _, raw_date, _ = dialog_service.get_param_from_dialogflow(body)
199
-
200
- if raw_date is None:
201
- from_time, to_time = dialog_service.process_dates_to_timestamp()
202
- from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date)
203
-
204
- origin_office = parameters.get("origin_office")
205
- dest_office = parameters.get("dest_office")
206
-
207
- origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = None, None, None, None, None, None
208
- if origin_office:
209
- origin_id, origin_code = await dialog_service.find_id_and_code_provine_by_name_office(origin_office)
210
- origin_ids = await dialog_service.find_id_office_by_name_office(origin_office)
211
- elif raw_departure_city:
212
- origin_id, origin_code = get_origin_id_and_code(raw_departure_city)
213
- if dest_office:
214
- dest_id, dest_code = await dialog_service.find_id_and_code_provine_by_name_office(dest_office)
215
- dest_ids = await dialog_service.find_id_office_by_name_office(dest_office)
216
- elif raw_destination_city:
217
- dest_id, dest_code = get_origin_id_and_code(raw_destination_city)
218
- route_ids = await dialog_service.search_all_route_ids(origin_code=origin_code, from_id=origin_id, orign_ids=origin_ids, dest_code=dest_code, to_id=dest_id, dest_ids=dest_ids)
219
-
220
- payload = {
221
- "from_time": from_time,
222
- "to_time": to_time,
223
- "route_ids": route_ids,
224
- "ticket_count": 1,
225
- "sort_by": ["price", "departure_time"]
226
- }
227
- try:
228
- respone = await api.post("/search/trips", payload=payload)
229
- total = respone["data"]["total"]
230
- if total > 0:
231
- price = respone["data"]["items"][0]["price"]
232
- text = [f"Tuyến xe **{raw_departure_city}** - **{raw_destination_city}**\n"
233
- f"Giá vé: **{price}** VND.\n \
234
- Bạn có muốn đặt vé không?"
235
- ]
236
- payload = {
237
- "richContent": [
238
- [
239
- {
240
- "type": "chips",
241
- "options": [
242
- {"text": "Có, tôi muốn đặt vé"},
243
- {"text": "Xem giá vé tuyến xe khác"},
244
- {"text": "Không, cảm ơn"}
245
- ]
246
- }
247
- ]
248
- ]
249
- }
250
- return DialogFlowResponseAPI(text=text, payload=payload)
251
- text = [f"Hệ thống không tìm thấy tuyến xe **{raw_departure_city}** - **{raw_destination_city}**.\n Quý khách vui lòng thử lại với lộ trình khác hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."]
252
- payload={
253
- "richContent": [
254
- [
255
- {
256
- "type": "chips",
257
- "options": [
258
- {"text": "Xem giá vé lịch trình khác"},
259
- {"text": "Không, cảm ơn"}
260
- ]
261
- }
262
- ]
263
- ]
264
- }
265
-
266
- return DialogFlowResponseAPI(text=text, payload=payload)
267
- except:
268
- return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."])
269
-
270
- @router.post('/trip/list')
271
- async def get_trip_list(request: Request) -> Response:
272
- body = await request.json()
273
- session_info = body.get("sessionInfo", {})
274
- parameters = session_info.get("parameters")
275
-
276
- raw_departure_city, raw_destination_city, raw_ticket_number, raw_date, _ = dialog_service.get_param_from_dialogflow(body)
277
-
278
- origin_office = parameters.get("origin_office")
279
- dest_office = parameters.get("dest_office")
280
- time = parameters.get("time-select")
281
-
282
- time = extra_time_dialogflow(time)
283
-
284
- if isinstance(time, list):
285
- parameters = {
286
- "is_time_ambiguous": True
287
- }
288
- return DialogFlowResponseAPI(parameters=parameters)
289
-
290
- from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date)
291
-
292
- ticket_count = int(raw_ticket_number) if raw_ticket_number else 1
293
-
294
- origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = None, None, None, None, None, None
295
- if origin_office:
296
- origin_id, origin_code = await dialog_service.find_id_and_code_provine_by_name_office(origin_office)
297
- origin_ids = await dialog_service.find_id_office_by_name_office(origin_office)
298
- elif raw_departure_city:
299
- origin_id, origin_code = get_origin_id_and_code(raw_departure_city)
300
- if dest_office:
301
- dest_id, dest_code = await dialog_service.find_id_and_code_provine_by_name_office(dest_office)
302
- dest_ids = await dialog_service.find_id_office_by_name_office(dest_office)
303
- elif raw_destination_city:
304
- dest_id, dest_code = get_origin_id_and_code(raw_destination_city)
305
- route_ids = await dialog_service.search_all_route_ids(origin_code=origin_code, from_id=origin_id, orign_ids=origin_ids, dest_code=dest_code, to_id=dest_id, dest_ids=dest_ids)
306
-
307
- try:
308
- data = await dialog_service.search_trip(from_time, to_time, route_ids, ticket_count)
309
- if len(data) > 0:
310
-
311
- if origin_office and dest_office:
312
- trip_by_time_office = dialog_service.get_trip_by_time_and_office_id(data, time, origin_ids, dest_ids)
313
- # Nếu có chuyến xe theo thời gian và văn phòng chỉ định
314
- if trip_by_time_office:
315
- parameters = {
316
- "is_valid_trip": True,
317
- "trip": trip_by_time_office
318
- }
319
- return DialogFlowResponseAPI(parameters=parameters)
320
-
321
- # Nếu không có chuyến xe theo thời gian và văn phòng chỉ định
322
- # Danh sách chuyến xe khớp với văn phòng đón hoặc văn phòng trả
323
- # data_by_office = dialog_service.get_all_trip_by_office(data, origin_ids, dest_ids)
324
- # elif origin_office:
325
- # data_by_office = dialog_service.get_all_trip_by_office(data,origin_id=origin_ids)
326
- # elif dest_office:
327
- # data_by_office = dialog_service.get_all_trip_by_office(data, dest_id=dest_ids)
328
- # else:
329
-
330
- data_by_office = data
331
- # Tìm 4 chuyến xe gần thời gian chỉ định nhất
332
- trip_surrounding_time = dialog_service.get_surrounding_trip(data_by_office, time, num_trip=4)
333
- trip_dialogflow = []
334
-
335
- for trip in trip_surrounding_time:
336
- if ticket_count <= trip["empty_seat_quantity"]:
337
- trip_dialogflow.append({"trip_id": trip["id"], "route":f'{trip["raw_departure_time"]} | {trip["route"]["origin_hub_name"]} => {trip["route"]["dest_hub_name"]}'})
338
- text = ["Quý khách vui lòng lựa chọn chuyến xe\n" + "\n".join(f'{i+1}. {trip["route"]}' for i, trip in enumerate(trip_dialogflow))]
339
- payload={
340
- "richContent": [
341
- [
342
- {
343
- "type": "chips",
344
- "options": [
345
- {"text": trip["route"]} for trip in (trip_dialogflow)
346
- ]
347
- }
348
- ]
349
- ]
350
- }
351
- parameters = {
352
- "trip_select": trip_dialogflow,
353
- "trips": trip_surrounding_time,
354
- }
355
- return DialogFlowResponseAPI(text=text, payload=payload,parameters=parameters)
356
-
357
- text = [f"Hệ thống không tìm thấy tuyến xe **{raw_departure_city}** - **{raw_destination_city}**.\n Quý khách vui lòng thử lại với lộ trình khác hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."]
358
- payload={
359
- "richContent": [
360
- [
361
- {
362
- "type": "chips",
363
- "options": [
364
- {"text": "Xem tuyến xe khác"},
365
- {"text": "Không, cảm ơn"}
366
- ]
367
- }
368
- ]
369
- ]
370
- }
371
-
372
- return DialogFlowResponseAPI(text=text, payload=payload)
373
- except Exception as e:
374
- print(e)
375
- return JSONResponse(
376
- {
377
- "fulfillment_response": {
378
- "messages": [
379
- {
380
- "text": {
381
- "text": ["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."]
382
- }
383
- }
384
- ]
385
- },
386
- }
387
- )
388
-
389
- # Không xài
390
- @router.post('/trip/route/list')
391
- async def booking_trip(request: Request) -> Response:
392
- body = await request.json()
393
- session_info = body.get("sessionInfo", {})
394
- parameters = session_info.get("parameters")
395
-
396
- raw_departure_city, raw_destination_city, raw_ticket_number, raw_date, raw_time_of_day = dialog_service.get_param_from_dialogflow(body)
397
-
398
- origin_office = parameters.get("origin_office")
399
- dest_office = parameters.get("dest_office")
400
-
401
- from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date)
402
-
403
- ticket_count = int(raw_ticket_number) if raw_ticket_number else 1
404
-
405
- origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = None, None, None, None, None, None
406
- if origin_office:
407
- origin_id, origin_code = await dialog_service.find_id_and_code_provine_by_name_office(origin_office)
408
- origin_ids = await dialog_service.find_id_office_by_name_office(origin_office)
409
- elif raw_departure_city:
410
- origin_id, origin_code = get_origin_id_and_code(raw_departure_city)
411
- if dest_office:
412
- dest_id, dest_code = await dialog_service.find_id_and_code_provine_by_name_office(dest_office)
413
- dest_ids = await dialog_service.find_id_office_by_name_office(dest_office)
414
- elif raw_destination_city:
415
- dest_id, dest_code = get_origin_id_and_code(raw_destination_city)
416
- route_ids = await dialog_service.search_all_route_ids(origin_code=origin_code, from_id=origin_id, orign_ids=origin_ids, dest_code=dest_code, to_id=dest_id, dest_ids=dest_ids)
417
- try:
418
- data = await dialog_service.search_trip(from_time, to_time, route_ids, ticket_count)
419
- if len(data) > 0:
420
- trips = []
421
- routes_name = []
422
- for trip in data:
423
- if ticket_count <= trip["empty_seat_quantity"]:
424
- route = f"{trip['route']['origin_hub_name']} => {trip['route']['dest_hub_name']}"
425
- if trip["route"] and route not in routes_name:
426
- routes_name.append(f"{trip['route']['origin_hub_name']} => {trip['route']['dest_hub_name']}")
427
- trips.append({
428
- "route_name": f"{trip['route']['origin_hub_name']} => {trip['route']['dest_hub_name']}",
429
- "route_id": trip["route_id"],
430
- "id": trip["id"],
431
- "departure_date": trip["raw_departure_date"],
432
- "departure_time": trip["raw_departure_time"],
433
- "kind": trip["seat_type_name"],
434
- "way_id": trip["way_id"]
435
-
436
- })
437
- text = ["Quý khách vui lòng lựa chọn chuyến xe\n" + "\n".join(f"{i+1}. {name}" for i, name in enumerate(routes_name))]
438
- payload={
439
- "richContent": [
440
- [
441
- {
442
- "type": "chips",
443
- "options": [
444
- {"text": name} for name in (routes_name)
445
- ]
446
- }
447
- ]
448
- ]
449
- }
450
- parameters = {
451
- "trip_list": trips,
452
- "routes_name": routes_name
453
- }
454
- return DialogFlowResponseAPI(text=text, payload=payload,parameters=parameters)
455
-
456
- text = [f"Hệ thống không tìm thấy tuyến xe **{raw_departure_city}** - **{raw_destination_city}**.\n Quý khách vui lòng thử lại với lộ trình khác hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."]
457
- payload={
458
- "richContent": [
459
- [
460
- {
461
- "type": "chips",
462
- "options": [
463
- {"text": "Xem tuyến xe khác"},
464
- {"text": "Không, cảm ơn"}
465
- ]
466
- }
467
- ]
468
- ]
469
- }
470
-
471
- return DialogFlowResponseAPI(text=text, payload=payload)
472
- except Exception as e:
473
- print(e)
474
- return JSONResponse(
475
- {
476
- "fulfillment_response": {
477
- "messages": [
478
- {
479
- "text": {
480
- "text": ["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."]
481
- }
482
- }
483
- ]
484
- },
485
- }
486
- )
487
-
488
- @router.post('/trip/check-trip')
489
- async def is_valid_select_trip(request: Request) -> Response:
490
- body = await request.json()
491
- raw_input = body.get("text", "")
492
- session_info = body.get("sessionInfo", {})
493
- parameters = session_info.get("parameters")
494
- trip_select: list[dict[str, any]] = parameters.get("trip_select")
495
- trips: list[dict[str, any]] = parameters.get("trips")
496
-
497
- if trip_select is None and trips is None:
498
- return DialogFlowResponseAPI()
499
-
500
- if raw_input:
501
- raw_input = raw_input.strip()
502
- for item in trip_select:
503
- if item["route"] == raw_input:
504
- id = int(item["trip_id"])
505
- trip = dialog_service.get_trip_by_id(id, trips)
506
- parameters = {
507
- "is_valid_trip": True,
508
- "trip": trip
509
- }
510
- return DialogFlowResponseAPI(parameters=parameters)
511
-
512
- parameters = {
513
- "is_valid_trip": False,
514
- }
515
- return DialogFlowResponseAPI(parameters=parameters)
516
-
517
- @router.post('/trip/check-time-ambiguous')
518
- async def check_time_ambiguous(request: Request) -> Response:
519
- try:
520
- body = await request.json()
521
- session_info = body.get("sessionInfo", {})
522
- parameters = session_info.get("parameters")
523
- time = parameters.get("time-select")
524
-
525
- parameters = {}
526
-
527
- time = extra_time_dialogflow(time)
528
-
529
-
530
- if isinstance(time, list) or time is None:
531
- parameters["is_time_ambiguous"] = True
532
- else:
533
- parameters["is_time_ambiguous"] = None
534
-
535
- return DialogFlowResponseAPI(parameters=parameters)
536
-
537
- except Exception as e:
538
- print(e)
539
- return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."])
540
- @router.post('/trip/select-time-ambiguous')
541
- async def select_time(request: Request) -> Response:
542
- try:
543
- body = await request.json()
544
- session_info = body.get("sessionInfo", {})
545
- parameters = session_info.get("parameters")
546
- time = parameters.get("time-select")
547
-
548
- parameters = {}
549
-
550
- time = extra_time_dialogflow(time)
551
-
552
- text = []
553
-
554
- if time is None:
555
- text = ["Quý khách vui lòng chỉ định thời gian đi cụ thể trong ngày?"]
556
-
557
- elif isinstance(time, list):
558
- text = [f"Quý khách dự định đi vào lúc"]
559
- payload={
560
- "richContent": [
561
- [
562
- {
563
- "type": "chips",
564
- "options": [
565
- {"text": item} for item in (time)
566
- ]
567
- }
568
- ]
569
- ]
570
- }
571
- else:
572
- parameters["is_time_ambiguous"] = None
573
-
574
- return DialogFlowResponseAPI(text=text,payload=payload,parameters=parameters)
575
-
576
- except Exception as e:
577
- print(e)
578
- return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."])
579
-
580
-
581
- # Không xài
582
- @router.post('/trip/time-trip-list')
583
- async def time_trip(request: Request) -> Response:
584
- try:
585
- body = await request.json()
586
- session_info = body.get("sessionInfo", {})
587
- parameters = session_info.get("parameters")
588
- trip_list: list[Dict[str, any]] = parameters.get("trip_list", [])
589
- raw_route_id = parameters.get("route_id")
590
- route_name = parameters.get("route_name", "")
591
- raw_time = parameters.get("time-select")
592
- time_list = parameters.get("time_list", [])
593
- is_has_time = parameters.get("is_has_time", False)
594
-
595
- route_id = int(raw_route_id) if raw_route_id else None
596
-
597
- text = []
598
- quick_time_reply = []
599
- parameters = {}
600
-
601
-
602
-
603
- if raw_time:
604
- time = extra_time_dialogflow(raw_time)
605
- quick_time_reply = time_list
606
-
607
- if isinstance(time, list):
608
- parameters["time_select"] = None
609
- parameters["is_time_ambiguous"] = True
610
- text = [f"Quý khách dự định đi vào lúc"]
611
- payload={
612
- "richContent": [
613
- [
614
- {
615
- "type": "chips",
616
- "options": [
617
- {"text": item} for item in (time)
618
- ]
619
- }
620
- ]
621
- ]
622
- }
623
- return DialogFlowResponseAPI(text=text, payload=payload ,parameters=parameters)
624
- else:
625
- if is_has_time:
626
- text = [f"Quý khách lựa chọn thời gian chuyến {route_name}\n" + " | ".join([item["time"] for item in quick_time_reply])]
627
- parameters["is_has_time"] = None
628
- else:
629
- parameters["is_has_time"] = True
630
- return DialogFlowResponseAPI(text=text,parameters=parameters)
631
- else:
632
- for trip in trip_list:
633
- if (trip["route_id"]) == route_id:
634
- time_list.append({"time": trip["departure_time"], "trip_id": trip["id"]})
635
- quick_time_reply = time_list
636
- text = [f"Quý khách lựa chọn thời gian chuyến {route_name}\n" + " | ".join([item["time"] for item in quick_time_reply])]
637
-
638
- parameters["time_list"] = time_list
639
- payload={
640
- "richContent": [
641
- [
642
- {
643
- "type": "chips",
644
- "options": [
645
- {"text": time["time"]} for time in (quick_time_reply)
646
- ]
647
- }
648
- ]
649
- ]
650
- }
651
- return DialogFlowResponseAPI(text=text, payload=payload ,parameters=parameters)
652
- except Exception as e:
653
- print(e)
654
- return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."])
655
-
656
- # Không xài
657
- @router.post('/trip/check-time-select')
658
- async def is_valid_select_time(request: Request) -> Response:
659
- try:
660
- body = await request.json()
661
- session_info = body.get("sessionInfo", {})
662
- parameters = session_info.get("parameters")
663
- trip_list: list[Dict[str, any]] = parameters.get("trip_list", [])
664
- raw_route_id = parameters.get("route_id")
665
-
666
- raw_departure_city, raw_destination_city, raw_ticket_number, raw_date, raw_time_of_day = dialog_service.get_param_from_dialogflow(body)
667
- route_id = int(raw_route_id) if raw_route_id else None
668
-
669
-
670
- from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date)
671
- ticket_count = int(raw_ticket_number) if raw_ticket_number else 1
672
-
673
- origin_office = parameters.get("origin_office")
674
- dest_office = parameters.get("dest_office")
675
-
676
- origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = None, None, None, None, None, None
677
- if origin_office:
678
- origin_id, origin_code = await dialog_service.find_id_and_code_provine_by_name_office(origin_office)
679
- origin_ids = await dialog_service.find_id_office_by_name_office(origin_office)
680
- elif raw_departure_city:
681
- origin_id, origin_code = get_origin_id_and_code(raw_departure_city)
682
- if dest_office:
683
- dest_id, dest_code = await dialog_service.find_id_and_code_provine_by_name_office(dest_office)
684
- dest_ids = await dialog_service.find_id_office_by_name_office(dest_office)
685
- elif raw_destination_city:
686
- dest_id, dest_code = get_origin_id_and_code(raw_destination_city)
687
- route_ids = await dialog_service.search_all_route_ids(origin_code=origin_code, from_id=origin_id, orign_ids=origin_ids, dest_code=dest_code, to_id=dest_id, dest_ids=dest_ids)
688
-
689
-
690
- time_list: list[Dict[str, any]] = parameters.get("time_list", [])
691
- time_select = parameters.get("time-select")
692
- route_name = parameters.get("route_name")
693
- is_has_time = parameters.get("is_has_time")
694
-
695
- if len(time_list) == 0:
696
- for trip in trip_list:
697
- if (trip["route_id"]) == route_id:
698
- time_list.append({"time": trip["departure_time"], "trip_id": trip["id"]})
699
-
700
- if time_select:
701
- time_select = extra_time_dialogflow(time_select)
702
- for time in time_list:
703
- if time_select == time["time"]:
704
- id = int(time["trip_id"])
705
- trip = await dialog_service.search_trip_by_id(id, from_time, to_time, route_ids, ticket_count)
706
- departure_date = trip["raw_departure_date"]
707
- parameters = {
708
- "is_valid_time": True,
709
- "departure_time": time_select,
710
- "departure_date": departure_date,
711
- "trip": trip,
712
- }
713
- text = [f'Quý khách chọn chuyến **{time_select}** | **{route_name}**']
714
- return DialogFlowResponseAPI(text=text, parameters=parameters)
715
- if is_has_time:
716
- time_list = find_surrounding_times(time_list, time_select)
717
- parameters = {
718
- "time_list": time_list,
719
- "is_valid_time": False
720
- }
721
-
722
- return DialogFlowResponseAPI(parameters=parameters)
723
-
724
- parameters = {
725
- "time_select": None,
726
- "is_valid_time": False
727
- }
728
- text = []
729
- return DialogFlowResponseAPI(text=text, parameters=parameters)
730
- except Exception as e:
731
- print(e)
732
- return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."])
733
-
734
-
735
- @router.post('/trip/seats')
736
- async def seats_trip(request: Request) -> Response:
737
- try:
738
- body = await request.json()
739
- session_info = body.get("sessionInfo", {})
740
- parameters = session_info.get("parameters")
741
-
742
- trip = parameters.get("trip", None)
743
- route_id = int(trip.get("route_id")) if trip.get("route_id") else None
744
- trip_id = int(trip.get("id")) if trip.get("id") else None
745
-
746
- departure_date: str = trip.get("raw_departure_date")
747
- departure_time: str = trip.get("raw_departure_time")
748
- kind: str = trip.get("seat_type_name")
749
-
750
- seats = await dialog_service.seats_trip(route_id, trip_id, departure_date, departure_time, kind)
751
- seats_empty = [ seat for seat in seats if seat["bookStatus"] == 0 ]
752
- seats_empty.sort(key=lambda x: x["chair"])
753
- text=["Quý khách vui lòng chọn ghế"]
754
- payload = {}
755
- if seats_empty:
756
- payload={
757
- "richContent": [
758
- [
759
- {
760
- "type": "chips",
761
- "options": [
762
- {"text": seat["chair"]} for seat in (seats_empty)
763
- ]
764
- }
765
- ]
766
- ]
767
- }
768
- else:
769
- text = ["Không còn ghế trống. Quý khách vui lòng lựa chọn chuyến khác"]
770
- parameters = {
771
- "seat_list": seats
772
- }
773
- return DialogFlowResponseAPI(text=text, payload=payload, parameters=parameters)
774
- except Exception as e:
775
- print(e)
776
- return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."])
777
- @router.post('/trip/check-seat-select')
778
- async def is_valid_select_seat(request: Request) -> Response:
779
- try:
780
- body = await request.json()
781
- session_info = body.get("sessionInfo", {})
782
- parameters = session_info.get("parameters")
783
-
784
- trip: list[dict[str, any]] = parameters.get("trip", None)
785
-
786
- route_id = int(trip.get("route_id")) if trip.get("route_id") else None
787
-
788
- trip_id = int(trip.get("id")) if trip.get("id") else None
789
-
790
- departure_date: str = trip.get("raw_departure_date")
791
-
792
- departure_time: str = trip.get("raw_departure_time")
793
-
794
- kind: str = trip.get("seat_type_name")
795
-
796
- seat: str = parameters.get("seat")
797
-
798
- is_valid = await dialog_service.is_valid_select_seat(seat, route_id, trip_id, departure_date, departure_time, kind)
799
- if is_valid:
800
- parameters = {
801
- "is_valid_seat": True,
802
- "seat": seat
803
- }
804
- text = [f"Quý khách chọn ghế **{seat.upper()}**"]
805
- else:
806
- parameters = {
807
- "is_valid_seat": False,
808
- "seat": None
809
- }
810
- text = [f"Ghế **{seat.upper()}** không hợp lệ. Quý khách vui lòng chọn ghế khác"]
811
- return DialogFlowResponseAPI(text=text, parameters=parameters)
812
-
813
- except Exception as e:
814
- return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."])
815
-
816
- @router.post('/trip/check-exist-user-info')
817
- async def check_exist_user_info(request: Request) -> Response:
818
- body = await request.json()
819
- session_info = body.get("sessionInfo", {})
820
- parameters = session_info.get("parameters")
821
-
822
- is_exist_user_info = await dialog_service.check_exist_user_info()
823
-
824
- user_info = {}
825
-
826
- if is_exist_user_info:
827
- user_info = await dialog_service.get_user_info()
828
-
829
- user_name = user_info.get("user_name")
830
- phone_number = user_info.get("phone_number")
831
- email = user_info.get("email")
832
-
833
- parameters = {
834
- "is_user_exist": is_exist_user_info,
835
- "user_name": user_name,
836
- "phone_number": phone_number,
837
- "email": email
838
- }
839
-
840
- return DialogFlowResponseAPI(parameters=parameters)
841
-
842
- @router.post('/trip/extract-user-name')
843
- async def get_user_name(request: Request) -> Response:
844
- body = await request.json()
845
- session_info = body.get("sessionInfo", {})
846
- parameters = session_info.get("parameters")
847
-
848
- raw_text_user_name = (body.get("text",""))
849
-
850
- ner: NER = request.app.state.ner
851
-
852
- user_name = await dialog_service.extract_user_name(text=raw_text_user_name, ner=ner)
853
-
854
- parameters = {
855
- "user_name": user_name
856
- }
857
-
858
- return DialogFlowResponseAPI(parameters=parameters)
859
-
860
- @router.post('/trip/stop/pickup')
861
- async def pickup(request: Request) -> Response:
862
- body = await request.json()
863
- session_info = body.get("sessionInfo", {})
864
- parameters = session_info.get("parameters")
865
-
866
- trip: list[dict[str, any]] = parameters.get("trip", {})
867
-
868
- route_id = int(trip.get("route_id")) if trip.get("route_id") else None
869
-
870
- way_id = int(trip.get("way_id")) if trip.get("way_id") else None
871
-
872
- pickup_list = await dialog_service.pickup_list(route_id, way_id)
873
-
874
- text=["Quý khách vui lòng chọn điểm đón"]
875
- payload={
876
- "richContent": [
877
- [
878
- {
879
- "type": "chips",
880
- "options": [
881
- {"text": pickup["name"]} for pickup in (pickup_list)
882
- ]
883
- }
884
- ]
885
- ]
886
- }
887
- parameters = {
888
- "pickup_list": pickup_list
889
- }
890
-
891
- return DialogFlowResponseAPI(text=text, payload=payload, parameters=parameters)
892
-
893
- @router.post('/trip/check-pickup-select')
894
- async def is_valid_select_pickup(request: Request) -> Response:
895
- body = await request.json()
896
- session_info = body.get("sessionInfo", {})
897
- parameters = session_info.get("parameters")
898
-
899
- trip: list[dict[str, any]] = parameters.get("trip", {})
900
-
901
- route_id = int(trip.get("route_id")) if trip.get("route_id") else None
902
-
903
- way_id = int(trip.get("way_id")) if trip.get("way_id") else None
904
-
905
- raw_input = (body.get("text",""))
906
- pickup = raw_input.strip()
907
-
908
- is_valid = await dialog_service.is_valid_pickup(pickup, route_id, way_id)
909
-
910
- if is_valid:
911
- parameters = {
912
- "is_valid_pickup": True,
913
- "pick_up": pickup
914
- }
915
- text = [f"Quý khách chọn điểm đón **{pickup}**"]
916
- else:
917
- parameters = {
918
- "is_valid_pickup": False,
919
- }
920
- # text = [f"Điểm đón không hợp lệ. Quý khách vui lòng chọn điểm đón khác"]
921
- text = []
922
-
923
- return DialogFlowResponseAPI(text=text, parameters=parameters)
924
-
925
- @router.post('/trip/stop/dropoff')
926
- async def dropoff(request: Request) -> Response:
927
- body = await request.json()
928
- session_info = body.get("sessionInfo", {})
929
- parameters = session_info.get("parameters")
930
-
931
- trip: list[dict[str, any]] = parameters.get("trip", {})
932
-
933
- route_id = int(trip.get("route_id")) if trip.get("route_id") else None
934
-
935
- way_id = int(trip.get("way_id")) if trip.get("way_id") else None
936
-
937
- dropoff_list = await dialog_service.dropoff_list(route_id, way_id)
938
-
939
- text=["Quý khách vui lòng chọn điểm trả khách"]
940
- payload={
941
- "richContent": [
942
- [
943
- {
944
- "type": "chips",
945
- "options": [
946
- {"text": dropoff["name"]} for dropoff in (dropoff_list)
947
- ]
948
- }
949
- ]
950
- ]
951
- }
952
- parameters = {
953
- "dropoff_list": dropoff_list
954
- }
955
-
956
- return DialogFlowResponseAPI(text=text, payload=payload, parameters=parameters)
957
-
958
- @router.post('/trip/check-dropoff-select')
959
- async def is_valid_select_dropoff(request: Request) -> Response:
960
- body = await request.json()
961
- session_info = body.get("sessionInfo", {})
962
- parameters = session_info.get("parameters")
963
-
964
- trip: list[dict[str, any]] = parameters.get("trip", {})
965
-
966
- route_id = int(trip.get("route_id")) if trip.get("route_id") else None
967
-
968
- way_id = int(trip.get("way_id")) if trip.get("way_id") else None
969
-
970
- raw_input = (body.get("text",""))
971
- dropoff = raw_input.strip()
972
-
973
- is_valid = await dialog_service.is_valid_dropoff(dropoff, route_id, way_id)
974
-
975
- if is_valid:
976
- parameters = {
977
- "is_valid_dropoff": True,
978
- "drop_off": dropoff
979
- }
980
- text = [f"Quý khách chọn điểm trả khách **{dropoff}**"]
981
- else:
982
- parameters = {
983
- "is_valid_dropoff": False,
984
- }
985
- # text = [f"Điểm trả khách không hợp lệ. Quý khách vui lòng chọn điểm trả khách khác"]
986
- text = []
987
-
988
- return DialogFlowResponseAPI(text=text, parameters=parameters)
989
-
990
- @router.post('/ticket/info')
991
- async def response_ticket_info(request: Request) -> Response:
992
- body = await request.json()
993
- session_info = body.get("sessionInfo", {})
994
- parameters = session_info.get("parameters")
995
-
996
- raw_user_name = parameters.get("user_name")
997
- user_name = raw_user_name["name"] if raw_user_name else None
998
-
999
- phone_number = parameters.get("phone_number")
1000
-
1001
- email = parameters.get("email")
1002
-
1003
- seat: str = parameters.get("seat")
1004
-
1005
- pickup = parameters.get("pick_up")
1006
-
1007
- dropoff = parameters.get("drop_off")
1008
-
1009
- trip = parameters.get("trip", {})
1010
-
1011
- route_name = trip["route"]["name"]
1012
-
1013
- time = trip["raw_departure_time"]
1014
-
1015
- date = trip["raw_departure_date"]
1016
-
1017
- price = int(trip.get("price")) if trip.get("price") else None
1018
-
1019
- text = [
1020
- f" \
1021
- **Thông tin hành khách**\n\
1022
- **Họ và tên** {user_name} \n\
1023
- **Số điện thoại** {phone_number}\n\
1024
- **Email** {email} \n\
1025
- **Thông tin lượt đi**\n\
1026
- **Tuyến xe** {route_name} \n\
1027
- **Thời gian xuất bến** {time} {date} \n\
1028
- **Số ghế** {seat.upper()} \n\
1029
- **Điểm lên xe** {pickup} \n\
1030
- **Điểm trả khách** {dropoff} \n\
1031
- **Tổng tiền lượt đi** {price} VND \
1032
- "
1033
- ]
1034
-
1035
- payload={
1036
- "richContent": [
1037
- [
1038
- {
1039
- "type": "chips",
1040
- "options": [
1041
- {"text": "Đặt vé"},
1042
- {"text": "Không, cảm ơn"}
1043
- ]
1044
- }
1045
- ]
1046
- ]
1047
- }
1048
-
1049
-
1050
- return DialogFlowResponseAPI(text=text, payload=payload)
1051
-
1052
-
1053
- @router.get("/")
1054
- def home():
1055
- return "Hello World!"
1056
-
1057
- @router.get('/chatbot', response_class=HTMLResponse)
1058
- async def index(request: Request):
1059
- return templates.TemplateResponse("index.html", {"request": request, "title": "Dawi Chatbot"})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/dialogflow/services/__init__.py DELETED
File without changes
app/dialogflow/services/__pycache__/__init__.cpython-310.pyc DELETED
Binary file (181 Bytes)
 
app/dialogflow/services/__pycache__/api.cpython-39.pyc DELETED
Binary file (2.86 kB)
 
app/dialogflow/services/__pycache__/dialog_service.cpython-310.pyc DELETED
Binary file (8.72 kB)
 
app/dialogflow/services/__pycache__/external_api.cpython-39.pyc DELETED
Binary file (2.86 kB)
 
app/dialogflow/services/__pycache__/origin_codes.cpython-310.pyc DELETED
Binary file (352 Bytes)
 
app/dialogflow/services/dialog_service.py DELETED
@@ -1,432 +0,0 @@
1
- from bisect import bisect_left
2
- from datetime import datetime, timedelta
3
-
4
- from fastapi import logger
5
-
6
- from app.ner.services.ner import NER
7
-
8
- from common.external.external_api import api
9
- from core.conf import settings
10
-
11
- class DialogService:
12
- @staticmethod
13
- def to_datetime_from_Dialogflow(time: dict):
14
- date_time = datetime(int(time["year"]), int(time["month"]), int(time["day"]))
15
- return date_time
16
-
17
- @staticmethod
18
- def process_dates_to_timestamp(from_time: datetime = None, to_time: datetime = None):
19
- if to_time is None and from_time is not None:
20
- to_time = from_time.replace(hour=23, minute=59, second=59)
21
-
22
- if from_time is None:
23
- today = datetime.today().date()
24
- from_time = datetime.combine(today, datetime.min.time())
25
- to_time = datetime.combine(today, datetime.max.time()) - timedelta(microseconds=1)
26
-
27
- return int(from_time.timestamp()) * 1000 , int(to_time.timestamp()) * 1000
28
-
29
- def get_param_from_dialogflow(self, body: any):
30
- session_info = body.get("sessionInfo", {})
31
- parameters = session_info.get("parameters", {}) if isinstance(session_info.get("parameters"), dict) else {}
32
- raw_date = parameters.get("date")
33
- if raw_date is not None:
34
- raw_date = self.to_datetime_from_Dialogflow(raw_date)
35
- raw_departure_city = parameters.get("departure_city")
36
- raw_destination_city = parameters.get("destination_city")
37
- raw_ticket_number = parameters.get("ticket_number")
38
- raw_time_of_day = parameters.get("time_of_day")
39
- return raw_departure_city, raw_destination_city, raw_ticket_number, raw_date, raw_time_of_day
40
-
41
- # Danh sách route_id API v1
42
- @staticmethod
43
- async def search_route_ids_one_way_by_metadata(origin_code: str = None, from_id: int = None, orign_ids: int = None, dest_code: str = None, to_id: str = None, dest_ids: str = None):
44
- params = {k: v for k, v in {
45
- "OriginCode": origin_code,
46
- "FromId": from_id,
47
- "OriginIds": orign_ids,
48
- "DestCode": dest_code,
49
- "ToId": to_id,
50
- "DestIds": dest_ids
51
- }.items() if v is not None}
52
- response = await api.get(f'/metadata/office/routes' , params=params)
53
- route_ids = []
54
- if isinstance(response, list):
55
- route_ids = [route.get("routeId", None) for route in response]
56
- return route_ids
57
- return []
58
-
59
- # Danh sách route_id v2 khi API route v1 không có dữ liệu
60
- @staticmethod
61
- async def search_route_ids_one_way_by_origin_dest_code(origin: str, dest: str):
62
- try:
63
- route_ids = []
64
- response = await api.get(f'/booking/api/v2/routes/from/{origin}/to/{dest}')
65
- if response.get("Status") == 200:
66
- if response.get("Data"):
67
- data = response.get("Data")
68
- route_ids = [ route.get("Id", None) for route in data ]
69
- return route_ids
70
- except Exception as e:
71
- print(e)
72
- raise Exception("Error fetching seats data")
73
-
74
- async def search_all_route_ids(self, origin_code: str = None, from_id: int = None, orign_ids: int = None, dest_code: str = None, to_id: str = None, dest_ids: str = None):
75
- route_ids = await self.search_route_ids_one_way_by_metadata(origin_code, from_id, orign_ids, dest_code, to_id, dest_ids)
76
- if len(route_ids) == 0:
77
- route_ids = await self.search_route_ids_one_way_by_origin_dest_code(origin_code, dest_code)
78
- return route_ids
79
- # Danh sách ghế
80
- @staticmethod
81
- async def seats_trip(route_id: int, trip_id:int, departure_date: str, departure_time: str, kind: str):
82
- try:
83
- params ={k: v for k, v in {
84
- "departureDate": departure_date,
85
- "departureTime": departure_time,
86
- "kind": kind
87
- }.items() if v is not None}
88
- if params is None:
89
- return []
90
- response = await api.get(api_base=settings.API_BASE_URL_VATO, endpoint=f"/seats/{route_id}/{trip_id}" , params=params)
91
- if not response.get("data"):
92
- return []
93
- seats = response["data"]
94
- return seats
95
- except Exception as e:
96
- print(e)
97
- raise Exception("Error fetching seats data")
98
-
99
- # Call API để lấy danh sách chuyến đi
100
- @staticmethod
101
- async def search_trip(from_time: int, to_time: int, route_ids: list[int], ticket_count: int = 1):
102
- try:
103
- if len(route_ids) == 0:
104
- return []
105
- ###
106
- payload = { k: v for k, v in {
107
- "channel": "web_client",
108
- "size":300,
109
- "only_online_trip": True,
110
- "from_time": from_time,
111
- "to_time": to_time,
112
- "route_ids": route_ids,
113
- "ticket_count": ticket_count,
114
- "sort_by": ["price", "departure_time"]
115
- }.items() if v is not None
116
- }
117
-
118
- data = []
119
- response = await api.post("/search/trips", payload=payload)
120
- if response.get("status") == 200:
121
- if response.get("data"):
122
- if response["data"].get("total") > 0:
123
- data = response["data"]["items"]
124
- return data
125
- except Exception as e:
126
- print(e)
127
- raise Exception("Error fetching trip data")
128
-
129
- # Chuyến đi khớp với thời gian và văn phòng đón trả
130
- @staticmethod
131
- def get_trip_by_time_and_office_id(trips: list, time: str ,origin_id: int, dest_id: int) -> dict:
132
- if time is None or origin_id is None or dest_id is None:
133
- return {}
134
-
135
- for trip in trips:
136
- if trip and trip["raw_departure_time"] == time and trip["route"]["origin_hub_office_id"] == origin_id and trip["route"]["dest_hub_office_id"] == dest_id:
137
- return trip
138
- return {}
139
-
140
-
141
- # Danh sách chuyến đi có thể chọn được theo văn phòng
142
- @staticmethod
143
- def get_all_trip_by_office(trips: list, origin_id: int = None, dest_id: int = None) -> list:
144
- if origin_id is None and dest_id is None:
145
- return trips
146
-
147
- result = []
148
- for trip in trips:
149
- pickup_points = trip.get("pickup_points", [])
150
- pickup_point_ids = {p["OfficeId"] for p in pickup_points if "OfficeId" in p}
151
-
152
- origin_match = (
153
- (origin_id is None) or
154
- (trip["route"]["origin_hub_office_id"] == origin_id) or
155
- (origin_id in pickup_point_ids)
156
- )
157
-
158
- dest_match = (
159
- (dest_id is None) or
160
- (trip["route"]["dest_hub_office_id"] == dest_id) or
161
- (dest_id in pickup_point_ids)
162
- )
163
-
164
- if origin_match and dest_match:
165
- result.append(trip)
166
-
167
- return result if result else trips
168
- # Danh sách 4 chuyến đi xung quanh thời gian chỉ định
169
- @staticmethod
170
- def get_surrounding_trip(trips: list, time: str, num_trip: int = 4) -> list:
171
- if not time or not trips or num_trip <= 0:
172
- return []
173
-
174
- time_trips = [ trip["raw_departure_time"] for trip in trips]
175
- index = bisect_left(time_trips, time)
176
-
177
- half = num_trip // 2
178
- start = max(0, index - half)
179
- end = min(len(trips), index + half + (num_trip % 2))
180
-
181
- missing = num_trip - (end - start)
182
-
183
- if missing > 0:
184
- extra_start = min(missing, start)
185
- start -= extra_start
186
- missing -= extra_start
187
-
188
- if missing > 0:
189
- extra_end = min(missing, len(trips) - end)
190
- end += extra_end
191
-
192
- return trips[start:end]
193
-
194
-
195
-
196
- async def is_valid_select_seat(self, seat: str,route_id: int, trip_id:int, departure_date: str, departure_time: str, kind: str):
197
- if seat is None:
198
- return False
199
- seats = await self.seats_trip(route_id, trip_id, departure_date, departure_time, kind)
200
- for seat_data in seats:
201
- if seat_data['chair'].lower() == seat.lower() and seat_data['bookStatus'] == 0 :
202
- return True
203
- return False
204
-
205
- async def search_trip_by_id(self, trip_id: int, from_time: int, to_time: int, route_ids: list[int], ticket_count: int = 1):
206
- trip = await self.search_trip(from_time, to_time, route_ids, ticket_count)
207
- for item in trip:
208
- if trip_id == item["id"]:
209
- return item
210
- return None
211
- @staticmethod
212
- def get_trip_by_id(trip_id: int, trips: list):
213
- for trip in trips:
214
- if trip and trip["id"] == trip_id:
215
- return trip
216
- return {}
217
-
218
- @staticmethod
219
- async def stops(route_id: int, way_id: int):
220
- try:
221
- if route_id is None or way_id is None:
222
- return []
223
- params ={ k: v for k,v in {
224
- "wayId": way_id,
225
- }.items() if v is not None}
226
- response = await api.get(api_base=settings.API_BASE_URL_VATO, endpoint=f"/stops/{route_id}", params=params)
227
- if not response.get("data"):
228
- return []
229
- data = response["data"]
230
- return data
231
- except Exception as e:
232
- print(e)
233
- raise Exception("Error fetching stops data")
234
-
235
- async def pickup_list(self, route_id: int, way_id: int):
236
- try:
237
- data = await self.stops(route_id, way_id)
238
- if not data:
239
- return []
240
-
241
- pickup_list = []
242
-
243
- for pickup in data:
244
- if pickup["type"] == 0 or pickup["type"] == -1:
245
- pickup_list.append(pickup)
246
- return pickup_list
247
-
248
- except Exception as e:
249
- print(e)
250
- raise Exception("Error fetching pickup list data")
251
-
252
- async def is_valid_pickup(self, pickup: str, route_id: int, way_id: int):
253
- if pickup is None:
254
- return False
255
- pickup_list = await self.pickup_list(route_id, way_id)
256
- for pickup_data in pickup_list:
257
- pickup_name: str = pickup_data['name']
258
- if pickup_name.lower() == pickup.lower():
259
- return True
260
- return False
261
-
262
- async def dropoff_list(self, route_id: int, way_id: int):
263
- try:
264
- data = await self.stops(route_id, way_id)
265
- if not data:
266
- return []
267
-
268
- dropoff_list = []
269
-
270
- for dropoff in data:
271
- if dropoff["type"] == 1 or ( dropoff["type"] == -1 and dropoff["presentBeforeMinutes"] >= 0 ):
272
- dropoff_list.append(dropoff)
273
- return dropoff_list
274
-
275
- except Exception as e:
276
- print(e)
277
- raise Exception("Error fetching dropoff list data")
278
-
279
- async def is_valid_dropoff(self, dropoff: str, route_id: int, way_id: int):
280
- if dropoff is None:
281
- return False
282
- dropoff_list = await self.dropoff_list(route_id, way_id)
283
- for dropoff_data in dropoff_list:
284
- if dropoff_data['name'] == dropoff:
285
- return True
286
- return False
287
- @staticmethod
288
- async def search_pickup_points(origin: str = None, dest: str = None) -> dict:
289
- session_id = str(int(datetime.now().timestamp()))
290
- params = {k: v for k, v in {
291
- "origin": origin,
292
- "dest": dest,
293
- "session_id": session_id,
294
- }.items() if v is not None
295
- }
296
-
297
- response = await api.get('/search/metadata/pickup-points', params=params)
298
- if response.get("status") == 200:
299
- data = response.get("data")
300
- return data
301
- return {}
302
-
303
- async def find_id_office_by_name_office(self, office_name: str):
304
- data = await self.search_pickup_points(origin=office_name)
305
- if data.get("origin"):
306
- origins = data["origin"]
307
- for origin in origins:
308
- if origin.get("group"):
309
- groups = origin["group"]
310
- for group in groups:
311
- if group.get("name"):
312
- name: str = group["name"]
313
- if name.lower() == office_name.lower():
314
- office_id = group["officeId"]
315
- office_id = office_id[0] if isinstance(office_id, tuple) else office_id
316
- return office_id
317
- return None
318
-
319
- async def find_id_provine_by_name_office(self, office_name: str):
320
- data = await self.search_pickup_points(origin=office_name)
321
- if data.get("origin"):
322
- origins = data["origin"]
323
- for origin in origins:
324
- if origin.get("group"):
325
- groups = origin["group"]
326
- for group in groups:
327
- if group.get("name"):
328
- name: str = group["name"]
329
- if name.lower() == office_name.lower():
330
- province_id = group["provinceId"]
331
- return province_id
332
- return None
333
-
334
- async def find_all_origin_by_name_office(self, office_name: str):
335
- data = await self.search_pickup_points(origin=office_name)
336
- if data.get("origin"):
337
- origins = data["origin"]
338
- return origins
339
- return None
340
-
341
- async def find_id_and_code_provine_by_name_office(self, office_name: str):
342
- data = await self.search_pickup_points(origin=office_name)
343
- if data.get("origin"):
344
- origins = data["origin"]
345
- for origin in origins:
346
- if origin.get("group"):
347
- groups = origin["group"]
348
- for group in groups:
349
- if group.get("name"):
350
- name: str = group["name"]
351
- if name.lower() == office_name.lower():
352
- province_id = group["provinceId"]
353
- province_code = group["provinceCode"]
354
- province_id = province_id[0] if isinstance(province_id, tuple) else province_id
355
- return province_id, province_code
356
- return None, None
357
-
358
- async def get_origin_city_from_office(self, origin_office: str):
359
- data = await self.search_pickup_points(origin=origin_office)
360
- if data.get("origin"):
361
- origins = data["origin"]
362
- for origin in origins:
363
- if origin.get("group"):
364
- groups = origin["group"]
365
- for group in groups:
366
- if group.get("name"):
367
- name:str = group["name"]
368
- if name.lower() == origin_office.lower():
369
- return group["provinceName"]
370
- return None
371
-
372
- async def get_destination_city_from_office(self, dest_office: str):
373
- data = await self.search_pickup_points(dest=dest_office)
374
- if data.get("dest"):
375
- dests = data["dest"]
376
- for dest in dests:
377
- if dest.get("group"):
378
- groups = dest["group"]
379
- for group in groups:
380
- if group.get("name"):
381
- name: str = group["name"]
382
- if name.lower() == dest_office.lower():
383
- return group["provinceName"]
384
- return None
385
-
386
- async def check_exist_user_info(self, user_id: str = None):
387
- try:
388
- # response = await api.get(f'/user/{user_id}')
389
- # if response.get("status") == 200:
390
- # return True
391
- return True
392
-
393
- # return False
394
- except Exception as e:
395
- logger.error(f"Error checking user info: {e}")
396
- return False
397
-
398
- async def get_user_info(self, user_id: str = None):
399
- try:
400
- # response = await api.get(f'/user/{user_id}')
401
- # if response.get("status") == 200:
402
- # return response.get("data")
403
- user_info = {
404
- "user_name": { "name": "Đại", "original": "Đại" },
405
- "phone_number": "0987654321",
406
- "email": "[email protected]"
407
- }
408
-
409
- return user_info
410
-
411
- # return None
412
- except Exception as e:
413
- logger.error(f"Error fetching user info: {e}")
414
- return None
415
- @staticmethod
416
- async def extract_user_name(text: str, ner: NER):
417
- if text is None:
418
- return None
419
-
420
- user_name_pred = await ner.predict(text=text, entity_tag="PERSON")
421
-
422
- if user_name_pred:
423
- user_name = user_name_pred[0]
424
-
425
- if user_name:
426
- user_name_resp = { "name": user_name, "original": user_name }
427
- return user_name_resp
428
-
429
- return None
430
-
431
- dialog_service: DialogService = DialogService()
432
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/dialogflow/services/origin_codes.py DELETED
@@ -1,14 +0,0 @@
1
- import json
2
-
3
- with open("static/files/origin-codes.json", "r", encoding="utf-8") as file:
4
- origin_codes = json.load(file)
5
-
6
- def get_origin_id_and_code(origin: str):
7
- for x in origin_codes:
8
- if x and x.get("name"):
9
- if origin == x["name"]:
10
- id = x["id"],
11
- code = x["code"]
12
- id = id[0] if isinstance(id, tuple) else id
13
- return id, code
14
- return None, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/dto/request.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, field_validator
2
+ from typing import Any
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ from app.dto.trip import TripDTO
7
+
8
+
9
+ class LocationRequest(BaseModel):
10
+ origin_office: str | None = None
11
+ departure_city: str | None = None
12
+ dest_office: str | None = None
13
+ destination_city: str | None = None
14
+
15
+
16
+ class TimeRequest(BaseModel):
17
+ time_select: dict | None = Field(default_factory=dict)
18
+
19
+
20
+ class TripQueryRequest(BaseModel):
21
+ departure_city: str | None = None
22
+ destination_city: str | None = None
23
+ origin_office: str | None = None
24
+ dest_office: str | None = None
25
+ ticket_number: int | None = None
26
+ date: dict | None = Field(default_factory=dict)
27
+ time_select: dict | None = Field(default_factory=dict)
28
+ id_selected: int | None = None
29
+
30
+ @field_validator("ticket_number", "id_selected", mode="before")
31
+ def validate_ticket_number(cls, v):
32
+ return int(v) if v else None
33
+
34
+ @field_validator("id_selected", mode="before")
35
+ def validate_selected_pickup(cls, v):
36
+ return int(v) if v else None
37
+
38
+
39
+ class TicketRequest(BaseModel):
40
+ user_name: str | None = None
41
+ phone_number: str | None = None
42
+ email: str | None = None
43
+ seat: str | None = None
44
+ pickup: str | None = None
45
+ dropoff: str | None = None
46
+ trip_data: TripDTO | None = None
47
+
48
+
49
+ class SeatQueryRequest(BaseModel):
50
+ seat: str | None = None
51
+ trip_data: TripDTO | None = None
52
+
53
+
54
+ class SessionInfo(BaseModel):
55
+ session: str | None = None
56
+ parameters: dict[str, Any] = Field(default_factory=dict)
57
+
58
+
59
+ class FulfillmentInfo(BaseModel):
60
+ tag: str | None = None
61
+
62
+
63
+ class DialogflowWebhookRequest(BaseModel):
64
+ text: str | None = None
65
+ sessionInfo: SessionInfo | None = None
66
+ fulfillmentInfo: FulfillmentInfo | None = None
67
+ detectIntentResponseId: str | None = None
68
+
69
+ def get_parameters(self) -> dict[str, Any]:
70
+ return self.sessionInfo.parameters if self.sessionInfo else {}
71
+
72
+ def get_session(self) -> str:
73
+ return (
74
+ self.sessionInfo.session
75
+ if self.sessionInfo and self.sessionInfo.session
76
+ else ""
77
+ )
78
+
79
+ def get_text(self) -> str:
80
+ return self.text.strip() if self.text else ""
81
+
82
+ def get_tag(self) -> str:
83
+ return (
84
+ self.fulfillmentInfo.tag
85
+ if self.fulfillmentInfo and self.fulfillmentInfo.tag
86
+ else ""
87
+ )
88
+
89
+
90
+ class StopRequest(BaseModel):
91
+ trip_data: TripDTO | None = None
92
+ id_selected: int | None = None
93
+
94
+ @field_validator("id_selected", mode="before")
95
+ def validate_selected_pickup(cls, v):
96
+ return int(v) if v else None
app/dto/response.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi.responses import JSONResponse
2
+ from pydantic import BaseModel, Field
3
+ from typing import List, Dict, Any, Optional
4
+
5
+
6
+ class MessageText(BaseModel):
7
+ text: list[str] = Field(default_factory=list)
8
+
9
+
10
+ class Message(BaseModel):
11
+ text: MessageText | None = None
12
+ payload: dict | None = None
13
+
14
+
15
+ class FulfillmentResponse(BaseModel):
16
+ messages: list[Message] = Field(default_factory=list)
17
+
18
+
19
+ class SessionInfo(BaseModel):
20
+ parameters: dict[str, Any] = Field(default_factory=dict)
21
+
22
+
23
+ class DialogflowResponse(BaseModel):
24
+ fulfillment_response: FulfillmentResponse
25
+ sessionInfo: Optional[SessionInfo] = Field(default_factory=dict)
26
+
27
+
28
+ def DialogFlowResponseAPI(
29
+ text: list[str] = None,
30
+ payload: dict | None = None,
31
+ parameters: dict | None = None,
32
+ ):
33
+
34
+ messages = []
35
+ if text:
36
+ messages.append(Message(text=MessageText(text=text)))
37
+
38
+ if payload:
39
+ messages.append(Message(payload=payload))
40
+
41
+ response_data = DialogflowResponse(
42
+ fulfillment_response=FulfillmentResponse(messages=messages),
43
+ sessionInfo=SessionInfo(parameters=parameters or {}),
44
+ )
45
+
46
+ return JSONResponse(content=response_data.model_dump())
47
+
48
+
49
+ def dialogflow_response(
50
+ text: list[str] | None = None,
51
+ payload: dict | None = None,
52
+ parameters: dict | None = None,
53
+ ) -> JSONResponse:
54
+ messages: List[Message] = []
55
+ if text:
56
+ messages.append(Message(text=MessageText(text=text)))
57
+ if payload:
58
+ messages.append(Message(payload=payload))
59
+
60
+ response = DialogflowResponse(
61
+ fulfillment_response=FulfillmentResponse(messages=messages),
62
+ sessionInfo=SessionInfo(parameters=parameters or {}),
63
+ )
64
+ return JSONResponse(content=response.model_dump(mode="json"))
65
+
66
+
67
+ def dialogflow_error(message: str) -> JSONResponse:
68
+ return dialogflow_response(text=[message])
69
+
70
+
71
+ def dialogflow_choice(
72
+ prompt: str, options: list[str], parameters: dict | None = None
73
+ ) -> JSONResponse:
74
+ chips = {
75
+ "richContent": [[{"type": "chips", "options": [{"text": o} for o in options]}]]
76
+ }
77
+ return dialogflow_response(text=[prompt], payload=chips, parameters=parameters)
78
+
79
+
80
+ class DialogflowResponseBuilder:
81
+ def __init__(self):
82
+ self._messages: list[Message] = []
83
+ self._parameters: dict = {}
84
+
85
+ def add_text(self, lines: list[str]) -> "DialogflowResponseBuilder":
86
+ if lines:
87
+ self._messages.append(Message(text=MessageText(text=lines)))
88
+ return self
89
+
90
+ def add_payload(self, payload: dict) -> "DialogflowResponseBuilder":
91
+ if payload:
92
+ self._messages.append(Message(payload=payload))
93
+ return self
94
+
95
+ def add_choice_chips(
96
+ self,
97
+ options: list[str] | list[dict],
98
+ key: str = None,
99
+ prompt: str | None = None,
100
+ ) -> "DialogflowResponseBuilder":
101
+ if options:
102
+ chips = {
103
+ "richContent": [
104
+ [
105
+ {
106
+ "type": "chips",
107
+ "options": [
108
+ (
109
+ {"text": option.get(key, "")}
110
+ if isinstance(option, dict)
111
+ else {"text": option}
112
+ )
113
+ for option in options
114
+ ],
115
+ }
116
+ ]
117
+ ]
118
+ }
119
+ self.add_payload(chips)
120
+ if prompt:
121
+ self.add_text([prompt])
122
+ return self
123
+
124
+ def set_parameters(self, parameters: dict[str, any]) -> "DialogflowResponseBuilder":
125
+ if parameters:
126
+ self._parameters = parameters
127
+ return self
128
+
129
+ def build(self) -> JSONResponse:
130
+ response = DialogflowResponse(
131
+ fulfillment_response=FulfillmentResponse(messages=self._messages),
132
+ sessionInfo=SessionInfo(parameters=self._parameters),
133
+ )
134
+ return JSONResponse(content=response.model_dump(mode="json"))
135
+
136
+
137
+ ## Use Builder return (
138
+ # DialogflowResponseBuilder()
139
+ # .add_choice_chips(["Xem vé", "Đặt vé"], prompt="Bạn muốn làm gì tiếp?")
140
+ # .set_parameters({"next_step": "booking"})
141
+ # .build()
142
+ # )
143
+
144
+
145
+ class DialogServiceResult(BaseModel):
146
+ text: list[str] | None = None
147
+ options: list[str] | list[dict] | None = None
148
+ parameters: dict | None = None
app/dto/seat.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class SeatDTO(BaseModel):
5
+ bookStatus: int
6
+ id: int
7
+ chair: str
8
+ rowNo: int
9
+ columnNo: int
10
+ discount: float
11
+ floorNo: int
12
+ inSelect: int
13
+ lockChair: int
14
+ lock: bool
15
+ originPrice: float
16
+ price: float
17
+ promotion: str | None = None
app/dto/ticket.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class PassengerDTO(BaseModel):
5
+ CustName: str
6
+ CustMobile: str
7
+ CustEmail: str
8
+ CustDestinationAddress: str
9
+
10
+
11
+ class DropoffDTO(BaseModel):
12
+ office_id: int
13
+
14
+
15
+ class InfoTicketDTO(BaseModel):
16
+ PickUpName: str
17
+ PickUpStreet: str
18
+ OfficePickupId: int
19
+ DropOffName: str
20
+ DropOffStreet: str
21
+ OfficeDropOffId: int
22
+ Dropoff: DropoffDTO
23
+ CarBookingId: int
24
+ DepartureDate: str
25
+ DepartureTime: str
26
+ Kind: str
27
+ WayId: int
28
+ DestCode: str
29
+ DestName: str
30
+ OriginCode: str
31
+ OriginName: str
32
+ Price: int
33
+ RouteId: int
34
+ RouteName: str
35
+ NumOfTicket: int
36
+ SeatIds: list[int]
37
+ SeatNames: list[str]
38
+ Passengers: list[PassengerDTO]
39
+ Channel: str
40
+
41
+
42
+ class TicketBookingDTO(BaseModel):
43
+ CustName: str
44
+ CustMobile: str
45
+ CustEmail: str
46
+ CustDestinationAddress: str
47
+ CustId: str
48
+ PickUpName: str
49
+ PickUpStreet: str
50
+ OfficePickupId: int
51
+ DropOffName: str
52
+ DropOffStreet: str
53
+ OfficeDropOffId: int
54
+ Dropoff: DropoffDTO
55
+ CarBookingId: int
56
+ DepartureDate: str
57
+ DepartureTime: str
58
+ Kind: str
59
+ WayId: int
60
+ DestCode: str
61
+ DestName: str
62
+ OriginCode: str
63
+ OriginName: str
64
+ Price: int
65
+ RouteId: int
66
+ RouteName: str
67
+ NumOfTicket: int
68
+ SeatIds: list[int]
69
+ SeatNames: list[str]
70
+ Passengers: list[PassengerDTO]
71
+ Channel: str
72
+ DeviceId: str | None = None
73
+ EnglishTicket: int
74
+ SeatDiscounts: list[str] # assuming it's list of strings; adjust as needed
75
+ CustAddress: str
76
+ CustCode: str
77
+ CustHomeAddress: str
78
+ CustMobile2: str
79
+ CustSN: str
80
+ InfoTicket: list[InfoTicketDTO]
app/dto/trip.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field, field_validator
2
+
3
+
4
+ class TripSearchDTO(BaseModel):
5
+ channel: str = "web_client"
6
+ size: int = 300
7
+ only_online_trip: bool = True
8
+ ticket_count: int
9
+ seat_type_id: list = Field(default_factory=list)
10
+ from_time: int
11
+ to_time: int
12
+ route_ids: list[int]
13
+ origin_office_id: list = Field(default_factory=list)
14
+ dest_office_id: list = Field(default_factory=list)
15
+ postion: list = Field(default_factory=list)
16
+ floor: list = Field(default_factory=list)
17
+ sort_by: list[str] = ["price", "departure_time"]
18
+
19
+
20
+ class TripOptionDTO(BaseModel):
21
+ trip_id: int
22
+ route: str
23
+
24
+ @field_validator("trip_id", mode="before")
25
+ def convert_float_to_int(cls, v):
26
+ return int(v) if v is not None else None
27
+
28
+
29
+ class Route(BaseModel):
30
+ name: str | None = None
31
+
32
+
33
+ class TripDTO(BaseModel):
34
+ id: int | None = None
35
+ route_id: int | None = None
36
+ way_id: int | None = None
37
+ route: Route | None = None
38
+ raw_departure_date: str | None = None
39
+ raw_departure_time: str | None = None
40
+ seat_type_name: str | None = None
41
+ price: int | None = None
42
+
43
+ @field_validator("id", "price", "route_id", "way_id", mode="before")
44
+ @classmethod
45
+ def convert_float_to_int(cls, v):
46
+ return int(v) if v is not None else None
47
+
48
+
49
+ class CoordinateDTO(BaseModel):
50
+ lat: float
51
+ lon: float
52
+
53
+
54
+ class PickupPointDTO(BaseModel):
55
+ OfficeId: int | None = None
56
+ Name: str | None = None
57
+ Address: str | None = None
58
+ Phone: str | None = None
59
+ PickUp: int | None = None
60
+ TimeOffice: int | None = None
61
+ PointKind: int | None = None
62
+ Note: str | None = None
63
+ PointKindName: str | None = None
64
+
65
+
66
+ class RouteDTO(BaseModel):
67
+ name: str
68
+ route_id: int
69
+ origin_code: str
70
+ origin_name: str
71
+ origin_hub_id: int
72
+ origin_hub_name: str
73
+ origin_hub_office_id: int
74
+ origin_hub_office_name: str
75
+ origin_hub_coords: CoordinateDTO
76
+ dest_code: str
77
+ dest_name: str
78
+ dest_hub_id: int
79
+ dest_hub_name: str
80
+ dest_hub_office_id: int
81
+ dest_hub_office_name: str
82
+ dest_hub_coords: CoordinateDTO
83
+ shuttle_enable: bool
84
+ allow_desktop: bool
85
+ allow_mobile_app: bool
86
+ allow_web_client: bool
87
+ allow_web_admin: bool
88
+
89
+
90
+ class ShuttleZoneResultDTO(BaseModel):
91
+ is_in_zone: bool
92
+ is_allow_user_to_toggle: bool
93
+ distance_to_user: float
94
+ zone: str | None = None
95
+ nearest_pickup_point: str | None = None
96
+
97
+
98
+ class ShuttleOptionDTO(BaseModel):
99
+ is_enable_shuttle: bool
100
+ route: RouteDTO
101
+ origin_result: ShuttleZoneResultDTO
102
+ dest_result: ShuttleZoneResultDTO
103
+
104
+
105
+ class TripItemDTO(BaseModel):
106
+ id: int
107
+ departure_time: int
108
+ raw_departure_time: str
109
+ raw_departure_date: str
110
+ arrival_time: int
111
+ duration: int
112
+ seat_type_id: int
113
+ seat_type_name: str
114
+ price: int
115
+ empty_seat_quantity: int
116
+ route_id: int
117
+ distance: int
118
+ route: RouteDTO
119
+ way_id: int
120
+ allow_online_booking: bool
121
+ online_booking_before: int
122
+ max_seats_per_booking: int
123
+ point_details: str | None = None
124
+ num_price: int
125
+ from_bus_station_address: str
126
+ to_bus_station_address: str
127
+ top_first_floor_quantity: int
128
+ middle_first_floor_quantity: int
129
+ last_first_floor_quantity: int
130
+ top_second_floor_quantity: int
131
+ middle_second_floor_quantity: int
132
+ last_second_floor_quantity: int
133
+ way_name: str
134
+ way_note: str
135
+ pickup_points: list[PickupPointDTO]
136
+ shuttle_option: ShuttleOptionDTO
app/dto/user.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class UserDTO(BaseModel):
5
+ user_name: str
6
+ phone_number: str
7
+ email: str
app/ner/services/ner.py CHANGED
@@ -1,11 +1,15 @@
 
 
1
  from core.conf import settings
2
 
 
3
  class NER:
4
  def __init__(self, model_dir: str = settings.NER_MODEL_DIR):
5
  self.model_dir = model_dir
6
  self.model = None
7
  self.tokenizer = None
8
  self.pipeline = None
 
9
 
10
  def load_model(self):
11
  from transformers import AutoTokenizer
@@ -13,68 +17,79 @@ class NER:
13
  from optimum.pipelines import pipeline
14
 
15
  self.tokenizer = AutoTokenizer.from_pretrained(
16
- self.model_dir,
17
- truncation=settings.TRUNCATE,
18
- max_length=settings.MAX_LENGTH
19
- )
20
  self.model = ORTModelForTokenClassification.from_pretrained(self.model_dir)
21
- self.pipeline = pipeline(task=settings.TASK_NAME,
22
- model=self.model,
23
- tokenizer=self.tokenizer,
24
- device=settings.DEVICE)
25
-
 
 
 
26
  async def predict(self, text: str, entity_tag: str = None):
 
 
 
 
27
  if self.pipeline is None:
28
  raise ValueError("Model not loaded. Please call load_model() first.")
29
-
30
  pred = self.pipeline(text)
31
-
32
  if entity_tag:
33
  return self.extract_entities(pred, entity_tag)
34
  return pred
35
-
36
- def extract_entities(self, result_pred: list[dict[str, any]], entity: str) -> list[str]:
 
 
37
  if self.pipeline is None:
38
  raise ValueError("Model not loaded. Please call load_model() first.")
39
  B_ENTITY = f"B-{entity}"
40
  I_ENTITY = f"I-{entity}"
41
-
42
  extracted_entities = []
43
  current_entity_tokens = []
44
-
45
  for item in result_pred:
46
  word = item["word"]
47
  entity_tag = item["entity"]
48
-
49
  if entity_tag == B_ENTITY:
50
  if current_entity_tokens:
51
- extracted_entities.append(self._combine_token(current_entity_tokens))
 
 
52
  current_entity_tokens = [word]
53
  elif entity_tag == I_ENTITY and current_entity_tokens:
54
  current_entity_tokens.append(word)
55
  else:
56
  if current_entity_tokens:
57
- extracted_entities.append(self._combine_token(current_entity_tokens))
 
 
58
  current_entity_tokens = []
59
-
60
  if current_entity_tokens:
61
  extracted_entities.append(self._combine_token(current_entity_tokens))
62
-
63
  return extracted_entities
64
 
65
  def _combine_token(self, tokens: list[str]) -> str:
66
  """Combines tokens into a single string, removing leading hashtags from the first token if present.
67
  Args:
68
  tokens (list[str]): List of tokens to combine.
69
-
70
  Returns:
71
  str: Combined string of tokens.
72
  """
73
  if not tokens:
74
  return ""
75
-
76
  words = []
77
-
78
  for token in tokens:
79
  if token.strip("#") != token:
80
  clean_token = token.strip("#")
@@ -84,5 +99,13 @@ class NER:
84
  words.append(clean_token)
85
  else:
86
  words.append(token)
87
-
88
  return " ".join(words)
 
 
 
 
 
 
 
 
 
1
+ from fastapi import Request
2
+ from loguru import logger
3
  from core.conf import settings
4
 
5
+
6
  class NER:
7
  def __init__(self, model_dir: str = settings.NER_MODEL_DIR):
8
  self.model_dir = model_dir
9
  self.model = None
10
  self.tokenizer = None
11
  self.pipeline = None
12
+ self.load_model()
13
 
14
  def load_model(self):
15
  from transformers import AutoTokenizer
 
17
  from optimum.pipelines import pipeline
18
 
19
  self.tokenizer = AutoTokenizer.from_pretrained(
20
+ self.model_dir, truncation=settings.TRUNCATE, max_length=settings.MAX_LENGTH
21
+ )
 
 
22
  self.model = ORTModelForTokenClassification.from_pretrained(self.model_dir)
23
+ self.pipeline = pipeline(
24
+ task=settings.TASK_NAME,
25
+ model=self.model,
26
+ tokenizer=self.tokenizer,
27
+ device=settings.DEVICE,
28
+ )
29
+ logger.info(f"Model loaded from {self.model_dir}")
30
+
31
  async def predict(self, text: str, entity_tag: str = None):
32
+
33
+ if not text:
34
+ return None
35
+
36
  if self.pipeline is None:
37
  raise ValueError("Model not loaded. Please call load_model() first.")
38
+
39
  pred = self.pipeline(text)
40
+
41
  if entity_tag:
42
  return self.extract_entities(pred, entity_tag)
43
  return pred
44
+
45
+ def extract_entities(
46
+ self, result_pred: list[dict[str, any]], entity: str
47
+ ) -> list[str]:
48
  if self.pipeline is None:
49
  raise ValueError("Model not loaded. Please call load_model() first.")
50
  B_ENTITY = f"B-{entity}"
51
  I_ENTITY = f"I-{entity}"
52
+
53
  extracted_entities = []
54
  current_entity_tokens = []
55
+
56
  for item in result_pred:
57
  word = item["word"]
58
  entity_tag = item["entity"]
59
+
60
  if entity_tag == B_ENTITY:
61
  if current_entity_tokens:
62
+ extracted_entities.append(
63
+ self._combine_token(current_entity_tokens)
64
+ )
65
  current_entity_tokens = [word]
66
  elif entity_tag == I_ENTITY and current_entity_tokens:
67
  current_entity_tokens.append(word)
68
  else:
69
  if current_entity_tokens:
70
+ extracted_entities.append(
71
+ self._combine_token(current_entity_tokens)
72
+ )
73
  current_entity_tokens = []
74
+
75
  if current_entity_tokens:
76
  extracted_entities.append(self._combine_token(current_entity_tokens))
77
+
78
  return extracted_entities
79
 
80
  def _combine_token(self, tokens: list[str]) -> str:
81
  """Combines tokens into a single string, removing leading hashtags from the first token if present.
82
  Args:
83
  tokens (list[str]): List of tokens to combine.
84
+
85
  Returns:
86
  str: Combined string of tokens.
87
  """
88
  if not tokens:
89
  return ""
90
+
91
  words = []
92
+
93
  for token in tokens:
94
  if token.strip("#") != token:
95
  clean_token = token.strip("#")
 
99
  words.append(clean_token)
100
  else:
101
  words.append(token)
102
+
103
  return " ".join(words)
104
+
105
+
106
+ def get_ner_model(request: Request) -> NER:
107
+ """
108
+ Dependency to get the NER model.
109
+ This can be used to inject the NER model into the endpoint.
110
+ """
111
+ return request.app.state.ner
app/ner/utils/__init__.py DELETED
File without changes
app/repositories/trip_repository.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Annotated
2
+ from fastapi import Depends
3
+ from typing_extensions import Literal
4
+ from app.client.futabus_client import FutabusClient
5
+
6
+
7
+ class TripRepository:
8
+
9
+ def __init__(self, client: FutabusClient):
10
+ self.client = client
11
+
12
+ async def get_all_route_ids(
13
+ self,
14
+ origin_code: str | None = None,
15
+ from_id: int | None = None,
16
+ origin_ids: int | None = None,
17
+ dest_code: str | None = None,
18
+ to_id: int | None = None,
19
+ dest_ids: int | None = None,
20
+ ) -> list[int]:
21
+ """
22
+ Lấy danh sách route_ids từ hệ thống external FUTABUS
23
+
24
+ Returns:
25
+ List[int]: Danh sách ID tuyến xe.
26
+ """
27
+
28
+ route_ids = await self.client.get_route_ids_by_metadata(
29
+ origin_code=origin_code,
30
+ from_id=from_id,
31
+ origin_ids=origin_ids,
32
+ dest_code=dest_code,
33
+ to_id=to_id,
34
+ dest_ids=dest_ids,
35
+ )
36
+
37
+ if not route_ids:
38
+ route_ids = await self.client.get_route_ids_by_code(
39
+ origin_code=origin_code,
40
+ dest_code=dest_code,
41
+ )
42
+ return route_ids
43
+
44
+ async def get_trip_list(
45
+ self, from_time: int, to_time: int, route_ids: list[int], ticket_count: int = 1
46
+ ) -> list:
47
+ return await self.client.search_trips(
48
+ from_time=from_time,
49
+ to_time=to_time,
50
+ route_ids=route_ids,
51
+ ticket_count=ticket_count,
52
+ )
53
+
54
+ async def get_stop(
55
+ self, route_id: int, way_id: int, role: Literal["pick-up", "drop-off"]
56
+ ) -> list:
57
+ """
58
+ Lấy danh sách các điểm đón trả khách của một tuyến xe cụ thể.
59
+
60
+ Args:
61
+ route_id (int): ID của tuyến xe.
62
+ way_id (int): ID của chiều đi hoặc về của tuyến xe.
63
+ role (Literal["pick-up", "drop-off"]): Vai trò của điểm dừng, có thể là "pick-up" hoặc "drop-off".
64
+ Returns:
65
+ list: Danh sách các điểm đón trả khách.
66
+ """
67
+ stops = await self.client.get_stop_by_route_and_way(
68
+ route_id=route_id, way_id=way_id
69
+ )
70
+ if role == "pick-up":
71
+ pickups = []
72
+
73
+ for stop in stops:
74
+ if stop["type"] == 0 or stop["type"] == -1:
75
+ pickups.append(stop)
76
+ return pickups
77
+
78
+ if role == "drop-off":
79
+ dropoffs = []
80
+
81
+ for stop in stops:
82
+ if stop["type"] == 1 or (
83
+ stop["type"] == -1 and stop["presentBeforeMinutes"] >= 0
84
+ ):
85
+ dropoffs.append(stop)
86
+ return dropoffs
87
+ else:
88
+ raise ValueError("Role must be either 'pick-up' or 'drop-off'")
app/resolvers/location_resolver.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Literal, Optional
2
+ from fastapi.params import Depends
3
+ from typing_extensions import Annotated
4
+ from app.services.location_service import LocationService
5
+ from app.resolvers.origin_code import get_city_id_and_code
6
+
7
+
8
+ class LocationResolver:
9
+ def __init__(self, location_service: LocationService):
10
+ self.location_service = location_service
11
+
12
+ async def resolve_info(
13
+ self,
14
+ office: Optional[str],
15
+ city: Optional[str],
16
+ role: Literal["origin", "dest"],
17
+ ) -> tuple[Optional[int], Optional[str], Optional[int]]:
18
+ if office:
19
+ id, code, _ = await self.location_service.get_city_by_office(
20
+ office_name=office, role=role
21
+ )
22
+ ids = await self.location_service.get_office_id_by_name(office)
23
+ return id, code, ids
24
+ if city:
25
+ id, code = get_city_id_and_code(city)
26
+ return id, code, None
27
+ return None, None, None
app/resolvers/origin_code.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import logging
3
+ import os
4
+ from typing import Optional, Tuple
5
+
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ ORIGIN_CODES_PATH = "static/files/origin-codes.json"
10
+
11
+
12
+ def load_origin_codes() -> list[dict]:
13
+ try:
14
+ with open(ORIGIN_CODES_PATH, "r", encoding="utf-8") as file:
15
+ return json.load(file)
16
+ except (FileNotFoundError, json.JSONDecodeError):
17
+ logger.error(f"Error loading origin codes from {ORIGIN_CODES_PATH}")
18
+ return []
19
+
20
+
21
+ def get_city_id_and_code(origin_name: str) -> Tuple[Optional[int], Optional[str]]:
22
+ """
23
+ Truy xuất province_id và province_code từ file JSON tĩnh theo tên tỉnh/thành phố.
24
+
25
+ Args:
26
+ origin_name (str): Tên thành phố gốc
27
+
28
+ Returns:
29
+ Tuple[province_id, province_code] hoặc (None, None) nếu không tìm thấy
30
+ """
31
+ origin_name = origin_name.strip().lower()
32
+ for entry in load_origin_codes():
33
+ name = entry.get("name", "").lower()
34
+ if name == origin_name:
35
+ return entry.get("id"), entry.get("code")
36
+ logger.warning(f"ID and Code not found for name: {origin_name}")
37
+ return None, None
app/router.py DELETED
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- from fastapi import APIRouter
4
-
5
- from app.dialogflow.api.router import v1 as dialogflow_v1
6
-
7
- router = APIRouter()
8
-
9
- router.include_router(dialogflow_v1)
 
 
 
 
 
 
 
 
 
 
app/{dialogflow/schemas → services}/__init__.py RENAMED
File without changes
app/services/dialog_service.py ADDED
@@ -0,0 +1,537 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # from bisect import bisect_left
2
+ # from datetime import datetime, timedelta
3
+
4
+ # from fastapi import logger
5
+
6
+ # from app.ner.services.ner import NER
7
+
8
+ # from common.external.external_api import api
9
+ # from core.conf import settings
10
+
11
+
12
+ # class DialogService:
13
+ # @staticmethod
14
+ # def to_datetime_from_Dialogflow(time: dict):
15
+ # date_time = datetime(int(time["year"]), int(time["month"]), int(time["day"]))
16
+ # return date_time
17
+
18
+ # @staticmethod
19
+ # def process_dates_to_timestamp(
20
+ # from_time: datetime = None, to_time: datetime = None
21
+ # ):
22
+ # if to_time is None and from_time is not None:
23
+ # to_time = from_time.replace(hour=23, minute=59, second=59)
24
+
25
+ # if from_time is None:
26
+ # today = datetime.today().date()
27
+ # from_time = datetime.combine(today, datetime.min.time())
28
+ # to_time = datetime.combine(today, datetime.max.time()) - timedelta(
29
+ # microseconds=1
30
+ # )
31
+
32
+ # return int(from_time.timestamp()) * 1000, int(to_time.timestamp()) * 1000
33
+
34
+ # def get_param_from_dialogflow(self, body: any):
35
+ # session_info = body.get("sessionInfo", {})
36
+ # parameters = (
37
+ # session_info.get("parameters", {})
38
+ # if isinstance(session_info.get("parameters"), dict)
39
+ # else {}
40
+ # )
41
+ # raw_date = parameters.get("date")
42
+ # if raw_date is not None:
43
+ # raw_date = self.to_datetime_from_Dialogflow(raw_date)
44
+ # raw_departure_city = parameters.get("departure_city")
45
+ # raw_destination_city = parameters.get("destination_city")
46
+ # raw_ticket_number = parameters.get("ticket_number")
47
+ # raw_time_of_day = parameters.get("time_of_day")
48
+ # return (
49
+ # raw_departure_city,
50
+ # raw_destination_city,
51
+ # raw_ticket_number,
52
+ # raw_date,
53
+ # raw_time_of_day,
54
+ # )
55
+
56
+ # # Danh sách route_id API v1
57
+ # @staticmethod
58
+ # async def search_route_ids_one_way_by_metadata(
59
+ # origin_code: str = None,
60
+ # from_id: int = None,
61
+ # orign_ids: int = None,
62
+ # dest_code: str = None,
63
+ # to_id: str = None,
64
+ # dest_ids: str = None,
65
+ # ):
66
+ # params = {
67
+ # k: v
68
+ # for k, v in {
69
+ # "OriginCode": origin_code,
70
+ # "FromId": from_id,
71
+ # "OriginIds": orign_ids,
72
+ # "DestCode": dest_code,
73
+ # "ToId": to_id,
74
+ # "DestIds": dest_ids,
75
+ # }.items()
76
+ # if v is not None
77
+ # }
78
+ # response = await api.get(f"/metadata/office/routes", params=params)
79
+ # route_ids = []
80
+ # if isinstance(response, list):
81
+ # route_ids = [route.get("routeId", None) for route in response]
82
+ # return route_ids
83
+ # return []
84
+
85
+ # # Danh sách route_id v2 khi API route v1 không có dữ liệu
86
+ # @staticmethod
87
+ # async def search_route_ids_one_way_by_origin_dest_code(origin: str, dest: str):
88
+ # try:
89
+ # route_ids = []
90
+ # response = await api.get(f"/booking/api/v2/routes/from/{origin}/to/{dest}")
91
+ # if response.get("Status") == 200:
92
+ # if response.get("Data"):
93
+ # data = response.get("Data")
94
+ # route_ids = [route.get("Id", None) for route in data]
95
+ # return route_ids
96
+ # except Exception as e:
97
+ # print(e)
98
+ # raise Exception("Error fetching seats data")
99
+
100
+ # async def search_all_route_ids(
101
+ # self,
102
+ # origin_code: str = None,
103
+ # from_id: int = None,
104
+ # orign_ids: int = None,
105
+ # dest_code: str = None,
106
+ # to_id: str = None,
107
+ # dest_ids: str = None,
108
+ # ):
109
+ # route_ids = await self.search_route_ids_one_way_by_metadata(
110
+ # origin_code, from_id, orign_ids, dest_code, to_id, dest_ids
111
+ # )
112
+ # if len(route_ids) == 0:
113
+ # route_ids = await self.search_route_ids_one_way_by_origin_dest_code(
114
+ # origin_code, dest_code
115
+ # )
116
+ # return route_ids
117
+
118
+ # # Danh sách ghế
119
+ # @staticmethod
120
+ # async def seats_trip(
121
+ # route_id: int, trip_id: int, departure_date: str, departure_time: str, kind: str
122
+ # ):
123
+ # try:
124
+ # params = {
125
+ # k: v
126
+ # for k, v in {
127
+ # "departureDate": departure_date,
128
+ # "departureTime": departure_time,
129
+ # "kind": kind,
130
+ # }.items()
131
+ # if v is not None
132
+ # }
133
+ # if params is None:
134
+ # return []
135
+ # response = await api.get(
136
+ # api_base=settings.API_BASE_URL_VATO,
137
+ # endpoint=f"/seats/{route_id}/{trip_id}",
138
+ # params=params,
139
+ # )
140
+ # if not response.get("data"):
141
+ # return []
142
+ # seats = response["data"]
143
+ # return seats
144
+ # except Exception as e:
145
+ # print(e)
146
+ # raise Exception("Error fetching seats data")
147
+
148
+ # # Call API để lấy danh sách chuyến đi
149
+ # @staticmethod
150
+ # async def search_trip(
151
+ # from_time: int, to_time: int, route_ids: list[int], ticket_count: int = 1
152
+ # ):
153
+ # try:
154
+ # if len(route_ids) == 0:
155
+ # return []
156
+ # ###
157
+ # payload = {
158
+ # k: v
159
+ # for k, v in {
160
+ # "channel": "web_client",
161
+ # "size": 300,
162
+ # "only_online_trip": True,
163
+ # "from_time": from_time,
164
+ # "to_time": to_time,
165
+ # "route_ids": route_ids,
166
+ # "ticket_count": ticket_count,
167
+ # "sort_by": ["price", "departure_time"],
168
+ # }.items()
169
+ # if v is not None
170
+ # }
171
+
172
+ # data = []
173
+ # response = await api.post("/search/trips", payload=payload)
174
+ # if response.get("status") == 200:
175
+ # if response.get("data"):
176
+ # if response["data"].get("total") > 0:
177
+ # data = response["data"]["items"]
178
+ # return data
179
+ # except Exception as e:
180
+ # print(e)
181
+ # raise Exception("Error fetching trip data")
182
+
183
+ # # Chuyến đi khớp với thời gian và văn phòng đón trả
184
+ # @staticmethod
185
+ # def get_trip_by_time_and_office_id(
186
+ # trips: list, time: str, origin_id: int, dest_id: int
187
+ # ) -> dict:
188
+ # if time is None or origin_id is None or dest_id is None:
189
+ # return {}
190
+
191
+ # for trip in trips:
192
+ # if (
193
+ # trip
194
+ # and trip["raw_departure_time"] == time
195
+ # and trip["route"]["origin_hub_office_id"] == origin_id
196
+ # and trip["route"]["dest_hub_office_id"] == dest_id
197
+ # ):
198
+ # return trip
199
+ # return {}
200
+
201
+ # # Danh sách chuyến đi có thể chọn được theo văn phòng
202
+ # @staticmethod
203
+ # def get_all_trip_by_office(
204
+ # trips: list, origin_id: int = None, dest_id: int = None
205
+ # ) -> list:
206
+ # if origin_id is None and dest_id is None:
207
+ # return trips
208
+
209
+ # result = []
210
+ # for trip in trips:
211
+ # pickup_points = trip.get("pickup_points", [])
212
+ # pickup_point_ids = {p["OfficeId"] for p in pickup_points if "OfficeId" in p}
213
+
214
+ # origin_match = (
215
+ # (origin_id is None)
216
+ # or (trip["route"]["origin_hub_office_id"] == origin_id)
217
+ # or (origin_id in pickup_point_ids)
218
+ # )
219
+
220
+ # dest_match = (
221
+ # (dest_id is None)
222
+ # or (trip["route"]["dest_hub_office_id"] == dest_id)
223
+ # or (dest_id in pickup_point_ids)
224
+ # )
225
+
226
+ # if origin_match and dest_match:
227
+ # result.append(trip)
228
+
229
+ # return result if result else trips
230
+
231
+ # # Danh sách 4 chuyến đi xung quanh thời gian chỉ định
232
+ # @staticmethod
233
+ # def get_surrounding_trip(trips: list, time: str, num_trip: int = 4) -> list:
234
+ # if not time or not trips or num_trip <= 0:
235
+ # return []
236
+
237
+ # time_trips = [trip["raw_departure_time"] for trip in trips]
238
+ # index = bisect_left(time_trips, time)
239
+
240
+ # half = num_trip // 2
241
+ # start = max(0, index - half)
242
+ # end = min(len(trips), index + half + (num_trip % 2))
243
+
244
+ # missing = num_trip - (end - start)
245
+
246
+ # if missing > 0:
247
+ # extra_start = min(missing, start)
248
+ # start -= extra_start
249
+ # missing -= extra_start
250
+
251
+ # if missing > 0:
252
+ # extra_end = min(missing, len(trips) - end)
253
+ # end += extra_end
254
+
255
+ # return trips[start:end]
256
+
257
+ # async def is_valid_select_seat(
258
+ # self,
259
+ # seat: str,
260
+ # route_id: int,
261
+ # trip_id: int,
262
+ # departure_date: str,
263
+ # departure_time: str,
264
+ # kind: str,
265
+ # ):
266
+ # if seat is None:
267
+ # return False
268
+ # seats = await self.seats_trip(
269
+ # route_id, trip_id, departure_date, departure_time, kind
270
+ # )
271
+ # for seat_data in seats:
272
+ # if (
273
+ # seat_data["chair"].lower() == seat.lower()
274
+ # and seat_data["bookStatus"] == 0
275
+ # ):
276
+ # return True
277
+ # return False
278
+
279
+ # async def search_trip_by_id(
280
+ # self,
281
+ # trip_id: int,
282
+ # from_time: int,
283
+ # to_time: int,
284
+ # route_ids: list[int],
285
+ # ticket_count: int = 1,
286
+ # ):
287
+ # trip = await self.search_trip(from_time, to_time, route_ids, ticket_count)
288
+ # for item in trip:
289
+ # if trip_id == item["id"]:
290
+ # return item
291
+ # return None
292
+
293
+ # @staticmethod
294
+ # def get_trip_by_id(trip_id: int, trips: list):
295
+ # for trip in trips:
296
+ # if trip and trip["id"] == trip_id:
297
+ # return trip
298
+ # return {}
299
+
300
+ # @staticmethod
301
+ # async def stops(route_id: int, way_id: int):
302
+ # try:
303
+ # if route_id is None or way_id is None:
304
+ # return []
305
+ # params = {
306
+ # k: v
307
+ # for k, v in {
308
+ # "wayId": way_id,
309
+ # }.items()
310
+ # if v is not None
311
+ # }
312
+ # response = await api.get(
313
+ # api_base=settings.API_BASE_URL_VATO,
314
+ # endpoint=f"/stops/{route_id}",
315
+ # params=params,
316
+ # )
317
+ # if not response.get("data"):
318
+ # return []
319
+ # data = response["data"]
320
+ # return data
321
+ # except Exception as e:
322
+ # print(e)
323
+ # raise Exception("Error fetching stops data")
324
+
325
+ # async def pickup_list(self, route_id: int, way_id: int):
326
+ # try:
327
+ # data = await self.stops(route_id, way_id)
328
+ # if not data:
329
+ # return []
330
+
331
+ # pickup_list = []
332
+
333
+ # for pickup in data:
334
+ # if pickup["type"] == 0 or pickup["type"] == -1:
335
+ # pickup_list.append(pickup)
336
+ # return pickup_list
337
+
338
+ # except Exception as e:
339
+ # print(e)
340
+ # raise Exception("Error fetching pickup list data")
341
+
342
+ # async def is_valid_pickup(self, pickup: str, route_id: int, way_id: int):
343
+ # if pickup is None:
344
+ # return False
345
+ # pickup_list = await self.pickup_list(route_id, way_id)
346
+ # for pickup_data in pickup_list:
347
+ # pickup_name: str = pickup_data["name"]
348
+ # if pickup_name.lower() == pickup.lower():
349
+ # return True
350
+ # return False
351
+
352
+ # async def dropoff_list(self, route_id: int, way_id: int):
353
+ # try:
354
+ # data = await self.stops(route_id, way_id)
355
+ # if not data:
356
+ # return []
357
+
358
+ # dropoff_list = []
359
+
360
+ # for dropoff in data:
361
+ # if dropoff["type"] == 1 or (
362
+ # dropoff["type"] == -1 and dropoff["presentBeforeMinutes"] >= 0
363
+ # ):
364
+ # dropoff_list.append(dropoff)
365
+ # return dropoff_list
366
+
367
+ # except Exception as e:
368
+ # print(e)
369
+ # raise Exception("Error fetching dropoff list data")
370
+
371
+ # async def is_valid_dropoff(self, dropoff: str, route_id: int, way_id: int):
372
+ # if dropoff is None:
373
+ # return False
374
+ # dropoff_list = await self.dropoff_list(route_id, way_id)
375
+ # for dropoff_data in dropoff_list:
376
+ # if dropoff_data["name"] == dropoff:
377
+ # return True
378
+ # return False
379
+
380
+ # @staticmethod
381
+ # async def search_pickup_points(origin: str = None, dest: str = None) -> dict:
382
+ # session_id = str(int(datetime.now().timestamp()))
383
+ # params = {
384
+ # k: v
385
+ # for k, v in {
386
+ # "origin": origin,
387
+ # "dest": dest,
388
+ # "session_id": session_id,
389
+ # }.items()
390
+ # if v is not None
391
+ # }
392
+
393
+ # response = await api.get("/search/metadata/pickup-points", params=params)
394
+ # if response.get("status") == 200:
395
+ # data = response.get("data")
396
+ # return data
397
+ # return {}
398
+
399
+ # async def find_id_office_by_name_office(self, office_name: str):
400
+ # data = await self.search_pickup_points(origin=office_name)
401
+ # if data.get("origin"):
402
+ # origins = data["origin"]
403
+ # for origin in origins:
404
+ # if origin.get("group"):
405
+ # groups = origin["group"]
406
+ # for group in groups:
407
+ # if group.get("name"):
408
+ # name: str = group["name"]
409
+ # if name.lower() == office_name.lower():
410
+ # office_id = group["officeId"]
411
+ # office_id = (
412
+ # office_id[0]
413
+ # if isinstance(office_id, tuple)
414
+ # else office_id
415
+ # )
416
+ # return office_id
417
+ # return None
418
+
419
+ # async def find_id_provine_by_name_office(self, office_name: str):
420
+ # data = await self.search_pickup_points(origin=office_name)
421
+ # if data.get("origin"):
422
+ # origins = data["origin"]
423
+ # for origin in origins:
424
+ # if origin.get("group"):
425
+ # groups = origin["group"]
426
+ # for group in groups:
427
+ # if group.get("name"):
428
+ # name: str = group["name"]
429
+ # if name.lower() == office_name.lower():
430
+ # province_id = group["provinceId"]
431
+ # return province_id
432
+ # return None
433
+
434
+ # async def find_all_origin_by_name_office(self, office_name: str):
435
+ # data = await self.search_pickup_points(origin=office_name)
436
+ # if data.get("origin"):
437
+ # origins = data["origin"]
438
+ # return origins
439
+ # return None
440
+
441
+ # async def find_id_and_code_provine_by_name_office(self, office_name: str):
442
+ # data = await self.search_pickup_points(origin=office_name)
443
+ # if data.get("origin"):
444
+ # origins = data["origin"]
445
+ # for origin in origins:
446
+ # if origin.get("group"):
447
+ # groups = origin["group"]
448
+ # for group in groups:
449
+ # if group.get("name"):
450
+ # name: str = group["name"]
451
+ # if name.lower() == office_name.lower():
452
+ # province_id = group["provinceId"]
453
+ # province_code = group["provinceCode"]
454
+ # province_id = (
455
+ # province_id[0]
456
+ # if isinstance(province_id, tuple)
457
+ # else province_id
458
+ # )
459
+ # return province_id, province_code
460
+ # return None, None
461
+
462
+ # async def get_origin_city_from_office(self, origin_office: str):
463
+ # data = await self.search_pickup_points(origin=origin_office)
464
+ # if data.get("origin"):
465
+ # origins = data["origin"]
466
+ # for origin in origins:
467
+ # if origin.get("group"):
468
+ # groups = origin["group"]
469
+ # for group in groups:
470
+ # if group.get("name"):
471
+ # name: str = group["name"]
472
+ # if name.lower() == origin_office.lower():
473
+ # return group["provinceName"]
474
+ # return None
475
+
476
+ # async def get_destination_city_from_office(self, dest_office: str):
477
+ # data = await self.search_pickup_points(dest=dest_office)
478
+ # if data.get("dest"):
479
+ # dests = data["dest"]
480
+ # for dest in dests:
481
+ # if dest.get("group"):
482
+ # groups = dest["group"]
483
+ # for group in groups:
484
+ # if group.get("name"):
485
+ # name: str = group["name"]
486
+ # if name.lower() == dest_office.lower():
487
+ # return group["provinceName"]
488
+ # return None
489
+
490
+ # async def check_exist_user_info(self, user_id: str = None):
491
+ # try:
492
+ # # response = await api.get(f'/user/{user_id}')
493
+ # # if response.get("status") == 200:
494
+ # # return True
495
+ # return True
496
+
497
+ # # return False
498
+ # except Exception as e:
499
+ # logger.error(f"Error checking user info: {e}")
500
+ # return False
501
+
502
+ # async def get_user_info(self, user_id: str = None):
503
+ # try:
504
+ # # response = await api.get(f'/user/{user_id}')
505
+ # # if response.get("status") == 200:
506
+ # # return response.get("data")
507
+ # user_info = {
508
+ # "user_name": {"name": "Đại", "original": "Đại"},
509
+ # "phone_number": "0987654321",
510
+ # "email": "[email protected]",
511
+ # }
512
+
513
+ # return user_info
514
+
515
+ # # return None
516
+ # except Exception as e:
517
+ # logger.error(f"Error fetching user info: {e}")
518
+ # return None
519
+
520
+ # @staticmethod
521
+ # async def extract_user_name(text: str, ner: NER):
522
+ # if text is None:
523
+ # return None
524
+
525
+ # user_name_pred = await ner.predict(text=text, entity_tag="PERSON")
526
+
527
+ # if user_name_pred:
528
+ # user_name = user_name_pred[0]
529
+
530
+ # if user_name:
531
+ # user_name_resp = {"name": user_name, "original": user_name}
532
+ # return user_name_resp
533
+
534
+ # return None
535
+
536
+
537
+ # dialog_service: DialogService = DialogService()
app/services/helper.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+
3
+
4
+ def extra_time_dialogflow(time: dict) -> list | str | None:
5
+ """
6
+ Format time dictionary to string.
7
+ Nếu time có ambiguous (past, future) => trả về list [past_time_str, future_time_str]
8
+ Nếu time rõ ràng => trả về string "HH:mm"
9
+ """
10
+
11
+ def is_time_ambiguous(time_obj: dict) -> bool:
12
+ return any(time_obj.get(key) for key in ["past", "future", "partial"])
13
+
14
+ if time is None:
15
+ return None
16
+
17
+ if is_time_ambiguous(time):
18
+ past_time = time.get("past", {})
19
+ future_time = time.get("future", {})
20
+
21
+ past_hours = int(past_time.get("hours", 0))
22
+ past_minutes = int(past_time.get("minutes", 0))
23
+ past_time_str = f"{past_hours:02d}:{past_minutes:02d}"
24
+
25
+ future_hours = int(future_time.get("hours", 0))
26
+ future_minutes = int(future_time.get("minutes", 0))
27
+ future_time_str = f"{future_hours:02d}:{future_minutes:02d}"
28
+
29
+ return sorted([past_time_str, future_time_str])
30
+
31
+ else:
32
+ hours = int(time["hours"])
33
+ minutes = int(time["minutes"])
34
+ return f"{hours:02d}:{minutes:02d}"
35
+
36
+
37
+ def get_time_range(from_time: datetime = None, to_time: datetime = None):
38
+ """
39
+ Chuyển thời gian từ Dialogflow về dạng timestap cho search trip.
40
+ Thông thường sẽ truyền vào ngày và trả về thời gian đầu và cuối ngày
41
+
42
+ Args:
43
+ from_time (datetime): Thời gian bắt đầu
44
+ to_time (datetime): Thời gian kết thúc
45
+ Returns:
46
+ from_time (timestamp)
47
+ to_time (timestamp)
48
+ """
49
+ if to_time is None and from_time is not None:
50
+ to_time = from_time.replace(hour=23, minute=59, second=59)
51
+
52
+ if from_time is None:
53
+ today = datetime.today().date()
54
+ from_time = datetime.combine(today, datetime.min.time())
55
+ to_time = datetime.combine(today, datetime.max.time()) - timedelta(
56
+ microseconds=1
57
+ )
58
+
59
+ return int(from_time.timestamp()) * 1000, int(to_time.timestamp()) * 1000
60
+
61
+
62
+ def to_datetime_from_Dialogflow(raw_date: dict) -> datetime:
63
+ """
64
+ Convert raw date from Dialogflow to datetime object.
65
+ """
66
+ if not raw_date:
67
+ return None
68
+
69
+ year = int(raw_date.get("year", 0))
70
+ month = int(raw_date.get("month", 1))
71
+ day = int(raw_date.get("day", 1))
72
+
73
+ return datetime(year, month, day)
74
+
75
+
76
+ def get_weekday_name(date: datetime) -> tuple:
77
+ """
78
+ Get the name of the weekday from its number.
79
+ :param weekday: Weekday number (0-6).
80
+ :return: Name of the weekday.
81
+ """
82
+ vietnam_weekdays = {
83
+ 0: "Thứ hai",
84
+ 1: "Thứ ba",
85
+ 2: "Thứ tư",
86
+ 3: "Thứ năm",
87
+ 4: "Thứ sáu",
88
+ 5: "Thứ bảy",
89
+ 6: "Chủ nhật",
90
+ }
91
+
92
+ weekday_ids = date.weekday()
93
+ weekday = (
94
+ vietnam_weekdays.get(weekday_ids) if vietnam_weekdays.get(weekday_ids) else ""
95
+ )
96
+
97
+ return date.strftime("%d-%m-%Y"), weekday