BibexPy 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bibex_core/MergeDB.py +1189 -0
- bibex_core/__init__.py +13 -0
- bibex_core/modules/__init__.py +35 -0
- bibex_core/modules/api_utils.py +1172 -0
- bibex_core/modules/c1_utils.py +423 -0
- bibex_core/modules/file_utils.py +71 -0
- bibex_core/modules/ml_utils.py +332 -0
- bibex_core/modules/post_process.py +50 -0
- bibex_core/modules/stats_utils.py +216 -0
- bibex_core/scp2xlsx.py +235 -0
- bibex_core/wos2xlsx.py +159 -0
- bibex_core/xlsx2vos.py +185 -0
- bibexpy/__init__.py +9 -0
- bibexpy/__main__.py +6 -0
- bibexpy/_server/__init__.py +1 -0
- bibexpy/_server/config.py +92 -0
- bibexpy/_server/jobs/__init__.py +3 -0
- bibexpy/_server/jobs/runner.py +301 -0
- bibexpy/_server/main.py +211 -0
- bibexpy/_server/models/__init__.py +0 -0
- bibexpy/_server/models/project.py +25 -0
- bibexpy/_server/routers/__init__.py +0 -0
- bibexpy/_server/routers/audit.py +65 -0
- bibexpy/_server/routers/convert.py +101 -0
- bibexpy/_server/routers/disambiguate.py +177 -0
- bibexpy/_server/routers/downloads.py +26 -0
- bibexpy/_server/routers/enrich.py +42 -0
- bibexpy/_server/routers/export.py +116 -0
- bibexpy/_server/routers/export_folder.py +97 -0
- bibexpy/_server/routers/filter.py +131 -0
- bibexpy/_server/routers/jobs.py +72 -0
- bibexpy/_server/routers/merge.py +437 -0
- bibexpy/_server/routers/prepare.py +241 -0
- bibexpy/_server/routers/projects.py +49 -0
- bibexpy/_server/routers/quality.py +288 -0
- bibexpy/_server/routers/records.py +204 -0
- bibexpy/_server/routers/report.py +111 -0
- bibexpy/_server/routers/settings.py +383 -0
- bibexpy/_server/routers/system.py +209 -0
- bibexpy/_server/routers/tools.py +257 -0
- bibexpy/_server/routers/upload.py +110 -0
- bibexpy/_server/services/__init__.py +0 -0
- bibexpy/_server/services/analyses.py +364 -0
- bibexpy/_server/services/audit.py +340 -0
- bibexpy/_server/services/bibex_adapter.py +47 -0
- bibexpy/_server/services/bibtex_writer.py +104 -0
- bibexpy/_server/services/converter.py +205 -0
- bibexpy/_server/services/disambiguation/__init__.py +19 -0
- bibexpy/_server/services/disambiguation/blocking.py +581 -0
- bibexpy/_server/services/disambiguation/cache.py +47 -0
- bibexpy/_server/services/disambiguation/deepseek_client.py +203 -0
- bibexpy/_server/services/disambiguation/orcid.py +261 -0
- bibexpy/_server/services/disambiguation/pipeline.py +985 -0
- bibexpy/_server/services/disambiguation/similarity.py +91 -0
- bibexpy/_server/services/enricher.py +338 -0
- bibexpy/_server/services/exporter.py +112 -0
- bibexpy/_server/services/filter_engine.py +300 -0
- bibexpy/_server/services/merger.py +202 -0
- bibexpy/_server/services/methodology.py +102 -0
- bibexpy/_server/services/report_export.py +300 -0
- bibexpy/_server/services/ris_writer.py +62 -0
- bibexpy/_server/services/smart_merger.py +967 -0
- bibexpy/_server/services/storage.py +165 -0
- bibexpy/_web/404/index.html +1 -0
- bibexpy/_web/404.html +1 -0
- bibexpy/_web/_next/static/chunks/117-7ad5186126fa076d.js +2 -0
- bibexpy/_web/_next/static/chunks/156-75beffb8ae92a7f3.js +1 -0
- bibexpy/_web/_next/static/chunks/169-301c04fcec22686f.js +4 -0
- bibexpy/_web/_next/static/chunks/2-ecc1c36a5632ac61.js +1 -0
- bibexpy/_web/_next/static/chunks/244-be343e0f8aa00fb5.js +1 -0
- bibexpy/_web/_next/static/chunks/367-b7457c2b95f6932b.js +1 -0
- bibexpy/_web/_next/static/chunks/382-9cd163eed632e241.js +1 -0
- bibexpy/_web/_next/static/chunks/392-afa8bfa4c1a9893d.js +1 -0
- bibexpy/_web/_next/static/chunks/507-63607ded9f83c1aa.js +1 -0
- bibexpy/_web/_next/static/chunks/624-cae565632f47217d.js +1 -0
- bibexpy/_web/_next/static/chunks/739-a00c6b9c0f11813e.js +1 -0
- bibexpy/_web/_next/static/chunks/862-8e1bd5f93c082c02.js +1 -0
- bibexpy/_web/_next/static/chunks/app/_not-found/page-873ba59b5eb7c342.js +1 -0
- bibexpy/_web/_next/static/chunks/app/layout-0d5fc68ac8971a04.js +1 -0
- bibexpy/_web/_next/static/chunks/app/page-cf2bea5edc74459a.js +1 -0
- bibexpy/_web/_next/static/chunks/app/projects/[id]/convert/page-ab6dd645ae113b64.js +1 -0
- bibexpy/_web/_next/static/chunks/app/projects/[id]/disambiguate/page-dd98482cc47344e9.js +1 -0
- bibexpy/_web/_next/static/chunks/app/projects/[id]/enrich/page-4727e431468514e9.js +1 -0
- bibexpy/_web/_next/static/chunks/app/projects/[id]/export/page-f9005d164bb22cb0.js +1 -0
- bibexpy/_web/_next/static/chunks/app/projects/[id]/layout-07755fda55b3ae1f.js +1 -0
- bibexpy/_web/_next/static/chunks/app/projects/[id]/merge/page-45b95318ea7beccf.js +1 -0
- bibexpy/_web/_next/static/chunks/app/projects/[id]/records/page-bfad80457da9eb0b.js +1 -0
- bibexpy/_web/_next/static/chunks/app/projects/[id]/report/page-f05767e74158e995.js +1 -0
- bibexpy/_web/_next/static/chunks/app/projects/[id]/upload/page-8df78ca9cdecbcb8.js +1 -0
- bibexpy/_web/_next/static/chunks/app/projects/page-99960711ac6a13e9.js +1 -0
- bibexpy/_web/_next/static/chunks/app/settings/page-002df809d6c5a227.js +1 -0
- bibexpy/_web/_next/static/chunks/app/tools/page-27eee1c6a590859f.js +1 -0
- bibexpy/_web/_next/static/chunks/fd9d1056-f2718be92181f426.js +1 -0
- bibexpy/_web/_next/static/chunks/framework-f66176bb897dc684.js +1 -0
- bibexpy/_web/_next/static/chunks/main-app-d138a6e2f8c847f6.js +1 -0
- bibexpy/_web/_next/static/chunks/main-c4b9ddbf514503ef.js +1 -0
- bibexpy/_web/_next/static/chunks/pages/_app-72b849fbd24ac258.js +1 -0
- bibexpy/_web/_next/static/chunks/pages/_error-7ba65e1336b92748.js +1 -0
- bibexpy/_web/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- bibexpy/_web/_next/static/chunks/webpack-c81f7fd28659d64f.js +1 -0
- bibexpy/_web/_next/static/css/1c4638f3e39cf127.css +3 -0
- bibexpy/_web/_next/static/ypx2IQAkZiSaFiKWnzZGB/_buildManifest.js +1 -0
- bibexpy/_web/_next/static/ypx2IQAkZiSaFiKWnzZGB/_ssgManifest.js +1 -0
- bibexpy/_web/images/PROMPTS.md +83 -0
- bibexpy/_web/images/about-illustration.png +0 -0
- bibexpy/_web/images/authors/alperen-sahin.jpg +0 -0
- bibexpy/_web/images/authors/burak-can-kara.jpg +0 -0
- bibexpy/_web/images/authors/taskin-dirsehan.jpg +0 -0
- bibexpy/_web/images/bibexpy-logo-full.png +0 -0
- bibexpy/_web/images/bibexpy-logo-header.png +0 -0
- bibexpy/_web/images/bibexpy-logo-in.png +0 -0
- bibexpy/_web/images/bibexpy-logo.webp +0 -0
- bibexpy/_web/images/hero-illustration.png +0 -0
- bibexpy/_web/images/loader.gif +0 -0
- bibexpy/_web/images/workflow-diagram.png +0 -0
- bibexpy/_web/index.html +1 -0
- bibexpy/_web/index.txt +11 -0
- bibexpy/_web/projects/_/convert/index.html +1 -0
- bibexpy/_web/projects/_/convert/index.txt +12 -0
- bibexpy/_web/projects/_/disambiguate/index.html +1 -0
- bibexpy/_web/projects/_/disambiguate/index.txt +12 -0
- bibexpy/_web/projects/_/enrich/index.html +1 -0
- bibexpy/_web/projects/_/enrich/index.txt +12 -0
- bibexpy/_web/projects/_/export/index.html +1 -0
- bibexpy/_web/projects/_/export/index.txt +12 -0
- bibexpy/_web/projects/_/merge/index.html +1 -0
- bibexpy/_web/projects/_/merge/index.txt +12 -0
- bibexpy/_web/projects/_/records/index.html +1 -0
- bibexpy/_web/projects/_/records/index.txt +12 -0
- bibexpy/_web/projects/_/report/index.html +1 -0
- bibexpy/_web/projects/_/report/index.txt +12 -0
- bibexpy/_web/projects/_/upload/index.html +1 -0
- bibexpy/_web/projects/_/upload/index.txt +12 -0
- bibexpy/_web/projects/index.html +1 -0
- bibexpy/_web/projects/index.txt +11 -0
- bibexpy/_web/settings/index.html +1 -0
- bibexpy/_web/settings/index.txt +11 -0
- bibexpy/_web/tools/README.md +65 -0
- bibexpy/_web/tools/bibexcel.svg +6 -0
- bibexpy/_web/tools/bibliometrix.png +0 -0
- bibexpy/_web/tools/biblioshiny.svg +12 -0
- bibexpy/_web/tools/citavi.svg +6 -0
- bibexpy/_web/tools/citespace.svg +7 -0
- bibexpy/_web/tools/citnetexplorer.svg +8 -0
- bibexpy/_web/tools/endnote.svg +6 -0
- bibexpy/_web/tools/excel.svg +1 -0
- bibexpy/_web/tools/gephi.svg +66 -0
- bibexpy/_web/tools/histcite.svg +7 -0
- bibexpy/_web/tools/index.html +1 -0
- bibexpy/_web/tools/index.txt +11 -0
- bibexpy/_web/tools/jabref.svg +122 -0
- bibexpy/_web/tools/latex.svg +1 -0
- bibexpy/_web/tools/mendeley.svg +1 -0
- bibexpy/_web/tools/openrefine.svg +15 -0
- bibexpy/_web/tools/overleaf.svg +1 -0
- bibexpy/_web/tools/papers.svg +7 -0
- bibexpy/_web/tools/powerbi.svg +1 -0
- bibexpy/_web/tools/python.svg +1 -0
- bibexpy/_web/tools/r.svg +1 -0
- bibexpy/_web/tools/refworks.svg +5 -0
- bibexpy/_web/tools/scite.svg +5 -0
- bibexpy/_web/tools/tableau.svg +1 -0
- bibexpy/_web/tools/vosviewer.png +0 -0
- bibexpy/_web/tools/zotero.svg +1 -0
- bibexpy/cli.py +242 -0
- bibexpy-2.0.0.dist-info/METADATA +105 -0
- bibexpy-2.0.0.dist-info/RECORD +171 -0
- bibexpy-2.0.0.dist-info/WHEEL +5 -0
- bibexpy-2.0.0.dist-info/entry_points.txt +2 -0
- bibexpy-2.0.0.dist-info/licenses/LICENSE +674 -0
- bibexpy-2.0.0.dist-info/top_level.txt +2 -0
bibex_core/scp2xlsx.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import re
|
|
3
|
+
import os
|
|
4
|
+
from typing import List, Union
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def abbrev_title(title: str) -> str:
|
|
8
|
+
# This function can be used to abbreviate journal names
|
|
9
|
+
# Simple version for now
|
|
10
|
+
return title.replace('.', '').upper()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def labelling(data: pd.DataFrame) -> pd.DataFrame:
|
|
14
|
+
"""Converts column labels to standard format"""
|
|
15
|
+
|
|
16
|
+
df_tag = pd.DataFrame([
|
|
17
|
+
["Abbreviated Source Title", "JI"],
|
|
18
|
+
["Affiliations", "C1"],
|
|
19
|
+
["Authors", "AU"],
|
|
20
|
+
["Author Names", "AU"],
|
|
21
|
+
["Author full names", "AF"],
|
|
22
|
+
["Source title", "SO"],
|
|
23
|
+
["Titles", "TI"],
|
|
24
|
+
["Title", "TI"],
|
|
25
|
+
["Publication Year", "PY"],
|
|
26
|
+
["Year", "PY"],
|
|
27
|
+
["Volume", "VL"],
|
|
28
|
+
["Issue", "IS"],
|
|
29
|
+
["Page count", "PP"],
|
|
30
|
+
["Cited by", "TC"],
|
|
31
|
+
["DOI", "DI"],
|
|
32
|
+
["Link", "URL"],
|
|
33
|
+
["Abstract", "AB"],
|
|
34
|
+
["Author Keywords", "DE"],
|
|
35
|
+
["Indexed Keywords", "ID"],
|
|
36
|
+
["Index Keywords", "ID"],
|
|
37
|
+
["Funding Details", "FU"],
|
|
38
|
+
["Funding Texts", "FX"],
|
|
39
|
+
["Funding Text 1", "FX"],
|
|
40
|
+
["References", "CR"],
|
|
41
|
+
["Correspondence Address", "RP"],
|
|
42
|
+
["Publisher", "PU"],
|
|
43
|
+
["Open Access", "OA"],
|
|
44
|
+
["Language of Original Document", "LA"],
|
|
45
|
+
["Document Type", "DT"],
|
|
46
|
+
["Source", "DB"],
|
|
47
|
+
["EID", "UT"]
|
|
48
|
+
], columns=['orig', 'tag'])
|
|
49
|
+
|
|
50
|
+
# Mevcut sütun isimlerini al
|
|
51
|
+
label = pd.DataFrame({'orig': data.columns})
|
|
52
|
+
|
|
53
|
+
# Eşleştirme yap
|
|
54
|
+
label = label.merge(df_tag, on='orig', how='left')
|
|
55
|
+
|
|
56
|
+
# Eşleşmeyen sütunlar için orijinal ismi kullan
|
|
57
|
+
label['tag'] = label['tag'].fillna(label['orig'])
|
|
58
|
+
|
|
59
|
+
# Sütun isimlerini güncelle
|
|
60
|
+
data.columns = label['tag']
|
|
61
|
+
|
|
62
|
+
return data
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def csvScopus2df(files: Union[str, List[str]]) -> pd.DataFrame:
|
|
66
|
+
"""
|
|
67
|
+
Reads and processes Scopus CSV files
|
|
68
|
+
|
|
69
|
+
Parameters:
|
|
70
|
+
-----------
|
|
71
|
+
files : str or list of str
|
|
72
|
+
Path to CSV files to process
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
--------
|
|
76
|
+
pd.DataFrame
|
|
77
|
+
Processed data frame
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
if isinstance(files, str):
|
|
81
|
+
files = [files]
|
|
82
|
+
|
|
83
|
+
all_data = []
|
|
84
|
+
|
|
85
|
+
for i, file in enumerate(files):
|
|
86
|
+
try:
|
|
87
|
+
# Read CSV file
|
|
88
|
+
df = pd.read_csv(file,
|
|
89
|
+
dtype=str, # Read all columns as string
|
|
90
|
+
na_values='', # Mark empty values as NA
|
|
91
|
+
keep_default_na=False, # Don't use default NA values
|
|
92
|
+
encoding='utf-8') # Use UTF-8 encoding
|
|
93
|
+
|
|
94
|
+
# Replace NA values with empty string
|
|
95
|
+
df = df.fillna('')
|
|
96
|
+
|
|
97
|
+
if i > 0:
|
|
98
|
+
# Find common columns
|
|
99
|
+
common_cols = list(set(df.columns) & set(all_data[0].columns))
|
|
100
|
+
# Merge using only common columns
|
|
101
|
+
all_data.append(df[common_cols])
|
|
102
|
+
else:
|
|
103
|
+
all_data.append(df)
|
|
104
|
+
|
|
105
|
+
except Exception as e:
|
|
106
|
+
print(f"Error: Failed to read file {file}: {str(e)}")
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
if not all_data:
|
|
110
|
+
raise ValueError("No files could be read!")
|
|
111
|
+
|
|
112
|
+
# Merge all dataframes
|
|
113
|
+
DATA = pd.concat(all_data, ignore_index=True)
|
|
114
|
+
|
|
115
|
+
# Organize column labels
|
|
116
|
+
DATA = labelling(DATA)
|
|
117
|
+
|
|
118
|
+
# Clean author names
|
|
119
|
+
if 'AU' in DATA.columns:
|
|
120
|
+
DATA['AU'] = DATA['AU'].str.replace('.', '', regex=False)
|
|
121
|
+
DATA['AU'] = DATA['AU'].str.replace(',', ';', regex=False)
|
|
122
|
+
|
|
123
|
+
# Store raw institution information
|
|
124
|
+
if 'C1' not in DATA.columns:
|
|
125
|
+
DATA['C1'] = ''
|
|
126
|
+
|
|
127
|
+
# Process journal abbreviations
|
|
128
|
+
if 'JI' in DATA.columns:
|
|
129
|
+
DATA['J9'] = DATA['JI'].str.replace('.', '', regex=False)
|
|
130
|
+
else:
|
|
131
|
+
if 'SO' in DATA.columns:
|
|
132
|
+
DATA['J9'] = DATA['SO'].apply(abbrev_title)
|
|
133
|
+
DATA['JI'] = DATA['J9']
|
|
134
|
+
|
|
135
|
+
# Store DI and URL values (before converting to uppercase)
|
|
136
|
+
di_values = DATA.get('DI', None)
|
|
137
|
+
url_values = DATA.get('URL', None)
|
|
138
|
+
|
|
139
|
+
# Convert all string columns to uppercase
|
|
140
|
+
for col in DATA.columns:
|
|
141
|
+
if DATA[col].dtype == 'object':
|
|
142
|
+
DATA[col] = DATA[col].str.upper()
|
|
143
|
+
|
|
144
|
+
# Restore DI and URL values
|
|
145
|
+
if di_values is not None:
|
|
146
|
+
DATA['DI'] = di_values
|
|
147
|
+
if url_values is not None:
|
|
148
|
+
DATA['URL'] = url_values
|
|
149
|
+
|
|
150
|
+
return DATA
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def save_to_excel(file_paths: Union[str, List[str]], output_path: str) -> bool:
|
|
154
|
+
"""
|
|
155
|
+
Processes Scopus CSV files and saves as Excel file
|
|
156
|
+
|
|
157
|
+
Parameters:
|
|
158
|
+
-----------
|
|
159
|
+
file_paths : str or list of str
|
|
160
|
+
Path to CSV files to process
|
|
161
|
+
output_path : str
|
|
162
|
+
Path to output Excel file
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
--------
|
|
166
|
+
bool
|
|
167
|
+
Whether the operation was successful
|
|
168
|
+
"""
|
|
169
|
+
try:
|
|
170
|
+
# Convert data
|
|
171
|
+
df = csvScopus2df(file_paths)
|
|
172
|
+
|
|
173
|
+
# Save as Excel
|
|
174
|
+
df.to_excel(output_path, index=False)
|
|
175
|
+
print(f"Data successfully saved to {output_path}")
|
|
176
|
+
return True
|
|
177
|
+
except Exception as e:
|
|
178
|
+
print(f"Error occurred: {str(e)}")
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if __name__ == "__main__":
|
|
183
|
+
try:
|
|
184
|
+
# Show current working directory
|
|
185
|
+
print(f"Current working directory: {os.getcwd()}")
|
|
186
|
+
|
|
187
|
+
while True:
|
|
188
|
+
# Get file names from user
|
|
189
|
+
print("\nEnter the full path or file names of Scopus CSV files separated by commas")
|
|
190
|
+
print("Example 1: data1.csv,data2.csv")
|
|
191
|
+
print("Example 2: C:/Users/data1.csv,D:/Data/data2.csv")
|
|
192
|
+
print("(Type 'q' to quit)")
|
|
193
|
+
|
|
194
|
+
input_str = input().strip()
|
|
195
|
+
if input_str.lower() == 'q':
|
|
196
|
+
print("Program terminating...")
|
|
197
|
+
break
|
|
198
|
+
|
|
199
|
+
input_files = [f.strip() for f in input_str.split(',')]
|
|
200
|
+
|
|
201
|
+
# Get output file name
|
|
202
|
+
print("\nEnter the name or full path of the output Excel file")
|
|
203
|
+
print("Example 1: output.xlsx")
|
|
204
|
+
print("Example 2: C:/Users/output.xlsx")
|
|
205
|
+
output_file = input().strip()
|
|
206
|
+
|
|
207
|
+
# Check file extensions
|
|
208
|
+
input_files = [f if f.endswith('.csv') else f + '.csv' for f in input_files]
|
|
209
|
+
if not output_file.endswith('.xlsx'):
|
|
210
|
+
output_file += '.xlsx'
|
|
211
|
+
|
|
212
|
+
# Check if files exist
|
|
213
|
+
missing_files = [f for f in input_files if not os.path.exists(f)]
|
|
214
|
+
if missing_files:
|
|
215
|
+
print("\nError: The following files were not found:")
|
|
216
|
+
for f in missing_files:
|
|
217
|
+
print(f"- {f}")
|
|
218
|
+
print("\nPlease check the file names and try again.")
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
# Process files and save to Excel
|
|
222
|
+
print("\nProcessing files...")
|
|
223
|
+
success = save_to_excel(input_files, output_file)
|
|
224
|
+
if success:
|
|
225
|
+
print("\nOperation completed successfully!")
|
|
226
|
+
print(f"Result file: {os.path.abspath(output_file)}")
|
|
227
|
+
|
|
228
|
+
# Ask if user wants to continue
|
|
229
|
+
print("\nDo you want to process more files? (Y/N)")
|
|
230
|
+
if input().strip().upper() != 'Y':
|
|
231
|
+
print("Program terminating...")
|
|
232
|
+
break
|
|
233
|
+
|
|
234
|
+
except Exception as e:
|
|
235
|
+
print(f"\nAn unexpected error occurred: {str(e)}")
|
bibex_core/wos2xlsx.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import re
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def remove_strange_char(text):
|
|
7
|
+
# Remove strange characters
|
|
8
|
+
return re.sub(r'[^\x00-\x7F]+', '', text)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def safe_str_replace(x, old, new):
|
|
12
|
+
# Safe string replacement operation
|
|
13
|
+
if pd.isna(x):
|
|
14
|
+
return x
|
|
15
|
+
try:
|
|
16
|
+
return str(x).replace(old, new)
|
|
17
|
+
except:
|
|
18
|
+
return x
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def isi2df(D):
|
|
22
|
+
# Remove empty lines and strange characters
|
|
23
|
+
D = [line for line in D if len(line.strip()) > 1]
|
|
24
|
+
D = [remove_strange_char(line) for line in D]
|
|
25
|
+
|
|
26
|
+
# Remove lines starting with FN and VR
|
|
27
|
+
D = [line for line in D if not line.startswith(('FN ', 'VR '))]
|
|
28
|
+
|
|
29
|
+
# Combine lines starting with spaces with the label of the previous line
|
|
30
|
+
for i in range(1, len(D)):
|
|
31
|
+
if D[i].startswith(' '):
|
|
32
|
+
D[i] = D[i - 1][:3] + D[i][3:]
|
|
33
|
+
|
|
34
|
+
# Find starting indices of documents
|
|
35
|
+
papers = [i for i, line in enumerate(D) if line.startswith('PT ')]
|
|
36
|
+
nP = len(papers)
|
|
37
|
+
|
|
38
|
+
if nP == 0:
|
|
39
|
+
raise ValueError("No valid ISI format found. Please check your file.")
|
|
40
|
+
|
|
41
|
+
# Calculate number of lines for each document
|
|
42
|
+
row_papers = []
|
|
43
|
+
for i in range(len(papers) - 1):
|
|
44
|
+
row_papers.append(papers[i + 1] - papers[i])
|
|
45
|
+
row_papers.append(len(D) - papers[-1])
|
|
46
|
+
|
|
47
|
+
# Create document number for each line
|
|
48
|
+
num_papers = []
|
|
49
|
+
for i, count in enumerate(row_papers):
|
|
50
|
+
num_papers.extend([i + 1] * count)
|
|
51
|
+
|
|
52
|
+
# Create DataFrame
|
|
53
|
+
data = {
|
|
54
|
+
'Tag': [line[:3].strip() for line in D],
|
|
55
|
+
'content': [line[3:].strip() for line in D],
|
|
56
|
+
'Paper': num_papers
|
|
57
|
+
}
|
|
58
|
+
df = pd.DataFrame(data)
|
|
59
|
+
|
|
60
|
+
# Combine Tag and content
|
|
61
|
+
df = df.groupby(['Paper', 'Tag'])['content'].apply('---'.join).reset_index()
|
|
62
|
+
|
|
63
|
+
# Pivot operation
|
|
64
|
+
df = df.pivot(index='Paper', columns='Tag', values='content').reset_index()
|
|
65
|
+
|
|
66
|
+
# Check mandatory fields
|
|
67
|
+
mandatory_tags = ['AU', 'DE', 'C1', 'RP', 'CR', 'PY', 'SO', 'TI', 'TC']
|
|
68
|
+
missing_tags = [tag for tag in mandatory_tags if tag not in df.columns]
|
|
69
|
+
if missing_tags:
|
|
70
|
+
print("\nWarning: Some mandatory metadata fields are missing. Bibliometrix functions may not work properly!")
|
|
71
|
+
print("Missing fields:", missing_tags)
|
|
72
|
+
|
|
73
|
+
# Fields that should be separated by commas
|
|
74
|
+
comma_tags = ['AU', 'AF', 'CR']
|
|
75
|
+
for tag in comma_tags:
|
|
76
|
+
if tag in df.columns:
|
|
77
|
+
df[tag] = df[tag].apply(lambda x: safe_str_replace(x, '---', ';'))
|
|
78
|
+
|
|
79
|
+
# Replace --- with space for other fields
|
|
80
|
+
other_tags = [col for col in df.columns if col not in comma_tags]
|
|
81
|
+
for tag in other_tags:
|
|
82
|
+
if tag in df.columns:
|
|
83
|
+
df[tag] = df[tag].apply(lambda x: safe_str_replace(x, '---', ' ').strip() if pd.notnull(x) else x)
|
|
84
|
+
|
|
85
|
+
# Add C1raw column
|
|
86
|
+
if 'C1' in df.columns:
|
|
87
|
+
df['C1raw'] = df['C1'].copy()
|
|
88
|
+
# Remove author information in square brackets
|
|
89
|
+
df['C1'] = df['C1'].apply(lambda x: re.sub(r'\[.*?\]', '', str(x)) if pd.notnull(x) else x)
|
|
90
|
+
df['C1'] = df['C1'].apply(lambda x: safe_str_replace(x, '.', '.;') if pd.notnull(x) else x)
|
|
91
|
+
|
|
92
|
+
# Add database information
|
|
93
|
+
df['DB'] = 'ISI'
|
|
94
|
+
|
|
95
|
+
# Clean commas in author names
|
|
96
|
+
if 'AU' in df.columns:
|
|
97
|
+
df['AU'] = df['AU'].apply(lambda x: safe_str_replace(x, ',', ' ').strip() if pd.notnull(x) else x)
|
|
98
|
+
|
|
99
|
+
# Convert all string columns to uppercase except DI
|
|
100
|
+
di_col = None
|
|
101
|
+
if 'DI' in df.columns:
|
|
102
|
+
di_col = df['DI'].copy()
|
|
103
|
+
|
|
104
|
+
for col in df.columns:
|
|
105
|
+
if df[col].dtype == 'object':
|
|
106
|
+
df[col] = df[col].apply(lambda x: str(x).upper() if pd.notnull(x) else x)
|
|
107
|
+
|
|
108
|
+
if di_col is not None:
|
|
109
|
+
df['DI'] = di_col
|
|
110
|
+
|
|
111
|
+
# Remove Paper column
|
|
112
|
+
df = df.drop('Paper', axis=1)
|
|
113
|
+
|
|
114
|
+
return df
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def save_to_excel(file_path, output_path):
|
|
118
|
+
try:
|
|
119
|
+
# Read file
|
|
120
|
+
with open(file_path, 'r', encoding='utf-8') as file:
|
|
121
|
+
lines = file.readlines()
|
|
122
|
+
|
|
123
|
+
if not lines:
|
|
124
|
+
raise ValueError("File is empty!")
|
|
125
|
+
|
|
126
|
+
# Convert data
|
|
127
|
+
df = isi2df(lines)
|
|
128
|
+
|
|
129
|
+
# Save as Excel
|
|
130
|
+
df.to_excel(output_path, index=False)
|
|
131
|
+
print(f"Data successfully saved to {output_path}")
|
|
132
|
+
return True
|
|
133
|
+
except Exception as e:
|
|
134
|
+
print(f"Error occurred: {str(e)}")
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
if __name__ == "__main__":
|
|
139
|
+
# Get file names from user
|
|
140
|
+
print("Enter the name of the ISI format file (e.g., data.txt):")
|
|
141
|
+
input_file = input().strip()
|
|
142
|
+
|
|
143
|
+
print("Enter the name of the output Excel file (e.g., output.xlsx):")
|
|
144
|
+
output_file = input().strip()
|
|
145
|
+
|
|
146
|
+
# Check file extensions
|
|
147
|
+
if not input_file.endswith(('.txt', '.isi')):
|
|
148
|
+
input_file += '.txt'
|
|
149
|
+
if not output_file.endswith('.xlsx'):
|
|
150
|
+
output_file += '.xlsx'
|
|
151
|
+
|
|
152
|
+
# Check if file exists
|
|
153
|
+
if not os.path.exists(input_file):
|
|
154
|
+
print(f"Error: File {input_file} not found!")
|
|
155
|
+
else:
|
|
156
|
+
# Process file and save to Excel
|
|
157
|
+
success = save_to_excel(input_file, output_file)
|
|
158
|
+
if success:
|
|
159
|
+
print("\nOperation completed successfully!")
|
bibex_core/xlsx2vos.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from tqdm import tqdm # İlerleme çubuğu için tqdm kütüphanesi
|
|
3
|
+
|
|
4
|
+
def convert_excel_to_wos(input_excel_path, output_txt_path):
|
|
5
|
+
# Excel dosyasını pandas kullanarak aç
|
|
6
|
+
df = pd.read_excel(input_excel_path)
|
|
7
|
+
|
|
8
|
+
# Başlıklara göre sütun isimlerini eşleştir
|
|
9
|
+
desired_columns = {
|
|
10
|
+
"PT": "PT", # Publication Type
|
|
11
|
+
"AU": "AU", # Authors
|
|
12
|
+
"AF": "AF", # Author Full Names
|
|
13
|
+
"TI": "TI", # Title
|
|
14
|
+
"SO": "SO", # Source
|
|
15
|
+
"LA": "LA", # Language
|
|
16
|
+
"DT": "DT", # Document Type
|
|
17
|
+
"DE": "DE", # Keywords
|
|
18
|
+
"ID": "ID", # Keywords Plus
|
|
19
|
+
"AB": "AB", # Abstract
|
|
20
|
+
"C1": "C1", # Author Address
|
|
21
|
+
"C3": "C3", # Additional Address
|
|
22
|
+
"RP": "RP", # Reprint Address
|
|
23
|
+
"EM": "EM", # Email
|
|
24
|
+
"FU": "FU", # Funding
|
|
25
|
+
"FX": "FX", # Funding Text
|
|
26
|
+
"CR": "CR", # Cited References
|
|
27
|
+
"NR": "NR", # Number of References
|
|
28
|
+
"TC": "TC", # Times Cited
|
|
29
|
+
"Z9": "Z9", # Total Times Cited
|
|
30
|
+
"U1": "U1", # Usage Count (Last 180 Days)
|
|
31
|
+
"U2": "U2", # Usage Count (Since 2013)
|
|
32
|
+
"PU": "PU", # Publisher
|
|
33
|
+
"PI": "PI", # Publisher City
|
|
34
|
+
"PA": "PA", # Publisher Address
|
|
35
|
+
"SN": "SN", # ISSN
|
|
36
|
+
"EI": "ISSN", # Electronic ISSN
|
|
37
|
+
"J9": "J9", # 29-Character Source Abbreviation
|
|
38
|
+
"JI": "JI", # ISO Source Abbreviation
|
|
39
|
+
"PD": "PD", # Publication Date
|
|
40
|
+
"PY": "PY", # Year Published
|
|
41
|
+
"VL": "VL", # Volume
|
|
42
|
+
"AR": "Art. No.", # Article Number
|
|
43
|
+
"DI": "DI", # DOI
|
|
44
|
+
"EA": "EA", # Early Access Date
|
|
45
|
+
"PG": "PG", # Page Count
|
|
46
|
+
"WC": "WC", # Web of Science Categories
|
|
47
|
+
"WE": "WE", # Web of Science Index
|
|
48
|
+
"SC": "SC", # Research Areas
|
|
49
|
+
"GA": "GA", # Document Delivery Number
|
|
50
|
+
"UT": "UT", # Accession Number
|
|
51
|
+
"DA": "DA" # Date of Export
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Değerleri saklamak için boş listeler oluştur
|
|
55
|
+
values_dict = {column: [] for column in desired_columns}
|
|
56
|
+
|
|
57
|
+
# Verileri başlıklara göre al
|
|
58
|
+
for idx, row in df.iterrows():
|
|
59
|
+
for key, header_name in desired_columns.items():
|
|
60
|
+
cell_value = row.get(header_name, "")
|
|
61
|
+
values_dict[key].append(cell_value if pd.notna(cell_value) else "")
|
|
62
|
+
|
|
63
|
+
with open(output_txt_path, 'w', encoding='utf-8') as file:
|
|
64
|
+
# Dosya başlangıcı (bir kere)
|
|
65
|
+
file.write("FN Clarivate Analytics Web of Science\n")
|
|
66
|
+
file.write("VR 1.0\n\n")
|
|
67
|
+
|
|
68
|
+
# Her kayıt için bilgileri dosyaya yaz
|
|
69
|
+
for idx in range(len(df)):
|
|
70
|
+
# Her kayıt PT ile başlar
|
|
71
|
+
pt_value = values_dict["PT"][idx] if values_dict["PT"][idx] else "J"
|
|
72
|
+
file.write(f"PT {pt_value}\n")
|
|
73
|
+
|
|
74
|
+
# AU (Author) bilgisi
|
|
75
|
+
au = values_dict["AU"][idx]
|
|
76
|
+
if au:
|
|
77
|
+
au_list = au.split(';') # Yazarları ayır
|
|
78
|
+
formatted_au_list = [author.strip() for author in au_list if author.strip()]
|
|
79
|
+
file.write(f"AU {formatted_au_list[0]}\n") # AU belirtecinin ilk satırını AU satırına yaz
|
|
80
|
+
for author in formatted_au_list[1:]:
|
|
81
|
+
file.write(f" {author}\n") # Diğer yazarları alt satırlara yaz ve önde üç boşluk bırak
|
|
82
|
+
else:
|
|
83
|
+
file.write("AU \n")
|
|
84
|
+
|
|
85
|
+
# AF (Author Full Name) bilgisi
|
|
86
|
+
af = values_dict["AF"][idx]
|
|
87
|
+
if af:
|
|
88
|
+
af_list = af.split(';') # Yazar tam isimlerini ayır
|
|
89
|
+
file.write(f"AF {af_list[0].strip()}\n") # AF belirtecinin ilk satırını AF satırına yaz
|
|
90
|
+
for author_full in af_list[1:]:
|
|
91
|
+
file.write(f" {author_full.strip()}\n") # Diğer isimleri alt satırlara yaz ve önde üç boşluk bırak
|
|
92
|
+
else:
|
|
93
|
+
file.write("AF \n")
|
|
94
|
+
|
|
95
|
+
# Diğer makale bilgilerini yaz
|
|
96
|
+
file.write(f"TI {values_dict['TI'][idx]}\n") # Makale başlığı
|
|
97
|
+
file.write(f"SO {values_dict['SO'][idx]}\n") # Kaynak başlık (Source)
|
|
98
|
+
file.write(f"LA {values_dict['LA'][idx]}\n") # Dil bilgisi (Language)
|
|
99
|
+
file.write(f"DT {values_dict['DT'][idx]}\n") # Belge türü (Document Type)
|
|
100
|
+
file.write(f"DE {values_dict['DE'][idx]}\n") # Anahtar kelimeler (Keywords)
|
|
101
|
+
file.write(f"ID {values_dict['ID'][idx]}\n") # Keywords Plus
|
|
102
|
+
file.write(f"AB {values_dict['AB'][idx]}\n") # Özet (Abstract)
|
|
103
|
+
|
|
104
|
+
# C1 (Author Address) information - Each address on a new line
|
|
105
|
+
c1 = values_dict['C1'][idx]
|
|
106
|
+
af = values_dict['AF'][idx]
|
|
107
|
+
|
|
108
|
+
if c1 and af:
|
|
109
|
+
# Split authors and addresses
|
|
110
|
+
authors = [a.strip() for a in af.split(';') if a.strip()]
|
|
111
|
+
addresses = [addr.strip() for addr in c1.split(';') if addr.strip()]
|
|
112
|
+
|
|
113
|
+
# If we have both authors and addresses
|
|
114
|
+
if authors and addresses:
|
|
115
|
+
# İlk satır için "C1" kullan
|
|
116
|
+
file.write(f"C1 [{authors[0]}] {addresses[0]}\n")
|
|
117
|
+
|
|
118
|
+
# Diğer satırlar için " " ile başla
|
|
119
|
+
current_addr_idx = 1
|
|
120
|
+
|
|
121
|
+
# Normal eşleşmeleri yaz
|
|
122
|
+
for i in range(1, min(len(authors), len(addresses))):
|
|
123
|
+
file.write(f" [{authors[i]}] {addresses[i]}\n")
|
|
124
|
+
current_addr_idx = i + 1
|
|
125
|
+
|
|
126
|
+
# Eğer fazla yazar varsa, son adresle eşleştir
|
|
127
|
+
if len(authors) > len(addresses):
|
|
128
|
+
last_address = addresses[-1]
|
|
129
|
+
for i in range(current_addr_idx, len(authors)):
|
|
130
|
+
file.write(f" [{authors[i]}] {last_address}\n")
|
|
131
|
+
else:
|
|
132
|
+
file.write("C1 \n")
|
|
133
|
+
|
|
134
|
+
file.write(f"C3 {values_dict['C3'][idx]}\n") # Other address information
|
|
135
|
+
file.write(f"RP {values_dict['RP'][idx]}\n") # Reprint Address
|
|
136
|
+
file.write(f"EM {values_dict['EM'][idx]}\n") # Email Address
|
|
137
|
+
file.write(f"FU {values_dict['FU'][idx]}\n") # Funding Information
|
|
138
|
+
file.write(f"FX {values_dict['FX'][idx]}\n") # Funding Text
|
|
139
|
+
|
|
140
|
+
# CR (Cited References) information - from CR_raw column
|
|
141
|
+
cr = values_dict['CR'][idx]
|
|
142
|
+
if cr:
|
|
143
|
+
cr_list = [ref.strip() for ref in cr.split(';') if ref.strip()] # Filter empty references
|
|
144
|
+
if cr_list:
|
|
145
|
+
# Writing process
|
|
146
|
+
file.write(f"CR {cr_list[0]}\n") # First reference
|
|
147
|
+
for ref in cr_list[1:]:
|
|
148
|
+
file.write(f" {ref}\n") # Other references start with 3 spaces
|
|
149
|
+
else:
|
|
150
|
+
file.write("CR \n")
|
|
151
|
+
|
|
152
|
+
# Diğer gerekli bilgileri yaz
|
|
153
|
+
file.write(f"NR {values_dict['NR'][idx]}\n") # Atıf sayısı (Number of References)
|
|
154
|
+
file.write(f"TC {values_dict['TC'][idx]}\n") # Toplam atıf sayısı (Times Cited)
|
|
155
|
+
file.write(f"Z9 {values_dict['Z9'][idx]}\n") # Total Times Cited
|
|
156
|
+
file.write(f"U1 {values_dict['U1'][idx]}\n") # Usage Count (Last 180 Days)
|
|
157
|
+
file.write(f"U2 {values_dict['U2'][idx]}\n") # Usage Count (Since 2013)
|
|
158
|
+
file.write(f"PU {values_dict['PU'][idx]}\n") # Publisher
|
|
159
|
+
file.write(f"PI {values_dict['PI'][idx]}\n") # Publisher City
|
|
160
|
+
file.write(f"PA {values_dict['PA'][idx]}\n") # Publisher Address
|
|
161
|
+
file.write(f"SN {values_dict['SN'][idx]}\n") # ISSN
|
|
162
|
+
file.write(f"EI {values_dict['EI'][idx]}\n") # Electronic ISSN
|
|
163
|
+
file.write(f"J9 {values_dict['J9'][idx]}\n") # 29-Character Source Abbreviation
|
|
164
|
+
file.write(f"JI {values_dict['JI'][idx]}\n") # ISO Source Abbreviation
|
|
165
|
+
file.write(f"PD {values_dict['PD'][idx]}\n") # Publication Date
|
|
166
|
+
file.write(f"PY {values_dict['PY'][idx]}\n") # Year Published
|
|
167
|
+
file.write(f"VL {values_dict['VL'][idx]}\n") # Volume
|
|
168
|
+
file.write(f"AR {values_dict['AR'][idx]}\n") # Article Number
|
|
169
|
+
file.write(f"DI {values_dict['DI'][idx]}\n") # DOI
|
|
170
|
+
file.write(f"EA {values_dict['EA'][idx]}\n") # Early Access Date
|
|
171
|
+
file.write(f"PG {values_dict['PG'][idx]}\n") # Page Count
|
|
172
|
+
file.write(f"WC {values_dict['WC'][idx]}\n") # Web of Science Categories
|
|
173
|
+
file.write(f"WE {values_dict['WE'][idx]}\n") # Web of Science Index
|
|
174
|
+
file.write(f"SC {values_dict['SC'][idx]}\n") # Research Areas
|
|
175
|
+
file.write(f"GA {values_dict['GA'][idx]}\n") # Document Delivery Number
|
|
176
|
+
file.write(f"UT {values_dict['UT'][idx]}\n") # Accession Number
|
|
177
|
+
file.write(f"DA {values_dict['DA'][idx]}\n") # Date of Export
|
|
178
|
+
|
|
179
|
+
# Her kayıt ER ile biter
|
|
180
|
+
file.write("ER\n\n")
|
|
181
|
+
|
|
182
|
+
# Dosya sonu
|
|
183
|
+
file.write("EF\n")
|
|
184
|
+
|
|
185
|
+
print(f"Successfully converted file to WoS format: '{output_txt_path}'")
|
bibexpy/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""BibexPy v2 — self-hosted bibliometric data preparation tool.
|
|
2
|
+
|
|
3
|
+
Son kullanıcı bu paketi `pip install bibexpy` ile kurar ve `bibexpy` komutuyla
|
|
4
|
+
çalıştırır. Gömülü FastAPI sunucusu + Next.js static UI tek pakette gelir;
|
|
5
|
+
Node.js / npm gerektirmez.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "2.0.0"
|
|
9
|
+
__all__ = ["__version__"]
|
bibexpy/__main__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""BibexPy gömülü FastAPI sunucusu (build-time kopya). Runtime sys.path'e eklenir."""
|