Spaces:
Running
Running
Commit
·
2013214
1
Parent(s):
b373187
initial config
Browse files- .env +1 -0
- Procfile +1 -0
- app.py +99 -0
- budget_suggestion/__pycache__/budget_suggestion.cpython-312.pyc +0 -0
- budget_suggestion/budget_suggestion.py +34 -0
- chatbot/__pycache__/__init__.cpython-312.pyc +0 -0
- chatbot/__pycache__/chatbot.cpython-312.pyc +0 -0
- chatbot/__pycache__/moods.cpython-312.pyc +0 -0
- chatbot/__pycache__/transaction_categories.cpython-312.pyc +0 -0
- chatbot/chatbot.py +84 -0
- chatbot/moods.py +5 -0
- chatbot/transaction_categories.py +24 -0
- expense_forecast/Expense_Forecasting.py +68 -0
- expense_forecast/__pycache__/Expense_Forcasting.cpython-312.pyc +0 -0
- expense_forecast/__pycache__/Expense_Forecasting.cpython-312.pyc +0 -0
- expense_forecast/sample_data_extended.csv +37 -0
- read_image/__pycache__/scan_bills.cpython-312.pyc +0 -0
- read_image/scan_bills.py +39 -0
- read_image/test.jpg +0 -0
- requirements.txt +13 -0
- speech_transcribe/__pycache__/speech_transcribe.cpython-312.pyc +0 -0
- speech_transcribe/speech_transcribe.py +36 -0
- speech_transcribe/vn.wav +0 -0
.env
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
API_KEY = AIzaSyCU1wkyt7csYhluYLVCbGAGT5Ud3VBtBjo
|
Procfile
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
web: gunicorn app:app
|
app.py
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Flask, request, jsonify
|
2 |
+
import pandas as pd
|
3 |
+
from expense_forecast.Expense_Forecasting import get_input_data, forecast_expense, model_fit, df
|
4 |
+
from chatbot.chatbot import classify_transaction, chat
|
5 |
+
from speech_transcribe.speech_transcribe import transcribe_file
|
6 |
+
from budget_suggestion.budget_suggestion import budget_suggestion
|
7 |
+
from read_image.scan_bills import scan_bills
|
8 |
+
import os
|
9 |
+
from flask_cors import CORS
|
10 |
+
|
11 |
+
app = Flask(__name__)
|
12 |
+
CORS(app)
|
13 |
+
|
14 |
+
@app.route('/monthly_expense_prediction', methods=['POST'])
|
15 |
+
def handle_predict():
|
16 |
+
data = request.get_json()
|
17 |
+
input_data = get_input_data(
|
18 |
+
data['Income (VND)'],
|
19 |
+
data['Interest rate (%)'],
|
20 |
+
data['Inflation rate (%)'],
|
21 |
+
data['Holidays']
|
22 |
+
)
|
23 |
+
forecast = forecast_expense(model_fit, input_data, df)
|
24 |
+
return jsonify({'forecasted_expense': forecast})
|
25 |
+
|
26 |
+
@app.route('/transaction_classification', methods=['POST'])
|
27 |
+
def handle_classify():
|
28 |
+
data = request.get_json()
|
29 |
+
result = classify_transaction(data['prompt'])
|
30 |
+
return jsonify(result)
|
31 |
+
|
32 |
+
@app.route('/chat', methods=['POST'])
|
33 |
+
def chat_with_user():
|
34 |
+
data = request.get_json()
|
35 |
+
mood = data['mood']
|
36 |
+
prompt = data['prompt']
|
37 |
+
response = chat(mood, prompt)
|
38 |
+
return jsonify({'response': response})
|
39 |
+
|
40 |
+
@app.route('/speech_transcribe', methods=['POST'])
|
41 |
+
def handle_transcribe_speech():
|
42 |
+
if 'file' not in request.files:
|
43 |
+
return jsonify({'error': 'No file part'}), 400
|
44 |
+
file = request.files['file']
|
45 |
+
if file.filename == '':
|
46 |
+
return jsonify({'error': 'No selected file'}), 400
|
47 |
+
temp_path = 'temp_audio.wav'
|
48 |
+
file.save(temp_path)
|
49 |
+
try:
|
50 |
+
transcription = transcribe_file(temp_path)
|
51 |
+
return jsonify({'transcription': transcription})
|
52 |
+
except Exception as e:
|
53 |
+
return jsonify({'error': str(e)}), 500
|
54 |
+
finally:
|
55 |
+
if os.path.exists(temp_path):
|
56 |
+
os.remove(temp_path)
|
57 |
+
|
58 |
+
@app.route('/suggest_budget', methods=['POST'])
|
59 |
+
def handle_suggest_budget():
|
60 |
+
data = request.get_json()
|
61 |
+
amount = data['income']
|
62 |
+
suggestion_text = budget_suggestion(amount)
|
63 |
+
budget_dict = {}
|
64 |
+
items = suggestion_text.split(',')
|
65 |
+
for item in items:
|
66 |
+
if ':' in item:
|
67 |
+
key, value = item.split(':')
|
68 |
+
budget_dict[key.strip()] = value.strip()
|
69 |
+
return jsonify(budget_dict)
|
70 |
+
|
71 |
+
@app.route('/scan_bills', methods=['POST'])
|
72 |
+
def handle_scan_bills():
|
73 |
+
if 'file' not in request.files:
|
74 |
+
return jsonify({'error': 'No file part'}), 400
|
75 |
+
file = request.files['file']
|
76 |
+
if file.filename == '':
|
77 |
+
return jsonify({'error': 'No selected file'}), 400
|
78 |
+
temp_path = 'temp_image.jpg'
|
79 |
+
file.save(temp_path)
|
80 |
+
try:
|
81 |
+
result = scan_bills(temp_path)
|
82 |
+
key, value = result.split(':')
|
83 |
+
value = value.strip()
|
84 |
+
result_dict = {}
|
85 |
+
result_dict[key] = value
|
86 |
+
return jsonify(result_dict)
|
87 |
+
except Exception as e:
|
88 |
+
return jsonify({'error': str(e)}), 500
|
89 |
+
finally:
|
90 |
+
if os.path.exists(temp_path):
|
91 |
+
os.remove(temp_path)
|
92 |
+
|
93 |
+
@app.route('/hello-world', methods=['GET'])
|
94 |
+
def hello():
|
95 |
+
return "Hello World!"
|
96 |
+
|
97 |
+
if __name__ == '__main__':
|
98 |
+
# app.run(debug=True)
|
99 |
+
app.run(host='0.0.0.0', port=7860)
|
budget_suggestion/__pycache__/budget_suggestion.cpython-312.pyc
ADDED
Binary file (2.1 kB). View file
|
|
budget_suggestion/budget_suggestion.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import google.generativeai as genai
|
2 |
+
import os
|
3 |
+
from dotenv import load_dotenv
|
4 |
+
|
5 |
+
load_dotenv()
|
6 |
+
|
7 |
+
API_KEY = os.getenv('API_KEY')
|
8 |
+
|
9 |
+
genai.configure(api_key=API_KEY)
|
10 |
+
budget_model = genai.GenerativeModel(
|
11 |
+
model_name='gemini-2.0-flash',
|
12 |
+
system_instruction='Bạn hãy giúp người dùng chia tỷ lệ ngân sách cho từng mục chi tiêu trong tháng này dựa vào thu nhập của họ'
|
13 |
+
'Ví dụ: tôi có thu nhập 10000000 VND, tôi dành khoảng 3 triệu cho ăn uống, 3 triệu cho nhà ở, 2 triệu cho tiết kiệm và 2 triệu còn lại cho các mục khác'
|
14 |
+
'Hãy trả lời theo dạng: Ăn uống: 3000000 VND, Nhà ở: 3000000 VND, Tiết kiệm: 2000000 VND, Các mục khác: 2000000 VND'
|
15 |
+
'Chỉ trả lời theo dạng trên mà không kèm theo từ nào khác, chỉ gồm những danh mục trên'
|
16 |
+
'Hãy trả lời với bối cảnh là cá nhân đang sinh sống tại thành phố Hà Nội'
|
17 |
+
)
|
18 |
+
|
19 |
+
def budget_suggestion(income):
|
20 |
+
prompt = (
|
21 |
+
f"Người dùng có thu nhập là {income} VND, hãy giúp họ chia tỷ lệ ngân sách cho từng mục chi tiêu trong tháng này"
|
22 |
+
)
|
23 |
+
try:
|
24 |
+
response = budget_model.generate_content(
|
25 |
+
contents=prompt,
|
26 |
+
generation_config={
|
27 |
+
"temperature": 0.7,
|
28 |
+
"max_output_tokens": 100
|
29 |
+
}
|
30 |
+
)
|
31 |
+
suggestion = response.text.strip()
|
32 |
+
except Exception as e:
|
33 |
+
suggestion = f'Lỗi trong quá trình xử lý: {e}'
|
34 |
+
return suggestion
|
chatbot/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (205 Bytes). View file
|
|
chatbot/__pycache__/chatbot.cpython-312.pyc
ADDED
Binary file (4.15 kB). View file
|
|
chatbot/__pycache__/moods.cpython-312.pyc
ADDED
Binary file (485 Bytes). View file
|
|
chatbot/__pycache__/transaction_categories.cpython-312.pyc
ADDED
Binary file (1.18 kB). View file
|
|
chatbot/chatbot.py
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import google.generativeai as genai
|
2 |
+
import re
|
3 |
+
import os
|
4 |
+
from dotenv import load_dotenv
|
5 |
+
from chatbot.moods import Mood
|
6 |
+
from chatbot.transaction_categories import TransactionCategory
|
7 |
+
|
8 |
+
load_dotenv()
|
9 |
+
|
10 |
+
API_KEY = os.getenv('API_KEY')
|
11 |
+
|
12 |
+
categories = ', '.join([c.value for c in TransactionCategory])
|
13 |
+
|
14 |
+
genai.configure(api_key=API_KEY)
|
15 |
+
category_classification_model = genai.GenerativeModel(
|
16 |
+
model_name='gemini-2.0-flash',
|
17 |
+
system_instruction='Bạn cần giúp tôi phân loại giao dịch từ danh sách có sẵn'
|
18 |
+
)
|
19 |
+
|
20 |
+
def extract_amount(prompt):
|
21 |
+
match = re.search(r'(\d+)\s*(k|K)?', prompt)
|
22 |
+
if match:
|
23 |
+
amount = int(match.group(1))
|
24 |
+
if match.group(2):
|
25 |
+
amount *= 1000
|
26 |
+
return amount
|
27 |
+
return None
|
28 |
+
|
29 |
+
def classify_transaction(prompt):
|
30 |
+
if not prompt:
|
31 |
+
return {'amount': None, 'category': TransactionCategory.UNKNOWN.value}
|
32 |
+
amount = extract_amount(prompt)
|
33 |
+
llm_prompt = (
|
34 |
+
f" Dựa vào mô tả giao dịch từ người dùng {prompt}, hãy xác định xem đây thuộc loại giao dịch nào trong danh sách này: {categories} "
|
35 |
+
"Ví dụ, nếu mô tả là 'Ăn sáng 20k' thì có nghĩa là đây là giao dịch thuộc loại 'Ăn uống' với số tiền là 20.000đ "
|
36 |
+
"Nếu không xác định được, hãy cho nó vào danh mục 'Các chi phí khác' "
|
37 |
+
"Nếu hoàn toàn không phải bất cứ giao dịch thu hoặc chi nào, hãy trả lời 'Không xác định' "
|
38 |
+
"Chú ý: chỉ trả về tên loại giao dịch chứ không trả về số tiền hay bất cứ từ nào khác "
|
39 |
+
)
|
40 |
+
try:
|
41 |
+
response = category_classification_model.generate_content(
|
42 |
+
contents=llm_prompt,
|
43 |
+
generation_config={
|
44 |
+
"temperature": 0.7,
|
45 |
+
"max_output_tokens": 100
|
46 |
+
}
|
47 |
+
)
|
48 |
+
category = response.text.strip()
|
49 |
+
except Exception as e:
|
50 |
+
category = f'Lỗi trong quá trình xử lý: {e}'
|
51 |
+
return {
|
52 |
+
"amount" : amount,
|
53 |
+
"category" : category
|
54 |
+
}
|
55 |
+
|
56 |
+
chat_model = genai.GenerativeModel(
|
57 |
+
model_name='gemini-2.0-flash',
|
58 |
+
system_instruction='Bạn là một trợ lý ảo trong việc quản lý tài chính cá nhân, hãy đưa ra một vài nhận xét về giao dịch của người dùng hoặc lời khuyên có ích '
|
59 |
+
'Nếu như mô tả của người dùng không phải thuộc lĩnh vực này, hãy trả lời "Tôi không thể trả lời dựa trên thông tin bạn cung cấp"'
|
60 |
+
)
|
61 |
+
|
62 |
+
def chat(mood, prompt):
|
63 |
+
llm_prompt = (
|
64 |
+
f'Bạn có trách nhiệm trò chuyện cùng người dùng, bạn phải tỏ ra {Mood[mood].value} về việc chi tiêu của họ. '
|
65 |
+
'Người dùng nói: ' + prompt + ' '
|
66 |
+
'Bạn chỉ đưa ra phản hồi, không hỏi thêm gì từ người dùng.'
|
67 |
+
)
|
68 |
+
try:
|
69 |
+
response = chat_model.generate_content(
|
70 |
+
contents=llm_prompt,
|
71 |
+
generation_config={
|
72 |
+
"temperature": 0.7,
|
73 |
+
"max_output_tokens": 100
|
74 |
+
}
|
75 |
+
)
|
76 |
+
return response.text.strip()
|
77 |
+
except Exception as e:
|
78 |
+
return f'Lỗi trong quá trình xử lý: {e}'
|
79 |
+
|
80 |
+
# input = 'ăn sáng 20k'
|
81 |
+
# result = classify_transaction(input)
|
82 |
+
# print(result)
|
83 |
+
# response = chat(mood=Mood.IRRITATION.value, prompt=input)
|
84 |
+
# print(response)
|
chatbot/moods.py
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from enum import Enum
|
2 |
+
|
3 |
+
class Mood(Enum):
|
4 |
+
IRRITATION = 'Tức giận'
|
5 |
+
ECOURAGEMENT = 'Khích lệ'
|
chatbot/transaction_categories.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from enum import Enum
|
2 |
+
|
3 |
+
class TransactionCategory(Enum):
|
4 |
+
EATING = "Ăn uống"
|
5 |
+
HOUSING = "Nhà ở"
|
6 |
+
TRANSPORT = "Di chuyển"
|
7 |
+
EDUCATION = "Giáo dục"
|
8 |
+
GIFTS = "Quà tặng"
|
9 |
+
BILLS = "Hóa đơn & Tiện ích"
|
10 |
+
SHOPPING = "Mua sắm"
|
11 |
+
BEAUTY = "Làm đẹp"
|
12 |
+
FAMILY = "Gia đình"
|
13 |
+
PETS = "Vật nuôi"
|
14 |
+
HEALTH = "Sức khỏe"
|
15 |
+
ENTERTAINMENT = "Giải trí"
|
16 |
+
WORK = "Công việc"
|
17 |
+
INSURANCE = "Bảo hiểm"
|
18 |
+
OTHER = "Các chi phí khác"
|
19 |
+
DEBT = "Trả nợ"
|
20 |
+
SPORTS = "Thể thao"
|
21 |
+
INVESTMENT = "Đầu tư"
|
22 |
+
SALARY = "Lương"
|
23 |
+
OTHER_INCOME = "Thu nhập khác"
|
24 |
+
UNKNOWN = "Không xác định"
|
expense_forecast/Expense_Forecasting.py
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
import matplotlib.pyplot as plt
|
3 |
+
from statsmodels.tsa.statespace.sarimax import SARIMAX
|
4 |
+
import os
|
5 |
+
|
6 |
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
7 |
+
csv_path = os.path.join(script_dir, 'sample_data_extended.csv')
|
8 |
+
df = pd.read_csv(csv_path)
|
9 |
+
|
10 |
+
def preprocess_data(df):
|
11 |
+
df = df.dropna()
|
12 |
+
# Set the time column as the index
|
13 |
+
df['Time'] = pd.to_datetime(df['Time'])
|
14 |
+
df = df.set_index('Time')
|
15 |
+
# convert string to float and remove commas
|
16 |
+
df['Total expense (VND)'] = df['Total expense (VND)'].str.replace(',', '').astype(float)
|
17 |
+
df['Income (VND)'] = df['Income (VND)'].str.replace(',', '').astype(float)
|
18 |
+
return df
|
19 |
+
|
20 |
+
def plot_data(df):
|
21 |
+
plt.figure(figsize=(10, 6))
|
22 |
+
plt.plot(df['Total expense (VND)'], label='Total expense (VND)')
|
23 |
+
plt.plot(df['Income (VND)'], label='Income (VND)')
|
24 |
+
plt.legend(loc='best')
|
25 |
+
plt.show()
|
26 |
+
|
27 |
+
def fit_model(df):
|
28 |
+
# define the output and exxogenous variables
|
29 |
+
y = df['Total expense (VND)']
|
30 |
+
exog = df[['Income (VND)', 'Interest rate (%)', 'Inflation rate (%)', 'Holidays']]
|
31 |
+
# define orders for the model
|
32 |
+
order = (1, 1, 1)
|
33 |
+
seasonal_order = (1, 1, 1, 12)
|
34 |
+
# fit the model
|
35 |
+
model = SARIMAX(y, exog=exog, order=order, seasonal_order=seasonal_order)
|
36 |
+
model_fit = model.fit()
|
37 |
+
return model_fit
|
38 |
+
|
39 |
+
def get_input_data(income, interest_rate, inflation_rate, holidays):
|
40 |
+
income = float(income)
|
41 |
+
interest_rate = float(interest_rate)
|
42 |
+
inflation_rate = float(inflation_rate)
|
43 |
+
holidays = int(holidays)
|
44 |
+
input_data = pd.DataFrame({
|
45 |
+
'Income (VND)': [income],
|
46 |
+
'Interest rate (%)': [interest_rate],
|
47 |
+
'Inflation rate (%)': [inflation_rate],
|
48 |
+
'Holidays': [holidays]
|
49 |
+
})
|
50 |
+
return input_data
|
51 |
+
|
52 |
+
def forecast_expense(model_fit, input_data, df):
|
53 |
+
forecast = model_fit.predict(start=len(df), end=len(df), exog=input_data)
|
54 |
+
return forecast.iloc[0]
|
55 |
+
|
56 |
+
df = preprocess_data(df)
|
57 |
+
model_fit = fit_model(df)
|
58 |
+
|
59 |
+
# def main():
|
60 |
+
# global df
|
61 |
+
# df = preprocess_data(df)
|
62 |
+
# model_fit = fit_model(df)
|
63 |
+
# input_data = get_input_data(10000000, 5, 3, 0)
|
64 |
+
# forecast = forecast_expense(model_fit, input_data)
|
65 |
+
# print(forecast)
|
66 |
+
|
67 |
+
# if __name__ == '__main__':
|
68 |
+
# main()
|
expense_forecast/__pycache__/Expense_Forcasting.cpython-312.pyc
ADDED
Binary file (2.77 kB). View file
|
|
expense_forecast/__pycache__/Expense_Forecasting.cpython-312.pyc
ADDED
Binary file (3.2 kB). View file
|
|
expense_forecast/sample_data_extended.csv
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Time,Income (VND),Interest rate (%),Inflation rate (%),Holidays,Total expense (VND)
|
2 |
+
1/1/2022,"18,090,000",6.5,2.43,1,"12,575,093"
|
3 |
+
2/1/2022,"18,090,000",6.2,2.53,1,"13,085,146"
|
4 |
+
3/1/2022,"18,090,000",7.2,2.33,0,"6,955,509"
|
5 |
+
4/1/2022,"18,090,000",7,2.54,1,"9,961,749"
|
6 |
+
5/1/2022,"18,090,000",7.5,2.56,1,"10,270,506"
|
7 |
+
6/1/2022,"18,090,000",8,2.33,0,"6,913,467"
|
8 |
+
7/1/2022,"20,058,000",7.8,2.4,0,"7,330,779"
|
9 |
+
8/1/2022,"20,058,000",7.9,2.46,0,"6,679,634"
|
10 |
+
9/1/2022,"20,058,000",7.5,2.44,1,"9,481,953"
|
11 |
+
10/1/2022,"20,058,000",7.9,2.41,0,"6,959,445"
|
12 |
+
11/1/2022,"20,058,000",8.1,2.46,0,"7,356,952"
|
13 |
+
12/1/2022,"20,058,000",8.3,2.5,1,"9,916,820"
|
14 |
+
1/1/2023,"20,058,000",7.6,2.52,1,"12,910,931"
|
15 |
+
2/1/2023,"21,760,000",7.1,2.54,1,"13,517,360"
|
16 |
+
3/1/2023,"21,760,000",6.7,2.47,0,"7,168,542"
|
17 |
+
4/1/2023,"21,760,000",6.6,2.7,1,"9,800,059"
|
18 |
+
5/1/2023,"21,760,000",6.7,2.57,1,"10,883,733"
|
19 |
+
6/1/2023,"21,760,000",6.4,2.55,0,"7,100,469"
|
20 |
+
7/1/2023,"21,760,000",6.5,2.4,0,"7,552,425"
|
21 |
+
8/1/2023,"23,550,000",6.8,2.61,0,"7,093,214"
|
22 |
+
9/1/2023,"23,550,000",6.3,2.5,1,"9,837,942"
|
23 |
+
10/1/2023,"23,550,000",6.1,2.43,0,"7,601,792"
|
24 |
+
11/1/2023,"23,550,000",6,2.49,0,"7,980,883"
|
25 |
+
12/1/2023,"23,550,000",6.6,2.58,1,"10,441,683"
|
26 |
+
1/1/2024,"23,550,000",6.8,2.62,1,"12,777,962"
|
27 |
+
2/1/2024,"23,550,000",6.9,2.73,1,"14,329,909"
|
28 |
+
3/1/2024,"24,440,000",6,2.6,0,"7,968,521"
|
29 |
+
4/1/2024,"24,440,000",5.8,2.77,1,"10,805,263"
|
30 |
+
5/1/2024,"24,440,000",5.6,2.65,1,"10,645,173"
|
31 |
+
6/1/2024,"24,440,000",5.5,2.5,0,"7,095,471"
|
32 |
+
7/1/2024,"25,390,000",5.3,2.49,0,"7,955,498"
|
33 |
+
8/1/2024,"25,390,000",5.2,2.71,0,"7,316,647"
|
34 |
+
9/1/2024,"25,390,000",5.1,2.6,1,"10,255,177"
|
35 |
+
10/1/2024,"25,390,000",5,2.53,0,"7,828,662"
|
36 |
+
11/1/2024,"25,390,000",5.25,2.54,0,"8,500,142"
|
37 |
+
12/1/2024,"25,390,000",5.25,2.73,1,"10,564,971"
|
read_image/__pycache__/scan_bills.cpython-312.pyc
ADDED
Binary file (1.95 kB). View file
|
|
read_image/scan_bills.py
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import google.generativeai as genai
|
2 |
+
import PIL.Image
|
3 |
+
import os
|
4 |
+
from dotenv import load_dotenv
|
5 |
+
from chatbot.transaction_categories import TransactionCategory
|
6 |
+
|
7 |
+
load_dotenv()
|
8 |
+
|
9 |
+
API_KEY = os.getenv('API_KEY')
|
10 |
+
|
11 |
+
categories = ', '.join([c.value for c in TransactionCategory])
|
12 |
+
|
13 |
+
genai.configure(api_key=API_KEY)
|
14 |
+
scan_model = genai.GenerativeModel(
|
15 |
+
model_name='gemini-2.0-flash',
|
16 |
+
system_instruction='Hãy giúp tôi quét hóa đơn và trích xuất thông tin từ hóa đơn này'
|
17 |
+
f'Hãy giúp tôi phân loại giao dịch từ danh sách có sẵn: {categories} và cho tôi biết số tiền của giao dịch đó'
|
18 |
+
'Ví dụ: Mua sắm 200000 VND'
|
19 |
+
'Chỉ trả lời theo dạng: Tên danh mục: số tiền VND'
|
20 |
+
)
|
21 |
+
|
22 |
+
def scan_bills(image_path):
|
23 |
+
organ = PIL.Image.open(image_path)
|
24 |
+
try:
|
25 |
+
response = scan_model.generate_content(
|
26 |
+
contents=[organ],
|
27 |
+
generation_config={
|
28 |
+
"temperature": 0.7,
|
29 |
+
"max_output_tokens": 100
|
30 |
+
}
|
31 |
+
)
|
32 |
+
result = response.text.strip()
|
33 |
+
except Exception as e:
|
34 |
+
result = f'Lỗi trong quá trình xử lý: {e}'
|
35 |
+
return result
|
36 |
+
|
37 |
+
# if __name__ == '__main__':
|
38 |
+
# path = r'C:\Users\duong\Documents\UET-VNU\K68-CS1\HKII_2024-2025\Software_Engineering\ai_services\read_image\test.jpg'
|
39 |
+
# print(scan_bills(path))
|
read_image/test.jpg
ADDED
![]() |
requirements.txt
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
google-generativeai
|
2 |
+
python-dotenv
|
3 |
+
flask
|
4 |
+
flask-cors
|
5 |
+
scikit-learn
|
6 |
+
pandas
|
7 |
+
numpy
|
8 |
+
torch
|
9 |
+
transformers
|
10 |
+
librosa
|
11 |
+
matplotlib
|
12 |
+
statsmodels
|
13 |
+
uvicorn
|
speech_transcribe/__pycache__/speech_transcribe.cpython-312.pyc
ADDED
Binary file (1.75 kB). View file
|
|
speech_transcribe/speech_transcribe.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
from transformers import AutoProcessor, AutoModelForCTC
|
3 |
+
import librosa
|
4 |
+
|
5 |
+
processor = AutoProcessor.from_pretrained("nguyenvulebinh/wav2vec2-base-vietnamese-250h")
|
6 |
+
model = AutoModelForCTC.from_pretrained("nguyenvulebinh/wav2vec2-base-vietnamese-250h")
|
7 |
+
|
8 |
+
def transcribe(audio_data, sampling_rate):
|
9 |
+
if len(audio_data.shape) > 1:
|
10 |
+
audio_data = audio_data.mean(axis=1)
|
11 |
+
|
12 |
+
if sampling_rate != 16000:
|
13 |
+
raise ValueError("Sampling rate phải là 16 kHz")
|
14 |
+
|
15 |
+
inputs = processor(audio_data,
|
16 |
+
sampling_rate=sampling_rate,
|
17 |
+
return_tensors="pt",
|
18 |
+
padding="longest")
|
19 |
+
|
20 |
+
with torch.no_grad():
|
21 |
+
logits = model(input_values=inputs.input_values).logits
|
22 |
+
|
23 |
+
predicted_ids = torch.argmax(logits, dim=-1)
|
24 |
+
|
25 |
+
transcription = processor.batch_decode(predicted_ids)
|
26 |
+
|
27 |
+
return transcription[0]
|
28 |
+
|
29 |
+
def transcribe_file(file_path):
|
30 |
+
audio_data, sampling_rate = librosa.load(file_path, sr=16000)
|
31 |
+
return transcribe(audio_data, sampling_rate)
|
32 |
+
|
33 |
+
# if __name__ == "__main__":
|
34 |
+
# file_path = "vn.wav"
|
35 |
+
# result = transcribe_file(file_path)
|
36 |
+
# print(result)
|
speech_transcribe/vn.wav
ADDED
Binary file (428 kB). View file
|
|