ABAO77 commited on
Commit
a30d85d
·
verified ·
1 Parent(s): b3f0f2e

Upload 14 files

Browse files
Files changed (14) hide show
  1. DataProcessing.py +88 -0
  2. Dockerfile +75 -0
  3. README.md +10 -4
  4. app.py +233 -0
  5. config.py +35 -0
  6. constants.py +11 -0
  7. database.py +293 -0
  8. dictionary.py +501 -0
  9. get_default_weight.py +93 -0
  10. get_destinations.py +66 -0
  11. load_data.py +38 -0
  12. model_predict_onnx.py +59 -0
  13. requirements.txt +12 -0
  14. user_weights.py +205 -0
DataProcessing.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import string
2
+
3
+ import underthesea
4
+
5
+ from dictionary import (emotion2wordform_dict, mispelling_dict, number_dict, translate_dict, wordform2vnese_dict)
6
+
7
+
8
+ #10 Remove stopwords
9
+ def remove_stopwords(input_text, stopwords_file='Datasets/Query/stopword.txt'):
10
+ # Read the custom stop words from the file
11
+ with open(stopwords_file, 'r', encoding='utf-8') as file:
12
+ stopwords = set(line.strip() for line in file)
13
+
14
+ cleaned_words = [word for word in input_text.split() if word.lower() not in stopwords]
15
+ cleaned_text = ' '.join(cleaned_words)
16
+
17
+ return cleaned_text
18
+
19
+ #9 word segmentation
20
+ def word_segment(text):
21
+ return underthesea.word_tokenize(text, format="text")
22
+
23
+ #8 Remove numbers
24
+ def remove_numbers(input_string):
25
+ # Use the isalpha() method to filter out numeric characters
26
+ cleaned_string = ''.join(char for char in input_string if not char.isdigit())
27
+ return cleaned_string
28
+
29
+ #7
30
+ def remove_extra_whitespace(input_string):
31
+ words = input_string.split()
32
+ return ' '.join(words)
33
+
34
+ #6 Tranform Number to text (8 - tám)
35
+ def number2text(sentence):
36
+ words = sentence.split()
37
+ converted_words = [number_dict.get(word, word) for word in words]
38
+ converted_sentence = ' '.join(converted_words)
39
+ return converted_sentence
40
+
41
+ #5 Transform mispelling words, acronyms, .....(include translate english words)
42
+ def translate2word(sentence, dictionary = translate_dict):
43
+ sentence = " " + sentence.strip() + " "
44
+ for key, value_list in dictionary.items():
45
+ for value in value_list:
46
+ sentence = sentence.replace(value, key)
47
+ return sentence
48
+
49
+ def mispell2word(sentence, dictionary = mispelling_dict):
50
+ sentence = " " + sentence.strip() + " "
51
+ for key, value_list in dictionary.items():
52
+ for value in value_list:
53
+ sentence = sentence.replace(value, key)
54
+ return sentence
55
+
56
+ #4 Transform word from into vietnamese (colonsmile - cười)
57
+ def word_form2Vnese(sentence):
58
+ words = sentence.split()
59
+ converted_words = [wordform2vnese_dict.get(word, word) for word in words]
60
+ converted_sentence = ' '.join(converted_words)
61
+ return converted_sentence
62
+
63
+ #3 f
64
+ def remove_punctuation(input_string):
65
+ # Create a translation table to remove all punctuation characters
66
+ translator = str.maketrans('', '', string.punctuation)
67
+
68
+ # Use the translate method to remove punctuation
69
+ cleaned_string = input_string.translate(translator)
70
+
71
+ return cleaned_string
72
+
73
+ #2 emoticon to word form ( :) - colonsmile )
74
+ def emoticon2word(sentence):
75
+ words = sentence.split()
76
+ converted_words = [emotion2wordform_dict.get(word, word) for word in words]
77
+ converted_sentence = ' '.join(converted_words)
78
+ return converted_sentence
79
+
80
+ #1 lower case
81
+ def lower_case(text):
82
+ return text.lower()
83
+
84
+ def data_preprocessing(text):
85
+ return remove_stopwords(word_segment(remove_extra_whitespace(number2text(mispell2word(remove_punctuation(lower_case(text)))))))
86
+
87
+ def read_input(input): #hàm cuối cùng khi đọc và xử lí input sentence
88
+ return data_preprocessing(input)
Dockerfile ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FROM python:3.11-slim
2
+
3
+ # # Install git and git-lfs
4
+ # RUN apt-get update && \
5
+ # apt-get install -y git git-lfs && \
6
+ # apt-get clean && \
7
+ # rm -rf /var/lib/apt/lists/*
8
+
9
+ # # Add a non-root user for better security
10
+ # RUN useradd -m -u 1000 user
11
+
12
+ # # Switch to non-root user
13
+ # USER user
14
+
15
+ # # Ensure pip, setuptools, and wheel are up to date
16
+ # RUN python -m pip install --upgrade pip setuptools wheel
17
+
18
+ # # Set PATH to include user installs
19
+ # ENV PATH="/home/user/.local/bin:$PATH"
20
+
21
+ # # Set the working directory inside the container
22
+ # WORKDIR /app
23
+
24
+ # # Copy requirements.txt file and install dependencies
25
+ # COPY --chown=user ./requirements.txt /app/requirements.txt
26
+
27
+ # # Install only necessary dependencies from the requirements.txt
28
+ # RUN pip install --no-cache-dir -r /app/requirements.txt
29
+
30
+ # # Create Saved_Model directory and download model from Hugging Face
31
+ # RUN mkdir -p /app/Saved_Model && \
32
+ # cd /app && \
33
+ # git lfs install && \
34
+ # git clone https://huggingface.co/spaces/darkbreakerk/triventure_ai && \
35
+ # cp -r triventure_ai/Model_API/Saved_Model/* Saved_Model/ && \
36
+ # rm -rf triventure_ai
37
+
38
+ # # Copy the rest of the application code to the working directory
39
+ # COPY --chown=user . /app
40
+
41
+ # # Expose the port that your app will run on
42
+ # EXPOSE 7880
43
+
44
+ # # Run the Python script when the container starts
45
+ # CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7880"]
46
+
47
+ # Use the slim variant of Python 3.11 to reduce the size
48
+ FROM python:3.11-slim
49
+
50
+ # Add a non-root user for better security
51
+ RUN useradd -m -u 1000 user
52
+
53
+ # Switch to non-root user
54
+ USER user
55
+
56
+ # Ensure pip, setuptools, and wheel are up to date
57
+ RUN python -m pip install --upgrade pip setuptools wheel
58
+
59
+ # Set PATH to include user installs
60
+ ENV PATH="/home/user/.local/bin:$PATH"
61
+
62
+ # Set the working directory inside the container
63
+ WORKDIR /app
64
+
65
+ # Copy requirements.txt file and install dependencies
66
+ COPY --chown=user ./requirements.txt /app/requirements.txt
67
+
68
+ # Install only necessary dependencies from the requirements.txt
69
+ RUN pip install --no-cache-dir -r /app/requirements.txt
70
+
71
+ # Copy the rest of the application code to the working directory
72
+ COPY --chown=user . /app
73
+
74
+ # Start the FastAPI application with uvicorn
75
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,10 +1,16 @@
1
  ---
2
- title: TriVenture Personalize
3
- emoji: 📊
4
- colorFrom: red
5
- colorTo: green
6
  sdk: docker
7
  pinned: false
 
 
8
  ---
9
 
 
 
 
 
10
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Triventure Personalize
3
+ emoji: 📉
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: docker
7
  pinned: false
8
+ license: apache-2.0
9
+ short_description: AI of Triventure project
10
  ---
11
 
12
+ # Triventure AI
13
+
14
+ This project uses MongoDB for data storage. The database is configured in the `docker-compose.yml` file and can be accessed through the MongoDB Express interface at http://localhost:8081.
15
+
16
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import Any, Dict, List
3
+ from dotenv import load_dotenv
4
+ load_dotenv()
5
+ import uvicorn
6
+ from fastapi import APIRouter, FastAPI, HTTPException
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ from pydantic import BaseModel
9
+ from model_predict_onnx import onnx_predictor
10
+ from user_weights import (get_all_users, get_user_metadata,
11
+ get_user_weights,
12
+ track_question_tags,
13
+ update_user_metadata,
14
+ update_user_weights,
15
+ update_weights_from_feedback,
16
+ update_weights_from_query)
17
+ from get_destinations import (get_destinations_list,get_question_vector)
18
+ from get_default_weight import feature_names, weights_bias_vector
19
+ from database import db
20
+
21
+ # Define request models
22
+ class WeightUpdateRequest(BaseModel):
23
+ tag_indices: List[int]
24
+ new_weights: List[float]
25
+ metadata: Dict[str, Any] = {}
26
+
27
+ class FeedbackRequest(BaseModel):
28
+ destination_id: int
29
+ tag_id: int
30
+ rating: int # 1-5 stars
31
+
32
+ router = APIRouter(prefix="/model", tags=["Model"])
33
+
34
+ @router.get("/get_question_tags/{question}")
35
+ async def get_question_tags(question: str):
36
+ # Get the prediction
37
+ original_sentence, predicted_tags = onnx_predictor.predict(question)
38
+
39
+ # Print the sentence and its predicted tags
40
+ print("Sentence:", original_sentence)
41
+ print("Predicted Tags:", predicted_tags)
42
+ return {"question_tags": predicted_tags}
43
+
44
+ @router.get("/get_destinations_list/{question_tags}/{top_k}")
45
+ async def get_destinations_list_api(question_tags: str, top_k:str):
46
+ # Get the prediction
47
+ question_vector = get_question_vector(question_tags)
48
+ destinations_list = get_destinations_list(question_vector, int(top_k))
49
+
50
+ print("destinations_list:", destinations_list)
51
+ return {"destinations_list": destinations_list}
52
+
53
+ @router.get("/get_destinations_list_by_question/{question}/{top_k}")
54
+ async def get_destinations_list_api(question: str, top_k: str):
55
+ # Get the prediction
56
+ original_sentence, question_tags = onnx_predictor.predict(question)
57
+
58
+ # Print the sentence and its predicted tags
59
+ print("Sentence:", original_sentence)
60
+ print("Predicted Tags:", question_tags)
61
+ # Get the prediction
62
+ question_tags = " ".join(question_tags)
63
+ question_vector = get_question_vector(question_tags)
64
+ destinations_list = get_destinations_list(question_vector, int(top_k))
65
+
66
+ print("destinations_list:", destinations_list)
67
+ return {"destinations_list": destinations_list}
68
+
69
+ @router.get("/get_destinations_list_by_question/{question}/{top_k}/{user_id}")
70
+ def get_destinations_list_with_user_api(question: str, top_k: str, user_id: str):
71
+ """
72
+ Get a list of destinations based on a question and user-specific weights.
73
+
74
+ Parameters:
75
+ question (str): The question to get destinations for.
76
+ top_k (str): The number of destinations to return.
77
+ user_id (str): The ID of the user.
78
+
79
+ Returns:
80
+ dict: A dictionary containing the list of destinations.
81
+ """
82
+ # Get the prediction
83
+ original_sentence, question_tags = onnx_predictor.predict(question)
84
+
85
+ # Print the sentence and its predicted tags
86
+ print("Sentence:", original_sentence)
87
+ print("Predicted Tags:", question_tags)
88
+
89
+ # Track the question tags for the user
90
+ track_question_tags(user_id, question_tags)
91
+
92
+ # Update weights based on query tags
93
+ update_weights_from_query(user_id, question_tags, feature_names, weights_bias_vector)
94
+
95
+ # Get the prediction
96
+ question_tags_str = " ".join(question_tags)
97
+ question_vector = get_question_vector(question_tags_str)
98
+ destinations_list = get_destinations_list(question_vector, int(top_k), user_id)
99
+
100
+ print("destinations_list:", destinations_list)
101
+ return {"destinations_list": destinations_list}
102
+
103
+ @router.get("/users")
104
+ def get_users():
105
+ """
106
+ Get a list of all users.
107
+
108
+ Returns:
109
+ dict: A dictionary containing the list of users.
110
+ """
111
+ users = get_all_users()
112
+ return {"users": users}
113
+
114
+ @router.get("/users/{user_id}")
115
+ def get_user(user_id: str):
116
+ """
117
+ Get the metadata for a user.
118
+
119
+ Parameters:
120
+ user_id (str): The ID of the user.
121
+
122
+ Returns:
123
+ dict: A dictionary containing the user's metadata.
124
+ """
125
+ metadata = get_user_metadata(user_id)
126
+ return {"metadata": metadata}
127
+
128
+ @router.get("/users/{user_id}/weights")
129
+ def get_user_weights_api(user_id: str):
130
+ """
131
+ Get the weights for a user.
132
+
133
+ Parameters:
134
+ user_id (str): The ID of the user.
135
+
136
+ Returns:
137
+ dict: A dictionary containing the user's weights.
138
+ """
139
+ weights = get_user_weights(user_id, weights_bias_vector)
140
+ # Convert numpy array to list for JSON serialization
141
+ weights_list = weights.tolist() if weights is not None else None
142
+ return {"user_id": user_id, "weights": weights_list}
143
+
144
+ @router.post("/users/{user_id}/weights")
145
+ def update_user_weights_api(user_id: str, request: WeightUpdateRequest):
146
+ """
147
+ Update the weights for a user.
148
+
149
+ Parameters:
150
+ user_id (str): The ID of the user.
151
+ request (WeightUpdateRequest): The request containing the tag indices, new weights, and metadata.
152
+
153
+ Returns:
154
+ dict: A dictionary indicating whether the update was successful.
155
+ """
156
+ # Validate the request
157
+ if len(request.tag_indices) != len(request.new_weights):
158
+ raise HTTPException(status_code=400, detail="Tag indices and new weights must have the same length")
159
+
160
+ # Update the weights
161
+ success = update_user_weights(user_id, request.tag_indices, request.new_weights, weights_bias_vector)
162
+
163
+ # Update the metadata
164
+ if success and request.metadata:
165
+ update_user_metadata(user_id, request.metadata)
166
+
167
+ return {"success": success}
168
+
169
+ @router.post("/users/{user_id}/feedback")
170
+ def record_user_feedback(user_id: str, request: FeedbackRequest):
171
+ """
172
+ Record user feedback on a specific tag for a specific destination.
173
+
174
+ Parameters:
175
+ user_id (str): The ID of the user.
176
+ request (FeedbackRequest): The request containing the destination ID, tag ID, and rating.
177
+
178
+ Returns:
179
+ dict: A dictionary indicating whether the feedback was recorded successfully.
180
+ """
181
+ # Validate the request
182
+ if request.rating < 1 or request.rating > 5:
183
+ raise HTTPException(status_code=400, detail="Rating must be between 1 and 5")
184
+
185
+ # Update weights based on feedback
186
+ success = update_weights_from_feedback(
187
+ user_id,
188
+ request.destination_id,
189
+ request.tag_id,
190
+ request.rating,
191
+ weights_bias_vector
192
+ )
193
+
194
+ return {"success": success}
195
+
196
+ @router.get("/tags")
197
+ def get_tags():
198
+ """
199
+ Get a list of all tags.
200
+
201
+ Returns:
202
+ dict: A dictionary containing the list of tags.
203
+ """
204
+ return {"tags": feature_names.tolist()}
205
+
206
+ app = FastAPI(docs_url="/")
207
+ app.add_middleware(
208
+ CORSMiddleware,
209
+ allow_origins=['*'],
210
+ allow_credentials=True,
211
+ allow_methods=['*'],
212
+ allow_headers=['*'],
213
+ expose_headers=['*',]
214
+ )
215
+
216
+ app.include_router(router)
217
+
218
+ @app.on_event("startup")
219
+ def startup_event():
220
+ """
221
+ Connect to the database when the API starts.
222
+ """
223
+ db.connect()
224
+
225
+ @app.on_event("shutdown")
226
+ def shutdown_event():
227
+ """
228
+ Close the database connection when the API shuts down.
229
+ """
230
+ db.close()
231
+
232
+ if __name__ == "__main__":
233
+ uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 7880)))
config.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import pandas as pd
3
+ from sklearn.feature_extraction.text import CountVectorizer
4
+ from constants import DEFAULT_TEXT_ANNOTATION_FILE, DEFAULT_DESTINATIONS
5
+ # Load the JSON data
6
+ with open(DEFAULT_TEXT_ANNOTATION_FILE, 'r', encoding='utf-8') as file:
7
+ data = json.load(file)
8
+
9
+ # Prepare sentences and labels
10
+ sentences = [item[0] for item in data["annotations"]]
11
+ labels = [item[1]['entities'] for item in data["annotations"]]
12
+ # Define tags
13
+ tags = data["classes"]
14
+ # tags = ['<pad>'] + tags
15
+
16
+ # Convert tags to indices
17
+ tag2idx = {tag: 0 for idx, tag in enumerate(tags)}
18
+ for label in labels:
19
+ for entity in label:
20
+ tag2idx[entity[1]] = tag2idx[entity[1]] + 1
21
+ # Sort the dictionary by values
22
+ sorted_tags_dict = dict(sorted(tag2idx.items(), key=lambda item: item[1],reverse=True))
23
+ sorted_tags = {key: value for key, value in sorted_tags_dict.items()}
24
+ sorted_tags = list(sorted_tags)
25
+
26
+ for i in range(len(sorted_tags)):
27
+ sorted_tags[i] = sorted_tags[i].replace(" ", "_")
28
+
29
+ destinations = pd.read_excel(DEFAULT_DESTINATIONS)
30
+
31
+ vectorizer = CountVectorizer(max_features=10000, stop_words="english")
32
+ tags_vector = vectorizer.fit_transform(destinations["tags"].values.astype('U')).toarray()
33
+ tags_vector = tags_vector[1:]
34
+
35
+ feature_names = vectorizer.get_feature_names_out()
constants.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
4
+
5
+ DEFAULT_TEXT_ANNOTATION_FILE = os.path.join(BASE_DIR,"triventure_model_api", "Datasets", "Query", "datasets_text.json")
6
+ DEFAULT_TAGS = os.path.join(BASE_DIR, "triventure_model_api", "Datasets", "Query", "tags.json")
7
+ DEFAULT_DESTINATIONS = os.path.join(BASE_DIR, "triventure_model_api", "Datasets", "Places", "des_retags_copilot.xlsx")
8
+ DEFAULT_WEIGHTS_BIAS_VECTOR = os.path.join(BASE_DIR, "triventure_model_api", "Model_API", "Saved_Model", "weights_bias_vector.npy")
9
+ DEFAULT_FEATURE_NAMES = os.path.join(BASE_DIR, "triventure_model_api", "Model_API", "Saved_Model", "feature_names.npy")
10
+ DEFAULT_PRETRAIN_MODEL_NAME_TOKENIZER = "vinai/phobert-base-v2"
11
+ ONNX_MODEL_PATH = os.path.join(BASE_DIR, "triventure_model_api", "Model_API", "Saved_Model", "key_ner.onnx")
database.py ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import datetime
4
+
5
+ import numpy as np
6
+ import pymongo
7
+ from pymongo import MongoClient
8
+ from bson.binary import Binary
9
+ from dotenv import load_dotenv
10
+ from bson import ObjectId
11
+ from loguru import logger
12
+
13
+ load_dotenv()
14
+
15
+ print(os.environ.get("MONGODB_URL"))
16
+
17
+
18
+ class Database:
19
+ def __init__(self):
20
+ self.client = None
21
+ self.db = None
22
+
23
+ def connect(self):
24
+ """
25
+ Connect to the MongoDB database.
26
+ """
27
+ try:
28
+ # Connect to MongoDB
29
+ self.client = MongoClient(os.environ.get("MONGODB_URL"))
30
+ self.db = self.client[os.environ.get("DB_NAME", "triventure")]
31
+
32
+ # Create users collection if it doesn't exist
33
+ if "user" not in self.db.list_collection_names():
34
+ self.db.create_collection("user")
35
+
36
+ return True
37
+ except Exception as e:
38
+ logger.error(f"Error connecting to database: {e}")
39
+ return False
40
+
41
+ def close(self):
42
+ """
43
+ Close the database connection.
44
+ """
45
+ if self.client:
46
+ self.client.close()
47
+
48
+ def create_tables(self):
49
+ """
50
+ Create the necessary collections if they don't exist.
51
+
52
+ Note: This method is kept for backward compatibility but is no longer used
53
+ for creating user-specific collections. Instead, user data is stored in collections
54
+ that are created on-demand when saving user data.
55
+ """
56
+ try:
57
+ # Ensure users collection exists
58
+ if "user" not in self.db.list_collection_names():
59
+ self.db.create_collection("user")
60
+
61
+ return True
62
+ except Exception as e:
63
+ logger.error(f"Error creating collections: {e}")
64
+ return False
65
+
66
+ def create_user_table(self, user_id):
67
+ """
68
+ Create a collection for a specific user if it doesn't exist.
69
+
70
+ Parameters:
71
+ user_id (str): The ID of the user.
72
+
73
+ Returns:
74
+ bool: True if the collection was created successfully, False otherwise.
75
+ """
76
+ try:
77
+ # # User data is stored in the "user_data" collection
78
+ # if "user" not in self.db.list_collection_names():
79
+ # self.db.create_collection("user")
80
+
81
+ # Add user to the users collection if not exists
82
+ self.db.user.update_one(
83
+ {"_id": ObjectId(user_id)},
84
+ {"$setOnInsert": {"created_at": datetime.datetime.now()}},
85
+ upsert=True,
86
+ )
87
+
88
+ return True
89
+ except Exception as e:
90
+ logger.error(f"Error creating user collection: {e}")
91
+ return False
92
+
93
+ def save_user_weights(self, user_id, weights):
94
+ """
95
+ Save user weights to the database.
96
+
97
+ Parameters:
98
+ user_id (str): The ID of the user.
99
+ weights (numpy.ndarray): The weights to save.
100
+
101
+ Returns:
102
+ bool: True if the weights were saved successfully, False otherwise.
103
+ """
104
+ try:
105
+ # Create user entry if it doesn't exist
106
+ # if not self.create_user_table(user_id):
107
+ # return False
108
+
109
+ # Convert numpy array to bytes
110
+ weights_bytes = Binary(weights.tobytes())
111
+
112
+ # Update or insert weights
113
+ self.db.user.update_one(
114
+ {"_id": ObjectId(user_id)},
115
+ {
116
+ "$set": {
117
+ "weights": weights_bytes,
118
+ "weights_updated_at": datetime.datetime.now(),
119
+ }
120
+ },
121
+ )
122
+
123
+ return True
124
+ except Exception as e:
125
+ logger.error(f"Error saving user weights: {e}")
126
+ return False
127
+
128
+ def get_user_weights(self, user_id, default_weights):
129
+ """
130
+ Get user weights from the database.
131
+
132
+ Parameters:
133
+ user_id (str): The ID of the user.
134
+ default_weights (numpy.ndarray): The default weights to use if the user doesn't have weights.
135
+
136
+ Returns:
137
+ numpy.ndarray: The weights for the user.
138
+ """
139
+ try:
140
+ # Get weights from user_data collection
141
+ result = self.db.user.find_one({"_id": ObjectId(user_id)})
142
+
143
+ if result and "weights" in result:
144
+ # Convert bytes to numpy array
145
+ weights_bytes = result["weights"]
146
+ weights = np.frombuffer(weights_bytes, dtype=default_weights.dtype)
147
+ return weights.reshape(default_weights.shape)
148
+
149
+ return default_weights
150
+ except Exception as e:
151
+ logger.error(f"Error getting user weights: {e}")
152
+ return default_weights
153
+
154
+ def user_weights_exist(self, user_id):
155
+ """
156
+ Check if weights exist for the given user.
157
+
158
+ Parameters:
159
+ user_id (str): The ID of the user.
160
+
161
+ Returns:
162
+ bool: True if weights exist for the user, False otherwise.
163
+ """
164
+ try:
165
+ # Check if weights exist in user_data collection
166
+ result = self.db.user.find_one({"_id": ObjectId(user_id)})
167
+
168
+ return result is not None
169
+ except Exception as e:
170
+ logger.error(f"Error checking if user weights exist: {e}")
171
+ return False
172
+
173
+ def save_user_metadata(self, user_id, metadata):
174
+ """
175
+ Save user metadata to the database.
176
+
177
+ Parameters:
178
+ user_id (str): The ID of the user.
179
+ metadata (dict): The metadata to save.
180
+
181
+ Returns:
182
+ bool: True if the metadata was saved successfully, False otherwise.
183
+ """
184
+ try:
185
+ # Create user entry if it doesn't exist
186
+ # if not self.create_user_table(user_id):
187
+ # return False
188
+
189
+ # Update or insert metadata
190
+ self.db.user.update_one(
191
+ {"_id": ObjectId(user_id)},
192
+ {
193
+ "$set": {
194
+ "metadata": metadata,
195
+ "weights_updated_at": datetime.datetime.now(),
196
+ }
197
+ },
198
+ )
199
+
200
+ return True
201
+ except Exception as e:
202
+ logger.error(f"Error saving user metadata: {e}")
203
+ return False
204
+
205
+ def get_user_metadata(self, user_id):
206
+ """
207
+ Get user metadata from the database.
208
+
209
+ Parameters:
210
+ user_id (str): The ID of the user.
211
+
212
+ Returns:
213
+ dict: The metadata for the user.
214
+ """
215
+ try:
216
+ # Get metadata from user_data collection
217
+ result = self.db.user.find_one({"_id": ObjectId(user_id)})
218
+
219
+ if result and "metadata" in result:
220
+ return result["metadata"]
221
+
222
+ return {}
223
+ except Exception as e:
224
+ logger.error(f"Error getting user metadata: {e}")
225
+ return {}
226
+
227
+ def get_all_user_metadata(self):
228
+ """
229
+ Get metadata for all users from the database.
230
+
231
+ Returns:
232
+ dict: A dictionary mapping user IDs to their metadata.
233
+ """
234
+ try:
235
+ # Get metadata for all users
236
+ results = self.db.user.find({})
237
+
238
+ # Build a dictionary of user_id -> metadata
239
+ metadata = {}
240
+ for result in results:
241
+ if "_id" in result and "metadata" in result:
242
+ metadata[result["_id"]] = result["metadata"]
243
+
244
+ return metadata
245
+ except Exception as e:
246
+ logger.error(f"Error getting all user metadata: {e}")
247
+ return {}
248
+
249
+ def get_user_metadata(self, user_id):
250
+ """
251
+ Get metadata for a specific user from the database.
252
+
253
+ Parameters:
254
+ user_id (str): The ID of the user.
255
+
256
+ Returns:
257
+ dict: The metadata for the user.
258
+ """
259
+ try:
260
+ # Get metadata from user_data collection
261
+ result = self.db.user.find_one({"_id": ObjectId(user_id)})
262
+
263
+ if result and "metadata" in result:
264
+ return result["metadata"]
265
+
266
+ return {}
267
+ except Exception as e:
268
+ logger.error(f"Error getting user metadata: {e}")
269
+ return {}
270
+
271
+ def get_all_users(self):
272
+ """
273
+ Get a list of all users from the database.
274
+
275
+ Returns:
276
+ list: A list of all user IDs.
277
+ """
278
+ try:
279
+ # Get all users from users collection
280
+ results = self.db.user.find({})
281
+
282
+ # Extract user IDs
283
+ user_ids = [str(result["_id"]) for result in results]
284
+ print("user_ids", user_ids)
285
+
286
+ return user_ids
287
+ except Exception as e:
288
+ logger.error(f"Error getting all users: {e}")
289
+ return []
290
+
291
+
292
+ # Create a singleton instance
293
+ db = Database()
dictionary.py ADDED
@@ -0,0 +1,501 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ emotion2wordform_dict = {
2
+ ':)' :'colonsmile',
3
+ ':(' :'colonsad',
4
+ '@@' :'colonsurprise',
5
+ '<3' :'colonlove',
6
+ ':d' :'colonsmilesmile',
7
+ ':3' :'coloncontemn',
8
+ ':v' :'colonbigsmile',
9
+ ':_' :'coloncc',
10
+ ':p' :'colonsmallsmile',
11
+ '>>' :'coloncolon',
12
+ ':">' :'colonlovelove',
13
+ '^^' :'colonhihi',
14
+ ':' :'doubledot',
15
+ ":'(" :'colonsadcolon',
16
+ ':’(' :'colonsadcolon',
17
+ ':@' :'colondoublesurprise',
18
+ 'v.v' :'vdotv',
19
+ '...' :'dotdotdot',
20
+ '/' :'fraction',
21
+ 'c#' :'cshrap',
22
+ '=)' :'coloneyesmile',
23
+ ':3' : 'coloncontemn',
24
+ '^^' : 'colonhihi',
25
+ 'c++' : 'cplusplus',
26
+ '&' : 'and',
27
+ '?' : 'dotthinking',
28
+ '3dot0' : 'dotnumbers',
29
+ }
30
+
31
+ wordform2vnese_dict = {
32
+ 'colonsmile' : 'cười nhẹ',
33
+ 'colonsad' : 'buồn',
34
+ 'colonsurprise' : 'ngạc nhiên',
35
+ 'colonlove' : 'trái tim',
36
+ 'colonsmilesmile' : 'cười tươi',
37
+ 'coloncontemn' : 'dễ thương',
38
+ 'colonbigsmile' : 'cười nhạt',
39
+ 'coloncc' : 'thích',
40
+ 'colonsmallsmile' : 'vui vẻ',
41
+ 'coloncolon' : 'tuyệt',
42
+ 'colonlovelove' : 'thẹn thùng',
43
+ 'colonhihi' : 'hạnh phúc',
44
+ 'doubledot' : 'như là',
45
+ 'colonsadcolon' : 'khóc',
46
+ 'colondoublesurprise': 'hơi ngạc nhiên',
47
+ 'vdotv' : 'vân vân',
48
+ 'dotdotdot' : 'vân vân',
49
+ 'fraction' : 'chia',
50
+ 'cshrap' : 'ngôn ngữ lập trình',
51
+ 'coloneyesmile' : 'hạnh phúc',
52
+ 'cplusplus' : 'ngôn ngữ lập trình',
53
+ 'and' : 'và',
54
+ 'dotthinking' : 'thắc mắc',
55
+ 'dotnumbers' : 'chấm',
56
+ }
57
+
58
+
59
+ mispelling_dict = {
60
+ #-----------------------------------------------------------------------------------------------------#
61
+ # Từ phức:
62
+
63
+ 'lặp đi lặp lại': ['lập đi lập lại','lập đi lặp lại','lặp đi lập lại','lap di lap lai'],
64
+ 'yêu cầu': ['yêu câu','yêu cau','yeu cầu','yeu câu','yeu cau'],
65
+ 'kiến thức': ['kiến thực','kiến thưc','kiến thúc','kiến thuc','kiên thức','kiên thưc','kiên thúc','kiên thuc','kien thức','kien thưc','kien thúc','kien thuc'],
66
+ 'bục giảng': ['bụt giảng', 'bục giãng','bụt giãng','buc giang','but giang','buc giảng','but giảng'],
67
+ 'tận tâm': ['tân tâm','tận tam','tân tam','tan tam'],
68
+ 'tăng cường': ['tăng cương','tăng cuong','tang cuong','tang cường','tang cương'],
69
+ 'trang thiết bị': ['trang thiet bi','trang thết bị','trang thêt bị','trang thiet bị','trang thiêt bị','trang thiêt bị','trang thiêt bi','trang thiêt bi','trang thiết bi','trang thết','trang thiết'],
70
+ 'thiết bị': ['thết bị','thêt bị','thêt bi','thet bị','thet bi','thiêt bị','thiêt bi','thiết bi','thiet bi','thiet bị'],
71
+ 'thống nhất cùng': ['thống nhất cũng'],
72
+ 'khó khăn': ['khó khắn','khó khắn','khó khẵn','khó khặn','khó khan','kho khăn'],
73
+ 'phòng máy': ['phỏng máy','phong máy','phong may'],
74
+ 'khảo sát': ['khảo sat','khão sát','khão sat','khạo sát','khạo sat','khao sát','khảo sác','khảo sác','khảo sac','khao sat'],
75
+ ' học sinh ': [' hs ',' học sin ',' hoc sin ','hoc sinh'],
76
+ 'dễ hiểu': ['dễ hiêu','dê hieu','dê hiêu','de hieu'],
77
+ 'khó hiểu': ['khó hiêu','khó hieu','kho hiểu','kho hiêu','kho hieu'],
78
+ ' giảng dạy ': [' giảng dạ ', ' giản dạy ', ' giảng dạyy ',' giảng dậy ',' giang day '],
79
+ ' bình thường ': [' bình thưởng ', ' bình thườn ',' binh thuong '],
80
+ 'kéo dài' : ['kéo đài', 'keo dài', 'keo dai'],
81
+ 'bắt đầu' :['bắt đàu', 'bắt đau','bắt đâu','bắt dau','băt đầu','băt đâu','băt đau','băt dau','bat đầu','bat đâu','bat đau','bat dau'],
82
+ 'tìm hiểu' : ['tmf hiểu','tim hieu', 'timf hiểu','tìm hiều'],
83
+ 'áp đặt' : ['áp đặp', 'ap dat'],
84
+ 'lướt qua' : ['lước qua','lươt qua','luot qua'],
85
+ 'bổ sung' : ['bổ xung', 'bo sung', 'bô sung','bỏ sung'],
86
+ ' giảng viên ' : [' giãng viên ',' giãng vien ',' giảng vien ',' giang viên ',' giản viên ',' giảng viến ',' giang vien ',' gv '],
87
+ ' giáo viên ' : [' giá viên ',' giao viên ',' giao vien ',' giao viên ',' giáo vien '],
88
+ 'gần gũi': ['gần gủi','gần gui', 'gan gui'],
89
+ 'cộc lốc': ['cọc lốc','cọc lóc','coc loc'],
90
+ 'vấn đề': ['vẫn đê','vân đê','vấn đê','vân đề','van de','van đề','vấn đe','vấn de'],
91
+ ' mạnh dạn ': [' mạnh dạng '],
92
+ 'nhiệt tình': ['nhiêt tình','nhiet tình','nhiệt tinh', 'nhiet tinh'],
93
+ 'bài mẫu': ['bày mẫu','bai mẫu','bai mau'],
94
+ 'truyền đạt': ['truyền đật','truyên dat','truyền đat','truyên đạt','truyên dạt','truyên đat','truyen dat'],
95
+ 'trắc nghiệm': ['trăc nghiệm','trắc nghiem','trac nghiệm','trac nghiem'],
96
+ 'lập trình': ['lâp trình','lap trình','lap trinh'],
97
+ 'nhắc nhở': ['nhác nhở','nhac nho'],
98
+ 'lên lớp': ['lên lớn','len lop','lên lơp','lên lop'],
99
+ 'hoàn hảo': ['hoan hao'],
100
+ 'nhắn tin': ['nhăn tin','nhan tin'],
101
+ 'hứng thú': ['hướng thú','hung thu'],
102
+ 'cô dạy': ['cố dạy'],
103
+ 'hoàn chỉnh': ['hoàn chính','hoan chinh'],
104
+ 'tận tình': ['tân tình','tận hình','tận tinh','tan tinh'],
105
+ 'cuối cùng': ['cuối cung', 'cuoi cung'],
106
+ 'tiếp xúc': ['tiếp súc','tiếp xuc','tiep xúc','tiep xuc'],
107
+ ' thực tế ': [' thức tế ',' thức tê ',' thự tế ',' thực tê ',' thực te ',' thưc tế ',' thưc tê ',' thưc te ',' thuc te '],
108
+ 'tâm huyết': ['tâm huyến','tâm huyêt','tam huyêt','tam huyết','tam huyet'],
109
+ 'công nghệ phần mềm': ['cnpm','cong nghe phan mem'],
110
+ ' công nghệ ': [' côn nghệ ',' côn nghê ',' côn nghẹ ',' côn nghe ',' công nghê ',' công nghẹ ',' công nghe ',' cong nghệ ',' cong nghê ',' cong nghẹ ',' cong nghe '],
111
+ 'nâng cấp': ['năng cấp','nâng câp','nâng cap','nang câp','nang cấp','nang cap'],
112
+ ' không thể ': [' không thẻ ',' ko thể ',' khum thể ',' k thể ',' không the ',' ko the ',' k the ',' khum the ',' khong the '],
113
+ 'tham khảo': ['tam khảo','tham khao'],
114
+ 'giám sát': ['dám sát','giám sat','giam sát','giam sat'],
115
+ 'một số' : ['mốt số','môt số','môt sô','môt so','mot số','mot sô','mot so'],
116
+ 'hạn chế': ['hạn chê','hạn che','han che'],
117
+ 'cảm ơn' : ['cám ơn', 'cẻm ơn','kẽm ơn','cam on'],
118
+ 'ngắn gọn' : ['ngắn gọc','ngắn gọt','ngắn gõn','ngắn gôn','ngắn gon','ngan gon'],
119
+ ' đảm bảo chất lượng ' : [' đbcl ',' dbcl '],
120
+ 'đảm bảo' : ['đảm bao','đảm bão','đãm bão','đãm bảo','đãm bao','đam bảo','đam bao','đam bão','dam bảo','dam bao','dam bão'],
121
+ 'hiện nay' : ['hien nay', 'hiên nay','hiện nây'],
122
+ 'quan tâm' : ['quan tấm', 'quan tam'],
123
+ 'nền tảng' : ['nền tẳng', 'nen tang'],
124
+ 'hàng ngày' : ['hằng ngày','hang ngay'],
125
+ 'nâng cao' : ['năng cao','nâng cáo', 'nâng cào', 'nâng cảo','nang cáo', 'nang cào', 'nang cảo','nang cao'],
126
+ ' phòng đào tạo ' : [' pdt ',' pđt ','phòng đào tao','phòng dao tạo' ,'phong dao tao'],
127
+ 'đào tạo': ['đào tao','đào tảo','đào tão','đao tạo','đao tảo','đao tão','đao tao','dao tảo','dao dão','dao tạo','dao tao'],
128
+ 'khắt khe': ['khắc khe','khăc khe','khăt khe','khat khe'],
129
+ 'lưu loát': ['lưu lát','lưu loat','luu loát','luu loat','lưu lat','luu lat'],
130
+ ' sinh viên ': [' sv ',' svien ',' sinhvien ',' sinh vien ',' sinh diên ',' sanh diên ',' sanh viên ',' sanh vien '],
131
+ 'dễ thương': ['dẽ thương','dẽ thuong','dễ thuong','dê thương','dê thuong','de thương','de thuong'],
132
+ 'vật lý': ['vậy lý','vậy lí','vật ly','vật li','vât ly','vât li','vat ly','vat li'],
133
+ ' học kỳ ': [' hk ',' học ky ',' học ki ',' hoc ki ',' hoc ky ',' hoc kì ',' hoc kỳ '],
134
+ ' học kỳ 1 ': [' hk1 ',' học ky 1 ',' học ki 1 ',' hoc ki 1 ',' hoc ky 1 ',' hoc kì 1 ',' hoc kỳ 1 '],
135
+ ' học kỳ 2 ': [' hk2 ',' học ky 2 ',' học ki 2 ',' hoc ki 2 ',' hoc ky 2 ',' hoc kì 2 ',' hoc kỳ 2 '],
136
+ 'lười nhát': ['lười nhác','lười nhat','lười nhac','lươi nhát','lươi nhat','luoi nhat'],
137
+ ' bài tập ': [' bai tập ',' bài tâp ',' bài tap ',' bai tập ',' bai tâp ',' bai tap ',' btap ',' bt '],
138
+ ' bài tập về nhà ': [' btvn '],
139
+ ' ví dụ ': [' vd ',' vdu ',' vidu ',' ví du ',' vi dụ ',' vi du '],
140
+ 'chất lượng': ['chất lương','chất luong','chât lượng','chât lương','chât luong','chat lượng','chat lương','chat luong'],
141
+ ' nội dung ': [' nối dung ',' nối dun ',' nội dun ',' nôi dung ',' nôi dun ',' noi dung '],
142
+ 'thực hành': ['thực hanh','thưc hành','thưc hanh','thuc hành','thuc hanh'],
143
+ 'hướng dẫn': ['hướng dân','hướng dan','hương dẫn','hương dân','hương dan','huong dẫn','huong dân','huong dan'],
144
+ 'đầy đủ': ['đầy đu','đầy du','đây đủ','đây đu','đây du','đay đủ','đay đu','đay du','day đủ','day đu','day du'],
145
+ 'đúng giờ': ['đúng giơ','đúng gio','đung giờ','đung giơ ','đung gio','dung giờ','dung giơ','dung gio'],
146
+ 'thoải mái': ['thoải mai','thoai mái','thoai mai','thoãi mái','thoãi mai'],
147
+ 'chi tiết': ['chi tiêt','chi tiet','chy tiết','chy tiêt','chy tiet','chỉ tiết'],
148
+ 'tóm t��t': ['tóm tăt','tóm tat','tom tắt','tom tăt','tom tat'],
149
+ 'hiền lành': ['hiền lanh','hiên lành','hiên lanh','hien lành','hien lanh'],
150
+ 'tận tụy': ['tận tuy','tân tụy','tân tuy','tan tụy','tan tuy'],
151
+ ' vui vẻ ': [' vui ve ',' vui vee ',' vui vẻe ',' vui vẻee '],
152
+ 'hòa đồng': ['hòa đông','hòa đong','hòa dong','hoa đồng','hoa đông','hoa đong','hoa dong'],
153
+ 'thường xuyên': ['thường xuyen','thương xuyên','thương xuyen','thuong xuyên','thuong xuyen'],
154
+ 'khó chịu': ['khó chiu','kho chịu','khó chiệu','kho chiu'],
155
+ 'cơ sở': ['cơ sỏ','cơ sơ','co sở','co sỏ','co so','cơ sỡ','co sỡ'],
156
+ 'vật chất': ['vật chât','vật chát','vật chat','vât chất','vât chât','vât chát','vât chat','vat chất','vat chât','vat chát','vat chat'],
157
+ 'hấp dẫn': ['hấp dẩn','hấp dân','hấp dan','hâp dẫn','hâp dân','hâp dan','háp dẫn','háp dân','háp dan','hap dẫn','hap dân','hap dan'],
158
+ ' trẻ trung ': [' tre trung ',' trẽ trung ',' trẻ trun ',' trẽ trun '],
159
+ 'dạy tệ': ['dạy tê','dạy te','day tệ','day tê','day te'],
160
+ 'quá tệ': ['quá tê','quá te','qua tệ','qua tê','qua te'],
161
+ 'tích cực': ['tích cưc','tích cục','tích cuc','tich cực','tich cưc','tich cục','tich cuc'],
162
+ 'tiêu cực': ['tiêu cưc','tiêu cục','tiêu cuc','tieu cực','tieu cưc','tieu cục','tieu cuc'],
163
+ 'hiệu quả': ['hiệu qua','hiêu quả','hiêu qua','hieu quả','hieu qua'],
164
+ 'khô khan': ['khô khán','khô khàn','khô khản','khô khạn'],
165
+ 'cụ thể': ['cụ thê','cụ thẻ','cụ the','cu thể','cu thê','cu thẻ','cu the'],
166
+ 'lôi cuốn': ['lôi cuôn','lôi cuón','loi cuốn','loi cuôn','loi cuon'],
167
+ 'hỗ trợ': ['hồ trợ','hỗ trơ','hỗ tro','hổ trợ','hô trợ','hô trơ','hô tro','ho trợ','ho trơ','ho tro'],
168
+ 'soạn thảo': ['soản thảo','soạn thao','soan thảo','soan thao'],
169
+ 'theo dõi': ['theo giỏi','theo giõi','theo gioi','theo doi','theo dỏi'],
170
+ 'tự luận': ['tự luân','tự luạn','tự luan','tư luận','tư luân','tư luan','tu luận','tu luân','tu luận'],
171
+ 'đôi lúc': ['đội lúc','đội luc','đôi luc','đoi lúc','đoi luc','doi lúc','doi luc'],
172
+ 'đôi khi': ['đoi khi','doi khi'],
173
+ 'cập nhật': ['cập nhât','cập nhạt','cập nhat','câp nhật','câp nhạt','câp nhat','cap nhật','cap nhât','cap nhạt','cap nhat'],
174
+ 'tài liệu': ['tài liêu','tài liẹu','tài lieu','tai liệu','tai liêu','tai liẹu','tai lieu'],
175
+ 'ý kiến': ['y kiến','y kiên','y kien','ý kiên','ý kien'],
176
+ 'so với': ['so vời','so vơi','so voi','so vói','so vòi'],
177
+ 'cần dành': ['cần giành','can danh'],
178
+ 'hòa hợp': ['hóa hợp','hòa hơp','hòa hop','hoa hợp','hoa hơp','hoa hop'],
179
+ 'phương pháp':['phương phap','phuong pháp','phuong phap'],
180
+ 'bổ ích': ['bổ ich','bổ ít','bô ích','bô ich','bo ích','bo ich'],
181
+ ' dạy chán ': [' dạy chan ',' day chán ',' day chan ',' dạy cháng ',' dạy chang ',' day cháng '],
182
+ 'môn học': ['môn hoc','môn hóc','mon học','mon hóc','mon hoc'],
183
+ ' tuyệt vời ': [' tuyệt vơi ',' tuyệt vòi ',' tuyệt voi ',' tuyêt vời ',' tuyêt vơi ',' tuyêt vòi ',' tuyêt voi ',' tuyet vời ',' tuyet vơi ',' tuyet voi ',' tuỵt vời ',' tuỵt vơi ',' tuỵt vòi ',' tuỵt voi ',' tuyt vời ',' tuyt vơi ',' tuyt vòi ',' tuyệt dời ',' tuyệt dơi ',' tuyet doi '],
184
+ ' thời gian ': [' thời giang ',' thơi gian ',' thoi gian '],
185
+ 'tuy nhiên': ['tuy nhien','tuy nhiến','tuy nhiền','tuy nhiển','tuy nhiện','tuy nhién','tuy nhièn','tuy nhiẻn','tuy nhiẽn','tuy nhiẹn'],
186
+ 'cung cấp': ['cung câp','cung cap','cung cắp','cung căp','cung cáp'],
187
+ 'phòng học': ['phòng hoc','phòng hóc','phong học','phong hoc','phong hóc'],
188
+ 'xem xét': ['xem xet','xem set','xem sét','sem xét','sem xet','sem set','sem sét'],
189
+ 'vui tính': ['vui tình','vui tỉnh','vui tĩnh','vui tịnh','vui tinh'],
190
+ 'khả năng': ['khả nang','khả nâng','kha nâng','kha năng','kha nang','khã năng','khã nâng','khã nang'],
191
+ 'chu đáo': ['chu đao','chu đạo','chu đảo','chu đào','chu đão','chu dao'],
192
+ 'đề nghị': ['đề nghi','đê nghị','đê nghi','đe nghị','đe nghi','de nghị','de nghi'],
193
+ 'dạy ổn': ['dạy ôn','dạy on','day ổn','day ôn','day on'],
194
+ ' lan man ': [' lan mang ',' lang man ',' lang mang '],
195
+ 'lớp học': ['lớp hoc','lớp hóc','lơp học','lơp hóc','lơp hoc','lop hóc','lop học','lop hoc'],
196
+ 'bài thi': ['bai thi','bái thi','bải thi','bãi thi','bại thi'],
197
+ 'bài học': ['bài hoc','b��i hóc','bai học','bai hóc','bai hoc'],
198
+ 'làm biếng': ['làm biêng','làm bieng','lam biếng','lam biêng','lam bieng'],
199
+ 'cực kì': ['cực ki','cưc kì','cưc ki','cuc kì','cuc ki','cực ky','cưc kỳ','cưc ky','cuc kỳ','cuc ky'],
200
+ 'thân thiện': ['thân thiên','thân thiẹn','thân thien','than thiện','than thiên','than thiẹn','than thien'],
201
+ 'diễn đạt': ['diễn đat','diễn dạt','diễn dat','diên đạt','diên đat','diên dạt','diên dat','dien đạt','dien đat','dien dạt','dien dat'],
202
+ 'gì đâu': ['gì đau','gì dau','gi đâu','gi đau','gi dau','gí đâu','gí đau','gí dau','j đâu','j đau','j dau'],
203
+ 'thông báo': ['thông bao','thong báo','thong bao','thông bào','thông bảo','thông bão','thong bào','thong bảo','thong bão'],
204
+ 'thích thú': ['thích thu','thích thù','thích thủ','thích thũ','thích thụ','thich thu','thich thù','thich thủ','thich thũ','thich thụ'],
205
+ 'xứng đáng': ['xứng đang','xứng dáng','xứng dang','xưng đáng','xưng đang','xưng dáng','xưng dang','xung đáng','xung đang','xung dáng','xung dang'],
206
+ 'đa dạng': ['đa dang','da dạng','da dang','đa dảng','đa dãng','da dảng','da dãng'],
207
+ 'liên hệ': ['liên hê','liên hẹ','liên he','lien hệ','lien hê','lien hẹ','lien he'],
208
+ 'lý thuyết': ['lý thuyêt','lý thuýet','lý thuyet','ly thuyết','ly thuyêt','ly thuýet','ly thuyet','lí thuyêt','lí thuýet','lí thuyet','li thuyết','li thuyêt','li thuýet','li thuyet'],
209
+ 'vô cùng': ['vô cung','vo cùng','vo cung'],
210
+ 'ý nghĩa': ['ý nghia','ý nghía','ý nghìa','ý nghỉa','ý nghịa','y nghĩa','y nghia','y nghía','y nghìa','y nghỉa','y nghịa'],
211
+ 'cơ hội': ['cơ hôi','cơ hối','cơ hồi','cơ hổi','cơ hỗi','co hội','co hôi','co hối','co hồi','co hổi','co hỗi','cơ hoi','cơ họi','co hoi'],
212
+ 'thú vị': ['thú vi','thu vị','thu vi'],
213
+ 'thuận tiện': ['thuận tiên','thuận tien','thuân tiện','thuân tiên','thuân tien','thuan tiện','thuan tiên','thuan tien'],
214
+ 'trình bày': ['trình bay','trình báy','trình bảy','trình bãy','trình bạy','trinh bày','trinh báy','trinh bảy','trinh bãy','trinh bạy','trinh bay'],
215
+ ' vận dụng ': [' vận dụn ',' vận dung ',' vân dụng ',' vân dung ',' van dụng ',' van dung '],
216
+ 'hạn hẹp': ['hạn hep','han hẹp','han hep'],
217
+ 'tương tác': ['tương tac','tuong tác','tuong tac'],
218
+ 'buồn ngủ': ['buồn ngu','buồn ngũ','buồn ngụ','buồn ngú','buôn ngủ','buôn ngu','buôn ngũ','buôn ngụ','buôn ngú','buon ngủ','buon ngu','buon ngũ','buon ngụ','buon ngú'],
219
+ 'trao đổi': ['trao đôi','trao đỏi','trao đoi','trao đỗi','trao dỏi','trao dõi','trao doi'],
220
+ 'kĩ năng': ['kĩ nang','ki năng','ki nang','kỉ năng','kỉ nang','kỹ nang','ky năng','ky nang','kỷ năng','kỷ nang'],
221
+ 'trình độ': ['trình đô','trình đọ','trình đo','trình do','trinh độ','trinh đô','trinh đọ','trinh đo','trinh do'],
222
+ 'sư phạm': ['sư pham','su phạm','su pham'],
223
+ ' hăng say ': [' hăn say ',' hang say '],
224
+ 'câu hỏi': ['câu hõi','câu hoi','câu họi','cau hỏi','cau hõi','cau hoi','cau họi'],
225
+ ' thời khóa biểu ': [' thời khóa biễu ',' thơi khoa biêu ',' thoi khoa bieu ',' tkb '],
226
+ ' ứng dụng ': [' ứng dụn ',' ứn dụng ',' ứn dụn ',' ứng dung ',' ưng dụng ',' ưng dung ',' ung dụng '],
227
+ 'tương đối': ['tương đôi','tương đói','tương doi','tuong đối','tuong đôi','tuong đói','tuong doi'],
228
+ 'cải thiện': ['cải thiên','cải thiẹn','cải thien','cãi thiện','cãi thiên','cãi thiẹn','cãi thien','cai thiện','cai thiên','cai thiẹn','cai thien'],
229
+ 'chương trình': ['chương trinh','chuong trình','chuong trinh'],
230
+
231
+ #-----------------------------------------------------------------------------------------------------#
232
+
233
+ # Từ đơn:
234
+
235
+ ' về ': [' vê ',' dề ',' dềe ',' vềe ',' vềee ',' vè ',' vèe '],
236
+ ' ngành ': [' nganh ',' ngánh ',' ngảnh ',' ngãnh ',' ngạnh ',' nghành '],
237
+ ' được ': [' đươc ',' dc ',' dcc ',' dccc ',' đc ',' đcc ',' đccc ',' duoc ',' đuoc '],
238
+ ' chỗ ': [' chổ ',' chỗo '],
239
+ ' nè ': [' nà ',' nèe ',' nèee ',' nèeee '],
240
+ ' dễ ': [' de2 ',' dễe ',' dể ',' deef '],
241
+ ' có ' : [' co ',' cóo ',' cóoo ',' cóooo ', ' cóa ',' cóaa ',' cóaaa ', ' cóaaaa ',' coa '],
242
+ ' giảng ' : [' giảnh ',' giang ',' giãng '],
243
+ ' giờ ': [' giời ',' gờ ',' h ',' dờ '],
244
+ ' dạy ': [' giạy ',' dậy học ',' day '],
245
+ ' thêm ': [' them '],
246
+ ' bằng ': [' bắng ',' bang '],
247
+ ' gì ': [' gi ',' gí ',' j ',' jj ',' jjj '],
248
+ ' không ': [' k ',' ko ',' koo ',' kooo ',' khom ',' khomm ',' khum ',' khumm ',' hong ',' hongg ',' honggg ',' hông ',' hôngg ',' hônggg ',' khong ',' khongg ',' khonggg '],
249
+ ' rất ': [' rắt ',' rât ',' rat '],
250
+ ' sao ': [' saoooo ',' xao ',' saoo ',' saooo ',' s '],
251
+ ' vậy ': [' z ',' zz ',' dãy ',' dãyy ',' dãyyy ',' d ',' vay ',' vayy ',' vayyy ',' zay ',' zayy ',' zayyy ',' v '],
252
+ ' rõ ': [' rỏ ',' rõo ',' rõoo '],
253
+ ' dẫn ': [' dẩn ',' dẩnn ',' dẩnnn ',' dẫnn ',' dẫnnn '],
254
+ ' nhiều ': [' nhìu ',' nhìuu ',' nhìuuu ',' nhiềuu ',' nhiềuuu '],
255
+ ' cũng ': [' cũn ',' cũm ',' cx ',' cũngg ',' cũnggg '],
256
+ ' luôn ': [' luon ',' luônn ',' luônnn '],
257
+ ' buổi ': [' buôi ',' buối ',' buội ',' buỗi ',' buoi ',' bủi ',' bũi '],
258
+ ' tốt ': [' tôt ',' tôtt ',' tot ',' tott ',' tốtt ',' tốttt '],
259
+ ' đỉnh ': [' đink ',' đinkk ',' đĩnh ',' đĩnhh ',' dink ',' dinkk ',' đỉnhh ',' đỉnhhh '],
260
+ ' hay ': [' hayy ',' hayyy ',' hayyyy '],
261
+ ' nhưng ': [' nhưn ',' nhung '],
262
+ ' biết ': [' biêt ',' biêtt ',' biêttt ',' bik ',' bikk ',' bikkk ',' bit ',' bít ',' bítt ',' bíttt ',' biếtt ',' biếttt ',' pit ',' pít ',' pítt ',' píttt '],
263
+ ' rồi ': [' rùi ',' rùii ',' ròi ',' ròii ',' rầu ',' rầuu ',' gòi ',' gòii ',' rồii ',' rồiii ',' r '],
264
+ ' dở ': [' dởo ',' dỏm ',' dỏmm ',' dõm ',' dõmm ',' do '],
265
+ ' tệ ': [' cùi bắp ',' cùi mía ',' cùi ',],
266
+ ' cách ': [' cạch ',' cach '],
267
+ ' đúng ': [' đung ',' đún ',' đúm ',' đúngg ',' đúnggg '],
268
+ ' thôi ': [' thoi ',' thoii ',' thoiii ',' thoiiii ',' thoai ',' thoaii ',' thoaiii ',' thoaiiii ',' hoi ',' hoii ',' hoiii ',' hoiiii ',' thui ',' thuii ',' thuiii ',' thuiiii ',' thâu ',' thâuu ',' thâuuu ',' thâuuuu ',' thou ',' thouu ',' thouuu ',' thouuuu '],
269
+ ' ngủ ': [' ngu ',' ngu3 ',' ngủu ',' ngủuu '],
270
+ ' tuyệt ': [' tuyệt ',' tuỵt ',' tuyệtt ',' tuyệttt ',' tuyet '],
271
+ ' với ': [' vs ',' vớii ',' vớiii ',' dới ',' dớii ',' dớiii '],
272
+ ' hoài ': [' quài ',' quàii ',' quàiii ',' hoàii ',' hoàiii '],
273
+ ' trời ': [' xời ',' xờii ',' xờiii ',' trờii ',' trờiii ',' troi ',' tr '],
274
+ ' trường ': [' trương ',' truong '],
275
+ ' lớp ': [' lop ',' lơp '],
276
+ ' như ': [' nhu ',' nhưu '],
277
+ ' giỏi ': [' gioi ',' giói ',' giòi ',' giõi ',' giọi '],
278
+ ' cao ': [' kao ',' caoo ',' kaoo '],
279
+ ' hết ': [' het ',' hêt ',' hếtt '],
280
+ ' nữa ': [' nưa ',' nựa ',' nx ',' nữaa ',' nữaaa '],
281
+ ' điểm ': [' điêm ',' điem ',' diem '],
282
+ ' giữa ': [' giưa ', ' giua '],
283
+ ' giúp ': [' giup ',' giụp '],
284
+ ' thầy ': [' thày ']
285
+ }
286
+
287
+ translate_dict = {
288
+ ' bài trình chiếu ': [' slide ','slides'],
289
+ ' đầy đủ ': [' full '],
290
+ ' hội thảo ': [' seminar '],
291
+ ' thời hạn ': [' deadline ','deadlines', ' dealine ',' dl ',' dline '],
292
+ ' kiểm tra ': [' test ','tests',' check ','checks'],
293
+ ' tổng quan ': [' overview '],
294
+ ' được ': [' ok ',' oke ',' okela ',' okelaa ','okelaaa',' okay ',' okee ','okeee',' oki ',' okii ','okiii'],
295
+ ' bộ dụng cụ ': [' kit '],
296
+ ' thực hành ': [' lab '],
297
+ ' đăng tải ' : [' post ',' up '],
298
+ ' tải lên ' : [' upload ','uploads'],
299
+ ' chủ đề ' :[' topic '],
300
+ ' bài học ' : [' unit ','units'],
301
+ ' trò chơi ' :[' game ','games'],
302
+ ' phần mềm ' : [' wireshark ', ' silverlight ',' proteus '],
303
+ ' sao chép ' :[' copy ',' copies ','copys'],
304
+ ' dán ' : [' paste ','pastes'],
305
+ ' tệp ' : [' file ','files'],
306
+ ' giao diện ' : [' console ',' win form '],
307
+ ' mô hình ứng dụng ' : [' pattern serverside ',' lập tình windows ',' lập trình window ',' lập trình windows '],
308
+ ' lập trình hướng đối tượng ' : [' oop ',' java '],
309
+ ' ngôn ngữ lập trình ' : [' prolog '],
310
+ ' trang mạng ' :[' web ',' webs ',' wed ', ' website ','websites',' progressive web app ',' web app ',' web apps ',' webs app ',' webs apps ',' amp ',' seo '],
311
+ ' sách điện tử ': [' ebook ','ebooks', ' e-book ','e-books'],
312
+ ' công ty ' : [' altera '],
313
+ ' ngôn ngữ truy vấn ' : [' sql '],
314
+ ' dự án ': [' project ',' proj '],
315
+ ' chia sẻ ': [' share '],
316
+ ' cập nhật ': [' update '],
317
+ ' công nghệ thông tin ': [' it '],
318
+ ' trả lời ': ['reply',' rep '],
319
+ ' không ': [' nope ',' no ',' not '],
320
+ ' tiếng anh ': [' english ',' engrisk ',' englisk ',' eng '],
321
+ ' là ': [ ' is ',' are ',' was ',' were '],
322
+ ' rất ': [' very '],
323
+ ' tốt ': [' good ',' gud ',' gút ',' gut ',' gudd ',' best ',' the best ',' bestt ','besttt'],
324
+ ' khóa học ': [' courses ',' crs ',' course ',' coursera ','courseras'],
325
+ ' trình bày thử ': [' demo '],
326
+ ' hoàn hảo ': [' perfect ',' perf ','perfection'],
327
+ ' nhắn tin ': [' chat ',' chats ',' ib ',' inbox ',' ibox ',' chitchat ',' chatting '],
328
+ ' tư duy ': [' logic ','logics'],
329
+ ' nộp ': [' submit '],
330
+ ' trẻ trung ': [' teen '],
331
+ ' ngoài trời ' : [' outdoor ','outdoors'],
332
+ ' hoạt động ': [' activity ', ' activities '],
333
+ ' vui ' :[' funny ', ' fun ','funnys','funnies'],
334
+ ' hệ điều hành ' : [' linux ',' windows ', ' win server '],
335
+ ' góp ý ' : [' feed back ',' feed backs ',' feedback ',' feedbacks '],
336
+ ' phương pháp học tập ' : [' projectbase ','projectbases',' project-base ','project-bases'],
337
+ ' nhóm ' : [' team ',' group '],
338
+ ' dễ thương ' : [' cute ',' cutee ','cuteee'],
339
+ ' đào tạo ' : [' train ','trains',' training ','trainings','trained'],
340
+ ' thích ' : [' like ','likes'],
341
+ ' thất bại ' : ['failse',' fail ','failed',' fails '],
342
+ ' ông chủ ' : [' boss ','bosses'],
343
+ ' phòng trò chuyện ' : [' chat room ','chat rooms'],
344
+ ' thêm ' : [' add ','addition'],
345
+ ' tên ' : [' name ','names'],
346
+ ' kích cỡ ' : [' size ','sizes'],
347
+ ' thiết kế phần mềm ' : [' design pattern ',' design patterns ',' designs pattern ',' designs patterns '],
348
+ ' sơ đồ luồng dữ liệu ' : [' dfd '],
349
+ ' phiên bản ' : [' version ',' versions '],
350
+ ' vi mạch tích hợp ' : [' nfc ',' NearField Communication ',' NearField Communications ',' Near-Field Communication ', ' Near-Field Communications '],
351
+ ' dễ ' : [' easy ',' ez ',' easies ',' easys '],
352
+ 'từng bước một' : ['step by step','steps by steps','steps by step','step by steps'],
353
+ ' quá giờ ': [' over time ',' over times ',' overtime ',' overtimes '],
354
+ ' buổi thuyết trình ': [' buổi present ',' buổi presents ',' presentation ',' presentations '],
355
+ ' thuyết trình ': [' present ',' presents '],
356
+ ' tải ': [' down ',' downs ',' download ',' downloads '],
357
+ ' phong cách ': [' style ',' styles '],
358
+ ' cực kì ': [' max ',' maxx ',' maxxx '],
359
+ ' cấp độ ': [' level ',' levels ',' lv '],
360
+ ' bình luận ': [' comment ',' comments ',' cmt ',' cmts '],
361
+ ' sách giáo khoa ': [' textbook ',' textbooks ',' book ',' books '],
362
+ ' cơ bản ': [' basic ',' basics '],
363
+ ' phản biện ': [' debate ',' debates '],
364
+ ' trả lời tin nhắn ': [' rep mail ',' rep mails ',' reply mail ',' reply mails '],
365
+ ' nhắn tin ': [' mail ',' mails '],
366
+ ' đỉnh ': [' pro ',' proo ',' prooo ',' vip ',' vipp ',' vippp '],
367
+ ' bỏ phiếu ': [' vote ',' votes ']
368
+ }
369
+
370
+
371
+
372
+ number_dict = {
373
+ '0%': 'không phần trăm',
374
+ '5%': 'năm phần trăm',
375
+ '10%': 'mười phần trăm',
376
+ '15%': 'mười lăm phần trăm',
377
+ '20%': 'hai mươi phần trăm',
378
+ '25%': 'hai mươi lăm phần trăm',
379
+ '30%': 'ba mươi phần trăm',
380
+ '35%': 'ba mươi lăm phần trăm',
381
+ '40%': 'bốn mươi phần trăm',
382
+ '45%': 'bốn mươi lăm phần trăm',
383
+ '50%': 'năm mươi phần trăm',
384
+ '55%': 'năm mươi lăm phần trăm',
385
+ '60%': 'sáu mươi phần trăm',
386
+ '65%': 'sáu mươi lăm phần trăm',
387
+ '70%': 'bảy mươi phần trăm',
388
+ '75%': 'bảy mươi lăm phần trăm',
389
+ '80%': 'tám mươi phần trăm',
390
+ '85%': 'tám mươi lăm phần trăm',
391
+ '90%': 'chín mươi phần trăm',
392
+ '95%': 'chín mươi lăm phần trăm',
393
+ '100%': 'trăm phần trăm',
394
+
395
+
396
+
397
+ '0': 'không',
398
+ '1': 'một',
399
+ '2': 'hai',
400
+ '3': 'ba',
401
+ '4': 'bốn',
402
+ '5': 'năm',
403
+ '6': 'sáu',
404
+ '7': 'bảy',
405
+ '8': 'tám',
406
+ '9': 'chín',
407
+ '10': 'mười',
408
+ '11': 'mười một',
409
+ '12': 'mười hai',
410
+ '13': 'mười ba',
411
+ '14': 'mười bốn',
412
+ '15': 'mười lăm',
413
+ '16': 'mười sáu',
414
+ '17': 'mười bảy',
415
+ '18': 'mười tám',
416
+ '19': 'mười chín',
417
+ '20': 'hai mươi',
418
+ '21': 'hai mươi mốt',
419
+ '22': 'hai mươi hai',
420
+ '23': 'hai mươi ba',
421
+ '24': 'hai mươi bốn',
422
+ '25': 'hai mươi lăm',
423
+ '26': 'hai mươi sáu',
424
+ '27': 'hai mươi bảy',
425
+ '28': 'hai mươi tám',
426
+ '29': 'hai mươi chín',
427
+ '30': 'ba mươi',
428
+ '31': 'ba mươi mốt',
429
+ '32': 'ba mươi hai',
430
+ '33': 'ba mươi ba',
431
+ '34': 'ba mươi bốn',
432
+ '35': 'ba mươi lăm',
433
+ '36': 'ba mươi sáu',
434
+ '37': 'ba mươi bảy',
435
+ '38': 'ba mươi tám',
436
+ '39': 'ba mươi chín',
437
+ '40': 'bốn mươi',
438
+ '41': 'bốn mươi mốt',
439
+ '42': 'bốn mươi hai',
440
+ '43': 'bốn mươi ba',
441
+ '44': 'bốn mươi bốn',
442
+ '45': 'bốn mươi lăm',
443
+ '46': 'bốn mươi sáu',
444
+ '47': 'bốn mươi bảy',
445
+ '48': 'bốn mươi tám',
446
+ '49': 'bốn mươi chín',
447
+ '50': 'năm mươi',
448
+ '51': 'năm mươi mốt',
449
+ '52': 'năm mươi hai',
450
+ '53': 'năm mươi ba',
451
+ '54': 'năm mươi bốn',
452
+ '55': 'năm mươi lăm',
453
+ '56': 'năm mươi sáu',
454
+ '57': 'năm mươi bảy',
455
+ '58': 'năm mươi tám',
456
+ '59': 'năm mươi chín',
457
+ '60': 'sáu mươi',
458
+ '61': 'sáu mươi mốt',
459
+ '62': 'sáu mươi hai',
460
+ '63': 'sáu mươi ba',
461
+ '64': 'sáu mươi bốn',
462
+ '65': 'sáu mươi lăm',
463
+ '66': 'sáu mươi sáu',
464
+ '67': 'sáu mươi bảy',
465
+ '68': 'sáu mươi tám',
466
+ '69': 'sáu mươi chín',
467
+ '70': 'bảy mươi',
468
+ '71': 'bảy mươi mốt',
469
+ '72': 'bảy mươi hai',
470
+ '73': 'bảy mươi ba',
471
+ '74': 'bảy mươi bốn',
472
+ '75': 'bảy mươi lăm',
473
+ '76': 'bảy mươi sáu',
474
+ '77': 'bảy mươi bảy',
475
+ '78': 'bảy mươi tám',
476
+ '79': 'bảy mươi chín',
477
+ '80': 'tám mươi',
478
+ '81': 'tám mươi mốt',
479
+ '82': 'tám mươi hai',
480
+ '83': 'tám mươi ba',
481
+ '84': 'tám mươi bốn',
482
+ '85': 'tám mươi lăm',
483
+ '86': 'tám mươi sáu',
484
+ '87': 'tám mươi bảy',
485
+ '88': 'tám mươi tám',
486
+ '89': 'tám mươi chín',
487
+ '90': 'chín mươi',
488
+ '91': 'chín mươi mốt',
489
+ '92': 'chín mươi hai',
490
+ '93': 'chín mươi ba',
491
+ '94': 'chín mươi bốn',
492
+ '95': 'chín mươi lăm',
493
+ '96': 'chín mươi sáu',
494
+ '97': 'chín mươi bảy',
495
+ '98': 'chín mươi tám',
496
+ '99': 'chín mươi chín',
497
+ '100': 'một trăm'
498
+ }
499
+
500
+ # Từ đơn: 50 từ
501
+ # Từ phức: 163 từ
get_default_weight.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ from config import destinations, feature_names, sorted_tags_dict, tags_vector
4
+
5
+
6
+ def create_bias_weights():
7
+ """
8
+ Create a weights vector for bias based on the given tags and weights.
9
+ The function initializes a weights vector to zero, then maps the weights from the weights_tags_vector to the appropriate positions in the weights_vector based on the tags present in the destinations.
10
+ """
11
+ weights_tags_vector = [
12
+ [15, 15, 0.9, 15, 15, 10, 1, 5, 0.6, 0.9, 0.9, 0.8, 10, 10, 1, 15],
13
+ [15, 15, 0.9, 15, 15, 10, 15, 1, 10, 0.6, 0.9, 0.9, 0.8, 10, 10, 15, 0.8, 15],
14
+ [15, 0.9, 0.8, 15, 15, 1, 10, 10, 0.6, 0.9, 0.9, 0.8, 5, 5, 1, 15],
15
+ [15, 15, 0.9, 15, 0.7, 15, 15, 15, 1, 10, 10, 1, 0.9, 0.9, 0.9, 5, 5, 15, 0.8, 15],
16
+ [10, 10, 15, 15, 0.8, 0.9, 15, 15, 15, 1, 10, 10, 0.6, 0.5, 0.9, 0.9, 0.8, 0.7, 15, 15, 15, 15, 15],
17
+ [0.8, 0.9, 15, 0.8, 15, 0.9, 10, 15, 0.9, 0.9, 0.9, 0.8, 15, 10, 1, 15],
18
+ [0.9, 0.8, 5, 1, 0.9, 10, 15, 0.9, 0.9, 0.9, 0.9, 0.8, 15, 1, 1, 15],
19
+ [0.8, 0.9, 5, 1, 15, 15, 0.9, 0.9, 0.9, 0.8, 15, 1, 15],
20
+ [0.8, 0.7, 15, 15, 1, 10, 0.7, 0.7, 0.6, 5, 5, 15],
21
+ [0.8, 5, 1, 15, 15, 15, 0.7, 0.7, 15],
22
+ [0.8, 0.7, 1, 15, 15, 0.7, 0.7, 15],
23
+ [0.8, 0.7, 1, 15, 15, 15, 0.7, 0.9, 15],
24
+ [0.8, 0.7, 1, 15, 15, 0.7, 0.7, 15],
25
+ [0.8, 0.7, 1, 15, 15, 15, 0.7, 0.7, 15],
26
+ [0.8, 0.7, 1, 15, 15, 15, 1, 10, 15],
27
+ [10, 0.9, 0.8, 1, 15, 15, 15, 0.8, 10, 15],
28
+ [0.8, 15, 1, 15, 15, 0.8, 10, 15],
29
+ [10, 0.8, 1, 15, 1, 0.9, 0.8, 5, 0.8],
30
+ [0.8, 15, 1, 5, 0.9, 0.8, 0.7, 0.7],
31
+ [0.9, 0.8, 15, 1, 15, 0.7, 0.8, 0.7, 0.7, 5, 5, 15],
32
+ [0.8, 0.7, 1, 5, 0.9, 10, 10, 15],
33
+ [0.8, 1, 15, 15, 1, 0.9, 0.8, 0.8, 15],
34
+ [0.8, 1, 10, 5, 5, 15],
35
+ [0.8, 0.7, 1, 15, 15, 0.8, 0.9, 15],
36
+ [10, 10, 10, 1, 10, 0.8, 1, 5, 10, 10, 10, 10, 1, 0.9, 1, 1, 15],
37
+ [0.8, 0.7, 1, 15, 15, 0.8, 0.9, 15],
38
+ [0.8, 0.7, 1, 10, 10, 0.8, 0.9, 15],
39
+ [10, 0.8, 0.7, 15, 15, 1, 15, 15, 0.7, 0.7, 0.6, 5, 5, 1, 15],
40
+ [5, 0.8, 0.7, 5, 5, 1, 10, 10, 0.7, 0.7, 0.6, 5, 5, 1, 15],
41
+ [0.8, 0.7, 15, 5, 1, 10, 10, 10, 0.8, 0.7, 0.7, 5, 5, 5, 10, 15],
42
+ [5, 5, 10, 15, 15, 15, 15, 0.9, 0.8, 0.7, 0.7, 1, 15],
43
+ [10, 10, 15, 15, 10, 5, 1, 15, 15, 15, 15, 0.7, 5, 5, 0.8, 1, 15],
44
+ [10, 15, 15, 15, 10, 10, 1, 1, 1, 15, 15, 5, 5],
45
+ [0.8, 0.7, 0.6, 0.8, 1, 1, 1, 0.9, 0.8, 0.7, 0.7, 0.6, 5, 5, 1, 15],
46
+ [1, 0.8, 0.9, 0.7, 0.6, 1, 0.9, 0.8, 1, 1, 0.9, 0.8, 0.8, 0.7, 0.9, 5, 5, 15],
47
+ [1, 0.8, 0.9, 0.7, 0.6, 1, 0.9, 0.8, 1, 1, 0.9, 0.7, 0.6, 0.8, 0.8, 0.8, 0.7, 5, 5, 1, 0.7, 0.6, 15],
48
+ [0.9, 0.7, 1, 1, 0.8, 0.7, 0.8, 0.8, 0.7, 1, 1, 1, 1, 15]
49
+ ]
50
+ #Create a weights vector initialized to zero
51
+ weights_vector = np.zeros(tags_vector.shape)
52
+
53
+ # Map weights to the appropriate positions in the weights_vector
54
+ for i, row in enumerate(destinations["tags"][1:].values):
55
+ tags = row.split()
56
+ for tag, weight in zip(tags, weights_tags_vector[i]):
57
+ index = np.where(feature_names == tag.lower())[0][0]
58
+ weights_vector[i][index] = weight
59
+ np.save("Datasets/Weights/weights_bias.npy", weights_vector)
60
+
61
+ def create_freq_weights():
62
+ """
63
+ This function creates a weights vector for frequency-based weights based on the given tags and their frequencies.
64
+ The function initializes a weights vector to zero, then maps the weights from the sorted_tags_dict to the appropriate positions in the weights_vector based on the tags present in the destinations.
65
+ The weights are calculated as the ratio of the tag's frequency to the maximum frequency among all tags.
66
+
67
+ Parameters:
68
+ tags_vector (numpy.ndarray): A 2D numpy array representing the tags vector. Each row corresponds to a destination, and each column corresponds to a tag. The value at each position is 1 if the tag is present in the destination, and 0 otherwise.
69
+ sorted_tags_dict (dict): A dictionary where the keys are the tags and the values are their frequencies.
70
+ feature_names (numpy.ndarray): A 1D numpy array representing the names of the features (tags).
71
+ destinations (pandas.DataFrame): A pandas DataFrame containing the destinations data, including the tags column.
72
+
73
+ Returns:
74
+ numpy.ndarray: A 2D numpy array representing the weights vector for frequency-based weights. Each row corresponds to a destination, and each column corresponds to a tag. The value at each position represents the weight of the tag for that destination.
75
+ """
76
+ #Create a weights vector initialized to zero
77
+ weights_vector = np.zeros(tags_vector.shape)
78
+ max_freq = max(sorted_tags_dict.values())
79
+
80
+ # Map weights to the appropriate positions in the weights_vector
81
+ for i, row in enumerate(destinations['tags'][1:].values):
82
+ tags = row.split()
83
+ for tag in tags:
84
+ index = np.where(feature_names == tag.lower())[0][0]
85
+ weights_vector[i][index] = f"{(sorted_tags_dict[tag.replace('_', ' ')]/max_freq):.2f}"
86
+ np.save("Datasets/Weights/weights_freq.npy", weights_vector)
87
+
88
+ create_bias_weights()
89
+ create_freq_weights()
90
+
91
+ weights_bias_vector = np.load("Datasets/Weights/weights_bias.npy")
92
+ weights_freq = np.load("Datasets/Weights/weights_freq.npy")
93
+ weighted_tags_vector = weights_bias_vector
get_destinations.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ from config import vectorizer
4
+ from get_default_weight import destinations, weights_bias_vector
5
+ from user_weights import get_user_weights
6
+
7
+
8
+ def get_des_accumulation(question_vector, weights_bias_vector):
9
+ accumulation = 0
10
+ for index in range(len(weights_bias_vector)):
11
+ if question_vector[index]==1:
12
+ accumulation += weights_bias_vector[index]
13
+
14
+ return accumulation
15
+
16
+ def get_destinations_list(question_vector, top_k, user_id=None):
17
+ des = destinations
18
+ des = des[1:].reset_index(drop=True)
19
+ """
20
+ This function calculates the accumulated scores for each destination based on the given question vector and weights vector.
21
+ It then selects the top 5 destinations with the highest scores and returns their names.
22
+ Parameters:
23
+ question_vector (numpy.ndarray): A 1D numpy array representing the question vector. Each element corresponds to a tag, and its value is 1 if the tag is present in the question, and 0 otherwise.
24
+ weights_bias_vector (numpy.ndarray): A 2D numpy array representing the weights vector. Each row corresponds to a destination, and each column corresponds to a tag. The value at each position represents the weight of the tag for that destination.
25
+ Returns:
26
+ destinations_list: A list of strings representing the names of the top k destinations with the highest scores.
27
+ """
28
+ # Use user-specific weights if available, otherwise use default weights
29
+ weights_vector = weights_bias_vector
30
+ if user_id is not None:
31
+ weights_vector = get_user_weights(user_id, weights_bias_vector)
32
+
33
+ accumulation_dict = {}
34
+ for index in range(len(weights_vector)):
35
+ accumulation = get_des_accumulation(question_vector[0], weights_vector[index])
36
+ accumulation_dict[str(index)] = accumulation
37
+
38
+ top_keys = sorted(accumulation_dict, key=accumulation_dict.get, reverse=True)
39
+ print(f"Top keys: {top_keys}")
40
+ scores = [accumulation_dict[key] for key in top_keys]
41
+ q1_score = np.percentile(scores, 25)
42
+ destinations_list = []
43
+ for key in top_keys:
44
+ if accumulation_dict[key] > q1_score:
45
+ destinations_list.append(des["name"][int(key)])
46
+ print(f"{des['name'][int(key)]}: {accumulation_dict[key]}")
47
+
48
+ return destinations_list[:top_k]
49
+
50
+ def get_question_vector(question_tags):
51
+ """
52
+ Generate a question vector based on the given list of question tags.
53
+
54
+ Parameters:
55
+ question_tags (list): A list of strings representing the tags associated with the question.
56
+ Each tag is a word or phrase that describes a characteristic of a destination.
57
+
58
+ Returns:
59
+ numpy.ndarray: A 2D numpy array representing the question vector.
60
+ The array is transformed from the input list of question tags using a vectorizer.
61
+ Each row in the array corresponds to a tag, and its value is either 0 or 1.
62
+ The length of each row is equal to the number of unique tags in the dataset.
63
+ """
64
+ question_tags = [question_tags]
65
+ question_vector = vectorizer.transform(question_tags).toarray()
66
+ return question_vector
load_data.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from constants import DEFAULT_TEXT_ANNOTATION_FILE
3
+
4
+ with open(DEFAULT_TEXT_ANNOTATION_FILE, 'r', encoding='utf-8') as file:
5
+ data = json.load(file)
6
+
7
+ # Prepare sentences and labels
8
+ sentences = [item[0] for item in data["annotations"]]
9
+ """
10
+ List[str]: A list of sentences extracted from the dataset.
11
+ Each sentence corresponds to an annotation in the dataset.
12
+ """
13
+
14
+ labels = [item[1]['entities'] for item in data["annotations"]]
15
+ """
16
+ List[List[Tuple[str, str]]]: A list of entity labels for each sentence.
17
+ Each label is a tuple containing the entity text and its corresponding tag.
18
+ """
19
+
20
+ # Define tags
21
+ tags = data["classes"]
22
+ """
23
+ List[str]: A list of all possible entity tags (classes) in the dataset.
24
+ These tags will be used to label the tokens in each sentence.
25
+ """
26
+
27
+ # Convert tags to indices
28
+ tag2idx = {tag: 0 for idx, tag in enumerate(tags)}
29
+ for label in labels:
30
+ for entity in label:
31
+ tag2idx[entity[1]] = tag2idx[entity[1]] + 1
32
+ # Sort the dictionary by values
33
+ sorted_tags = dict(sorted(tag2idx.items(), key=lambda item: item[1],reverse=True))
34
+ sorted_tags = {key: value for key, value in sorted_tags.items() if value != 0}
35
+
36
+ new_tag = {'<pad>': 0}
37
+
38
+ sorted_tags = {**new_tag, **sorted_tags}
model_predict_onnx.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import onnxruntime as ort
3
+ from DataProcessing import read_input
4
+ from load_data import sorted_tags
5
+ from transformers import AutoTokenizer
6
+ from constants import ONNX_MODEL_PATH,DEFAULT_PRETRAIN_MODEL_NAME_TOKENIZER
7
+ class Key_Ner_ONNX_Predictor:
8
+ def __init__(self, model_path, tokenizer, tag_map):
9
+ """
10
+ Initialize the ONNX predictor.
11
+ Args:
12
+ model_path (str): Path to the ONNX model.
13
+ tokenizer (BertTokenizer): Tokenizer to process input sentences.
14
+ tag_map (Dict[int, str]): Mapping of indices to tags.
15
+ """
16
+ self.session = ort.InferenceSession(model_path, providers=["CPUExecutionProvider"])
17
+ self.tokenizer = tokenizer
18
+ self.tag_map = tag_map
19
+
20
+ def predict(self, sentence):
21
+ """
22
+ Predict tags using the ONNX model.
23
+ Args:
24
+ sentence (str): Input sentence.
25
+ Returns:
26
+ Tuple[str, List[str]]: Original sentence and predicted tags.
27
+ """
28
+ sentence = read_input(sentence)
29
+ tokens = self.tokenizer(sentence, return_tensors="np", padding=True, truncation=True)
30
+
31
+ # Convert to int64 (ONNX requirement)
32
+ input_ids = tokens["input_ids"].astype(np.int64)
33
+ attention_mask = tokens["attention_mask"].astype(np.int64)
34
+
35
+ # Run inference
36
+ outputs = self.session.run(None, {
37
+ "input_ids": input_ids,
38
+ "attention_mask": attention_mask
39
+ })
40
+
41
+ logits = outputs[0]
42
+ predicted_tags = np.argmax(logits, axis=2)[0]
43
+
44
+ # Convert indices to tags
45
+ predicted_tags = [self.tag_map[idx] for idx in predicted_tags]
46
+ print("PREDICTING", predicted_tags)
47
+ predicted_tags = set(predicted_tags)
48
+ predicted_tags.discard('<pad>')
49
+ predicted_tags = [tag.replace(" ", "_") for tag in predicted_tags]
50
+
51
+ return self.tokenizer.decode(input_ids[0], skip_special_tokens=True), predicted_tags
52
+
53
+ # Initialize ONNX-based predictor
54
+ tokenizer = AutoTokenizer.from_pretrained(DEFAULT_PRETRAIN_MODEL_NAME_TOKENIZER)
55
+ onnx_predictor = Key_Ner_ONNX_Predictor(
56
+ model_path=ONNX_MODEL_PATH,
57
+ tokenizer=tokenizer,
58
+ tag_map=dict(enumerate(sorted_tags))
59
+ )
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ python-dotenv
2
+ onnxruntime
3
+ uvicorn
4
+ pymongo
5
+ fastapi
6
+ pandas
7
+ scikit-learn
8
+ openpyxl
9
+ transformers
10
+ underthesea==6.8.4
11
+ underthesea_core==1.0.4
12
+ loguru
user_weights.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import datetime
2
+ import json
3
+
4
+ import numpy as np
5
+ from database import db
6
+
7
+
8
+ def get_user_weights(user_id, default_weights):
9
+ """
10
+ Get the weights for the given user. If the user doesn't have weights,
11
+ return the default weights.
12
+
13
+ Parameters:
14
+ user_id (str): The ID of the user.
15
+ default_weights (numpy.ndarray): The default weights to use if the user doesn't have weights.
16
+
17
+ Returns:
18
+ numpy.ndarray: The weights for the user.
19
+ """
20
+ weights = db.get_user_weights(user_id, default_weights)
21
+ if weights is not None:
22
+ return weights.copy()
23
+ else:
24
+ return default_weights.copy()
25
+
26
+ def user_weights_exist(user_id):
27
+ """
28
+ Check if weights exist for the given user.
29
+
30
+ Parameters:
31
+ user_id (str): The ID of the user.
32
+
33
+ Returns:
34
+ bool: True if weights exist for the user, False otherwise.
35
+ """
36
+ return db.user_weights_exist(user_id)
37
+
38
+ def save_user_weights(user_id, weights):
39
+ """
40
+ Save the weights for the given user.
41
+
42
+ Parameters:
43
+ user_id (str): The ID of the user.
44
+ weights (numpy.ndarray): The weights to save.
45
+
46
+ Returns:
47
+ bool: True if the weights were saved successfully, False otherwise.
48
+ """
49
+ return db.save_user_weights(user_id, weights)
50
+
51
+ def update_user_weights(user_id, tag_indices, new_weights, default_weights):
52
+ """
53
+ Update the weights for the given user at the specified tag indices.
54
+
55
+ Parameters:
56
+ user_id (str): The ID of the user.
57
+ tag_indices (list): The indices of the tags to update.
58
+ new_weights (list): The new weights for the tags.
59
+ default_weights (numpy.ndarray): The default weights to use if the user doesn't have weights.
60
+
61
+ Returns:
62
+ bool: True if the weights were updated successfully, False otherwise.
63
+ """
64
+ weights = get_user_weights(user_id, default_weights)
65
+
66
+ # Update the weights
67
+ for i, tag_index in enumerate(tag_indices):
68
+ for j in range(len(weights)):
69
+ weights[j][tag_index] = new_weights[i]
70
+
71
+ return save_user_weights(user_id, weights)
72
+
73
+ def update_weights_from_query(user_id, query_tags, feature_names, default_weights):
74
+ """
75
+ Update weights based on user query. For each tag in the query, if a destination
76
+ has that tag with a weight > 0, increase the weight by 5.
77
+
78
+ Parameters:
79
+ user_id (str): The ID of the user.
80
+ query_tags (list): The tags from the user's query.
81
+ feature_names (numpy.ndarray): The names of all features (tags).
82
+ default_weights (numpy.ndarray): The default weights to use if the user doesn't have weights.
83
+
84
+ Returns:
85
+ bool: True if the weights were updated successfully, False otherwise.
86
+ """
87
+ weights = get_user_weights(user_id, default_weights)
88
+ print(f"weights: {weights}")
89
+ # Find indices of query tags
90
+ tag_indices = []
91
+ for tag in query_tags:
92
+ # Find the index of the tag in feature_names
93
+ matches = np.where(feature_names == tag.lower())[0]
94
+ if len(matches) > 0:
95
+ tag_indices.append(matches[0])
96
+
97
+ # Update weights for destinations that have these tags
98
+ for tag_index in tag_indices:
99
+ for dest_index in range(len(weights)):
100
+ # If the destination has this tag with weight > 0, increase by 5
101
+ if weights[dest_index][tag_index] > 0:
102
+ weights[dest_index][tag_index] += 5
103
+
104
+ return save_user_weights(user_id, weights)
105
+
106
+ def update_weights_from_feedback(user_id, destination_id, tag_id, rating, default_weights):
107
+ """
108
+ Update weights based on user feedback (star rating).
109
+
110
+ Parameters:
111
+ user_id (str): The ID of the user.
112
+ destination_id (int): The ID of the destination.
113
+ tag_id (int): The ID of the tag.
114
+ rating (int): The star rating (1-5).
115
+ default_weights (numpy.ndarray): The default weights to use if the user doesn't have weights.
116
+
117
+ Returns:
118
+ bool: True if the weights were updated successfully, False otherwise.
119
+ """
120
+ weights = get_user_weights(user_id, default_weights)
121
+
122
+ # Adjust weight based on rating
123
+ if rating == 5:
124
+ weights[destination_id][tag_id] += 5
125
+ elif rating == 4:
126
+ weights[destination_id][tag_id] += 3
127
+ elif rating == 3:
128
+ weights[destination_id][tag_id] += 1
129
+ elif rating == 2:
130
+ weights[destination_id][tag_id] -= 3
131
+ elif rating == 1:
132
+ weights[destination_id][tag_id] -= 5
133
+
134
+ return save_user_weights(user_id, weights)
135
+
136
+ def get_user_metadata(user_id):
137
+ """
138
+ Get the metadata for a specific user.
139
+
140
+ Parameters:
141
+ user_id (str): The ID of the user.
142
+
143
+ Returns:
144
+ dict: The metadata for the user.
145
+ """
146
+ return db.get_user_metadata(user_id)
147
+
148
+ def update_user_metadata(user_id, metadata):
149
+ """
150
+ Update the metadata for the given user.
151
+
152
+ Parameters:
153
+ user_id (str): The ID of the user.
154
+ metadata (dict): The metadata for the user.
155
+
156
+ Returns:
157
+ bool: True if the metadata was updated successfully, False otherwise.
158
+ """
159
+ # Get existing metadata
160
+ existing_metadata = db.get_user_metadata(user_id)
161
+
162
+ # Update with new metadata
163
+ existing_metadata.update(metadata)
164
+
165
+ # Save updated metadata
166
+ return db.save_user_metadata(user_id, existing_metadata)
167
+
168
+ def track_question_tags(user_id, question_tags):
169
+ """
170
+ Track the tags from a user's question, keeping the last 5 questions.
171
+
172
+ Parameters:
173
+ user_id (str): The ID of the user.
174
+ question_tags (list): The tags from the user's question.
175
+
176
+ Returns:
177
+ bool: True if the tags were tracked successfully, False otherwise.
178
+ """
179
+ # Get existing metadata
180
+ metadata = db.get_user_metadata(user_id)
181
+
182
+ # Initialize recent_tags if it doesn't exist
183
+ if "recent_tags" not in metadata:
184
+ metadata["recent_tags"] = []
185
+
186
+ # Add new tags to the beginning of the list
187
+ metadata["recent_tags"].insert(0, {
188
+ "timestamp": str(datetime.datetime.now()),
189
+ "tags": question_tags
190
+ })
191
+
192
+ # Keep only the last 5 entries
193
+ metadata["recent_tags"] = metadata["recent_tags"][:5]
194
+
195
+ # Save updated metadata
196
+ return db.save_user_metadata(user_id, metadata)
197
+
198
+ def get_all_users():
199
+ """
200
+ Get a list of all users.
201
+
202
+ Returns:
203
+ list: A list of all user IDs.
204
+ """
205
+ return db.get_all_users()