File size: 3,532 Bytes
dbe2c62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import re

from difflib import SequenceMatcher

from . import Common_MyUtils as MyUtils

ex = MyUtils.exc

# ===============================
# 1. Abbreviation
# ===============================

# Phụ âm đầu
VALID_ONSETS = [
    "b", "c", "ch", "d", "đ", "g", "gh", "gi",
    "h", "k", "kh", "l", "m", "n", "ng", "ngh",
    "nh", "p", "ph", "q", "r", "s", "t", "th",
    "tr", "v", "x"
]

# Nguyên âm
VALID_NUCLEI = [
    "a", "ă", "â", "e", "ê", "i", "o", "ô", "ơ", "u", "ư", "y",

    "ia", "iê", "ya", "ya", "ua", "uô", "ưa", "ươ",
    "ai", "ao", "au", "ay", "âu", "ây",
    "eo", "êu",
    "ia", "iê", "yê",
    "oi", "ôi", "ơi",
    "ua", "uô", "ươ", "ưu", "uy", "uya"
]

# Phụ âm cuối
VALID_CODAS = ["c", "ch", "m", "n", "ng", "nh", "p", "t"]

# ===== Hàm kiểm tra viết tắt =====
def is_abbreviation(word: str) -> bool:
    """
    Trả về True nếu từ KHÔNG phải âm tiết tiếng Việt chuẩn,
    tức là có khả năng là viết tắt.
    Quy tắc:
    1. Không có nguyên âm hoặc nguyên âm không hợp lệ -> viết tắt
    2. Phụ âm đầu không hợp lệ -> viết tắt
    3. Phụ âm cuối không hợp lệ -> viết tắt
    4. Nhiều hơn 3 phần (đầu - nguyên âm - cuối) -> viết tắt
    """
    w = word.lower()
    w = re.sub(r'[^a-zăâêôơưđ]', '', w)

    if not w:
        return True

    # 1. Tìm phụ âm đầu
    onset = None
    for o in sorted(VALID_ONSETS, key=len, reverse=True):
        if w.startswith(o):
            onset = o
            break

    rest = w[len(onset):] if onset else w
    if onset is None and rest and rest[0] not in "aeiouyăâêôơư":
        return True  # phụ âm đầu không hợp lệ

    # 2. Tìm phụ âm cuối
    coda = None
    for c in sorted(VALID_CODAS, key=len, reverse=True):
        if rest.endswith(c):
            coda = c
            break

    nucleus = rest[:-len(coda)] if coda else rest

    # 3. Kiểm tra nguyên âm
    if not nucleus:
        return True
    if nucleus not in VALID_NUCLEI:
        return True

    # 4. Kiểm tra số phần
    parts = [p for p in [onset, nucleus, coda] if p]
    if len(parts) > 3:
        return True

    return False

# ===============================
# 2. Words
# ===============================

# ===== Hàm chuẩn hóa từ ======================
def normalize_word(w: str) -> str:
    return re.sub(r'[^A-Za-zÀ-ỹĐđ0-9]', '', w)

# ===== Hàm so sánh độ tương đồng =============
def similar(a, b):
    return SequenceMatcher(None, a, b).ratio()

# ===== Hàm chuyển số La Mã ===================
def is_roman(s):
    return bool(re.fullmatch(r'[IVXLC]+', s))

# ===== Chuyển số La Mã sang số Ả Rập =========
def roman_to_int(s):
    roman_numerals = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100}
    result, prev = 0, 0
    for c in reversed(s):
        val = roman_numerals.get(c, 0)
        if val < prev:
            result -= val
        else:
            result += val
            prev = val
    return result

# ===== Hàm loại bỏ khoảng trắng thừa =========
def strip_extra_spaces(s: str) -> str:
    if not isinstance(s, str):
        return s
    return re.sub(r'\s+', ' ', s).strip()

def merge_txt(RawDataDict, JsonKey, JsonField):
    paragraphs = RawDataDict.get(JsonKey, [])
    merged = "\n".join(p.get(JsonField, "").strip() for p in paragraphs if p.get(JsonField))
    merged = re.sub(r"\n{2,}", "\n", merged.strip())
    return merged