|
|
|
|
|
import pandas as pd
|
|
from pathlib import Path
|
|
import logging
|
|
import uuid
|
|
from datetime import datetime
|
|
import networkx as nx
|
|
import pickle
|
|
import string
|
|
|
|
|
|
DATA_PATH = Path("data/processed_data")
|
|
|
|
NETWORK_PATH = Path("output/networks")
|
|
|
|
|
|
FREQUENCY_FILENAME = "analysis_concept_frequencies"
|
|
SIMILARITY_FILENAME = "analysis_concept_similarities"
|
|
NETWORK_ANALYSIS_FILENAME = "analysis_network_results"
|
|
GRAPH_FILENAME = "concept_network"
|
|
EMBEDDINGS_FILENAME = "concept_embeddings"
|
|
|
|
|
|
|
|
DOC_COLUMNS = ['doc_id', 'filepath', 'publication_date', 'status', 'processed_text_path']
|
|
CONCEPT_COLUMNS = ['concept_id', 'name', 'aliases']
|
|
MENTION_COLUMNS = ['mention_id', 'doc_id', 'concept_id', 'context_snippet', 'start_char', 'end_char']
|
|
RELATIONSHIP_COLUMNS = ['relationship_id', 'source_concept_id', 'target_concept_id', 'type', 'mention_id', 'doc_id', 'sentence']
|
|
NETWORK_ANALYSIS_COLUMNS = ['concept_id', 'name', 'degree_centrality', 'betweenness_centrality', 'eigenvector_centrality', 'community_id']
|
|
|
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
|
|
|
def load_dataframe(filename: str, columns: list) -> pd.DataFrame:
|
|
filepath = DATA_PATH / f"{filename}.parquet"
|
|
if filepath.exists():
|
|
try:
|
|
df = pd.read_parquet(filepath)
|
|
logging.info(f"'{filepath}' başarıyla yüklendi.")
|
|
if columns:
|
|
for col in columns:
|
|
if col not in df.columns:
|
|
logging.warning(f"'{filepath}' dosyasında '{col}' sütunu eksik. Ekleniyor...")
|
|
df[col] = None
|
|
return df
|
|
except Exception as e:
|
|
logging.error(f"'{filepath}' yüklenirken hata oluştu: {e}")
|
|
return pd.DataFrame(columns=columns if columns else None)
|
|
else:
|
|
logging.info(f"'{filepath}' bulunamadı. Boş DataFrame oluşturuluyor.")
|
|
return pd.DataFrame(columns=columns if columns else None)
|
|
|
|
def save_dataframe(df: pd.DataFrame, filename: str):
|
|
DATA_PATH.mkdir(parents=True, exist_ok=True)
|
|
filepath = DATA_PATH / f"{filename}.parquet"
|
|
try:
|
|
for col in df.select_dtypes(include=['object']).columns:
|
|
if df[col].map(type).isin([list, dict, datetime, pd.Timestamp]).any(): continue
|
|
df[col] = df[col].where(pd.notnull(df[col]), None)
|
|
try: df[col] = df[col].astype(pd.StringDtype())
|
|
except TypeError: logging.debug(f"Sütun '{col}' StringDtype'a çevrilemedi, orijinal tip korunuyor.")
|
|
df.to_parquet(filepath, index=False)
|
|
logging.info(f"DataFrame başarıyla '{filepath}' olarak kaydedildi.")
|
|
except Exception as e:
|
|
logging.error(f"DataFrame '{filepath}' olarak kaydedilirken hata oluştu: {e}")
|
|
|
|
|
|
def add_document(filepath_str: str, publication_date) -> str | None:
|
|
documents_df = load_dataframe('documents', DOC_COLUMNS)
|
|
filepath_str = str(Path(filepath_str).resolve())
|
|
existing_doc = documents_df[documents_df['filepath'] == filepath_str]
|
|
if not existing_doc.empty:
|
|
existing_doc_id = existing_doc['doc_id'].iloc[0]
|
|
logging.warning(f"Doküman zaten kayıtlı: {filepath_str} (ID: {existing_doc_id})")
|
|
return str(existing_doc_id)
|
|
new_doc_id = str(uuid.uuid4())
|
|
try: pub_date_obj = pd.to_datetime(publication_date).date()
|
|
except ValueError: logging.error(f"Geçersiz tarih formatı: {publication_date}. None olarak kaydedilecek."); pub_date_obj = None
|
|
new_document_data = {'doc_id': new_doc_id, 'filepath': filepath_str, 'publication_date': pub_date_obj, 'status': 'added', 'processed_text_path': None}
|
|
new_row_df = pd.DataFrame([new_document_data])
|
|
if pub_date_obj is not None: new_row_df['publication_date'] = pd.to_datetime(new_row_df['publication_date']); dtype_dict = {'publication_date': 'datetime64[s]'}
|
|
else: dtype_dict = {}
|
|
documents_df = pd.concat([documents_df, new_row_df], ignore_index=True)
|
|
for col, dtype in dtype_dict.items():
|
|
try: documents_df[col] = documents_df[col].astype(dtype)
|
|
except TypeError: logging.warning(f"Sütun '{col}' tipi '{dtype}' olarak ayarlanamadı.")
|
|
save_dataframe(documents_df, 'documents')
|
|
logging.info(f"Yeni doküman eklendi: {filepath_str} (ID: {new_doc_id})")
|
|
return new_doc_id
|
|
|
|
def update_document_status(doc_id: str, new_status: str, text_path: str | None = None):
|
|
docs_df = load_dataframe('documents', DOC_COLUMNS)
|
|
doc_index = docs_df[docs_df['doc_id'] == doc_id].index
|
|
if not doc_index.empty:
|
|
idx = doc_index[0]
|
|
docs_df.loc[idx, 'status'] = new_status
|
|
if text_path: docs_df.loc[idx, 'processed_text_path'] = text_path
|
|
save_dataframe(docs_df, 'documents')
|
|
logging.info(f"Doküman durumu güncellendi: ID {doc_id} -> {new_status}")
|
|
else: logging.warning(f"Durumu güncellenecek doküman bulunamadı: ID {doc_id}")
|
|
|
|
|
|
def add_concept(raw_name: str) -> str | None:
|
|
concepts_df = load_dataframe('concepts', CONCEPT_COLUMNS)
|
|
name = raw_name.lower().strip().strip(string.punctuation + string.whitespace)
|
|
if name.endswith("'s"): name = name[:-2].strip()
|
|
name = ' '.join(name.split())
|
|
if not name or len(name) < 2: return None
|
|
existing_concept = concepts_df[concepts_df['name'] == name]
|
|
if not existing_concept.empty: return str(existing_concept['concept_id'].iloc[0])
|
|
new_concept_id = str(uuid.uuid4()); new_concept_data = {'concept_id': new_concept_id, 'name': name, 'aliases': [raw_name]}
|
|
new_row_df = pd.DataFrame([new_concept_data]); concepts_df = pd.concat([concepts_df, new_row_df], ignore_index=True)
|
|
concepts_df['aliases'] = concepts_df['aliases'].astype('object')
|
|
save_dataframe(concepts_df, 'concepts')
|
|
logging.info(f"Yeni konsept eklendi: '{name}' (Orijinal: '{raw_name}', ID: {new_concept_id})")
|
|
return new_concept_id
|
|
|
|
def add_mention(doc_id: str, concept_id: str, context: str, start: int, end: int) -> str | None:
|
|
if concept_id is None: return None
|
|
mentions_df = load_dataframe('mentions', MENTION_COLUMNS); new_mention_id = str(uuid.uuid4())
|
|
new_mention_data = {'mention_id': new_mention_id, 'doc_id': doc_id, 'concept_id': concept_id, 'context_snippet': context[:500], 'start_char': start, 'end_char': end}
|
|
new_row_df = pd.DataFrame([new_mention_data]); mentions_df = pd.concat([mentions_df, new_row_df], ignore_index=True)
|
|
save_dataframe(mentions_df, 'mentions'); return new_mention_id
|
|
|
|
def add_relationship(source_concept_id: str, target_concept_id: str, rel_type: str, mention_id: str | None, doc_id: str, sentence: str) -> str | None:
|
|
if source_concept_id is None or target_concept_id is None: return None
|
|
relationships_df = load_dataframe('relationships', RELATIONSHIP_COLUMNS); new_relationship_id = str(uuid.uuid4())
|
|
new_relationship_data = {'relationship_id': new_relationship_id, 'source_concept_id': source_concept_id, 'target_concept_id': target_concept_id, 'type': rel_type, 'mention_id': mention_id, 'doc_id': doc_id, 'sentence': sentence[:500]}
|
|
new_row_df = pd.DataFrame([new_relationship_data]); relationships_df = pd.concat([relationships_df, new_row_df], ignore_index=True)
|
|
save_dataframe(relationships_df, 'relationships'); return new_relationship_id
|
|
|
|
|
|
def save_network(graph: nx.Graph, filename: str):
|
|
NETWORK_PATH.mkdir(parents=True, exist_ok=True); filepath = NETWORK_PATH / f"{filename}.pkl"
|
|
try:
|
|
with open(filepath, 'wb') as f: pickle.dump(graph, f)
|
|
logging.info(f"NetworkX grafı başarıyla '{filepath}' olarak kaydedildi.")
|
|
except Exception as e: logging.error(f"Graf '{filepath}' olarak kaydedilirken hata: {e}")
|
|
|
|
def load_network(filename: str) -> nx.Graph | None:
|
|
filepath = NETWORK_PATH / f"{filename}.pkl"
|
|
if filepath.exists():
|
|
try:
|
|
with open(filepath, 'rb') as f: graph = pickle.load(f)
|
|
logging.info(f"NetworkX grafı '{filepath}' başarıyla yüklendi.")
|
|
return graph
|
|
except Exception as e: logging.error(f"Graf '{filepath}' yüklenirken hata: {e}"); return nx.Graph()
|
|
else: logging.warning(f"Graf dosyası bulunamadı: '{filepath}'"); return nx.Graph() |