commit e73258bff3d60898a9d7073db20b723f181f1c88 Author: ajokar Date: Mon Jan 20 16:35:35 2025 +0000 first push diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1bde658 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.log +*.pyc +*.json \ No newline at end of file diff --git a/conflict.py b/conflict.py new file mode 100644 index 0000000..82efcc8 --- /dev/null +++ b/conflict.py @@ -0,0 +1,94 @@ +import json +from tqdm import tqdm +import time + + +import torch +import os +from transformers import AutoTokenizer, AutoModel +from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig +os.environ['HF_HOME'] = "/home/admin/HFHOME" + +model_id = "meta-llama/Meta-Llama-3.1-8B-Instruct" +#model_id = "meta-llama/Llama-3.1-70B-Instruct" + +# use quantization to lower GPU usage +# 4 bit: +# bnb_config = BitsAndBytesConfig( +# load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16 +# ) +# 8 bit: +bnb_config = BitsAndBytesConfig( + load_in_8bit=True, bnb_8bit_use_double_quant=True, bnb_8bit_quant_type="nf8", bnb_8bit_compute_dtype=torch.bfloat16 +) + +tokenizer = AutoTokenizer.from_pretrained(model_id) +model = AutoModelForCausalLM.from_pretrained( + model_id, + torch_dtype=torch.bfloat16, + device_map="auto", + quantization_config=bnb_config +) +terminators = [ + tokenizer.eos_token_id, + tokenizer.convert_tokens_to_ids("<|eot_id|>") +] +model.generation_config.pad_token_id = tokenizer.eos_token_id #tokenizer.pad_token_id + +SYS_PROMPT = """ +You receive two Persian legal rule texts and analyze them carefully, explain your answer step by step, to see whether these two rules logically conflict with each other. +Finally, state the final conclusion for the presence or absence of conflict with the words "yes" or "no". +"""# Explain your answer step by step. + + +def format_prompt(SENTENCE1, SENTENCE2): + PROMPT = f"Rule 1: {SENTENCE1}. Rule 2: {SENTENCE2}." + return PROMPT + + + +def generate(formatted_prompt): + formatted_prompt = formatted_prompt[:50000] # to avoid GPU OOM + messages = [{"role":"system","content":SYS_PROMPT},{"role":"user","content":formatted_prompt}] + # tell the model to generate + input_ids = tokenizer.apply_chat_template( + messages, + add_generation_prompt=True, + return_tensors="pt" + ).to(model.device) + outputs = model.generate( + input_ids, + max_new_tokens=2048, + eos_token_id=terminators, + do_sample=True, + temperature=0.6, + top_p=0.9, + ) + response = outputs[0][input_ids.shape[-1]:] + return tokenizer.decode(response, skip_special_tokens=True) + + +def conflict(sentence1, sentence2): + formatted_prompt = format_prompt(sentence1, sentence2) + return generate(formatted_prompt) + + +if __name__ == "__main__": + print('start') + start_time = time.time() + #inputfile = open('./main_rules_lama70B_dataset_02.json', "r", encoding='utf-8') + result = conflict("حسین در حال حاضر در وزارت نفت و انرژی به استخدام دولت در آمده است.", + "حسین الان در دانشگاه دولتی مشغول به تحصیل است و هرکسی که در حال تحصیل باشد، از نظر قانون نمی تواند در دولت استخدام شود") + end_time = time.time() + + print("*********************************************************") + print("*********************************************************") + print() + print(result) + print() + print("*********************************************************") + print("*********************************************************") + + + print(f"elapsed time: {end_time-start_time}") + print("end") \ No newline at end of file diff --git a/data_helper.py b/data_helper.py new file mode 100644 index 0000000..fb1de18 --- /dev/null +++ b/data_helper.py @@ -0,0 +1,153 @@ + +import pickle +import re +import string +import os + + +class DataHelper(): + def __init__(self): + pass + + def clean_text(self, text_doc, new_line_elimination): + punctuations = r')(}{:؟!،؛»«.' + r"/<>?.,:;" + punctuations = '[' + punctuations + string.punctuation + ']' + punctuations = punctuations.replace("@", "") + + text_doc.strip() + + # pattern = ur'\s*@[a-zA-Z0-9]*\s*' + # tmp = re.findall(pattern, text_doc) + # newstring = re.sub(pattern, eliminate_pattern, text_doc) + + + #finding the numbers + pattern = r"[-+]?\d*\.\d+|\d+" + nums_list = re.findall(pattern, text_doc) + newstring = re.sub(pattern, 'floatingpointnumber', text_doc) + + + #pattern = '\s*' + punctuations + '+' + '\s*' + #tmp = re.findall(pattern, newstring) + #newstring = re.sub(pattern, self.add_space, newstring) + + # pattern = u'([a-zA-Z0-9]+)(\s*)(' + punctuations + u')(\s*)([a-zA-Z0-9]+)' + # rep = ur'\1\3\5' + # tmp = re.findall(pattern, newstring) + # newstring = re.sub(pattern, rep, newstring) + + pattern = r'[\n]+' + tmp = re.findall(pattern, newstring) + if new_line_elimination: + newstring = re.sub(pattern, " ", newstring) + else: + # newstring = re.sub(pattern, "\n", newstring) + pass + + punctuations = r")(}{:؟!-،؛»«.@$&%" + r"/<>?.,:;" + latinLettersDigits = r"a-zA-Z0-9" + pattern = r'[^' + punctuations + latinLettersDigits + 'آ-ی' + '‌' + '\d\s:]' + tmp = re.findall(pattern, newstring) + newstring = re.sub(pattern, self.eliminate_pattern, newstring) + + pattern = r'[ ]+' + tmp = re.findall(pattern, newstring) + newstring = re.sub(pattern, ' ', newstring) + + for number in nums_list: + pattern = 'floatingpointnumber' + newstring = re.sub(pattern, number, newstring, 1) + + return newstring + + def add_space(self, mystring): + mystring = mystring.group() # this method return the string matched by re + mystring = mystring.strip(' ') # ommiting the whitespace around the pucntuation + mystring = " " + mystring + " " # adding a space after and before punctuation + return mystring + + def replace_newline_with_dot(self, mystring): + return ' . ' + + def eliminate_pattern(self, mystring): + return "" + + def load_var(self, load_path): + file = open(load_path, 'rb') + variable = pickle.load(file) + file.close() + return variable + + def save_var(self, save_path, variable): + print("saving vars ...") + file = open(save_path, 'wb') + pickle.dump(variable, file) + print("variable saved.") + file.close() + + def build_stem_dictionary(self, normalizer, verb_tense_path, mokasar_noun_path): + path_dir = "resource/Persian_Dependency_Treebank/Data/2ndRep" + lexicon_stem = set() + verb_stem = set() + #verb_tense_map = {} + verb_p2f_map = {} + verb_f2p_map = {} + for fileName in os.listdir(path_dir): + file_path = path_dir + "/" + fileName + with open(file_path, "r") as input: + input_content = input.readlines() + for el in input_content: + el = normalizer.sub_alphabets(el) + el = el.split("\t") + if (len(el) > 2): + if (el[3] == 'V'): + tmp_pos = "V" + else: + tmp_pos = "N" + stem_word = el[2] + stem_word = stem_word.split("#") + stem_word = [x.strip('\u200c') for x in stem_word] + if (tmp_pos == "V" and len(stem_word) == 2): + if (len(stem_word[0]) != 0 and len(stem_word[1]) != 0): + verb_p2f_map[stem_word[0]] = stem_word[1] + verb_f2p_map[stem_word[1]] = stem_word[0] + verb_stem.add(stem_word[0]) + verb_stem.add(stem_word[1]) + if(tmp_pos == 'V' and len(stem_word) == 3): + if(len(stem_word[0]) != 0 and len(stem_word[1]) != 0 and len(stem_word[2]) !=0): + #verb_prifix.add(stem_word[0]) + verb_p2f_map[stem_word[1]] = stem_word[2] + verb_f2p_map[stem_word[2]] = stem_word[1] + verb_stem.add(stem_word[1]) + verb_stem.add(stem_word[2]) + for t in stem_word: + if len(t) > 1: + if (tmp_pos == 'N'): + lexicon_stem.add(t) + + with open(verb_tense_path, "r") as bon_file: + bon_file_content = bon_file.readlines() + for el in bon_file_content: + el = el.strip() + el = normalizer.sub_alphabets(el) + el = el.split() + el = [x.strip('\u200c') for x in el] + + verb_p2f_map[el[0]] = el[1] + verb_f2p_map[el[1]] = el[0] + verb_stem.add(el[0]) + verb_stem.add(el[1]) + + irregular_noun = {} + with open(mokasar_noun_path, "r") as input: + input_content = input.readlines() + for el in input_content: + el = normalizer.sub_alphabets(el) + el = el.replace("\t\t", "\t") + el = el.strip().split("\t") + el = [x.strip('\u200c') for x in el] + irregular_noun[el[0]] = el[1] + lexicon_stem.add(el[0]) + + verb_tense_map = [verb_p2f_map, verb_f2p_map] + return lexicon_stem, verb_stem, verb_tense_map, irregular_noun diff --git a/do_extractor.py b/do_extractor.py new file mode 100644 index 0000000..a72f9d8 --- /dev/null +++ b/do_extractor.py @@ -0,0 +1,200 @@ +""" +این فایل با نرمالایزر پارسیور کار می کند +""" +from datetime import datetime +# from elasticsearch import Elasticsearch +from threading import Thread +import torch +import time +import json +import os.path +import os +os.environ['HF_HOME'] = "/home/admin/HFHOME" +# from general_functions import normalize_content +from funcs import write_to_json, read_from_json + +from normalizer import Normalizer +from tokenizer import * +_normalizer = Normalizer(date_normalizing_needed=True) +address = os.getcwd() +# sections_list = read_from_json(address + '/data/clean_sections_11k.json') # Main File +# sections_list = read_from_json('../data/clean_sections_11k.json') # Main File +# sections_list = read_from_json('../data/simplized_sentences_110_2.json') # Main File +# sections_list = read_from_json('./data/main_sections_170k_metadata.json') # Main File + + +# not_have_two_token_kw = read_from_json('../data/not_have_two_token_kw.json') +# not_have_two_token_kw_list = [item3["id"] for item3 in not_have_two_token_kw] +not_have_two_token_kw_list = [] + +import json +from tqdm import tqdm +import time + +from transformers import AutoTokenizer, AutoModel +from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig +os.environ['HF_HOME'] = "/home/admin/HFHOME" + +#model_id = "meta-llama/Meta-Llama-3.1-8B-Instruct" +#model_id = "PartAI/Dorna2-Llama3.1-8B-Instruct" + +model_id = "meta-llama/Llama-3.1-70B-Instruct" + +# use quantization to lower GPU usage +# 4 bit: +# bnb_config = BitsAndBytesConfig( +# load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16 +# ) +# 8 bit: +bnb_config = BitsAndBytesConfig( + load_in_8bit=True, bnb_8bit_use_double_quant=True, bnb_8bit_quant_type="nf8", bnb_8bit_compute_dtype=torch.bfloat16 +) +print("Model Loading START:") +print(str(datetime.now())) +print() +tokenizer = AutoTokenizer.from_pretrained(model_id) +model = AutoModelForCausalLM.from_pretrained( + model_id, + torch_dtype=torch.bfloat16, + device_map="auto", + quantization_config=bnb_config +) +terminators = [ + tokenizer.eos_token_id, + tokenizer.convert_tokens_to_ids("<|eot_id|>") +] +model.generation_config.pad_token_id = tokenizer.eos_token_id #tokenizer.pad_token_id +print("Model Loading END:") +print(str(datetime.now())) +print() + +sections_list = read_from_json('./data/sections_110.json') # Main File +# if torch.cuda.is_available(): +# #model_id = "PartAI/Dorna-Llama3-8B-Instruct" +# # model_id = "meta-llama/Meta-Llama-3.1-8B-Instruct" + +# model_id = "meta-llama/Llama-3.1-70B-Instruct" +# model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto", torch_dtype=torch.bfloat16) +# tokenizer = AutoTokenizer.from_pretrained(model_id) + +# index_name_i = 'semantic_search-v10' + +# es = Elasticsearch( +# "http://127.0.0.1:6900", +# # ca_certs="/path/to/http_ca.crt", +# basic_auth=("elastic", "SG*7eGwg+KG2_*-1_mMm") +# ) + +counter = 0 +total = 0 +remained = 0 +id = '' +keywords_count = 15 + +def generateKeywords(text): + global remained + try: + keywords_count = (len(text) / 1000) * 15 + keywords_count = int(keywords_count) + if keywords_count == 0: + keywords_count = 1 + + + # messages = [{"role": "system", "content": "تو یک وکیل حقوق دان هستی و باید بتوانی متن های قانونی و حقوقی را بدون تغییر اصطلاحات فنی، به صورتی توضیح دهی که افراد غیر حقوق دان، معنای متن را درک کنند. " }, + # {"role": "user", "content": + # '''از "متن" حداقل {} عبارت های کلیدی مهم و پراهمیت را استخراج کن و عبارت های کلیدی را در قالب لیست به زبان فارسی چاپ کن و هر کلید عبارت کلیدی را در یک خط جدید قرار بده و هیچ گونه توضیحی در ابتدا یا انتهای پاسخ، اضافه نکن. + # هر عبارت کلیدی دارای یک شماره ترتیبی در ابتدای آن باشد. عبارت های کلیدی، دقیقا در متن موجود باشد. بسیار مهم و ضروری است که طول هر عبارت کلیدی حداقل دو توکن داشته باشد و عبارت کلیدی یک توکنی قابل قبول نیست. تاکید می کنم که هیچ عبارت کلیدی نباید فقط یک توکن داشته باشد. نام سازمان ها و نهادها و اشخاص حقوقی، حتما به عنوان عبارت کلیدی درنظر گرفته شود. هیچ عبارت کلیدی، فعل یا حرف اضافه نباشد و فقط شامل اسم هایی باشد که به هم اضافه شده اند. هیچ عبارت کلیدی نباید با حرف اضافه یا حرف «و» تمام شود. ضروری است که عبارت های کلیدی شامل ماده، بند، تبصره یا تاریخ ها نباشند.''' + # .format(keywords_count) + # }, + # {"role": "user", "content": + # '''"متن": {}'''.format(text) + # },] + messages = [{"role": "system", "content": "You are a lawyer and you must be able to explain legal texts without changing technical terms in a way that non-lawyers can understand the meaning of the text." }, + {"role": "user", "content": + '''Extract at least {} important and significant key phrases from the "text" and print the key phrases in the form of a list in Persian and put each key phrase on a new line and do not add any explanation at the beginning or end of the answer. +Each key phrase has a sequential number at the beginning. The key phrases must be present exactly in the text. It is very important and essential that the length of each key phrase has at least two tokens and a single-token key phrase is not acceptable. I emphasize that no key phrase should have only one token. The names of organizations, institutions and legal entities must be considered as key phrases. No key phrase should be a verb or a preposition and should only include nouns that are added together. No key phrase should end with a preposition or the letter "و". It is essential that key phrases do not include "ماده", "تبصره", "بند" or "تاریخ ها".''' + .format(keywords_count) + }, + {"role": "user", "content": + '''"متن": {}'''.format(text) + },] + + + input_ids = tokenizer.apply_chat_template( + messages, + add_generation_prompt=True, + return_tensors="pt" + ).to(model.device) + + terminators = [ + tokenizer.eos_token_id, + tokenizer.convert_tokens_to_ids("<|eot_id|>") + ] + model.generation_config.pad_token_id = tokenizer.pad_token_id + + + outputs = model.generate( + input_ids, + max_new_tokens=256, + eos_token_id=terminators, + do_sample=True, + temperature=0.6, + top_p=0.85, + ) + #lock0.release() + response = outputs[0][input_ids.shape[-1]:] + keywords = tokenizer.decode(response, skip_special_tokens=True) + #lock1.acquire() + # resp = es.update(index=index_name_i, id=id, doc={"content_keywords-llama3-str": str(keywords)}) + + + return keywords + + except Exception as inst: + print(type(inst)) # the exception type + print(inst.args) # arguments stored in .args + print("Exception: " + str(inst)) + + + +if __name__ == "__main__": + start_time = time.time() + print("start_time: "+str(datetime.now())) + + try: + keywords_dict = [] + + count = 1 + for content_item in sections_list: + id = content_item['id'] + # if not id in not_have_two_token_kw_list: + # continue + content = content_item['content'] + content_len = len(content.split()) + # کنارگذاشتن محتواهای با حجم زیاد + if content_len > 2000: + print("too long content " + str(id)) + continue + content = _normalizer.sub_alphabets(content) + keywords = generateKeywords(content) + print("section " + str(count) + "/" + str(len(not_have_two_token_kw_list)) + " keyword extracting ... ") + keywords_dict.append({ + 'id':id, + 'keywords':keywords + }) + if count % 500 == 0: + write_to_json(keywords_dict, f"./data/sections_kw_llama_8b_main_{count}.json") + keywords_dict = [] + count+=1 + + write_to_json(keywords_dict, f"./data/sections_kw_llama_8b_main_{count}.json") + + except Exception as inst: + print(type(inst)) # the exception type + print(inst.args) # arguments stored in .args + + end_time = time.time() + print("end_time: "+ str(datetime.now())) + operation_time = (int(end_time-start_time)/60)/60 + print(f"elapsed time: {operation_time} hours") + print(f"Finished!!!") \ No newline at end of file diff --git a/funcs.py b/funcs.py new file mode 100644 index 0000000..eb84708 --- /dev/null +++ b/funcs.py @@ -0,0 +1,119 @@ +import re +import os +import json +# from pandas import read_excel, DataFrame +def remove_signs(): + str = read_file() + # lines = + pattern = r"\(|\)" + str = re.sub(pattern,'', str) + # str = re.sub(')','', str) + # str = re.sub('/','', str) + + return str + +def read_file(): + with open('./data/DATASET_2.txt', 'r', encoding='utf-8') as file: + text = '' + try: + text = str(file.read()) + except: + pass + return text + +def read_file_by_address(file_address): + with open(file_address, 'r', encoding='utf-8') as file: + text = '' + try: + text = str(file.read()) + except: + pass + return text + +def save_to_file(result): + with open('./data/DATASET_3.txt', 'a+', encoding='utf-8') as file: + previous_result = '' + try: + previous_result = file.read() + except: + pass + file.write(result) + file.close() + +def save_to_file_by_address(file_address, text): + with open(file_address, 'a+', encoding='utf-8') as file: + previous_result = '' + try: + previous_result = file.read() + except: + pass + file.write(text) + file.close() + + +def read_from_excel(file_address, column_name): + # خواندن فایل اکسل + data = read_excel(file_address) + + # استخراج محتوای ستون مورد نظر + column_data = data[column_name] + return column_data + +def add_columndata_to_excel(file_address, column_name, columndata): + + # خواندن فایل اکسل + data = read_excel(file_address) + + # اضافه کردن ستون جدید به داده‌ها + data[column_name] = columndata + + # ذخیره کردن داده‌ها در فایل اکسل + data.to_excel(file_address, index=False) + +def write_to_excel(data_dict, file_name_and_address): + df = DataFrame(data_dict) + + # ذخیره DataFrame به عنوان فایل اکسل + df.to_excel(file_name_and_address, index=False) + return True + +def write_to_json(dict, file_address): + + # تبدیل دیکشنری به فرمت JSON + json_data = json.dumps(dict, indent=2, ensure_ascii=False) + + # ذخیره فایل + with open(file_address, 'w+', encoding='utf-8') as file: + file.write(json_data) + + return True + +def read_from_json(file_address): + data_dict = [] + # خواندن اطلاعات از فایل JSON + with open(file_address, 'r', encoding='utf-8') as file: + loaded_data = json.load(file) + + # نمایش اطلاعات خوانده شده + for item in loaded_data: + data_dict.append(item) + return data_dict + + +def separated_date_format_finder(date_ner): + result = False + date_ner = date_ner.replace('.','/') + date_ner = date_ner.replace('،','/') + date_ner = date_ner.replace('ر','/') + #date_pattern = r'\d{1,2} /\d{1,2} /\d{2,4}|\d{1,2}/\d{1,2}/\d{2,4}|\d{2,4} /\d{1,2} /\d{1,2}|\d{2,4}/\d{1,2}/\d{1,2}' + date_pattern = r'\b(?:(?:1[0-2]|0?[1-9])/?(?:3[01]|[12][0-9]|0?[1-9])/?(?:14[0-7][0-9]|13[0-9][0-9]|128[0-9])|(?:1[0-2]|0?[1-9])/?(?:3[01]|[12][0-9]|0?[1-9])/?(?:14[0-7][0-9]|13[0-9][0-9]|128[0-9]|[0-9]{2}))\b' + regex = re.compile(date_pattern) + match_dates = regex.finditer(date_ner) + for date_item in match_dates: + result = True + break + return result + +if __name__ == "__main__": + + pass \ No newline at end of file diff --git a/general_functions.py b/general_functions.py new file mode 100644 index 0000000..debce42 --- /dev/null +++ b/general_functions.py @@ -0,0 +1,800 @@ +from normalizer import Normalizer +from tokenizer import * +# import jalali +import re +from re import sub +import textwrap +from html import escape +from bs4 import BeautifulSoup + +# from lxml import etree +import datetime +#enumerate(token_list): +_normalizer = Normalizer(date_normalizing_needed=True) + +yeAr = r"ﻱ|ې|ێ|ے|ى|ي|ئ" +yeFr= r"ی" +keAr = r"ڭ|ﻚ|ﮎ|ﻜ|ﮏ|ګ|ﻛ|ﮑ|ﮐ|ڪ|ك" +keFr = r"ک" +mark1 = r'#\[#' +mark2 = r'#\]#' +hTag1 = r'<' +hTag2 = r'>' +tableTag=["table","tr", "th", "td", "TABLE", "TR", "TH", "TD"] +strTable = '' +for tag in tableTag: + if strTable != '': + strTable += '|' + strTable += '('+tag+')' +regTable = r'<(?P\/)*(?P'+strTable+')(?P[^>]+)*>' +regTableReplace = r'#[#\g\g\g#]#' + + +def isNeedHtml(html): + if "]+>' + + if exeptionTag.__len__ : + exceptTags = '' + for tag in exeptionTag: + if exceptTags != '': + exceptTags += '|' + exceptTags += '('+tag+')' + reg1 = r'<(?P/)*(?P'+exceptTags+')(?P[^>]+)*>' + html = sub(reg1, regTableReplace, html) + + + soup = BeautifulSoup(html, "html.parser") + text = soup.get_text("\n", strip=True) + + if exeptionTag.__len__ : + text = sub(mark1, hTag1, text) + text = sub(mark2, hTag2, text) + + return text + + +def removeHtmlNoTableTag(html): + + # خطا داره و هنگ می کنه در test2.py + html = sub(regTable, regTableReplace, html) + soup = BeautifulSoup(html, "html.parser") + text = soup.get_text("\n", strip=True) + + text = sub(mark1, hTag1, text) + text = sub(mark2, hTag2, text) + + return text + +def normalizerData(data): + global _normalizer + normalTitle, dates = _normalizer.normalize(data, return_dates=True) + tdates = [] + for d in dates: + if not d.startswith("num"): + try: + tsd = jdate2timestamp(d) + cdate = d + except: + try: + d = d.replace("y", "") + d = d.replace("m", "/") + d = d.replace("d", "/") + m = re.match(r"^(\d{4})\D(\d{1,2})\D(\d{1,2})$", d) + if m: + [year, month, day] = [ + int(m.group(1)), + int(m.group(2)), + int(m.group(3)), + ] + if year > 1200 and year < 1550: + if month < 1 or month > 12: + month = 1 + if day < 1 or day > 31: + day = 1 + cdate = str(year) + "/" + str(month) + "/" + str(day) + tsd = jdate2timestamp(cdate) + else: + # cdate = "1403/03/03" + # tsd = jdate2timestamp(cdate) + continue + else: + # cdate = "1403/03/03" + # tsd = jdate2timestamp(cdate) + continue + except: + # print("Error in:"+ d +" for id: " + id) + # cdate = "1403/03/03" + # tsd = jdate2timestamp(cdate) + continue + tdates.append({"date": cdate, "timestamp": tsd, "index": 0, "slice": ""}) + + return normalTitle,tdates + +def normalizerDate2(inputString): + global _normalizer + normalizedString, dates, recognized_dates, recognized_numbers = _normalizer.normalize(inputString, return_dates=True) + tdates = [] + for date_item in recognized_dates: + date_part = date_item['date'] + date_token_index = date_item['date_token_index'] + start_date_token_index = date_item['start_date_token_index'] + end_date_token_index = date_item['end_date_token_index'] + if not date_part.startswith("num"): + try: + cdate = date_part + tsd = jdate2timestamp(date_part) + except: + try: + date_part = date_part.replace("y", "") + date_part = date_part.replace("m", "/") + date_part = date_part.replace("d", "/") + m = re.match(r"^(\d{4})\D(\d{1,2})\D(\d{1,2})$", date_part) + if m: + [year, month, day] = [ + int(m.group(1)), + int(m.group(2)), + int(m.group(3)), + ] + if year > 1200 and year < 1550: + if month < 1 or month > 12: + month = 1 + if day < 1 or day > 31: + day = 1 + cdate = str(year) + "/" + str(month) + "/" + str(day) + tsd = jdate2timestamp(cdate) + else: + # cdate = "1403/03/03" + # tsd = jdate2timestamp(cdate) + continue + # else: + # # cdate = "1403/03/03" + # # tsd = jdate2timestamp(cdate) + # continue + except: + # print("Error in:"+ d +" for id: " + id) + # cdate = "1403/03/03" + # tsd = jdate2timestamp(cdate) + continue + import tokenizer as t + inputString_token = t.Tokenizer.tokenize_words(None,inputString) + # if start_date_token_index == end_date_token_index: + # end_date_token_index += 1 + # original_date_part = inputString_token[start_date_token_index:end_date_token_index] + # else: + original_date_part = inputString_token[start_date_token_index:end_date_token_index + 1] + original_date = '' + for part in original_date_part: + original_date = original_date + ' ' + part + original_date = original_date.strip() + tdates.append({"converted_date": date_item['date'], + "date": cdate , + "original_date" : original_date, + # "timestamp": tsd, + "date_token_index": date_token_index, + "start_date_token_index": start_date_token_index, + "end_date_token_index":end_date_token_index}) + ''' + for d in dates: + if not d.startswith("num"): + try: + tsd = jdate2timestamp(d) + cdate = d + except: + try: + d = d.replace("y", "") + d = d.replace("m", "/") + d = d.replace("d", "/") + m = re.match(r"^(\d{4})\D(\d{1,2})\D(\d{1,2})$", d) + if m: + [year, month, day] = [ + int(m.group(1)), + int(m.group(2)), + int(m.group(3)), + ] + if year > 1200 and year < 1550: + if month < 1 or month > 12: + month = 1 + if day < 1 or day > 31: + day = 1 + cdate = str(year) + "/" + str(month) + "/" + str(day) + tsd = jdate2timestamp(cdate) + else: + # cdate = "1403/03/03" + # tsd = jdate2timestamp(cdate) + continue + else: + # cdate = "1403/03/03" + # tsd = jdate2timestamp(cdate) + continue + except: + # print("Error in:"+ d +" for id: " + id) + # cdate = "1403/03/03" + # tsd = jdate2timestamp(cdate) + continue + tdates.append({"date": cdate, "timestamp": tsd, "index": 0, "slice": ""})''' + return normalizedString,tdates,recognized_numbers + +def OtherDateFormatNormalizer(inputString,pattern): + mainTextTemp = inputString + regex_pattern_Mah = r"y(\d{1,4})m(\d{1,2})d(\d{1,2})\sماه\s(\d{1,4})\sو\s(\d{1,3})\sو\s(\d{1,2})\sو\s(\d{1})\s" # y0m4d4 ماه 1000 و 300 و 50 و 4 + regex_pattern_MahSal = r"y(\d{1,4})m(\d{1,2})d(\d{1,2})\sماه\sسال\s(\d{1,4})\sو\s(\d{1,3})\sو\s(\d{1,2})\sو\s(\d{1})\s" # y0m4d4 ماه سال 1000 و 300 و 50 و 4 + regex_pattern_MahSal2 = r"y(\d{1,4})m(\d{1,2})d(\d{1,2})\sماه\sسال\sy(\d{1,4})m(\d{1,2})d(\d{1,2})\sو\s(\d{1,3})\sو\s(\d{1,2})\sو\s(\d{1})\s" # y0m4d4 ماه سال y1000m0d0 و 300 و 50 و 4 + regex_pattern_MahSal3 = r"y(\d{1,4})m(\d{1,2})d(\d{1,2})\sماه\sسال\sy(\d{1,4})m(\d{1,2})d(\d{1,2})" # y0m3d1 ماه سال y1353m0d0 + + if(pattern==1): + regex = re.compile(regex_pattern_Mah) + elif(pattern==2): + regex = re.compile(regex_pattern_MahSal) + elif(pattern==3): + regex = re.compile(regex_pattern_MahSal2) + elif(pattern==4): + regex = re.compile(regex_pattern_MahSal3) + + matches = regex.finditer(inputString) + for match in matches: + foundedPattern = match.group() + foundedPatternTemp = match.group() + if(pattern==1): + foundedPattern = foundedPattern.replace('ماه','') + else: + foundedPattern = foundedPattern.replace('سال','') + foundedPattern = foundedPattern.replace('ماه','') + foundedPattern = foundedPattern.strip() + tempString = foundedPattern + standardDatePattern = r"y(\d{1,4})m(\d{1,2})d(\d{1,2})" + #regex = re.compile(regex_pattern_Mah) + matchItems = re.finditer(standardDatePattern,tempString) + for item in matchItems: + tempPattern = item.group() + tempString = tempString.replace(tempPattern,'') + tempString = tempString.strip() + tempString = tempString.replace('و','') + tempString = tempString.strip() + tempArray = tempString.split() + year = 0 + for item in tempArray: + dateMatch = re.finditer(standardDatePattern,item) + regexFlag = True + for dateItem in dateMatch: + yearStr = dateItem.group()[1:5] + year += int(yearStr) + regexFlag = False + break + if(item.isalnum() and regexFlag): + year += int(item) + tempPattern = tempPattern.replace('y0','y'+str(year)) + mainTextTemp = mainTextTemp.replace(foundedPatternTemp,tempPattern+' ') + return mainTextTemp + + #foundedPattern = jdate2timestamp(foundedPattern) + #convertedText = regex.sub(foundedPattern,convertedText) + +def normalizerLongData(data): + dates = [] + if len(data) > 10000: + textParts = textwrap.wrap(data, 10000, break_long_words=False) + for part in textParts: + dates.extend(normalizerData(part)) + else: + dates = normalizerData(data) + return dates + +# ################## +# در ویندوز برای اعداد منفی که تاریخهای قبلی بود را خطا می داد +# rr = gdt.timestamp() +# ################# +def jdate2timestamp_old(dt): + ndt = dt.replace("y", "") + ndt = ndt.replace("m", "/") + ndt = ndt.replace("d", "/") + gd = jalali.Persian(ndt).gregorian_datetime() + # print(gd) + ztime = datetime.time(0, 0, 0, 0) + gdt = datetime.datetime.combine(gd, ztime) + # print(gdt) + rr = gdt.timestamp() + tst = int( round(rr) * 1000) + return tst + +def jdate2timestamp(dt): + ndt = dt.replace("y", "") + ndt = ndt.replace("m", "/") + ndt = ndt.replace("d", "/") + gd = jalali.Persian(ndt).gregorian_datetime() + base = datetime.date(1970, 1, 1) + rr = (gd-base).total_seconds() + tst = int( round(rr) * 1000) + return tst + + + +def getSortTimestamp(ts_date): + empty_date = -15000000000 + ts_ts = empty_date + try: + if ts_date != "": + ts_ts = jdate2timestamp(ts_date) + except: + ts_ts = empty_date + + return ts_ts + +def normalize_content(content): + text = _normalizer.sub_alphabets(content) + return text + +def normalYehKe(text): + if(text == None) : + return '' + + c1 = sub(yeAr, yeFr, text) + c2 = sub(keAr, keFr, c1) + c2 = c2.replace('\u00A0', '') + return c2.strip() + +_term_list = [] +def setTermList(): + global _term_list + if(_term_list.__len__() > 0): + return + _term_list = [ + { + "begin": jdate2timestamp("1285/07/14"), + "end": jdate2timestamp("1287/04/2"), + "term": "مجلس شورای ملی-دوره1", + "term_number": 1, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1288/8/24"), + "end": jdate2timestamp("1290/10/3"), + "term": "مجلس شورای ملی-دوره2", + "term_number": 2, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1293/9/14"), + "end": jdate2timestamp("1294/8/21"), + "term": "مجلس شورای ملی-دوره3", + "term_number": 3, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1300/4/1"), + "end": jdate2timestamp("1302/3/30"), + "term": "مجلس شورای ملی-دوره4", + "term_number": 4, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1302/11/22"), + "end": jdate2timestamp("1304/11/22"), + "term": "مجلس شورای ملی-دوره5", + "term_number": 5, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1305/4/19"), + "end": jdate2timestamp("1307/5/22"), + "term": "مجلس شورای ملی-دوره6", + "term_number": 6, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1307/7/19"), + "end": jdate2timestamp("1309/8/14"), + "term": "مجلس شورای ملی-دوره7", + "term_number": 7, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1309/9/24"), + "end": jdate2timestamp("1311/10/24"), + "term": "مجلس شورای ملی-دوره8", + "term_number": 8, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1311/12/24"), + "end": jdate2timestamp("1314/1/24"), + "term": "مجلس شورای ملی-دوره9", + "term_number": 9, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1314/3/15"), + "end": jdate2timestamp("1316/3/22"), + "term": "مجلس شورای ملی-دوره10", + "term_number": 10, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1316/6/20"), + "end": jdate2timestamp("1318/6/27"), + "term": "مجلس شورای ملی-دوره11", + "term_number": 11, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1318/8/3"), + "end": jdate2timestamp("1320/8/9"), + "term": "مجلس شورای ملی-دوره12", + "term_number": 12, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1320/8/22"), + "end": jdate2timestamp("1322/9/1"), + "term": "مجلس شورای ملی-دوره13", + "term_number": 13, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1322/12/16"), + "end": jdate2timestamp("1324/12/21"), + "term": "مجلس شورای ملی-دوره14", + "term_number": 14, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1326/4/25"), + "end": jdate2timestamp("1328/5/6"), + "term": "مجلس شورای ملی-دوره15", + "term_number": 15, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1328/11/20"), + "end": jdate2timestamp("1330/11/29"), + "term": "مجلس شورای ملی-دوره16", + "term_number": 16, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1331/2/7"), + "end": jdate2timestamp("1332/8/28"), + "term": "مجلس شورای ملی-دوره17", + "term_number": 17, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1332/12/27"), + "end": jdate2timestamp("1335/1/26"), + "term": "مجلس شورای ملی-دوره18", + "term_number": 18, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1335/3/10"), + "end": jdate2timestamp("1339/3/29"), + "term": "مجلس شورای ملی-دوره19", + "term_number": 19, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1339/12/2"), + "end": jdate2timestamp("1340/2/19"), + "term": "مجلس شورای ملی-دوره20", + "term_number": 20, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1342/7/14"), + "end": jdate2timestamp("1346/7/13"), + "term": "مجلس شورای ملی-دوره21", + "term_number": 21, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1346/7/14"), + "end": jdate2timestamp("1350/6/9"), + "term": "مجلس شورای ملی-دوره22", + "term_number": 22, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1350/6/9"), + "end": jdate2timestamp("1354/6/16"), + "term": "مجلس شورای ملی-دوره23", + "term_number": 23, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1354/6/17"), + "end": jdate2timestamp("1357/11/20"), + "term": "مجلس شورای ملی-دوره24", + "term_number": 24, + "majles_name": "شورای ملی", + }, + { + "begin": jdate2timestamp("1359/3/7"), + "end": jdate2timestamp("1363/3/6"), + "term": "مجلس شورای اسلامی-دوره1", + "term_number": 1, + "majles_name": "شورای اسلامی", + }, + { + "begin": jdate2timestamp("1363/3/7"), + "end": jdate2timestamp("1367/3/6"), + "term": "مجلس شورای اسلامی-دوره2", + "term_number": 2, + "majles_name": "شورای اسلامی", + }, + { + "begin": jdate2timestamp("1367/3/7"), + "end": jdate2timestamp("1371/3/6"), + "term": "مجلس شورای اسلامی-دوره3", + "term_number": 3, + "majles_name": "شورای اسلامی", + }, + { + "begin": jdate2timestamp("1371/3/7"), + "end": jdate2timestamp("1375/3/11"), + "term": "مجلس شورای اسلامی-دوره4", + "term_number": 4, + "majles_name": "شورای اسلامی", + }, + { + "begin": jdate2timestamp("1375/3/12"), + "end": jdate2timestamp("1379/3/6"), + "term": "مجلس شورای اسلامی-دوره5", + "term_number": 5, + "majles_name": "شورای اسلامی", + }, + { + "begin": jdate2timestamp("1379/3/7"), + "end": jdate2timestamp("1383/3/6"), + "term": "مجلس شورای اسلامی-دوره6", + "term_number": 6, + "majles_name": "شورای اسلامی", + }, + { + "begin": jdate2timestamp("1383/3/7"), + "end": jdate2timestamp("1387/3/6"), + "term": "مجلس شورای اسلامی-دوره7", + "term_number": 7, + "majles_name": "شورای اسلامی", + }, + { + "begin": jdate2timestamp("1387/3/7"), + "end": jdate2timestamp("1391/3/6"), + "term": "مجلس شورای اسلامی-دوره8", + "term_number": 8, + "majles_name": "شورای اسلامی", + }, + { + "begin": jdate2timestamp("1391/3/7"), + "end": jdate2timestamp("1395/3/7"), + "term": "مجلس شورای اسلامی-دوره9", + "term_number": 9, + "majles_name": "شورای اسلامی", + }, + { + "begin": jdate2timestamp("1395/3/8"), + "end": jdate2timestamp("1399/3/6"), + "term": "مجلس شورای اسلامی-دوره10", + "term_number": 10, + "majles_name": "شورای اسلامی", + }, + { + "begin": jdate2timestamp("1399/3/7"), + "end": jdate2timestamp("1403/3/6"), + "term": "مجلس شورای اسلامی-دوره11", + "term_number": 11, + "majles_name": "شورای اسلامی", + }, + ] + + +def getTermQanon(ts_date_timestamp, ts_ref): + setTermList() + global _term_list + term = "" + term_number = 0 + majles_name = "" + + if ts_ref == "هيات وزيران (دوره فترت)": + term = ts_ref + if ts_ref == "نخست وزير (مصدق)": + term = ts_ref + if ts_ref == "وزير عدليه (داور)": + term = ts_ref + if ts_ref == "شوراي انقلاب جمهوري اسلامي ايران": + term = ts_ref + + majles_name = term + if term == "": + for i in range(len(_term_list) - 1, -1, -1): + begin = _term_list[i]["begin"] + end = _term_list[i]["end"] + if ts_date_timestamp >= begin and ts_date_timestamp <= end: + term = _term_list[i]["term"] + term_number = _term_list[i]["term_number"] + majles_name = _term_list[i]["majles_name"] + break + + error = "" + if term == "": + # if ts_date_timestamp >= _term_list[0]["begin"] and ts_date_timestamp <= _term_list[len(_term_list)-1]["end"] : + if ts_date_timestamp <= _term_list[len(_term_list) - 1]["end"]: + for i in range(0, len(_term_list) - 1, 1): + end = _term_list[i]["end"] + if ts_date_timestamp <= end: + term = _term_list[i]["term"] + term_number = _term_list[i]["term_number"] + majles_name = _term_list[i]["majles_name"] + error = "تاریخ بین دو دوره" + break + else: + term_number = -1 + error = "تاریخ خارج از محدوده" + + return term, term_number, majles_name, error + +# این متد یک متن و ایندکس آغاز و پایان یک عبارت درون آن متن را دریافت می کند +# و شماره توکن آغازین و توکن پایانی مربوط به عبارت در متن را بر می گرداند +def token_state_finder(normalized_section_content, start_index, end_index): + before_substring = normalized_section_content[0:start_index-1].strip() + pattern_substring = normalized_section_content[start_index-1:end_index+1].strip() + before_substring_token_list = before_substring.strip().split() + pattern_token_list = pattern_substring.strip().split() + start_token_state = len(before_substring_token_list) + end_token_state = len(before_substring_token_list) + (len(pattern_token_list)-1) + pattern_tokens_state ={ + "start_token_state": start_token_state, + "end_token_state" : end_token_state + } + return pattern_tokens_state + +def find_number_indexes_in_string(normalized_string,recognized_numbers): + complete_recognized_numbers = [] + for item in recognized_numbers: + number_start_index, number_end_index = find_token_indexes_in_string(normalized_string,item['start_token_index'],item['end_token_index']) + content = normalized_string.split() + # if item['start_token_index']==item['end_token_index']: + # # حذف این بخش و باقی گذاشتن دستور ذیل الز زیر در کفایت درست کار کردن متد بررسی شود + + # number_token_list = content[item['start_token_index']] + # else: + number_token_list = content[item['start_token_index']:item['end_token_index']+1] + complete_recognized_numbers.append( + { + 'number_value' : item['number_value'], + 'number_token_list' : number_token_list, + 'start_token_index' : item['start_token_index'], + 'end_token_index' : item['end_token_index'], + "start_number_state": number_start_index, + "end_number_state" : number_end_index + } + ) + return complete_recognized_numbers + +# این متد متن اصلی یک متن، توکن آغازین و توکن پایانی مربوط به یک عبارت را می گیرد +# و ایندکس آغاز و ایندکس پایان متن وارد شده را بر می گرداند +def find_token_indexes_in_string(normalized_string,start_token_state,end_token_state): + before_tokens = normalized_string.split()[0:start_token_state] + content_tokens = normalized_string.split()[start_token_state:end_token_state + 1] + content_start_index = 0 + content_end_index = 0 + # شمردن تعداد کاراکترهای هر توکن در لیست توکن قبل از عدد + for token in before_tokens: + content_start_index += len(token) + # اضافه کردن تعداد فاصله های خالی یا همان اسپیس به عدد ایندکس شروع عدد + content_start_index += len(before_tokens) + 1 + + # شمردن تعداد کاراکترهای هر توکن در لیست توکن مربوط به عدد + for token in content_tokens: + content_end_index += len(token) + # اضافه کردن تعداد فاصله های خالی یا همان اسپیس به عدد ایندکس پایان عدد + content_end_index += (content_start_index - 1) + (len(content_tokens) - 1) + + return content_start_index, content_end_index + +# این متد، متنی را دریافت می کند و الگوهای تعریف شده را در آن جستجو می کند و آرایه ای از عبارات مطابق با هر الگو، +# شماره ایندکس شروع و پایان هر عبارت، عنوان و محتوای الگو، و شماره توکن شروع و توکن پایانی هر عبارت +# پیدا شده را بر می گرداند +def regex_patterns_finder(sectoin_content): + regex_patterns = { + "asle N asasi": r"اصل\s*شماره\s*(\d+)\s*قانون\s*اساسی\s*جمهوری\s*اسلامی\s*ایران", # اصل شماره فلان قانون اساسی جمهوری اسلامی ایران + "qanone asasi": r"(?") +] +model.generation_config.pad_token_id = tokenizer.eos_token_id #tokenizer.pad_token_id + +SYS_PROMPT = """You receive a Persian legal text and extract from it the keywords that are most important. +And you don't need to provide explanations or additional text. +Put each keyword on a single line." +"""# Explain your answer step by step. + + +def format_prompt(SENTENCE): + PROMPT = f"Persian legal text: {SENTENCE}." + return PROMPT + +def generate(formatted_prompt): + formatted_prompt = formatted_prompt[:50000] # to avoid GPU OOM + messages = [{"role":"system","content":SYS_PROMPT},{"role":"user","content":formatted_prompt}] + # tell the model to generate + input_ids = tokenizer.apply_chat_template( + messages, + add_generation_prompt=True, + return_tensors="pt" + ).to(model.device) + outputs = model.generate( + input_ids, + max_new_tokens=2048, + eos_token_id=terminators, + do_sample=True, + temperature=0.6, + top_p=0.9, + ) + response = outputs[0][input_ids.shape[-1]:] + return tokenizer.decode(response, skip_special_tokens=True) + +def get_rules(sentence): + formatted_prompt = format_prompt(sentence) + rules = generate(formatted_prompt).split('\n') + result = [r.strip() for r in rules if r.strip()] + return result + +if __name__ == "__main__": + print('start') + start_time = time.time() + inputfile = open('./data/main_classes_dataset_03.json', "r", encoding='utf-8') + data = json.load(inputfile) + inputfile.close() + counter = 1 + for c in tqdm(data): + for item in tqdm(data[c]): + content = item['content'] + item['keywords'] = get_rules(content) + print(f"section {counter} ...") + counter += 1 + + outputfile = open('./data/main_keywords_lama70B_dataset_03.json', "w", encoding='utf-8') + outputfile.write(json.dumps(data, ensure_ascii=False, indent = 4)) + outputfile.close() + end_time = time.time() + print(f"elapsed time: {end_time-start_time}") + print("end") + + exit() + + """ + system prompt version 2 for test: + + You are a lawyer and you must be able to explain legal texts without changing technical terms in a way that non-lawyers can understand the meaning of the text. + + user prompt version 2 for test: + + Extract at least {} important and significant key phrases from the "text" and print the key phrases in the form of a list in Persian and put each key phrase on a new line and do not add any explanation at the beginning or end of the answer. + Each key phrase has a sequential number at the beginning. The key phrases must be present exactly in the text. It is very important and essential that the length of each key phrase has at least two tokens and a single-token key phrase is not acceptable. I emphasize that no key phrase should have only one token. The names of organizations, institutions and legal entities must be considered as key phrases. No key phrase should be a verb or a preposition and should only include nouns that are added together. No key phrase should end with a preposition or the letter "و". It is essential that key phrases do not include "ماده", "تبصره", "بند" or "تاریخ ها". + """ \ No newline at end of file diff --git a/llama_givechi.py b/llama_givechi.py new file mode 100644 index 0000000..95121d3 --- /dev/null +++ b/llama_givechi.py @@ -0,0 +1,236 @@ +""" +این فایل با نرمالایزر پارسیور کار می کند +""" +from datetime import datetime +# from elasticsearch import Elasticsearch +from threading import Thread +import torch +import time +import json +import os.path +import os +from funcs import write_to_json, read_from_json +from normalizer import Normalizer +from tokenizer import * +_normalizer = Normalizer(date_normalizing_needed=True) +address = os.getcwd() +# sections_list = read_from_json(address + '/data/clean_sections_11k.json') # Main File +# sections_list = read_from_json('../data/clean_sections_11k.json') # Main File +# sections_list = read_from_json('../data/simplized_sentences_110_2.json') # Main File +# sections_list = read_from_json('./data/main_sections_170k_metadata.json') # Main File + +def read_from_json1(file_address): + data_dict = [] + # خواندن اطلاعات از فایل JSON + with open(file_address, 'r', encoding='utf-8') as file: + loaded_data = json.load(file) + + # نمایش اطلاعات خوانده شده + # for item in loaded_data: + # data_dict.append(item) + return loaded_data + +sections_list = read_from_json1("./data_givechi/main_classes_dataset_03.json") # Main File + +# not_have_two_token_kw = read_from_json('../data/not_have_two_token_kw.json') +# not_have_two_token_kw_list = [item3["id"] for item3 in not_have_two_token_kw] +not_have_two_token_kw_list = [] + +import json +os.environ['HF_HOME'] = "/home/admin/HFHOME" +from tqdm import tqdm +import time + +from transformers import AutoTokenizer, AutoModel +from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig + +#model_id = "meta-llama/Meta-Llama-3.1-8B-Instruct" +#model_id = "PartAI/Dorna2-Llama3.1-8B-Instruct" + +model_id = "meta-llama/Llama-3.1-70B-Instruct" + +# use quantization to lower GPU usage +# 4 bit: +# bnb_config = BitsAndBytesConfig( +# load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16 +# ) +# 8 bit: +bnb_config = BitsAndBytesConfig( + load_in_8bit=True, bnb_8bit_use_double_quant=True, bnb_8bit_quant_type="nf8", bnb_8bit_compute_dtype=torch.bfloat16 +) +print("Model Loading START:") +print(str(datetime.now())) +print() +tokenizer = AutoTokenizer.from_pretrained(model_id) +model = AutoModelForCausalLM.from_pretrained( + model_id, + torch_dtype=torch.bfloat16, + device_map="auto", + quantization_config=bnb_config +) +terminators = [ + tokenizer.eos_token_id, + tokenizer.convert_tokens_to_ids("<|eot_id|>") +] +model.generation_config.pad_token_id = tokenizer.eos_token_id #tokenizer.pad_token_id +print("Model Loading END:") +print(str(datetime.now())) +print() + +# if torch.cuda.is_available(): +# #model_id = "PartAI/Dorna-Llama3-8B-Instruct" +# # model_id = "meta-llama/Meta-Llama-3.1-8B-Instruct" + +# model_id = "meta-llama/Llama-3.1-70B-Instruct" +# model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto", torch_dtype=torch.bfloat16) +# tokenizer = AutoTokenizer.from_pretrained(model_id) + +# index_name_i = 'semantic_search-v10' + +# es = Elasticsearch( +# "http://127.0.0.1:6900", +# # ca_certs="/path/to/http_ca.crt", +# basic_auth=("elastic", "SG*7eGwg+KG2_*-1_mMm") +# ) + +counter = 0 +total = 0 +remained = 0 +id = '' +keywords_count = 15 + +def generateKeywords(law): + global remained + try: + keywords_count = (len(law) / 1000) * 15 + keywords_count = int(keywords_count) + if keywords_count == 0: + keywords_count = 1 + + + # messages = [{"role": "system", "content": "تو یک وکیل حقوق دان هستی و باید بتوانی متن های قانونی و حقوقی را بدون تغییر اصطلاحات فنی، به صورتی توضیح دهی که افراد غیر حقوق دان، معنای متن را درک کنند. " }, + # {"role": "user", "content": + # '''از "متن" حداقل {} عبارت های کلیدی مهم و پراهمیت را استخراج کن و عبارت های کلیدی را در قالب لیست به زبان فارسی چاپ کن و هر کلید عبارت کلیدی را در یک خط جدید قرار بده و هیچ گونه توضیحی در ابتدا یا انتهای پاسخ، اضافه نکن. + # هر عبارت کلیدی دارای یک شماره ترتیبی در ابتدای آن باشد. عبارت های کلیدی، دقیقا در متن موجود باشد. بسیار مهم و ضروری است که طول هر عبارت کلیدی حداقل دو توکن داشته باشد و عبارت کلیدی یک توکنی قابل قبول نیست. تاکید می کنم که هیچ عبارت کلیدی نباید فقط یک توکن داشته باشد. نام سازمان ها و نهادها و اشخاص حقوقی، حتما به عنوان عبارت کلیدی درنظر گرفته شود. هیچ عبارت کلیدی، فعل یا حرف اضافه نباشد و فقط شامل اسم هایی باشد که به هم اضافه شده اند. هیچ عبارت کلیدی نباید با حرف اضافه یا حرف «و» تمام شود. ضروری است که عبارت های کلیدی شامل ماده، بند، تبصره یا تاریخ ها نباشند.''' + # .format(keywords_count) + # }, + # {"role": "user", "content": + # '''"متن": {}'''.format(text) + # },] + messages = [{"role": "system", "content": '''You are a lawyer and legal expert who can interpret legal texts well, understand the semantic layers of the text and infer logical relationships in the text. +You can understand Persian and English and interpret expressions in a Legal Context. You are also an expert in Deontic Logic and can understand the logical structure of law and rules in text. + +Use uppercase English letters such as A, B, C, etc. to identify all possible propositions. Do not include negative tones such as ""not"" in the propositions. For example, if the sentence is ""It is not bored,"" you should use ""A: bored"" to represent it. +Next, for each proposition, use the symbol to represent its negative form. For example, the negative form of proposition A can be expressed as ¬A. +Now, carefully analyze the context and find causal relationship between propositions seriously. A causal expression is only established when the context directly and explicitly supports this relationship. Use arrows (→) to indicate causal relationships, for example, ""If A, then B"", ""B if A"" and ""A causes B"" etc. must be represented as A → B. +another example, ""if A and not C then B"" should be represented as A ∧ ¬C → B +if the causal expression is not strict and is somehow loose, do not state it. + +Extraction Criteria: +1- Defintion of a propostion: +a proposition is an statement which could be true or false. ""I'm an animal"" is a propostion, but ""Ministry of Roads and Urban Development"" is not. +Defintion of strict caustion: +A strictly causes B if and only if: +1. Whenever A occurs, B necessarily occurs. +2. The occurrence of B is entirely dependent on the occurrence of A. +3. There are no other factors or conditions that can independently cause B. +4. The relationship between A and B is invariant and holds in all relevant circumstances. + +Additional Instructions: +Do Not Add Extra Information: Provide only the extracted rules in the specified format without additional commentary or explanations. + +Formatting Instructions: +Language: Output must be in English. +output only propositions and causal expressions that are explicitly stated in the following text.''' }, + {"role": "user", "content": f'''"this is your text to process:{law}''' + },] + + + input_ids = tokenizer.apply_chat_template( + messages, + add_generation_prompt=True, + return_tensors="pt" + ).to(model.device) + + terminators = [ + tokenizer.eos_token_id, + tokenizer.convert_tokens_to_ids("<|eot_id|>") + ] + model.generation_config.pad_token_id = tokenizer.pad_token_id + + + outputs = model.generate( + input_ids, + max_new_tokens=256, + eos_token_id=terminators, + do_sample=True, + temperature=0.6, + top_p=0.85, + ) + #lock0.release() + response = outputs[0][input_ids.shape[-1]:] + keywords = tokenizer.decode(response, skip_special_tokens=True) + #lock1.acquire() + # resp = es.update(index=index_name_i, id=id, doc={"content_keywords-llama3-str": str(keywords)}) + + + return keywords + + except Exception as inst: + print(type(inst)) # the exception type + print(inst.args) # arguments stored in .args + print("Exception: " + str(inst)) + + + +if __name__ == "__main__": + start_time = time.time() + print("start_time: "+str(datetime.now())) + + try: + keywords_dict = [] + + count = 1 + + finall_data = [] + for q_class, current_section_list in sections_list.items(): + + + for content_item in current_section_list: + id = content_item['id'] + qanon_id = content_item['qanon_id'] + # if not id in not_have_two_token_kw_list: + # continue + content = content_item['content'] + content_len = len(content.split()) + # کنارگذاشتن محتواهای با حجم زیاد + if content_len > 2000: + print("too long content " + str(id)) + continue + content = _normalizer.sub_alphabets(content) + prompt_result = generateKeywords(content) + print("section " + str(count) + "/" + str(len(not_have_two_token_kw_list)) + " prompting ... ") + keywords_dict.append({ + 'id':id, + 'qanon-id':qanon_id, + 'content':content, + 'prompt-result':prompt_result + }) + if count ==5: + write_to_json(keywords_dict, f"./data_givechi/main_test_{count}.json") + # keywords_dict = [] + count+=1 + # finall_data.append({'class':q_class, 'sections': keywords_dict}) + finall_data.append({q_class : keywords_dict}) + + write_to_json(finall_data, "./data_givechi/main_classes_dataset_result.json") + + except Exception as inst: + print(type(inst)) # the exception type + print(inst.args) # arguments stored in .args + + end_time = time.time() + print("end_time: "+ str(datetime.now())) + operation_time = (int(end_time-start_time)/60)/60 + print(f"elapsed time: {operation_time} hours") + print(f"Finished!!!") \ No newline at end of file diff --git a/normalizer.py b/normalizer.py new file mode 100644 index 0000000..6d0982d --- /dev/null +++ b/normalizer.py @@ -0,0 +1,1370 @@ +from re import sub +import copy +import os +from tokenizer import Tokenizer +from data_helper import DataHelper +# import general_functions as gf +import traceback,sys + +class Normalizer(): + + def __init__(self, + half_space_char='\u200c', + date_normalizing_needed=False, + pinglish_conversion_needed=False, + train_file_path="resource/tokenizer/Bijan_khan_chunk.txt", + token_merger_path="resource/tokenizer/TokenMerger.pckl"): + self.dir_path = os.path.dirname(os.path.realpath(__file__)) + "/" + + self.dic1_path = self.dir_path + 'resource/normalizer/Dic1_new.txt' + self.dic2_path = self.dir_path + 'resource/normalizer/Dic2_new.txt' + self.dic3_path = self.dir_path + 'resource/normalizer/Dic3_new.txt' + self.dic1 = self.load_dictionary(self.dic1_path) + self.dic2 = self.load_dictionary(self.dic2_path) + self.dic3 = self.load_dictionary(self.dic3_path) + + self.date_normalizing_needed = date_normalizing_needed + self.pinglish_conversion_needed = pinglish_conversion_needed + self.data_helper = DataHelper() + + + if self.date_normalizing_needed or self.pinglish_conversion_needed: + self.tokenizer = Tokenizer() + self.date_normalizer = DateNormalizer() + self.pinglish_conversion = PinglishNormalizer() + + + + def load_dictionary(self, file_path): + dict = {} + with open(file_path, 'r', encoding='utf-8') as f: + g = f.readlines() + for Wrds in g: + wrd = Wrds.split(' ') + dict[wrd[0].strip()] = sub('\n', '', wrd[1].strip()) + return dict + + def sub_alphabets(self, doc_string): + # try: + # doc_string = doc_string.decode('utf-8') + # except UnicodeEncodeError: + # pass + # a0 = "ء" + # b0 = "ئ" + # c0 = sub(a0, b0, doc_string) + a11 = r"ﺁ|آ" + b11 = r"آ" + c11 = sub(a11, b11, doc_string) + a1 = r"ٲ|ٱ|إ|ﺍ|أ" + b1 = r"ا" + c1 = sub(a1, b1, c11) + a2 = r"ﺐ|ﺏ|ﺑ" + b2 = r"ب" + c2 = sub(a2, b2, c1) + a3 = r"ﭖ|ﭗ|ﭙ|ﺒ|ﭘ" + b3 = r"پ" + c3 = sub(a3, b3, c2) + a4 = r"ﭡ|ٺ|ٹ|ﭞ|ٿ|ټ|ﺕ|ﺗ|ﺖ|ﺘ" + b4 = r"ت" + c4 = sub(a4, b4, c3) + a5 = r"ﺙ|ﺛ" + b5 = r"ث" + c5 = sub(a5, b5, c4) + a6 = r"ﺝ|ڃ|ﺠ|ﺟ" + b6 = r"ج" + c6 = sub(a6, b6, c5) + a7 = r"ڃ|ﭽ|ﭼ" + b7 = r"چ" + c7 = sub(a7, b7, c6) + a8 = r"ﺢ|ﺤ|څ|ځ|ﺣ" + b8 = r"ح" + c8 = sub(a8, b8, c7) + a9 = r"ﺥ|ﺦ|ﺨ|ﺧ" + b9 = r"خ" + c9 = sub(a9, b9, c8) + a10 = r"ڏ|ډ|ﺪ|ﺩ" + b10 = r"د" + c10 = sub(a10, b10, c9) + a11 = r"ﺫ|ﺬ|ﻧ" + b11 = r"ذ" + c11 = sub(a11, b11, c10) + a12 = r"ڙ|ڗ|ڒ|ڑ|ڕ|ﺭ|ﺮ" + b12 = r"ر" + c12 = sub(a12, b12, c11) + a13 = r"ﺰ|ﺯ" + b13 = r"ز" + c13 = sub(a13, b13, c12) + a14 = r"ﮊ" + b14 = r"ژ" + c14 = sub(a14, b14, c13) + a15 = r"ݭ|ݜ|ﺱ|ﺲ|ښ|ﺴ|ﺳ" + b15 = r"س" + c15 = sub(a15, b15, c14) + a16 = r"ﺵ|ﺶ|ﺸ|ﺷ" + b16 = r"ش" + c16 = sub(a16, b16, c15) + a17 = r"ﺺ|ﺼ|ﺻ" + b17 = r"ص" + c17 = sub(a17, b17, c16) + a18 = r"ﺽ|ﺾ|ﺿ|ﻀ" + b18 = r"ض" + c18 = sub(a18, b18, c17) + a19 = r"ﻁ|ﻂ|ﻃ|ﻄ" + b19 = r"ط" + c19 = sub(a19, b19, c18) + a20 = r"ﻆ|ﻇ|ﻈ" + b20 = r"ظ" + c20 = sub(a20, b20, c19) + a21 = r"ڠ|ﻉ|ﻊ|ﻋ" + b21 = r"ع" + c21 = sub(a21, b21, c20) + a22 = r"ﻎ|ۼ|ﻍ|ﻐ|ﻏ" + b22 = r"غ" + c22 = sub(a22, b22, c21) + a23 = r"ﻒ|ﻑ|ﻔ|ﻓ" + b23 = r"ف" + c23 = sub(a23, b23, c22) + a24 = r"ﻕ|ڤ|ﻖ|ﻗ" + b24 = r"ق" + c24 = sub(a24, b24, c23) + a25 = r"ڭ|ﻚ|ﮎ|ﻜ|ﮏ|ګ|ﻛ|ﮑ|ﮐ|ڪ|ك" + b25 = r"ک" + c25 = sub(a25, b25, c24) + a26 = r"ﮚ|ﮒ|ﮓ|ﮕ|ﮔ" + b26 = r"گ" + c26 = sub(a26, b26, c25) + a27 = r"ﻝ|ﻞ|ﻠ|ڵ" + b27 = r"ل" + c27 = sub(a27, b27, c26) + a28 = r"ﻡ|ﻤ|ﻢ|ﻣ" + b28 = r"م" + c28 = sub(a28, b28, c27) + a29 = r"ڼ|ﻦ|ﻥ|ﻨ" + b29 = r"ن" + c29 = sub(a29, b29, c28) + a30 = r"ވ|ﯙ|ۈ|ۋ|ﺆ|ۊ|ۇ|ۏ|ۅ|ۉ|ﻭ|ﻮ|ؤ" + b30 = r"و" + c30 = sub(a30, b30, c29) + a31 = r"ﺔ|ﻬ|ھ|ﻩ|ﻫ|ﻪ|ۀ|ە|ة|ہ" + b31 = r"ه" + c31 = sub(a31, b31, c30) + a32 = r"ﭛ|ﻯ|ۍ|ﻰ|ﻱ|ﻲ|ں|ﻳ|ﻴ|ﯼ|ې|ﯽ|ﯾ|ﯿ|ێ|ے|ى|ي" + b32 = r"ی" + c32 = sub(a32, b32, c31) + a33 = r'¬' + b33 = r'‌' + c33 = sub(a33, b33, c32) + pa0 = r'•|·|●|·|・|∙|。|ⴰ' + pb0 = r'.' + pc0 = sub(pa0, pb0, c33) + pa1 = r',|٬|٫|‚|,' + pb1 = r'،' + pc1 = sub(pa1, pb1, pc0) + pa2 = r'؟|ʕ' + pb2 = r'' + pc2 = sub(pa2, pb2, pc1) + na0 = r'۰|٠' + nb0 = r'0' + nc0 = sub(na0, nb0, pc2) + na1 = r'۱|١' + nb1 = r'1' + nc1 = sub(na1, nb1, nc0) + na2 = r'۲|٢' + nb2 = r'2' + nc2 = sub(na2, nb2, nc1) + na3 = r'۳|٣' + nb3 = r'3' + nc3 = sub(na3, nb3, nc2) + na4 = r'۴|٤' + nb4 = r'4' + nc4 = sub(na4, nb4, nc3) + na5 = r'۵' + nb5 = r'5' + nc5 = sub(na5, nb5, nc4) + na6 = r'۶|٦' + nb6 = r'6' + nc6 = sub(na6, nb6, nc5) + na7 = r'۷|٧' + nb7 = r'7' + nc7 = sub(na7, nb7, nc6) + na8 = r'۸|٨' + nb8 = r'8' + nc8 = sub(na8, nb8, nc7) + na9 = r'۹|٩' + nb9 = r'9' + nc9 = sub(na9, nb9, nc8) + np2 = r'²' + nm2 = r'2' + ng2 = sub(np2, nm2, nc9) + ea1 = r'|ِ|ُ|َ|ٍ|ٌ|ً|' + eb1 = r'' + ec1 = sub(ea1, eb1, ng2) + ea1 = r'ـ|_' + eb1 = r'' + ec2 = sub(ea1, eb1, ec1) + Sa1 = r'( )+' + Sb1 = r' ' + Sc1 = sub(Sa1, Sb1, ec2) + Sa2 = r'(\n)+' + Sb2 = r'\n' + Sc2 = sub(Sa2, Sb2, Sc1) + Sd1 = '\u00A0'# No-Break Space (NBSP) character + Sd2 = Sc2.replace(Sd1, '') + + return Sd2.strip() + + def space_correction(self, doc_string): + a00 = r'^(بی|می|نمی)( )' + b00 = r'\1‌' + c00 = sub(a00, b00, doc_string) + a0 = r'( )(می|نمی|بی)( )' + b0 = r'\1\2‌' + c0 = sub(a0, b0, c00) + a1 = r'( )(هایی|ها|های|ایی|هایم|هایت|هایش|هایمان|هایتان|هایشان|ات|ان|ین' \ + r'|انی|بان|ام|ای|یم|ید|اید|اند|بودم|بودی|بود|بودیم|بودید|بودند|ست)( )' + b1 = r'‌\2\3' + c1 = sub(a1, b1, c0) + a2 = r'( )(شده|نشده)( )' + b2 = r'‌\2‌' + c2 = sub(a2, b2, c1) + a3 = r'( )(طلبان|طلب|گرایی|گرایان|شناس|شناسی|گذاری|گذار|گذاران|شناسان|گیری|پذیری|بندی|آوری|سازی|' \ + r'بندی|کننده|کنندگان|گیری|پرداز|پردازی|پردازان|آمیز|سنجی|ریزی|داری|دهنده|آمیز|پذیری' \ + r'|پذیر|پذیران|گر|ریز|ریزی|رسانی|یاب|یابی|گانه|گانه‌ای|انگاری|گا|بند|رسانی|دهندگان|دار)( )' + b3 = r'‌\2\3' + c3 = sub(a3, b3, c2) + return c3 + + def space_correction_plus1(self, doc_string): + out_sentences = '' + for wrd in doc_string.split(' '): + try: + out_sentences = out_sentences + ' ' + self.dic1[wrd] + except KeyError: + out_sentences = out_sentences + ' ' + wrd + return out_sentences + + def space_correction_plus2(self, doc_string): + out_sentences = '' + wrds = doc_string.split(' ') + L = wrds.__len__() + if L < 2: + return doc_string + cnt = 1 + for i in range(0, L - 1): + w = wrds[i] + wrds[i + 1] + try: + out_sentences = out_sentences + ' ' + self.dic2[w] + cnt = 0 + except KeyError: + if cnt == 1: + out_sentences = out_sentences + ' ' + wrds[i] + cnt = 1 + if cnt == 1: + out_sentences = out_sentences + ' ' + wrds[i + 1] + return out_sentences + + def space_correction_plus3(self, doc_string): + # Dict = {'گفتوگو': 'گفت‌وگو'} + out_sentences = '' + wrds = doc_string.split(' ') + L = wrds.__len__() + if L < 3: + return doc_string + cnt = 1 + cnt2 = 0 + for i in range(0, L - 2): + w = wrds[i] + wrds[i + 1] + wrds[i + 2] + try: + out_sentences = out_sentences + ' ' + self.dic3[w] + cnt = 0 + cnt2 = 2 + except KeyError: + if cnt == 1 and cnt2 == 0: + out_sentences = out_sentences + ' ' + wrds[i] + else: + cnt2 -= 1 + cnt = 1 + if cnt == 1 and cnt2 == 0: + out_sentences = out_sentences + ' ' + wrds[i + 1] + ' ' + wrds[i + 2] + elif cnt == 1 and cnt2 == 1: + out_sentences = out_sentences + ' ' + wrds[i + 2] + return out_sentences + + def normalize(self, doc_string, new_line_elimination=False, return_dates = False): + # normalized_string = gf.normalYehKe(doc_string) + normalized_string = self.sub_alphabets(doc_string) + #normalized_string = self.data_helper.clean_text(normalized_string, new_line_elimination).strip() + + #normalized_string = self.space_correction(self.space_correction_plus1(self.space_correction_plus2(self.space_correction_plus3(normalized_string)))).strip() + + # !!!آئین نامه را به آئین‌نامه تبدیل می کند و دو توکن را به یکی تبدیل می کند + #normalized_string = (self.space_correction_plus1(self.space_correction_plus2(self.space_correction_plus3(normalized_string)))).strip() + + #if self.pinglish_conversion_needed: + #normalized_string = self.pinglish_conversion.pingilish2persian(self.tokenizer.tokenize_words(normalized_string)) + + if self.date_normalizing_needed: + token_list = self.tokenizer.tokenize_words(normalized_string) + # نرمالایز کردن اعداد حروفی و ... + normalized_string, additional_token_index_array = self.date_normalizer.normalize_numbers2(token_list) + # نرمالایز و تشخیص تاریخ ها + normalized_string, dates, recognized_dates = self.date_normalizer.general_normalize_date(normalized_string, additional_token_index_array) + #normalized_string_list = normalized_string.strip().split() + #normalized_string, dates, recognized_dates = self.date_normalizer.normalize_dates(normalized_string_list, additional_token_index_array, token_list_len= len(normalized_string_list), previous_slice_index_array=[0,]) + # کنترل اعدادی که پشت سر هم آمده اند و با واو از هم جدا شده اند + normalized_string, recognized_numbers = self.date_normalizer.handle_continuous_numbers(normalized_string) + # در مواردی مانند وسیصد که واو به عدد سیصد چسبیده، ابتدا این دو را از هم جدا می کنیم + # تا عدد اصلی را به دست بیاوریم. در این صورت یک توکن اضافه تولید می شود که با این متد، توکن های اضافی را حذف می کنیم + normalized_string =self.date_normalizer.remove_additional_tokens(normalized_string.split(), additional_token_index_array) + if return_dates: + # مرتب کردن آرایه تاریخ های پیدا شده بر اساس توکن شروع عبارت حاوی تاریخ + recognized_dates.sort(key=lambda x: int(x['start_date_token_index']), reverse=False) + + return normalized_string, dates, recognized_dates, recognized_numbers + else: + return normalized_string + + +class DateNormalizer(): + def __init__(self): + + self.month_dict = { "فروردین": 1,"فروردینماه": 1,'فروردین\u200cماه':1, + "اردیبهشت": 2,"اردیبهشتماه": 2,'اردیبهشت\u200cماه':2, + "خرداد": 3,"خردادماه": 3, + "تیر": 4,"تیرماه": 4, + "مرداد": 5,"مردادماه": 5, + "شهریور": 6,"شهریورماه": 6, + "مهر": 7,"مهرماه": 7, + "آبان": 8,"آبانماه": 8,'آبان\u200cماه':8, + "آذر": 9,"آذرماه": 9, + "دی": 10,"دیماه": 10,'دی\u200cماه':10, + "بهمن": 11,"بهمنماه": 11,'بهمن\u200cماه':11, + "اسفند": 12,"اسفندماه": 12} + self.num_dict = {"صد": 100,"وصد": 100, "یکصد": 100, "ویکصد": 100, "هزار": 1000,"وهزار": 1000, + "یکهزار": 1000,"ویکهزار": 1000, + "میلیون": 1000000,"ملیون": 1000000, "ومیلیون": 1000000,"وملیون": 1000000, + "یکمیلیون": 1000000,"یکملیون": 1000000, + "ویکمیلیون": 1000000,"ویکملیون": 1000000, "دویست": 200,"ودویست": 200, + "ده": 10,"وده": 10, "نه": 9,"ونه": 9, "هشت": 8,"وهشت": 8, "هفت": 7,"وهفت": 7, + "شش": 6,"وشش": 6, "پنج": 5,"وپنج": 5,"چهار": 4,"وچهار": 4, "سه": 3,"وسه": 3, + "دو": 2,"ودو": 2, "یک": 1,"ویک": 1, "یازده": 11,"ویازده": 11, "سیزده": 13, "وسیزده": 13, + "چهارده": 14, "دوازده": 12, "پانزده": 15, "شانزده": 16, "هفده": 17,"هیفده": 17, + "وچهارده": 14, "ودوازده": 12, "وپانزده": 15, "وشانزده": 16, "وهفده": 17,"وهیفده": 17, + "هجده": 18,"هیجده": 18, "نوزده": 19, "بیست": 20, "سی": 30, "چهل": 40, "پنجاه": 50, + "وهجده": 18,"وهیجده": 18, "ونوزده": 19, "وبیست": 20, "وسی": 30, "وچهل": 40, "وپنجاه": 50, + "شصت": 60, "هفتاد": 70, "نود": 90, "سیصد": 300, "چهارصد": 400, + "وشصت": 60, "وهفتاد": 70, "ونود": 90, "وسیصد": 300, "وچهارصد": 400, + "پانصد": 500, "ششصد": 600, "هفتصد": 700, "هشتصد": 800, "نهصد": 900, + "وپانصد": 500, "وششصد": 600, "وهفتصد": 700, "وهشتصد": 800, "ونهصد": 900, + "هشتاد": 80, " ": 0, "میلیارد": 1000000000,"ملیارد": 1000000000, + "یکمیلیارد": 1000000000,"یکملیارد": 1000000000, + "وهشتاد": 80, " ": 0, "ومیلیارد": 1000000000,"وملیارد": 1000000000, + "ویکمیلیارد": 1000000000,"ویکملیارد": 1000000000, + "صدم": 100, "هزارم": 1000, "دویستم": 200, + "وصدم": 100, "وهزارم": 1000, "ودویستم": 200, + "دهم": 10, "نهم": 9, "هشتم": 8, "هفتم": 7, "ششم": 6, "پنجم": 5, + "ودهم": 10, "ونهم": 9, "وهشتم": 8, "وهفتم": 7, "وششم": 6, "وپنجم": 5, + "چهارم": 4, "سوم": 3, "دوم": 2, "یکم": 1, "اول": 1, "یازدهم": 11, "سیزدهم": 13, + "وچهارم": 4, "وسوم": 3, "ودوم": 2, "ویکم": 1, "واول": 1, "ویازدهم": 11, "وسیزدهم": 13, + "چهاردهم": 14, "دوازدهم": 12, "پانزدهم": 15, "شانزدهم": 16, "هفدهم": 17,"هیفدهم": 17, + "وچهاردهم": 14, "ودوازدهم": 12, "وپانزدهم": 15, "وشانزدهم": 16, "وهفدهم": 17,"وهیفدهم": 17, + "هجدهم": 18,"هیجدهم": 18, "نوزدهم": 19, "بیستم": 20, "چهلم": 40, "پنجاهم": 50, + "وهجدهم": 18,"وهیجدهم": 18, "ونوزدهم": 19, "وبیستم": 20, "وچهلم": 40, "وپنجاهم": 50, + "شصتم": 60, "هفتادم": 70, "نودم": 90, "سیصدم": 300, "چهارصدم": 400, + "وشصتم": 60, "وهفتادم": 70, "ونودم": 90, "وسیصدم": 300, "وچهارصدم": 400, + "پانصدم": 500, "ششصدم": 600, "هفتصدم": 700, "هشتصدم": 800, "نهصدم": 900, + "وپانصدم": 500, "وششصدم": 600, "وهفتصدم": 700, "وهشتصدم": 800, "ونهصدم": 900, + "هشتادم": 80,"وهشتادم": 80} + + def find_date_part(self, token_list): + import re + for index, element in enumerate(token_list): + # بررسی الگوی تاریخ 1398/12/18 + pattern_date = r'^(\d{4}\s*/\s*([1-9]|0[1-9]|1[0-2])\s*/\s*([1-9]|0[1-9]|[12][0-9]|3[01]))$' + date_pattern = re.compile(pattern_date) + match_date = date_pattern.match(element) + if match_date: + date_parts = match_date.string.split('/') + year = int(re.search(r'\d+', date_parts[0]).group()) + month = int(re.search(r'\d+', date_parts[1]).group()) + day = int(re.search(r'\d+', date_parts[2]).group()) + # اگر مقداری که در روز است، چهار رقمی باشد باید مقدار روز و سال را با هم عوض کرد + if day > 999: + day = int(re.search(r'\d+', date_parts[0]).group()) + year = int(re.search(r'\d+', date_parts[2]).group()) + formal_date = "y" + str(year) + "m" + str(month) + "d" + str(day) + return formal_date, index, index, index + + # 24 /12 /1401 بررسی الگوی تاریخ + pattern_date = r'\s*\d{2}\s*/\s*\d{2}\s*/\s*\d{4}\s*' + date_pattern = re.compile(pattern_date) + match_date = date_pattern.match(element) + if match_date: + date_parts = match_date.string.split('/') + year = int(re.search(r'\d+', date_parts[0]).group()) + month = int(re.search(r'\d+', date_parts[1]).group()) + day = int(re.search(r'\d+', date_parts[2]).group()) + # اگر مقداری که در روز است، چهار رقمی باشد باید مقدار روز و سال را با هم عوض کرد + if day > 999: + day = int(re.search(r'\d+', date_parts[0]).group()) + year = int(re.search(r'\d+', date_parts[2]).group()) + formal_date = "y" + str(year) + "m" + str(month) + "d" + str(day) + return formal_date, index, index, index + + # بررسی الگوی تاریخ 1402،10،06 + patterndate2 = r'^(\d{4}\s*،\s*([1-9]|0[1-9]|1[0-2])\s*،\s*([1-9]|0[1-9]|[12][0-9]|3[01]))$' + date_pattern = re.compile(patterndate2) + match_date2 = date_pattern.match(element) + if match_date2: + date_parts = match_date2.string.split('،') + year = int(re.search(r'\d+', date_parts[0]).group()) + month = int(re.search(r'\d+', date_parts[1]).group()) + day = int(re.search(r'\d+', date_parts[2]).group()) + # اگر مقداری که در روز است، چهار رقمی باشد باید مقدار روز و سال را با هم عوض کرد + if day > 999: + day = int(re.search(r'\d+', date_parts[0]).group()) + year = int(re.search(r'\d+', date_parts[2]).group()) + formal_date = "y" + str(year) + "m" + str(month) + "d" + str(day) + return formal_date, index, index, index + + # بررسی الگوی تاریخ 13ر04ر1345 + patterndate2 = r'^(\d{4}\s*ر\s*([1-9]|0[1-9]|1[0-2])\s*ر\s*([1-9]|0[1-9]|[12][0-9]|3[01]))$' + date_pattern = re.compile(patterndate2) + match_date2 = date_pattern.match(element) + if match_date2: + date_parts = match_date2.string.split('ر') + year = int(re.search(r'\d+', date_parts[0]).group()) + month = int(re.search(r'\d+', date_parts[1]).group()) + day = int(re.search(r'\d+', date_parts[2]).group()) + # اگر مقداری که در روز است، چهار رقمی باشد باید مقدار روز و سال را با هم عوض کرد + if day > 999: + day = int(re.search(r'\d+', date_parts[0]).group()) + year = int(re.search(r'\d+', date_parts[2]).group()) + formal_date = "y" + str(year) + "m" + str(month) + "d" + str(day) + return formal_date, index, index, index + + if element == "/": + if index-1 >= 0 and index+1 < len(token_list) \ + and token_list[index -1].isdigit() and token_list[index+1].isdigit(): + if index+3 < len(token_list) and token_list[index+2] == "/" \ + and token_list[index + 3].isdigit(): + if int(token_list[index-1]) < 1450 and int(token_list[index+1]) < 13 and \ + int(token_list[index+3]) < 32: + formal_date = [int(token_list[index-1]), int(token_list[index+1]), int(token_list[index+3])] + formal_date = "y" + str(formal_date[0]) + "m" + str(formal_date[1]) + "d" + str(formal_date[2]) + #return formal_date, index-1, index+3, index + return formal_date, index, index, index + elif int(token_list[index-1]) < 32 and int(token_list[index+1]) < 13 and \ + int(token_list[index+3]) < 1450: + formal_date = [int(token_list[index-1]), int(token_list[index+1]), int(token_list[index+3])] + formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0]) + #return formal_date, index-1, index+3, index + return formal_date, index, index, index + else: + formal_date = [int(token_list[index-1]), int(token_list[index+1]), int(token_list[index+3])] + formal_date = "num(" + str(formal_date[0]) + "/" + str(formal_date[1]) + "/" + str(formal_date[2]) + ")" + #return formal_date, index-1, index+3, index + return formal_date, index, index, index + elif (int(token_list[index-1]) < 1450 and int(token_list[index-1]) > 1250) and int(token_list[index+1]) < 13: + formal_date = [int(token_list[index-1]), int(token_list[index+ 1]), 0] + formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0]) + #return formal_date, index-1 , index+1, index + return formal_date, index , index, index + else: + formal_date = [int(token_list[index-1]), int(token_list[index+ 1])] + formal_date = "num(" + str(formal_date[0]) + "/" + str(formal_date[1]) + ")" + #return formal_date, index-1 , index+1, index + return formal_date, index , index, index + + if element in self.month_dict or element == "سال": + if index + 1 < len(token_list) and index - 1 > -2: + try: + start_date_index = end_date_index = 0 + if(token_list[index + 1] == 'ماه'): + if(token_list[index + 2] == 'سال' and str(token_list[index + 3]).isdigit()): + + if(int(token_list[index + 3])==1000): + # در این حالت، امکان دارد روز مربوط به این تاریخ هم به صورت حروف در متن قانون نوشته شده باشد + # به همین دلیل ، توکن های قبل از ماه را نیز کنترل می کنیم + start_date_index = index - 1 + day = int(token_list[index - 1]) + if(token_list[index - 2]=='و' and token_list[index - 3].isdigit()): + day += int(token_list[index - 3])# رقم دهگان روز + start_date_index = index - 3 + year = 1000 + if(len(token_list)>= index + 5): + if(token_list[index + 4]=='و' and token_list[index + 5].isdigit()): + year += int(token_list[index + 5])# رقم صدگان سال + end_date_index = index + 5 + if(len(token_list)>= index + 7): + if(token_list[index + 6]=='و' and token_list[index + 7].isdigit()): + year += int(token_list[index + 7])# رقم دهگان سال + end_date_index = index + 7 + if(len(token_list)>= index + 9): + if(token_list[index + 8]=='و' and token_list[index + 9].isdigit()): + year += int(token_list[index + 9])# رقم یکان سال + end_date_index = index + 9 + + formal_date = [day, int(self.month_dict[token_list[index]]), year]# مثلا 20 و 8 تیر ماه سال 1000 و 300 و 20 و 5 + + else: + start_date_index = index - 1 + end_date_index = index + 3 + day = int(token_list[index - 1]) + if(token_list[index - 2]=='و' and int(token_list[index - 1])<10 and int(token_list[index - 3])<31 and token_list[index - 3].isdigit()): + start_date_index = index - 3 + day += int(token_list[index - 3])# رقم دهگان روز + + formal_date = [day, int(self.month_dict[token_list[index]]), int(token_list[index + 3])]# مثلا 20 و 7 تیر ماه سال 1368 + + + formal_date = [day, int(self.month_dict[token_list[index]]), int(token_list[index + 3])]# مثلا 27 تیر ماه سال 1368 + + elif(str(token_list[index + 2]).isdigit()): + if(int(token_list[index + 2])==1000): + + if token_list[index-1].strip() == 'ام': # مثلا 30 ام تیر ماه 1000 و 300 و 80 و 2 + start_date_index = index - 2 + day = int(token_list[index - 2]) + else: + # در این حالت، امکان دارد روز مربوط به این تاریخ هم به صورت حروف در متن قانون نوشته شده باشد + # به همین دلیل ، توکن های قبل از ماه را نیز کنترل می کنیم + start_date_index = index - 1 + day = int(token_list[index - 1]) + + if(token_list[index - 2]=='و' and token_list[index - 3].isdigit()): + day += int(token_list[index - 3])# رقم دهگان روز + start_date_index = index - 3 + year = 1000 + if(len(token_list)>= index + 4): + if(token_list[index + 3]=='و' and token_list[index + 4].isdigit()): + year += int(token_list[index + 4])# رقم صدگان سال + end_date_index = index + 4 + if(len(token_list)>= index + 6): + if(token_list[index + 5]=='و' and token_list[index + 6].isdigit()): + year += int(token_list[index + 6])# رقم دهگان سال + end_date_index = index + 6 + if(len(token_list)>= index + 8): + if(token_list[index + 7]=='و' and token_list[index + 8].isdigit()): + year += int(token_list[index + 8])# رقم یکان سال + end_date_index = index + 8 + formal_date = [day, int(self.month_dict[token_list[index]]), year]# مثلا 20 و 7 تیر ماه 1000 و 300 و 20 و 5 + + else: + formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), int(token_list[index + 2])]# مثلا 27 تیر ماه سال 1368 + start_date_index = index - 1 + end_date_index = index + 2 + #formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), int(token_list[index + 2])] # مثلا 27 تیر ماه 1368 + + elif(token_list[index + 1] == 'سال' and (not(token_list[index + 2]).isdigit())): + + formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), int(token_list[index + 2])] # مثلا 27 تیر سال 1368 + start_date_index = index - 1 + end_date_index = index + 2 + else: + if(token_list[index + 1] == 'سال' and str(token_list[index + 2]).isdigit()): + + if(int(token_list[index + 2])==1000): + + # در این حالت، امکان دارد روز مربوط به این تاریخ هم به صورت حروف در متن قانون نوشته شده باشد + # به همین دلیل ، توکن های قبل از ماه را نیز کنترل می کنیم + start_date_index = index - 1 + day = int(token_list[index - 1]) + if(token_list[index - 2]=='و' and token_list[index - 3].isdigit()): + day += int(token_list[index - 3])# رقم دهگان روز + start_date_index = index - 3 + year = 1000 + if(len(token_list)>= index + 4): + if(token_list[index + 3]=='و' and token_list[index + 4].isdigit()): + year += int(token_list[index + 4])# رقم صدگان سال + end_date_index = index + 4 + if(len(token_list)>= index + 6): + if(token_list[index + 5]=='و' and token_list[index + 6].isdigit()): + year += int(token_list[index + 6])# # رقم دهگان سال !!! برای تاریخ بین 1399 تا 1410 هم همین روال درست بر می گرداند، هرچند منطق غلطی دارد چون مثلا سال 1402 رقم صدگان ندارد + end_date_index = index + 6 + if(len(token_list)>= index + 8): + if((len(token_list)>= index + 7 and token_list[index + 7]=='و') and (len(token_list)>= index + 8 and token_list[index + 8].isdigit())): + year += int(token_list[index + 8])# رقم یکان سال + end_date_index = index + 8 + + formal_date = [day, int(self.month_dict[token_list[index]]), year]# مثلا 20 و 8 تیر ماه سال 1000 و 300 و 20 و 5 + + else: + formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), int(token_list[index + 2])]# مثلا 27 تیر سال 1368 + start_date_index = index - 1 + end_date_index = index + 2 + + elif(str(token_list[index + 1]).isdigit()): + if(int(token_list[index + 1])==1000): + # در این حالت، امکان دارد روز مربوط به این تاریخ هم به صورت حروف در متن قانون نوشته شده باشد + # به همین دلیل ، توکن های قبل از ماه را نیز کنترل می کنیم + start_date_index = index - 1 + day = int(token_list[index - 1]) + if(token_list[index - 2]=='و' and token_list[index - 3].isdigit()): + day += int(token_list[index - 3])# رقم دهگان روز + start_date_index = index - 3 + year = 1000 + if(len(token_list)>= index + 3): + if(token_list[index + 2]=='و' and token_list[index + 3].isdigit()): + year += int(token_list[index + 3])# رقم صدگان سال + end_date_index = index + 3 + if(len(token_list)>= index + 5): + if(token_list[index + 4]=='و' and token_list[index + 5].isdigit()): + year += int(token_list[index + 5])# رقم دهگان سال !!! برای تاریخ بین 1399 تا 1410 هم همین روال درست بر می گرداند، هرچند منطق غلطی دارد چون مثلا سال 1402 رقم صدگان ندارد + end_date_index = index + 5 + if(len(token_list)>= index + 7): + if(token_list[index + 6]=='و' and token_list[index + 7].isdigit()): + year += int(token_list[index + 7])# رقم یکان سال + end_date_index = index + 7 + formal_date = [day, int(self.month_dict[token_list[index]]), year]# مثلا 20 و 7 تیر ماه 1000 و 300 و 20 و 5 + else: + formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), int(token_list[index + 1])]# مثلا 27 تیر 1320‌ + start_date_index = index - 1 + end_date_index = index + 1 + + else: + formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), int(token_list[index + 1])]# مثلا 27 تیر 1365 + start_date_index = index - 1 + end_date_index = index + 1 + formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0]) + if token_list[index - 1] and token_list[index + 1]: + return formal_date, start_date_index, end_date_index, start_date_index + + except Exception as e: + error = e.args[0] + try: + formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), 0] + formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0])#مثلا y1358m12d0 + return formal_date, index-1, index, index-1 + except: + try: + # مثلا سال 1400 + if token_list[index] == "سال": + formal_date = [int(token_list[index + 1]),0, 0] + formal_date = "y" + str(formal_date[0]) + "m" + str(formal_date[1]) + "d" + str(formal_date[2])# y1358m0d0 + return formal_date, index, index+1, index + elif token_list[index+1] == "ماه" and index + 1 < len(token_list): + if index + 2 < len(token_list) and token_list[index + 2].isdigit() and \ + int(token_list[index-2]) < 1450: + formal_date = [0,int(self.month_dict[token_list[index]]),int(token_list[index+2])] + formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0]) # y0m12d5 + return formal_date, index, index+2, index + else: + formal_date = [0,int(self.month_dict[token_list[index]]),0] + formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0]) #y0m12d0 + return formal_date, index, index+1, index + elif index + 1 < len(token_list) and token_list[index + 1].isdigit() and int(token_list[index-2]) < 1450: + formal_date = [0,int(self.month_dict[token_list[index]]),int(token_list[index+1])] + formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0]) # y0m12d5 + return formal_date, index, index+1, index + elif index-2 >= 0 and index+2 < len(token_list) and \ + token_list[index - 1] == "/" and token_list[index+1] == "/" and \ + token_list[index - 2].isdigit() and int(token_list[index-2]) < 32 and \ + token_list[index + 2].isdigit() and int(token_list[index+2]) < 1450: + formal_date = [int(token_list[index-2]),int(self.month_dict[token_list[index]]),int(token_list[index+2])] + formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0]) + return formal_date, index-2, index+2, index-2 + elif index + 2 < len(token_list) and token_list[index + 1] == 'سال' and \ + token_list[index + 2].isdigit() and int(token_list[index-2]) < 1450 : + formal_date = [0,int(self.month_dict[token_list[index]]),int(token_list[index+2])] + formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0]) + return formal_date, index, index+1, index + #else: + # print("Name : %s -> after1: %s -> after2: %s -> after3: %s" % (token_list[index], token_list[index+1], token_list[index+2], token_list[index+3])) + except: + pass + + def general_normalize_date(self,normalized_string, additional_token_index_array): + # 24 /12 /1401 جهت بررسی الگوی تاریخ + normalized_string, recognized_dates = DateNormalizer.separated_date_format_finder(normalized_string) + normalized_string_list = normalized_string.strip().split() + # جهت یافتن فرمت های مختلف تاریخ غیر از فرمت بررسی شده در بالا + normalized_string, dates, recognized_dates_2 = \ + self.normalize_dates(normalized_string_list, additional_token_index_array, + token_list_len = len(normalized_string_list), previous_slice_index_array=[0,]) + # تجمیع همه تاریخ ها + for item in recognized_dates_2: + recognized_dates.append(item) + return normalized_string, dates, recognized_dates + + # 24 /12 /1401 متد بررسی الگوی تاریخ + def separated_date_format_finder(normalized_string): + import re + date_pattern = r'\d{1,2} /\d{1,2} /\d{2,4}' + regex = re.compile(date_pattern) + match_dates = regex.finditer(normalized_string) + recognized_dates = [] + for date_item in match_dates: + position = date_item.span() + founded_item = date_item.group() + start_index = date_item.start() + 1 + end_index = date_item.end() - 1 + current_date = founded_item.replace(' ', '') + date_parts = current_date.split('/') + formal_date = "y" + str(date_parts[2]) + "m" + str(date_parts[1]) + "d" + str(date_parts[0]) + pattern_tokens_state = gf.token_state_finder(normalized_string, start_index, end_index) + recognized_dates.append( + { + "original_date" : founded_item, + "date" : formal_date, + "start_index" : start_index, + "end_index" : end_index, + "date_token_index" : pattern_tokens_state["start_token_state"], + "start_date_token_index": pattern_tokens_state["start_token_state"], + "end_date_token_index" : pattern_tokens_state["end_token_state"], + }) + normalized_string_list = normalized_string.strip().split() + for date_item in recognized_dates: + normalized_string_list[int(date_item['date_token_index'])] = date_item['date'] + normalized_string_list[int(date_item['start_date_token_index'])+1] = 'tttt' + normalized_string_list[int(date_item['end_date_token_index'])] = 'tttt' + normalized_string_temp = '' + for token in normalized_string_list: + normalized_string_temp = normalized_string_temp + ' ' + token + return normalized_string_temp.strip(), recognized_dates + + def normalize_dates(self, token_list, additional_token_index_array, token_list_len, previous_slice_index_array= []): + try: + finded = self.find_date_part(token_list) + except Exception as e: + finded = None + + filename = 'law_section_errors.txt' + exc_type, exc_value, exc_traceback = sys.exc_info() + frame = exc_traceback.tb_frame + function_array = traceback.extract_tb(exc_traceback) + current_function = function_array[len(function_array)-1] + error = f''' + filename : {current_function.filename} + function : {current_function.name} + err line no : {current_function.lineno} + err line : {current_function.line} + err message : {e} + ''' + # gf.save_error(error, filename) + + recognized_dates = [] + if finded != None: + date_part = finded[0] + start_date_token_index = finded[1] + end_date_token_index = finded[2] + date_token_index = finded[3] + befor_date_part = " ".join(x for x in token_list[:start_date_token_index]) + after_date_part = [x for x in token_list[end_date_token_index + 1:]] + previous_slice_index = token_list_len - len(after_date_part) + previous_slice_index_array.append(previous_slice_index) + after_normalized, after_dates, recognized_dates = self.normalize_dates(after_date_part, additional_token_index_array, token_list_len, previous_slice_index_array) + if after_dates == '': + after_dates = [] + if recognized_dates == '': + recognized_dates = [] + after_dates.insert(0, date_part) + previous_slice_index = previous_slice_index_array.pop(len(previous_slice_index_array)-2) + recognized_dates.append( + { + "date" : date_part, + "date_token_index" : date_token_index + previous_slice_index, + "start_date_token_index": start_date_token_index + previous_slice_index, + "end_date_token_index" : end_date_token_index + previous_slice_index + }) + + i = 0 + while((date_token_index - start_date_token_index) > i ): + befor_date_part = ''.join([befor_date_part," tttt"]) # به عنوان توکنی که از جنس تاریخ بوده t + i += 1 + i = 0 + while((end_date_token_index - date_token_index) > i ): + date_part = ''.join([date_part," tttt"]) # به عنوان توکنی که از جنس تاریخ بوده t + i += 1 + + return befor_date_part + " " + date_part + " " + after_normalized, after_dates, recognized_dates + else: + return " ".join(x for x in token_list), '','' + + def list2num(self, numerical_section_list): + value = 1 + l = len(numerical_section_list)>3 + if l : + value = 0 + for index, el in enumerate(numerical_section_list): + if self.is_number(el): + value = self.num_dict[el] + elif el == '000': + #value *= 1000 + pass + elif l == True: + value += float(el) + else: + value *= float(el) + return value + + def convert2num(self, numerical_section_list): + value = 0 + tmp_section_list = [] + for index, el in enumerate(numerical_section_list): + if self.is_number(el) or (el.replace('.', '', 1).isdigit()): + tmp_section_list.append(el) + elif el == "و": + value += self.list2num(tmp_section_list) + tmp_section_list[:] = [] + if len(tmp_section_list) > 0: + value += self.list2num(tmp_section_list) + tmp_section_list[:] = [] + try: + if (value-int(value) == 0): + return int(value) + else: + return value + except: + return 0 + + + def is_number(self, word): + return word in self.num_dict + + def find_number_location(self, token_list, addWithVaa = False): + start_index = 0 + number_section =[] + for i , el in enumerate(token_list): + if self.is_number(el) or (el.replace('.', '', 1).isdigit()): + start_index = i + number_section.append(start_index) + break + + i = start_index+1 + while(i < len(token_list)): + if token_list[i] == "و" and (i+1)= 0: + # تشخیص اینکه یک سلسله از اعداد که با واو از هم جدا شده اند تبصره های مربوط به یک ماده نباشند + is_tabsare_numbers = (token_list[token_index-2] + ' ' + token_list[token_index-1]=='تبصره های') + if token_index - 1 >= 0: + # تشخیص اینکه یک سلسله از اعداد که با واو از هم جدا شده اند مواد یک قانون نباشند + is_mavad_numbers = (token_list[token_index-1]=='مواد') + if token_index - 1 >= 0: + # تشخیص اینکه یک سلسله از اعداد که با واو از هم جدا شده اند ماده های یک قانون نباشند + is_madde_numbers = (token_list[token_index-1]=='ماده') + if is_tabsare_numbers or is_mavad_numbers or is_madde_numbers: + flag = False + start_token_index = end_token_index = token_index + number_parts_array.clear() + current_token_index = token_index + number_parts_array.append(token) + if current_token_index + 1 < len(token_list): + while ((flag) and (token_list[current_token_index+1] == 'و' or token_list[current_token_index+1].isdigit())): + number_parts_array.append(token_list[current_token_index+1]) + current_token_index += 1 + end_token_index = current_token_index + if not (current_token_index + 1 < len(token_list)): + break + + final_number = number_completter(number_parts_array) + token_list[token_index] = str(final_number) + recognized_numbers.append({ + 'number_value' : final_number, + 'number_token_list': number_parts_array, + 'start_token_index': start_token_index, + 'end_token_index' : end_token_index + }) + # جایگذاری مقدار رشته بی ارزش به جای توکن های مربوط به عدد جاری + # به منظور اینکه متن اصلی از نظر تعداد توکن تغییر نکند + for i in range(token_index + 1 , len(number_parts_array) + token_index): + token_list[i] = 'nnnn' + converted_string = converted_string + ' ' + token_list[token_index] + if token_index + 1 < len(token_list): + if flag == False and (token_list[token_index+1]=='و'): + flag = False + else: + flag = True + else: + converted_string = converted_string + ' ' + token + + return converted_string, recognized_numbers + + + + + def remove_additional_tokens(self,token_list,additional_token_index_array): + converted_string = '' + for index in additional_token_index_array: + del token_list[index] + + for token in token_list: + converted_string = converted_string + ' ' + token + return converted_string + +class PinglishNormalizer(): + def __init__(self): + self.data_helper = DataHelper() + self.file_dir = os.path.dirname(os.path.realpath(__file__)) + "/" + + self.en_dict_filename = self.file_dir + "resource/tokenizer/enDict" + self.en_dict = self.data_helper.load_var(self.en_dict_filename) + + self.fa_dict_filename = self.file_dir + "resource/tokenizer/faDict" + self.fa_dict = self.data_helper.load_var(self.fa_dict_filename) + + + def pingilish2persian(self, pinglish_words_list): + + for i, word in enumerate(pinglish_words_list): + if word in self.en_dict: + pinglish_words_list[i] = self.en_dict[word]#.decode("utf-8") + #inp = inp.replace(word, enDict[word], 1) + else: + ch = self.characterize(word) + pr = self.map_char(ch) + amir = self.make_word(pr) + for wd in amir: + am = self.escalation(wd) + asd = ''.join(am) + if asd in self.fa_dict: + pinglish_words_list[i] = asd#.decode("utf-8") + #inp = inp.replace(word, asd, 1) + inp = " ".join(x for x in pinglish_words_list) + return inp + + def characterize(self, word): + list_of_char = [] + i = 0 + while i < len(word): + char = word[i] + sw_out = self.switcher(char) + if (sw_out == None): + esp_out = None + if(i < len(word) - 1): + esp_out = self.esp_check(word[i], word[i + 1]) + if(esp_out == None): + list_of_char.append(word[i]) + else: + list_of_char.append(esp_out) + i += 1 + else: + list_of_char.append(sw_out) + i += 1 + return list_of_char + + def switcher(self, ch): + switcher = { + "c": None, + "k": None, + "z": None, + "s": None, + "g": None, + "a": None, + "u": None, + "e": None, + "o": None + } + return switcher.get(ch, ch) + + def esp_check(self, char1, char2): + st = char1 + char2 + if (st == "ch"): + return "ch" + elif (st == "kh"): + return "kh" + elif (st == "zh"): + return "zh" + elif (st == "sh"): + return "sh" + elif (st == "gh"): + return "gh" + elif (st == "aa"): + return "aa" + elif (st == "ee"): + return "ee" + elif (st == "oo"): + return "oo" + elif (st == "ou"): + return "ou" + else: + return None + + def map_char(self, word): + listm = [] + sw_out = self.map_switcher(word[0]) + i = 0 + if (sw_out == None): + listm.append(["ا"]) + i += 1 + if (word[0] == "oo"): + listm.append(["او"]) + i += 1 + while i < len(word): + listm.append(self.char_switcher(word[i])) + i += 1 + if word[len(word) - 1] == "e": + listm.append(["ه"]) + elif word[len(word) - 1] == "a": + listm.append(["ا"]) + elif word[len(word) - 1] == "o": + listm.append(["و"]) + elif word[len(word) - 1] == "u": + listm.append(["و"]) + + return listm + + def map_switcher(self, ch): + switcher = { + "a": None, + "e": None, + "o": None, + "u": None, + "ee": None, + + "ou": None + } + return switcher.get(ch, ch) + + def make_word(self, chp): + word_list = [[]] + for char in chp: + word_list_temp = [] + for tmp_word_list in word_list: + for chch in char: + tmp = copy.deepcopy(tmp_word_list) + tmp.append(chch) + word_list_temp.append(tmp) + word_list = word_list_temp + return word_list + + def escalation(self, word): + tmp = [] + i = 0 + t = len(word) + while i < t - 1: + tmp.append(word[i]) + if word[i] == word[i + 1]: + i += 1 + i += 1 + if i != t: + tmp.append(word[i]) + return tmp + + def char_switcher(self, ch): + switcher = { + 'a': ["", "ا"], + 'c': ["ث", "ص", "ص"], + 'h': ["ه", "ح"], + 'b': ["ب"], + 'p': ["پ"], + 't': ["ت", "ط"], + 's': ["س", "ص", "ث"], + 'j': ["ج"], + 'ch': ["چ"], + 'kh': ["خ"], + 'q': ["ق", "غ"], + 'd': ["د"], + 'z': ["ز", "ذ", "ض", "ظ"], + 'r': ["ر"], + 'zh': ["ژ"], + 'sh': ["ش"], + 'gh': [",ق", "غ"], + 'f': ["ف"], + 'k': ["ک"], + 'g': ["گ"], + 'l': ["ل"], + 'm': ["م"], + 'n': ["ن"], + 'v': ["و"], + 'aa': ["ا"], + 'ee': ["ی"], + 'oo': ["و"], + 'ou': ["و"], + 'i': ["ی"], + 'y': ["ی"], + ' ': [""], + 'w': ["و"], + 'e': ["", "ه"], + 'o': ["", "و"] + } + return switcher.get(ch, "") + +# این روال جهت جمع و ضرب کردن بخش های مختلف از صدگان و هزارگان و بالاتر از یک عدد که با حروف در متن وجود دارد کار می کند +def number_completter2(number_parts_array): + zarb_value = 0 + temp_number_array = [] + sum_number_parts = 0 + final_number = 0 + for index,item in enumerate(number_parts_array): + if item.isdigit(): + temp_number_array.append(int(item)) + if index + 1 >= len(number_parts_array): + for item3 in temp_number_array: + final_number += int(item3) + return final_number + current_value = number_parts_array[index+1] + if (current_value.isdigit() and (int(current_value) == 1000 or int(current_value) == 1000000 or int(current_value) == 1000000000)):# or int(current_value) == 1000000 + zarb_value = int(number_parts_array[index+1]) + for num_item in temp_number_array: + sum_number_parts += num_item + final_number = sum_number_parts * zarb_value + temp_array2 = [] + x = index + 2 + while x < len(number_parts_array): + # for x in range(index+2,len(number_parts_array)): + temp_array2.append(number_parts_array[x]) + if x + 1 < len(number_parts_array): + if number_parts_array[x+1].isdigit() and (int(number_parts_array[x+1]) == 1000 or int(number_parts_array[x+1]) == 1000000 or int(number_parts_array[x+1]) == 1000000000): + if int(number_parts_array[x+1]) > zarb_value: # 1000000>1000 + zarb_value = int(number_parts_array[x+1]) + # تابع بازگشتی برای محاسبه عدد نهایی + final_number += number_completter2(temp_array2) + final_number *= zarb_value + temp_array2.clear() + zarb_value = 0 + x += 2 # به این دلیل که مقدار ضرب را در آرایه تمپ ذخیره نکند + else: # 1000!>1000000 + zarb_value = int(number_parts_array[x+1]) + temp_num = 0 + if (zarb_value == 1000 or zarb_value == 1000000) and len(number_parts_array) > x + 1: + num_array = number_parts_array[x+2:len(number_parts_array)] + temp_num = number_completter(num_array) + if temp_num == None: + temp_num = 0 + + '''for i in range(x+2,len(number_parts_array)): + if(number_parts_array[i].isdigit()): + temp_num += int(number_parts_array[i]) + i+=1''' + temp_num2 = 1 + for num_item2 in temp_array2: + if(num_item2.isdigit()): + temp_num2 += int(num_item2) + + temp_num2 *= zarb_value + final_number += temp_num + temp_num2 + break + else: + x += 1 # + else: # اگر از رنج آرایه خارج شدیم + temp_num = 0 + # مقادیر موجود در آرایه تمپ را جمع کن + for num_item3 in temp_array2: + if(num_item3.isdigit()): + temp_num += int(num_item3) + # حاصل جمع اعداد موجود در آرایه ذخیره را در عدد نهایی که قبلا داشته ایم ضرب کن و از حلقه خارج شو + final_number *= temp_num + #final_number += temp_num + break + return final_number + +# این روال جهت جمع و ضرب کردن بخش های مختلف از صدگان و هزارگان و بالاتر از یک عدد که با حروف در متن وجود دارد کار می کند +def number_completter(number_parts_array): + zarb_value = 0 + previous_zarb_value = 0 + previous_number_parts_sum = 0 + temp_number_array = [] + number_parts_sum = 0 + final_number = 0 + current_number_part = 0 + for index,item in enumerate(number_parts_array): + if item.isdigit(): + if (not(int(item) == 1000 or int(item) == 1000000 or int(item) == 1000000000)): + temp_number_array.append(item) + continue + elif((int(item) == 1000 or int(item) == 1000000 or int(item) == 1000000000)): + zarb_value = int(item) + for num_item in temp_number_array: + number_parts_sum += int(num_item) + if number_parts_sum == 0 and previous_number_parts_sum == 0: + number_parts_sum = 1 + temp_number_array.clear() + + else:# for example 952 + zarb_value = 1 + for num_item in temp_number_array: + number_parts_sum += int(num_item) + current_number_part = number_parts_sum + previous_number_parts_sum + continue + if previous_zarb_value < zarb_value:# for example 1000 < 1000000000 + current_number_part = previous_number_parts_sum + number_parts_sum + current_number_part = zarb_value * current_number_part + else:# previous_zarb_value > zarb_value + if number_parts_sum == 0: + number_parts_sum = 1 + current_number_part = zarb_value * number_parts_sum + current_number_part += previous_number_parts_sum + + previous_number_parts_sum = current_number_part + current_number_part = 0 + previous_zarb_value = zarb_value + number_parts_sum = 0 + if len(temp_number_array) != 0: + remained_parts_sum = 0 + for num_item in temp_number_array: + remained_parts_sum += int(num_item) + final_number = previous_number_parts_sum + remained_parts_sum + return final_number + + final_number = previous_number_parts_sum + return final_number + + + + + + \ No newline at end of file diff --git a/resource/normalizer/Dic1_new.txt b/resource/normalizer/Dic1_new.txt new file mode 100644 index 0000000..9d063ed --- /dev/null +++ b/resource/normalizer/Dic1_new.txt @@ -0,0 +1,74 @@ +بیخبر بی‌خبر +بیتوجهی بی‌توجهی +بیطرفانه بی‌طرفانه +گفتوگو گفت‌وگو +آنها آن‌ها +پیشبرد پیش‌برد +روانشناختی روان‌شناختی +میباشد می‌باشد +لذتبخش لذت‌بخش +میدادند می‌دادند +مینویسد می‌نویسد +میبخشد می‌بخشد +بیقاعده بی‌قاعده +میباشند می‌باشند +موافقتنامه موافقت‌نامه +تخمگذار تخم‌گذار +پایینترین پایین‌ترین +گرمکن گرم‌کن +پیشبینی پیش‌بینی +برونگرا برون‌گرا +میدهد می‌دهد +فیلمبرداری فیلم‌برداری +آنسوی آن‌سوی +خدمتدهی خدمت‌دهی +اینگونه این‌گونه +کمکرسانی کمک‌رسانی +کلانشهر کلان‌شهر +سپردهگذار سپرده‌گذار +بنیانگذار بنیان‌گذار +رضایتبخش رضایت‌بخش +اصلاحطلبان اصلاح‌طلبان +استخوانبندی استخوان‌بندی +درونگرا درون‌گرا +میگردد می‌گردد +اصلاحطلب اصلاح‌طلب +میتوان می‌توان +عملکرد عمل‌کرد +میروم می‌روم +بزرگنمایی بزرگ‌نمایی +همجنس هم‌جنس +همانطور همان‌طور +بیشترین بیش‌ترین +انسانگرایی انسان‌گرایی +نمیباشند نمی‌باشند +جانبداری جانب‌داری +نمیتوانی نمی‌توانی +قانونگذار قانون‌گذار +میشدند می‌شدند +تفاهمنامه تفاهم‌نامه +آسیبپذیر آسیب‌پذیر +برونگرایی برون‌گرایی +جفتگیری جفت‌گیری +گرانبها گران‌بها +میشوند می‌شوند +کلاهبرداری کلاه‌برداری +جهتیابی جهت‌یابی +چشمپوشی چشم‌پوشی +بنیانگذاران بنیان‌گذاران +میکند می‌کند +الهامبخش الهام‌بخش +وقتگیر وقت‌گیر +پسلرزه پس‌لرزه +میکنند می‌کنند +میتواند می‌تواند +آرامبخش آرام‌بخش +بینام بی‌نام +غربزدگی غرب‌زدگی +بیتفاوت بی‌تفاوت +بیثباتی بی‌‌ثباتی +پاسخگویی پاسخ‌گویی +میگیرد می‌گیرد +جمعبندی جمع‌بندی +میشود می‌شود +میکنیم می‌کنیم \ No newline at end of file diff --git a/resource/normalizer/Dic2_new.txt b/resource/normalizer/Dic2_new.txt new file mode 100644 index 0000000..d2f1a5f --- /dev/null +++ b/resource/normalizer/Dic2_new.txt @@ -0,0 +1,112 @@ +مهماننوازی مهمان‌نوازی +صلیالله صلی‌الله +موافقتنامه موافقت‌نامه +اعتراضآمیز اعتراض‌آمیز +رییسجمهور رییس‌جمهور +چشمپوشی چشم‌پوشی +هیئتعلمی هیئت‌علمی‌ +الزامآور الزام‌آور +بیمهنامه بیمه‌نامه +آییننامه آیین‌نامه +بتنریزی بتن‌ریزی +تشییعجنازه تشییع‌جنازه +تامینکنندگان تامین‌کنندگان +پرسشنامه پرسش‌نامه +تحتالشعاع تحت‌الشعاع +شگفتانگیز شگفت‌انگیز +بزرگنمایی بزرگ‌نمایی +نیمههادی نیمه‌هادی +قابلکنترل قابل‌کنترل +روانپزشکی روان‌پزشکی +ضربالمثل ضرب‌المثل +اضافهکاری اضافه‌کاری +اختلافنظر اختلاف‌نظر +بینالملل بین‌الملل +یکطرفه یک‌طرفه +موجشکن موج‌شکن +عزتنفس عزت‌نفس +بیسیم بی‌سیم +شیبدار شیب‌دار +دستیابی دست‌یابی +روانشناختی روان‌شناختی +عقبنشینی عقب‌نشینی +بهطور به‌طور +خطچین خط‌چین +ادراکشده ادراک‌شده +خزانهداری خزانه‌داری +شیمیدرمانی شیمی‌درمانی +آنسوی ‌آن‌سوی +نقطهچین نقطه‌چین +منحصربهفرد منحصربه‌فرد +درحالتوسعه درحال‌توسعه +رضایتبخش رضایت‌بخش +قرضالحسنه قرض‌الحسنه +هرجومرج هرج‌ومرج +سیبزمینی سیب‌زمینی +میلیگرم میلی‌گرم +نخستوزیر نخست‌وزیر +تعیینکنندهای تعیین‌کننده‌ای +طاقتفرسا طاقت‌فرسا +قابلمشاهده قابل‌مشاهده +بهوسیله به‌وسیله +قابلدستیابی قابل‌دستیابی +الهامبخش الهام‌بخش +پیدرپی پی‌درپی +سرمایهداری سرمایه‌داری +لذتبخش لذت‌بخش +تخمگذار تخم‌گذار +گرمکن گرم‌کن +قابلتوجهی قابل‌توجهی +فیلمبرداری فیلم‌برداری +خدمتدهی خدمت‌دهی +معنیدار معنی‌دار +کلانشهری کلان‌شهری +گواهینامه گواهی‌نامه +همجنس هم‌جنس +همانطور همان‌طور +سیستمعامل سیستم‌عامل +حملونقل حمل‌ونقل +تفاهمنامه تفاهم‌نامه +بینالمللی بین‌المللی +کلاهبرداری کلاه‌برداری +نرمافزار نرم‌افزار +مضافالیه مضاف‌الیه +قطعنامهای قطعنامه‌ای +پاسخگویی پاسخ‌گویی +عکسبرداری عکس‌برداری +پسلرزه پس‌لرزه +خردهفروشی خرده‌فروشی +حقوقبشر حقوق‌بشر +تحلیلگران تحلیل‌گران +اینگونه این‌گونه +صرفهجویی صرفه‌جویی +علیالخصوص علی‌الخصوص +کلانشهرها کلان‌شهرها +حاصلضرب حاصل‌ضرب +اطلاعرسانی اطلاع‌رسانی +دندانپزشکی دندان‌پزشکی +پیشبرد پیش‌برد +ایدهال ایده‌ال +هیچگاه هیچ‌گاه +صنایعدستی صنایع‌دستی +سانتیمتر سانتی‌متر +پیشبینی پیش‌بینی +خلیجفارس خلیج‌فارس +تاریخنگاری تاریخ‌نگاری +هیچگونه هیچ‌گونه +راهاندازی راه‌اندازی +جستوجوی جست‌وجوی +حاشیهنشینی حاشیه‌نشینی +رنگآمیزی رنگ‌آمیزی +جمعآوری جمع‌‌آوری +وقتگیر وقت‌گیر +آرامبخش آرام‌بخش +غربزدگی غرب‌زدگی +کلانشهر کلان‌شهر +نرمافزاری نرم‌افزاری +بدینوسیله بدین‌وسیله +جمعبندی جمع‌بندی +گفتوگو گفت‌وگو +حملونقل حمل‌ونقل +آیتالله آیت‌الله +حجتالاسلام حجت‌الاسلام \ No newline at end of file diff --git a/resource/normalizer/Dic3_new.txt b/resource/normalizer/Dic3_new.txt new file mode 100644 index 0000000..a6ec074 --- /dev/null +++ b/resource/normalizer/Dic3_new.txt @@ -0,0 +1,5 @@ +حملونقل حمل‌ونقل +حجتالاسلاموالمسلمین حجت‌الاسلام‌والمسلمین +آیتاللهالعظمی آیت‌الله‌العظمی +گفتوگو گفت‌وگو +حملونقل حمل‌ونقل \ No newline at end of file diff --git a/resource/tokenizer/TokenMerger.pckl b/resource/tokenizer/TokenMerger.pckl new file mode 100644 index 0000000..9d83882 Binary files /dev/null and b/resource/tokenizer/TokenMerger.pckl differ diff --git a/resource/tokenizer/enDict b/resource/tokenizer/enDict new file mode 100644 index 0000000..83dda06 Binary files /dev/null and b/resource/tokenizer/enDict differ diff --git a/resource/tokenizer/faDict b/resource/tokenizer/faDict new file mode 100644 index 0000000..6fcee3f Binary files /dev/null and b/resource/tokenizer/faDict differ diff --git a/tokenizer.py b/tokenizer.py new file mode 100644 index 0000000..73d0e06 --- /dev/null +++ b/tokenizer.py @@ -0,0 +1,59 @@ +import re + + +class Tokenizer(): + def __init__(self): + pass + + def tokenize_words(self, doc_string): + token_list = doc_string.strip().split() + #token_list = [x.strip("\u200c").strip('\u200f') for x in token_list if len(x.strip("\u200c")) != 0] + new_token_list = [] + new_token = '' + for index,token in enumerate(token_list): + if len(token.strip("\u200c")) != 0: + new_token = token.strip("\u200c") + if len(token.strip("\u200f")) != 0: + new_token = new_token.strip('\u200f') + new_token_list.append(new_token) + return new_token_list + + def tokenize_sentences(self, doc_string): + #finding the numbers + pattern = r"[-+]?\d*\.\d+|\d+" + nums_list = re.findall(pattern, doc_string) + doc_string = re.sub(pattern, 'floatingpointnumber', doc_string) + + pattern = r'([!\.\?؟]+)[\n]*' + tmp = re.findall(pattern, doc_string) + doc_string = re.sub(pattern, self.add_tab, doc_string) + + pattern = r':\n' + tmp = re.findall(pattern, doc_string) + doc_string = re.sub(pattern, self.add_tab, doc_string) + + pattern = r';\n' + tmp = re.findall(pattern, doc_string) + doc_string = re.sub(pattern, self.add_tab, doc_string) + + pattern = r'؛\n' + tmp = re.findall(pattern, doc_string) + doc_string = re.sub(pattern, self.add_tab, doc_string) + + pattern = r'[\n]+' + doc_string = re.sub(pattern, self.add_tab, doc_string) + + for number in nums_list: + pattern = 'floatingpointnumber' + doc_string = re.sub(pattern, number, doc_string, 1) + + doc_string = doc_string.split('\t\t') + doc_string = [x for x in doc_string if len(x) > 0] + return doc_string + + def add_tab(self, mystring): + mystring = mystring.group() # this method return the string matched by re + mystring = mystring.strip(' ') # ommiting the whitespace around the pucntuation + mystring = mystring.strip('\n') # ommiting the newline around the pucntuation + mystring = " " + mystring + "\t\t" # adding a space after and before punctuation + return mystring \ No newline at end of file