First Version
This commit is contained in:
+212
@@ -0,0 +1,212 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[codz]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
#uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
#poetry.toml
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
||||
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
||||
#pdm.lock
|
||||
#pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# pixi
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
||||
#pixi.lock
|
||||
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
||||
# in the .venv directory. It is recommended not to include this directory in version control.
|
||||
.pixi
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.envrc
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Abstra
|
||||
# Abstra is an AI-powered process automation framework.
|
||||
# Ignore directories containing user credentials, local state, and settings.
|
||||
# Learn more at https://abstra.io/docs
|
||||
.abstra/
|
||||
|
||||
# Visual Studio Code
|
||||
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
||||
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
||||
# you could uncomment the following to ignore the entire vscode folder
|
||||
# .vscode/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
# Cursor
|
||||
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
||||
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
||||
# refer to https://docs.cursor.com/context/ignore-files
|
||||
.cursorignore
|
||||
.cursorindexingignore
|
||||
|
||||
# Marimo
|
||||
marimo/_static/
|
||||
marimo/_lsp/
|
||||
__marimo__/
|
||||
|
||||
#custom
|
||||
data/*
|
||||
db/*
|
||||
instance/*
|
||||
@@ -0,0 +1,173 @@
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
import random
|
||||
|
||||
query = "اقتصاد مقاومتی"
|
||||
|
||||
session = requests.Session()
|
||||
session.trust_env = False
|
||||
session.proxies = {"http": None, "https": None}
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0",
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Referer": "https://ganj.irandoc.ac.ir/",
|
||||
"Origin": "https://ganj.irandoc.ac.ir"
|
||||
}
|
||||
|
||||
|
||||
# گرفتن abstract
|
||||
def get_abstract(uid, retries=5):
|
||||
url = f"https://ganj.irandoc.ac.ir/api/v1/articles/{uid}/show_abstract"
|
||||
|
||||
for i in range(retries):
|
||||
try:
|
||||
res = session.get(url, headers=headers, timeout=10)
|
||||
|
||||
if res.status_code == 200:
|
||||
if not res.text.strip():
|
||||
print(" empty → احتمال بلاک")
|
||||
else:
|
||||
try:
|
||||
data = res.json()
|
||||
abstract = data.get("abstract")
|
||||
if abstract:
|
||||
return abstract
|
||||
except:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
print("Error:", e)
|
||||
|
||||
time.sleep(random.uniform(3, 7))
|
||||
|
||||
return None
|
||||
|
||||
|
||||
# گرفتن year
|
||||
def get_year(uid, retries=5):
|
||||
url = f"https://ganj.irandoc.ac.ir/api/v1/articles/{uid}"
|
||||
|
||||
for i in range(retries):
|
||||
try:
|
||||
res = session.get(url, headers=headers, timeout=10)
|
||||
|
||||
if res.status_code == 200:
|
||||
try:
|
||||
data = res.json()
|
||||
return data.get("ja_pub_yyyy")
|
||||
except:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
print("Year Error:", e)
|
||||
|
||||
time.sleep(random.uniform(3, 7))
|
||||
|
||||
return None
|
||||
|
||||
|
||||
page = 1
|
||||
all_results = []
|
||||
index_counter = 1
|
||||
|
||||
|
||||
while True:
|
||||
print(f"\n Fetching page {page}...")
|
||||
|
||||
search_url = (
|
||||
"https://ganj.irandoc.ac.ir/api/v1/search/main"
|
||||
f"?basicscope=1"
|
||||
f"&keywords={query}"
|
||||
f"&page={page}"
|
||||
f"&sort_by=1"
|
||||
f"&year_from=0"
|
||||
f"&year_to=1405"
|
||||
)
|
||||
|
||||
response = session.get(search_url, headers=headers)
|
||||
|
||||
if response.status_code != 200:
|
||||
print(" خطا در سرچ")
|
||||
break
|
||||
|
||||
data = response.json()
|
||||
results = data.get("results", [])
|
||||
|
||||
if not results:
|
||||
print(" پایان نتایج")
|
||||
break
|
||||
|
||||
for item in results:
|
||||
title = item.get("title")
|
||||
uid = item.get("uuid")
|
||||
|
||||
link = f"https://ganj.irandoc.ac.ir/articles/{uid}" if uid else None
|
||||
|
||||
# چکیده + سال
|
||||
abstract = None
|
||||
year = None
|
||||
|
||||
if uid:
|
||||
abstract = get_abstract(uid)
|
||||
year = get_year(uid)
|
||||
|
||||
# نویسنده / استاد
|
||||
authors, advisors = [], []
|
||||
for c in item.get("contributions", []):
|
||||
role = c.get("role", {}).get("title_en", "").lower()
|
||||
name = c.get("researcher", {}).get("full_name_fa", "")
|
||||
if role == "author" and name:
|
||||
authors.append(name)
|
||||
elif role == "thesis advisor" and name:
|
||||
advisors.append(name)
|
||||
|
||||
# دانشگاه
|
||||
universities = []
|
||||
for p in item.get("partnerships", []):
|
||||
org = p.get("organization", {})
|
||||
super_org = org.get("super_organization", {})
|
||||
uni = super_org.get("title") or org.get("title")
|
||||
if uni:
|
||||
universities.append(uni)
|
||||
|
||||
# مقطع
|
||||
grade = item.get("grade", {}).get("title_fa")
|
||||
|
||||
|
||||
all_results.append({
|
||||
"index": index_counter,
|
||||
"title": title,
|
||||
"year": year,
|
||||
"grade": grade,
|
||||
"authors": authors,
|
||||
"advisors": advisors,
|
||||
"universities": universities,
|
||||
"link": link,
|
||||
"abstract": abstract
|
||||
})
|
||||
|
||||
index_counter += 1
|
||||
|
||||
time.sleep(random.uniform(2, 5))
|
||||
|
||||
if len(all_results) % 15 == 0:
|
||||
print("⏸ استراحت برای جلوگیری از بلاک...")
|
||||
time.sleep(20)
|
||||
|
||||
page += 1
|
||||
time.sleep(random.uniform(2, 4))
|
||||
|
||||
|
||||
final_output = {
|
||||
"query": query,
|
||||
"total_results": len(all_results),
|
||||
"results": all_results
|
||||
}
|
||||
|
||||
with open("all_results.json", "w", encoding="utf-8") as f:
|
||||
json.dump(final_output, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print("\n DONE")
|
||||
print("Total:", len(all_results))
|
||||
@@ -0,0 +1,24 @@
|
||||
name: nlp
|
||||
channels:
|
||||
- conda-forge
|
||||
- defaults
|
||||
dependencies:
|
||||
- python=3.11
|
||||
- flask=3.0.3
|
||||
- flask-sqlalchemy=3.1.1
|
||||
- flask-login=0.6.3
|
||||
- numpy=1.26.4
|
||||
- fastapi=0.111.0
|
||||
- uvicorn=0.29.0
|
||||
- requests=2.32.3
|
||||
- python-dotenv=1.0.1
|
||||
- pypdf
|
||||
- pdfplumber
|
||||
- faiss-cpu
|
||||
- pip
|
||||
- pip:
|
||||
- openai
|
||||
- langchain-community
|
||||
- langchain-huggingface
|
||||
- langchain-text-splitters
|
||||
- sentence-transformers
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,18 @@
|
||||
from openai import OpenAI
|
||||
|
||||
|
||||
class LLMInterface:
|
||||
def __init__(self, personality):
|
||||
api_key = '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'
|
||||
self.client = OpenAI(api_key=api_key)
|
||||
self.personality = personality
|
||||
|
||||
def get_reponse(self, query):
|
||||
completion = self.client.chat.completions.create(
|
||||
model="gpt-4o-mini",
|
||||
messages=[
|
||||
{"role": "system", "content": self.personality},
|
||||
{"role": "user", "content": query}
|
||||
]
|
||||
)
|
||||
return completion.choices[0].message.content
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "53c03589",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# this file is preparing the value measure description\n",
|
||||
"# the file can generate wit hand without opposite values"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "92ebf93c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"with open('far_critions.json', 'r', encoding=\"utf8\") as file:\n",
|
||||
" data = json.load(file)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "9d399101",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"list_of_measures = []\n",
|
||||
"list_of_positive_only_measures = []\n",
|
||||
"\n",
|
||||
"for row in data:\n",
|
||||
" favorite = 'معیار اصلی: '\n",
|
||||
" if type(row['_source']['favorite']) == list:\n",
|
||||
" for f in row['_source']['favorite']:\n",
|
||||
" favorite += f\n",
|
||||
" else:\n",
|
||||
" favorite += row['_source']['favorite']\n",
|
||||
" \n",
|
||||
" description = 'توضیحات برگرفته از منابع: \\n'\n",
|
||||
" for fish in row['_source']['fish']:\n",
|
||||
" if len(fish['resource'])==0 or len(fish['text'])==0:\n",
|
||||
" continue\n",
|
||||
" description += 'منبع: '\n",
|
||||
" description += fish['resource']\n",
|
||||
" description += '\\n'\n",
|
||||
" description += 'متن: '\n",
|
||||
" description += fish['text']\n",
|
||||
" description += '\\n'\n",
|
||||
" \n",
|
||||
" opposite_value = 'معیار معکوس : '\n",
|
||||
" if type(row['_source']['unfavorite']) == list:\n",
|
||||
" for f in row['_source']['unfavorite']:\n",
|
||||
" opposite_value += f\n",
|
||||
" else:\n",
|
||||
" opposite_value += row['_source']['unfavorite']\n",
|
||||
" \n",
|
||||
" list_of_measures += [favorite + '\\n' + description + '\\n' + opposite_value]\n",
|
||||
" list_of_positive_only_measures += [favorite + '\\n' + description]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "3fdd7239",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"معیار اصلی: • در جهت اعتلای اسلام در موازنه جهانی باشد. (رؤیای مطلوب متصل به مفاهیم اعتقادی است که افراد انگیزه از خودگذشتگی نسبت به تحقق آن را دارند)\n",
|
||||
"توضیحات برگرفته از منابع: \n",
|
||||
"منبع: بیانات در دیدار قاریان\n",
|
||||
"متن: امروز ملتهای مسلمان درگیر مشکلات فراوانی هستند از ناحیهی تسلط کسانی که نگاهشان به آفرینش، نگاه مادی است؛ نگاه سودجویانه است؛ نگاه پست یک انسانی است که از معنویت بوئی نبرده. امروز تمدنی که امکانات نظامی را در اختیار مستکبران قرار داده، مبتنی بر نگاه مادی به عالم آفرینش است. همین نگاه مادی است که دنیا را بدبخت کرده؛ خود آنها را هم بدبخت کرده. وقتی نگاه، نگاه مادی بود، سودپرستانه بود، دور از معنویت بود، دور از اخلاق انسانی بود، نتیجه این میشود که قدرت نظامی و قدرت سیاسی و قدرت اطلاعاتی در راه به زنجیر کشیدن ملتها به کار میرود. تمدن غرب در این چند قرن اخیر که به اوج رسیده، هیچ هنری غیر از این نداشته است؛ بشریت را استثمار کردند، ملتها را به زنجیر کشیدند، از علمشان استفاده کردند برای نابودی تمدنهای ملتهای دیگر و غلبهی بر آنها، بر فرهنگ آنها، بر اقتصاد آنها ... هرجا پا گذاشتند، جز فساد و نابودی حرث و نسل - همان طور که خدای متعال در قرآن میفرماید- کاری نکردند. بله، برای اینکه بازار محصولات پیدا کنند، بازارهائی را آراسته کردند؛ مردم را با محصولات جدید آشنا کردند، برای اینکه تجارت خودشان را رونق بدهند. این تمدن، منقطع از معنویت و قرآن است.\n",
|
||||
"منبع: بیانات در دیدار اساتید دانشگاه ها\n",
|
||||
"متن: بسیاری از مباحث علوم انسانی، مبتنی بر فلسفههایی هستند که مبنایش مادیگری است، مبنایش حیوان انگاشتن انسان است، عدم مسئولیت انسان در قبال خداوند متعال است، نداشتن نگاه معنوی به انسان و جهان است. خوب، این علوم انسانی را ترجمه کنیم، آنچه را که غربیها گفتند و نوشتند، عیناً ما همان را بیاوریم به جوان خودمان تعلیم بدهیم، در واقع شکاکیت و تردید و بیاعتقادی به مبانی الهی و اسلامی و ارزشهای خودمان را در قالبهای درسی به جوانها منتقل کنیم؛ این چیز خیلی مطلوبی نیست. این از جملهی چیزهائی است که بایستی مورد توجه قرار بگیرد؛ هم در مجموعههای دولتی مثل وزارت علوم، هم در شورای عالی انقلاب فرهنگی، هم در هر مرکز تصمیمگیری که در اینجا وجود دارد؛ اعم از خود دانشگاهها و بیرون دانشگاهها.\n",
|
||||
"منبع: بیانات در دیدار طلاب مدرسه علمیه آیت الله مجتهدی\n",
|
||||
"متن: اینكه گفته میشود اسلامگرایانِ روشنفكر و روشنبین با تمدنغربی مخالفند، خود معاندان غربی نعل واژگونه میزنند؛ وانمود میكنند اینها با علم و پیشرفت مخالفند؛ در حالی كه مخالفت مؤمنِ به اسلام با تمدن غربی به خاطر پیشرفت علمی و مبارزهی با خرافات و علمی كردن همهی روابط اجتماعی نیست؛ به خاطر خلأ معنویت و فضیلت در این مجموعهی دنیایی است ... اما علم بدون هدایت و فضیلت و معنویت و انسانیت، و علم ناظرِ فقط به دنیا و غمض عین از آخرت، نتیجهاش همین [خشونت و وحشیگری اخلاقی] است. اول، جلوهیی به زندگی میبخشد و قدرت و ثروت و زیبایی میدهد- «للباطل جولة»- اما آخر قضیه این است؛ و این ادامه پیدا خواهد كرد. شما بدانید وحشیگری اخلاقی جوامع مترقی و متمدنی كه از معنویت بویی نبردهاند، روز به روز بیشتر خواهد شد و همین وحشیگری، لغزشگاه بزرگ تمدن غربیِ امروز است و آنها را سرنگون خواهد كرد.\n",
|
||||
"منبع: بیانات در دیدار نخبگان جوان علمی\n",
|
||||
"متن: یک عدّهای از غربزدهها، از صد سال پیش تبلیغ کردند که اگر میخواهید کشورتان پیشرفته بشود و ایران جلو بیفتد، بایستی بروید در ذیل غرب تعریف بشوید -غربزدگی؛ همان تعبیر غربزدگی که مرحوم آلاحمد مطرح کرد و نوشت- این فکرِ غلطی بود؛ حالا در مورد بعضی از آن کسانی که این فکر را القا میکردند، خائنانه هم بود، [امّا] بعضی خائنانه نبود، جاهلانه بود. امروز هم هنوز کسانی از دنبالههای آنها -و باید گفت تفالههای آنها- همین فکرها را ترویج میکنند که «ما در ذیل غرب بایستی پیشرفت کنیم»؛ نه، پنجاه سال، کشور در زمان دوران پهلوی در ذیل غرب زندگی کرد؛ پنجاه سال! یک مدّتی در آن اواسطِ رضاخان در ذیل آلمان، بعد یک مدّتی در ذیل آمریکا تا آخر؛ چه پیشرفتی برای کشور حاصل شد؟ جز بدبختی، جز عقبافتادگی، جز نابود شدن سرمایههای اساسی کشور، چه عاید کشور شد؟ نخیر، در ذیل غرب نمیشود، نمیتوان [پیشرفت کرد]؛ آنکسی که این حرف را بزند که کشور در ذیل غرب ممکن است پیشرفت بکند، خائن به کشور است اگر بداند چه میگوید؛ ممکن هم هست جاهل باشد.\n",
|
||||
"منبع: بیانات در دیدار فرماندهان نیروی هوایی ارتش\n",
|
||||
"متن: دنیا از غرق شدن در مادیگری سودی نبرد؛ دنیا از رواج دادن آزادیهای جنسی سودی نبرد؛ بشریت از آنچه که حرکتهای مادی در اروپا به وجود آوردند - رهائی از معنویت، رهائی از قیود الهی - سودی نبرد؛ نه عدالت را تأمین کرد، نه رفاه عمومی را تأمین کرد، نه امنیت را تأمین کرد، نه خانواده را حفظ کرد، نه توانست تربیت نسلهای بعدی را بدرستی انجام دهد؛ در همهی اینها ضرر کردند. بله، یک عدهای کمپانیدارها، بانکدارها و سازندگان سلاح به آلاف و الوف دست پیدا کردند - اینها بود - اما تمدن مادی غرب دستاورد بشری نداشت، دستاورد انسانی نداشت؛ خودشان را خوشبخت نکردند، جوامعی را هم که در زیر سایهی اینها و با تقلید از اینها زندگی کردند، خوشبخت نکردند پیام انقلاب اسلامی، پیام رهائی از این حرکت بدبختیزا و فلاکتآور است. توجه به معنویت، توجه به اخلاق الهی، در عین حال پرداختن به نیازهای بشری؛ همان چیزی که در اسلام هست؛ حد متوسط. نه افراط کلیساهای کاتولیکی و ارتدوکسی و سختگیریهای غلط، نه ولنگاریهائی که نقطهی مقابل آن در جوامع غربی بر اثر حرکت مادیشان به وجود آمد؛ هیچکدام از اینها درست نیست؛ هر دو غلط است. خط اسلام، خط میانه است؛ خط عدالت است. عدالت یک معنای فراگیر و شاملی دارد. در همهی زمینهها عدالت - یعنی هر چیز را به جای خود نهادن(۱) - باید مد نظر باشد؛ خط متوسط انسانی؛ «و کذلک جعلناکم امّة وسطا لتکونوا شهداء علی النّاس»\n",
|
||||
"منبع: بیانات در دیدار اساتید و اعضای هیأت علمی دانشگاه ها\n",
|
||||
"متن: آنچه که اساتید ما برای پیشرفت علم، وجههی همت خودشان باید قرار بدهند، اولاً «شجاعت علمی» در همهی بخشهاست؛ هم در بخشهای علوم انسانی، هم در بخشهای علوم تجربی، هم در بخشهای نزدیک به عمل و فناوری، و هم در بخشهای علوم پایه. نظریه را دنبال کنند، تولید کنند، خلق کنند، ابداع کنند، نقد کنند؛ چشمبسته و تقلیدی نباید کار را دنبال کرد. مشکل ما در گذشته همیشه این بوده که در زمینههای مختلف - در همهی زمینههای علمی - چشمبسته و تقلیدی نگاه کردیم که ببینیم غربیها چه میگویند ... ما از شاگردی کردنِ پیش کسی که بلد است، ننگمان نمیآید؛ ولی میگوییم نباید فکر کنیم که ما همیشه باید شاگرد بمانیم. معنای این نگاه تقلیدی، این است: ما همیشه باید شاگرد بمانیم. نوآوری لازم است. اعتماد به نفسِ شخصی و اعتماد به نفسِ ملی در اساتید ما، یک ضرورت است. اولاً، استاد ما شخصاً اعتماد به نفس داشته باشد و کار علمی بکند. به آن کار علمی اش تکیه بکند و افتخار کند. ثانیاً، اعتماد به نفسِ ملی داشته باشد. به قابلیت و تواناییهای این ملت اعتماد داشته باشد. اگر این معنا در یک استاد وجود داشته باشد، این سرریز خواهد شد در محیط درس، در کلاس درس، در انتقال به دانشجو و تأثیر تربیتی خودش را خواهد گذاشت. و ثالثاً پرکاری؛ ما یک مقداری از ناحیهی کمکاریهایمان و تنبلیهایمان - در بخشها و سطوح مختلف - ضربه خوردیم. باید کار کرد و از کار نباید خسته شد. بنابراین نوآوری و ابتکار، شجاعت علمی، اعتماد به نفس شخصی و ملی و کارِ متراکم و انبوه، علاج کارِ پیشرفت علمی ماست.\n",
|
||||
"منبع: بیانات در مراسم بیست و هفتمین سالگرد رحلت امام خمینی (رحمهالله)\n",
|
||||
"متن: استقلال فرهنگی در این است که سبک زندگی را، سبک زندگی اسلامی - ایرانی انتخاب بکنیم. بنده در باب سبک زندگی دو سه سال قبل از این مفصّل صحبت کردم؛ سبک زندگی از معماری، از زندگی شهری، از زیستِ انسانی، از پیوندهای اجتماعی تا همهی مسائل گوناگون را شامل میشود. تقلید از غرب و از بیگانه در سبک زندگی، درست نقطهی مقابل استقلال فرهنگی است. امروز نظام سلطه بر روی این مسئله دارد کار میکند؛ همین مسئلهی مهندسی اطّلاعات، این ابزارهای جدیدی که وارد میدان شده است، اینها همه ابزارهایی هستند برای تسلّط بر فرهنگ یک کشور. بنده با این حرف نمیخواهم بگویم این ابزارها را از زندگی خودمان خارج کنیم؛ نه، اینها ابزارهایی هستند که میتوانند مفید واقع بشوند امّا سلطهی دشمن را از این ابزارها بایستی سلب کرد. نمیتوانید شما برای اینکه مثلاً فرض بفرمایید رادیو و تلویزیون داشته باشید، رادیو تلویزیونتان را بدهید در اختیار دشمن؛ اینترنت هم همینجور است، فضای مجازی هم همینجور است، دستگاههای اطّلاعاتی و ابزارهای اطّلاعاتی هم همینجور است، اینها را نمیشود در اختیار دشمن قرار داد؛ امروز در اختیار دشمن است؛ وسیله و ابزار نفوذ فرهنگی است؛ ابزار سلطهی فرهنگی دشمن است.\n",
|
||||
"منبع: بیانیه گام دوم انقلاب خطاب به ملت ایران\n",
|
||||
"متن: انقلاب پُرشکوه ملّت ایران که بزرگترین و مردمیترین انقلاب عصر جدید است، تنها انقلابی است که یک چلّهی پُرافتخار را بدون خیانت به آرمانهایش پشت سر نهاده و در برابر همهی وسوسههایی که غیر قابل مقاومت به نظر میرسیدند، از کرامت خود و اصالت شعارهایش صیانت کرده و اینک وارد دوّمین مرحلهی خودسازی و جامعهپردازی و تمدّنسازی شده است.... آن روز که جهان میان شرق و غرب مادّی تقسیم شده بود و کسی گمان یک نهضت بزرگ دینی را نمیبُرد، انقلاب اسلامی ایران، با قدرت و شکوه پا به میدان نهاد؛ چهارچوبها را شکست؛ کهنگی کلیشهها را به رخ دنیا کشید؛ دین و دنیا را در کنار هم مطرح کرد و آغاز عصر جدیدی را اعلام نمود.\n",
|
||||
"منبع: بیانات در دیدار فرماندهان سپاه پاسداران انقلاب اسلامی\n",
|
||||
"متن: فرض کنید بیست سال دیگر، سی سال دیگر کشور جمهوری اسلامی را مثلاً با ۲۰۰ میلیون یا ۱۸۰ میلیون یا ۱۵۰ میلیون جمعیّت با پیشرفتهای شگرف مادّی و علمی و صنعتی و با استقرار معنویّت و عدالت؛ ببینید چه میشود؛ چه جاذبهای در بین آحاد بشر -مسلمان و غیر مسلمان- بهوجود میآورد؛ میخواهند این اتّفاق نیفتد. حیات طیّبه یعنی این؛ یعنی برویم بهسمت یک چنین وضعی برای زندگی کشور؛ انقلاب میخواهد ما را به اینجاها برساند. رفاه، عدالت، نشاط، نشاط کار، شوق کار، علم، فنّاوری، اینها همه جزو حیات طیّبه است؛ در کنار اینها معنویّت، رحم، خلقیّات اسلامی، سبک زندگی اسلامی، اینها جزو حیات طیّبه است. نظم جزو حیات طیّبه است؛ این حرکت بهسمت حیات طیّبه تمامنشدنی است. اَلآ اِلَی اللهِ تَصیرُ الاُمور؛(۹) این همان صیرورت به سمت خدا [است]. صیرورت یعنی حالیبهحالی شدن، چیزی در باطن ذات خود تغییر ایجاد کند و روزبهروز بهتر بشود؛ این را میگویند صیرورت؛ بشر به سمت خدا صیرورت دارد و جامعهی مطلوب اسلامی آن است که این صیرورت در آن وجود داشته باشد؛ این صیرورت تمامنشدنی است؛ همینطور بهطور دائم ادامه دارد؛ انقلاب این است\n",
|
||||
"\n",
|
||||
"معیار معکوس : • رؤیاهای مدرنیته، تصویر تحرک آفرین جامعه و عامل شیفتگی به الگوها و اسوههای بیگانه• رشد بی هدف در شاخص های زندگی\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(list_of_measures[8])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "864b6a7f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"np.save('Step0_output_list_of_measures', list_of_measures)\n",
|
||||
"np.save('Step0_output_list_of_positive-only_measures', list_of_positive_only_measures)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.13"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,478 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "f13c8350",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# thi files gets the list of measures and description as input and then generates a simple definition of the values and their opposite\n",
|
||||
"# the output is a JSON file (list_of_processed_measures.json) with inputs and outputs of the model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "fe5f6c5a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"MAX_NUMBER_OF_MEASURES = 128"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "212e2f85",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import re\n",
|
||||
"import joblib\n",
|
||||
"import time\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"INTRO_QUARY = \"\"\"\n",
|
||||
"من یک متن دارم (مثلاً بیانات، گزارش، سند راهبردی یا متن تحلیلی) که در آن به ویژگیها یا رفتارهای مطلوب در یک زمینه خاص (مثلاً مدیریت، کار جهادی، اخلاق سازمانی و...) اشاره شده است.\n",
|
||||
"\n",
|
||||
"لطفاً:\n",
|
||||
"\n",
|
||||
"1. فقط بخش «معیار برای تشخیص» و «توضیحات معیار» را تولید کن.\n",
|
||||
"2. بخش «معیار برای تشخیص» را ازبخش «معیار اصلی» در ورودی استفاده کن. میتوانی با برعکس کردن ورودی «معیار معکوس» آن را کامل کنی.\n",
|
||||
"3. در «توضیحات معیار»، با استفاده از منابع در متن ورودی، مفاهیم به کار برده در «معیار معکوس» و «معیار اصلی» را لیست کن و به صورت مفصل، دقیق و شفاف توضیح بده.\n",
|
||||
"4. زبان علمی، بیطرف و توصیفی باشد.\n",
|
||||
"5. از کلیگویی یا قضاوتگری مستقیم (مانند: \"بسیار درست است که...\" یا \"این تنها راه موفقیت است\") پرهیز کن (مثلاً ننویس «ضعیف»، «نامطلوب»، «منفی»، «ناکارآمد» یا «اشتباه»).\n",
|
||||
"از نوشتن متن اضافه بعد از لیست مفاهیم در انتهای خروجی پرهیز کن.\n",
|
||||
"\n",
|
||||
"متن ورودی:\n",
|
||||
"\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"INTRO_OPPOSITE = \"\"\"\n",
|
||||
"من یک معیار دارم که ویژگی یا رفتاری مطلوب را توصیف میکند. میخواهم معادل معکوس آن را تولید کنی؛ یعنی شرایطی که در آن، تحقیق یا فعالیتی با این معیار همخوانی ندارد. \n",
|
||||
"\n",
|
||||
"لطفاً:\n",
|
||||
"\n",
|
||||
"1. فقط بخش «معیار برای تشخیص» و «توضیحات معیار» را تولید کن.\n",
|
||||
"2. بخش «معیار برای تشخیص» را ازبخش «معیار معکوس» در ورودی استفاده کن. میتوانی با برعکس کردن ورودی «معیار اصلی» آن را کامل کنی.\n",
|
||||
"3. در «توضیحات معیار»، با استفاده از منابع در متن ورودی، مفاهیم به کار برده در «معیار معکوس» و «معیار اصلی» را لیست کن و به صورت دقیق و شفاف توضیح بده.\n",
|
||||
"4. زبان علمی، بیطرف و توصیفی باشد.\n",
|
||||
"5. از کلیگویی یا قضاوتگری مستقیم (مانند: \"بسیار درست است که...\" یا \"این تنها راه موفقیت است\") پرهیز کن (مثلاً ننویس «ضعیف»، «نامطلوب»، «منفی»، «ناکارآمد» یا «اشتباه»).\n",
|
||||
"از نوشتن متن اضافه بعد از لیست مفاهیم در انتهای خروجی پرهیز کن.\n",
|
||||
"\n",
|
||||
"متن ورودی:\n",
|
||||
"\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"QUESTION_ORIGINAL = \"\"\"\n",
|
||||
"لطفاً فقط ویژگیها و رفتارهایی را استخراج کن که متناسب با نوع مطلوب باشند، بهگونهای که بتوان از آنها به عنوان ابزار تحلیل کیفی در بررسی متون یا عملکردها استفاده کرد.\n",
|
||||
"\n",
|
||||
"1. فقط بخش «معیار برای تشخیص» و «توضیحات معیار» را تولید کن.\n",
|
||||
"2. بخش «معیار برای تشخیص» را ازبخش «معیار اصلی» در ورودی استفاده کن. میتوانی با برعکس کردن ورودی «معیار معکوس» آن را کامل کنی.\n",
|
||||
"3. برای «توضیحات معیار»، با استفاده از منابع و مفاهیم آمده در متن ورودی، مفاهیم به کار برده در «معیار برای تشخیص» را به صورت دقیق و شفاف توضیح بده تا امکان سنجش هر متنی با معیار به طور دقیق فراهم شود.\n",
|
||||
"4. زبان علمی، بیطرف و توصیفی باشد.\n",
|
||||
"5. از کلیگویی یا قضاوتگری مستقیم (مانند: \"بسیار درست است که...\" یا \"این تنها راه موفقیت است\") پرهیز کن (مثلاً ننویس «ضعیف»، «نامطلوب»، «منفی»، «ناکارآمد» یا «اشتباه»).\n",
|
||||
"از نوشتن متن اضافه بعد از لیست مفاهیم در انتهای خروجی پرهیز کن.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"QUESTION_OPPOSITE = \"\"\"\n",
|
||||
"لطفاً فقط ویژگیها و رفتارهایی را استخراج کن که متناسب با نوع معکوس باشند، بهگونهای که بتوان از آنها به عنوان ابزار تحلیل کیفی در بررسی متون یا عملکردها استفاده کرد.\n",
|
||||
"\n",
|
||||
"1. فقط بخش «معیار برای تشخیص» و «توضیحات معیار» را تولید کن.\n",
|
||||
"2. بخش «معیار برای تشخیص» را ازبخش «معیار معکوس» در ورودی استفاده کن. میتوانی با برعکس کردن ورودی «معیار اصلی» آن را کامل کنی.\n",
|
||||
"3. برای «توضیحات معیار»، با استفاده از منابع و مفاهیم آمده در متن ورودی، مفاهیم به کار برده در «معیار برای تشخیص» را به صورت دقیق و شفاف توضیح بده تا امکان سنجش هر متنی با معیار به طور دقیق فراهم شود.\n",
|
||||
"4. زبان علمی، بیطرف و توصیفی باشد.\n",
|
||||
"5. از کلیگویی یا قضاوتگری مستقیم (مانند: \"بسیار درست است که...\" یا \"این تنها راه موفقیت است\") پرهیز کن (مثلاً ننویس «ضعیف»، «نامطلوب»، «منفی»، «ناکارآمد» یا «اشتباه»).\n",
|
||||
"از نوشتن متن اضافه بعد از لیست مفاهیم در انتهای خروجی پرهیز کن.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"PERSONALITY = \"\"\"\n",
|
||||
"شما یک تحلیلگر بیطرف با تخصص در ارزیابی کیفی و ارزشی فعالیتهای اجتماعی، فرهنگی و سازمانی هستید. وظیفه شما استخراج و بازنویسی معیارهای رفتاری و ارزشی از متنهای مفهومی یا توصیفی است، بهگونهای که معیارها قابل استفاده برای ارزیابی و تحلیل پژوهشها، متون یا عملکردها باشند.\n",
|
||||
"\n",
|
||||
"در بازنویسی معیارها:\n",
|
||||
"- اگر هدف **استخراج معیار اصلی** است، تمرکز شما بر ویژگیهای مثبت، اخلاقمحور، علمی، کارآمد و انساندوستانه خواهد بود.\n",
|
||||
"- اگر هدف **استخراج معیار معکوس** است، تمرکز شما بر ویژگیهایی است که در تعارض با معیارهای اصلی هستند؛ با این حال باید از زبان منفی یا قضاوتگر خودداری کرده و تنها توصیفی علمی و بیطرف ارائه دهید.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_two_measures(output):\n",
|
||||
" # If the output contains a code block, extract the list using regex\n",
|
||||
" import re\n",
|
||||
"\n",
|
||||
" # Get only the code block (text between ```python and ```)\n",
|
||||
" match = re.search(r\"```python\\n(.*?)```\", output, re.DOTALL)\n",
|
||||
" if match:\n",
|
||||
" code = match.group(1)\n",
|
||||
" exec_globals = {}\n",
|
||||
" try:\n",
|
||||
" exec(code, exec_globals) # Be cautious with exec; only use with trusted content\n",
|
||||
" return [exec_globals[\"main_measure\"], exec_globals[\"opposite_measure\"]]\n",
|
||||
" except: \n",
|
||||
" print(\"No variables found.\")\n",
|
||||
" print(code)\n",
|
||||
" return ['', '']\n",
|
||||
"\n",
|
||||
" else:\n",
|
||||
" print(\"No code block or questions found.\")\n",
|
||||
" print(match)\n",
|
||||
" \n",
|
||||
" #print(code)\n",
|
||||
" #print(output)\n",
|
||||
" return ['', '']\n",
|
||||
"\n",
|
||||
"from LLMInterface import LLMInterface\n",
|
||||
"class MeasureSplitter(LLMInterface):\n",
|
||||
" def __init__(self, measures_file, positive_measures_file, personality=PERSONALITY, number_of_jobs=64):\n",
|
||||
" super().__init__(personality=personality)\n",
|
||||
" self.number_of_jobs = number_of_jobs\n",
|
||||
"\n",
|
||||
" self.list_of_measures = np.load(measures_file)\n",
|
||||
" self.list_of_measures = self.list_of_measures[:MAX_NUMBER_OF_MEASURES]\n",
|
||||
" self.list_of_positive_measures = np.load(positive_measures_file)\n",
|
||||
" self.list_of_positive_measures = self.list_of_positive_measures[:MAX_NUMBER_OF_MEASURES]\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" def convert_to_message(self, measure, intro_text=INTRO_QUARY, question=QUESTION_ORIGINAL):\n",
|
||||
" return intro_text +'\\n'+ measure +'\\n'+ question\n",
|
||||
"\n",
|
||||
" def get_ind_analysis(self, i):\n",
|
||||
" print(i)\n",
|
||||
" query_original = self.convert_to_message(self.list_of_positive_measures[i], INTRO_QUARY, QUESTION_ORIGINAL)\n",
|
||||
" output_original = self.get_reponse(query_original)\n",
|
||||
" query_opposite = self.convert_to_message(output_original + '\\n' + self.list_of_measures[i], INTRO_OPPOSITE, QUESTION_OPPOSITE)\n",
|
||||
" output_opposite = self.get_reponse(query_opposite)\n",
|
||||
" return {'measure': self.list_of_measures[i], 'query_original': query_original,\n",
|
||||
" 'output_original': output_original, 'query_opposite': query_opposite,\n",
|
||||
" 'output_opposite': output_opposite}\n",
|
||||
"\n",
|
||||
" def get_text_analysis(self):\n",
|
||||
" with joblib.parallel_backend(\"threading\"):\n",
|
||||
" output_analysis = [joblib.Parallel(n_jobs=self.number_of_jobs)(joblib.delayed(self.get_ind_analysis)(i) for i in range(len(self.list_of_measures)))]\n",
|
||||
" return output_analysis"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "42aa05cd",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ms = MeasureSplitter(measures_file='Step0_output_list_of_measures.npy',\n",
|
||||
" positive_measures_file='Step0_output_list_of_positive-only_measures.npy')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "a7c18498",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"01\n",
|
||||
"\n",
|
||||
"2\n",
|
||||
"3\n",
|
||||
"4\n",
|
||||
"5\n",
|
||||
"6\n",
|
||||
"7\n",
|
||||
"8\n",
|
||||
"9\n",
|
||||
"10\n",
|
||||
"11\n",
|
||||
"12\n",
|
||||
"13\n",
|
||||
"14\n",
|
||||
"15\n",
|
||||
"16\n",
|
||||
"17\n",
|
||||
"18\n",
|
||||
"19\n",
|
||||
"20\n",
|
||||
"21\n",
|
||||
"22\n",
|
||||
"23\n",
|
||||
"24\n",
|
||||
"25\n",
|
||||
"26\n",
|
||||
"27\n",
|
||||
"28\n",
|
||||
"29\n",
|
||||
"30\n",
|
||||
"31\n",
|
||||
"32\n",
|
||||
"33\n",
|
||||
"34\n",
|
||||
"35\n",
|
||||
"36\n",
|
||||
"37\n",
|
||||
"38\n",
|
||||
"39\n",
|
||||
"40\n",
|
||||
"41\n",
|
||||
"42\n",
|
||||
"43\n",
|
||||
"44\n",
|
||||
"45\n",
|
||||
"46\n",
|
||||
"47\n",
|
||||
"48\n",
|
||||
"49\n",
|
||||
"50\n",
|
||||
"51\n",
|
||||
"52\n",
|
||||
"53\n",
|
||||
"54\n",
|
||||
"55\n",
|
||||
"56\n",
|
||||
"57\n",
|
||||
"58\n",
|
||||
"59\n",
|
||||
"60\n",
|
||||
"61\n",
|
||||
"62\n",
|
||||
"63\n",
|
||||
"64\n",
|
||||
"65\n",
|
||||
"66\n",
|
||||
"67\n",
|
||||
"68\n",
|
||||
"69\n",
|
||||
"70\n",
|
||||
"71\n",
|
||||
"72\n",
|
||||
"73\n",
|
||||
"74\n",
|
||||
"75\n",
|
||||
"76\n",
|
||||
"77\n",
|
||||
"78\n",
|
||||
"79\n",
|
||||
"80\n",
|
||||
"81\n",
|
||||
"82\n",
|
||||
"83\n",
|
||||
"84\n",
|
||||
"85\n",
|
||||
"86\n",
|
||||
"87\n",
|
||||
"88\n",
|
||||
"89\n",
|
||||
"90\n",
|
||||
"91\n",
|
||||
"92\n",
|
||||
"93\n",
|
||||
"94\n",
|
||||
"95\n",
|
||||
"96\n",
|
||||
"97\n",
|
||||
"98\n",
|
||||
"99\n",
|
||||
"100\n",
|
||||
"101\n",
|
||||
"102\n",
|
||||
"103\n",
|
||||
"104\n",
|
||||
"105\n",
|
||||
"106\n",
|
||||
"107\n",
|
||||
"108\n",
|
||||
"109\n",
|
||||
"110\n",
|
||||
"111\n",
|
||||
"112\n",
|
||||
"113\n",
|
||||
"114\n",
|
||||
"115\n",
|
||||
"116\n",
|
||||
"117\n",
|
||||
"118\n",
|
||||
"119\n",
|
||||
"120\n",
|
||||
"121\n",
|
||||
"122\n",
|
||||
"123\n",
|
||||
"124\n",
|
||||
"125\n",
|
||||
"126\n",
|
||||
"127\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"res = ms.get_text_analysis()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "712d6a97",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# write all the measures in the output\n",
|
||||
"\n",
|
||||
"import json\n",
|
||||
"with open('Step1_output_list_of_all_processed_measures.json', 'w', encoding=\"utf8\") as f:\n",
|
||||
" json.dump(res[0], f, ensure_ascii=False)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "25625601",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"**معیار برای تشخیص:** ارائه یک «تصویر مطلوب» برای رشد هماهنگ دانش\n",
|
||||
"\n",
|
||||
"**توضیحات معیار:** \n",
|
||||
"رشد هماهنگ دانش به معنای توجه به ابعاد مختلف معرفت و علوم، بهویژه در زمینههایی همچون علوم انسانی و تجربی، و تلاش برای تولید و ابداع نظریههای جدید است. در این راستا، مفاهیمی همچون شجاعت علمی، اعتماد به نفس شخصی و ملی، و همچنین نوآوری و ابتکار از اهمیت بالایی برخوردارند. \n",
|
||||
"\n",
|
||||
"این حوزهها باید به روشی انسانی و معنوی پیگیری شوند که از تعارض با اصول اخلاقی و انسانی پرهیز کند. در نهایت، تلاش در راستای پیشرفت باید مبتنی بر استقلال فرهنگی و نظارت بر استفاده از ابزارهای نوین باشد تا از نفوذ ایدهها و تفکرات غیرسازنده جلوگیری شود. \n",
|
||||
"\n",
|
||||
"علاوه بر این، ارزشهای اخلاقی و معنوی باید در تمامی عرصههای علمی و اجتماعی مد نظر قرار گیرند تا از بروز خشونت، فساد و بیعدالتی جلوگیری شود. استقلال در انتخاب سبک زندگی و ارائه الگوهای علمی بر اساس ارزشهای اسلامی و ملی نیز از دیگر ابعاد مهم در این مسیر به شمار میآید.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(res[0][6]['output_original'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "49951a2f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"**معیار برای تشخیص:** رؤیاهای مدرنیته، تصویر تحرک آفرین جامعه و عامل شیفتگی به الگوها و اسوههای بیگانه\n",
|
||||
"\n",
|
||||
"**توضیحات معیار:** \n",
|
||||
"رؤیاهای مدرنیته به معنای نگریستن به پیشرفتهای تکنولوژیکی و فرهنگی از منظر مادی و دور از ارزشهای معنوی و انسانی است. این دیدگاه میتواند به تحرک آفرینی جامعه منجر شود، اما در عوض احتمال ترویج نگاهی یکجانبه و مادیگرایانه را نیز به دنبال دارد. این نگرش ممکن است منجر به شیفتگی به الگوها و اسوههایی از فرهنگهای بیگانه شود که ارزشی از فرهنگ بومی و ملی را نادیده میگیرد.\n",
|
||||
"\n",
|
||||
"رشد بیهدف در شاخصهای زندگی به دنبال پذیرش صرف معیارهای کمّی و ظاهری به جای معیارهای کیفی و ارزشی در زندگی فردی و اجتماعی است. این نوع رشد ممکن است تضمینکننده پیشرفت در برخی زمینهها باشد، اما بسترهای فرهنگی و اخلاقی را که لازمه تعالی واقعی جامعه است، نادیده میگیرد. \n",
|
||||
"\n",
|
||||
"مفاهیمی همچون تسلط مادی، نگاه سودجو، نداشتن نگاه معنوی و معیارهای مبتنی بر عقل و اخلاق میتوانند به بررسی این معیار کمک کنند. به ویژه، اشاره به خطرات مادیگرایی و دوری از معنویت در راستای تشخیص این معیار کمکرسان خواهد بود.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(res[0][6]['output_opposite'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "a599b017",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"### معیار برای تشخیص\n",
|
||||
"• سازماندهی جهادی بر اساس: کار برای خدا، اخلاص، تلاش خستگیناپذیر، عالمانه، باتدبیر، کارآمد.\n",
|
||||
"\n",
|
||||
"### توضیحات معیار\n",
|
||||
"1. **کار برای خدا**: این مفهوم به معنای انجام کارها با نیت خالص و برای رضایت الهی است. فرد باید انگیزههای شخصی را کنار گذاشته و به خدمت به جامعه و افراد نیازمند تمرکز کند.\n",
|
||||
" \n",
|
||||
"2. **اخلاص**: اخلاص به این اشاره دارد که فرد باید در فعالیتهای خود صداقت و خلوص نیت داشته باشد. این ویژگی به معنای نداشتن انگیزههای مادی و شخصی و همچنین تمرکز بر اهداف انسانی و اجتماعی است.\n",
|
||||
"\n",
|
||||
"3. **تلاش خستگیناپذیر**: این ویژگی نشاندهنده تداوم و پایمردی در انجام فعالیتها است. فرد باید توانایی ایجاد انگیزه و تلاش مستمر را داشته باشد تا در برابر چالشها ایستادگی کند.\n",
|
||||
"\n",
|
||||
"4. **عالمانه**: این جنبه به استفاده از دانش و اطلاعات علمی در انجام کارها اشاره دارد. به معنای تفکر منطقی، تحلیلی و مبتنی بر شواهد در فرآیند تصمیمگیری و برنامهریزی است.\n",
|
||||
"\n",
|
||||
"5. **باتدبیر**: این ویژگی شامل برنامهریزی دقیق، مدیریت منابع و اتخاذ تصمیمات هوشمندانه است. فرد باید قادر باشد تا برای رسیدن به اهداف، از منابع به شیوهای مؤثر استفاده کند.\n",
|
||||
"\n",
|
||||
"6. **کارآمد**: کارآمدی به معنای دستیابی به نتایج مناسب با استفاده بهینه از زمان و منابع است. فرد و یا سازمان باید بتوانند اهداف خود را با حداقل اتلاف منابع و زمان محقق کنند.\n",
|
||||
"\n",
|
||||
"این معیارها به ارزیابی و تحلیل فعالیتها و عملکردها کمک میکنند و امکان سنجش آنها بر اساس اصول اخلاقی و انسانی را فراهم میآورند.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(res[0][1]['output_original'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "da82832f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"### معیار برای تشخیص\n",
|
||||
"• انگیزه حضور فرد در سازمان، سلطه بر دیگران یا حرص و طمع است. انگیزه حضور فرد در سازمان، خودشیفتگی، اشرافیگری، لذتجویی، فکر جمعآوری زخارف دنیا. • عدم سازماندهی نیروهای مردمی و جهادی ترجیح دیوانسالاری فرسوده بر مدیریت جهادی • وابسته کردن نیروهای جهادی به برنامه و حمایت بنیاد\n",
|
||||
"\n",
|
||||
"### توضیحات معیار\n",
|
||||
"1. **سلطه بر دیگران**: این مفهوم به معنای تلاش برای به دست آوردن کنترل یا برتری بر دیگران با هدف دریافت منافع شخصی است. در فعالیتهای اجتماعی و جهادی، این ویژگی با روحیه همکاری و جمعگرایی در تضاد است.\n",
|
||||
"\n",
|
||||
"2. **حرص و طمع**: انگیزههای مادی و تلاش برای کسب ثروت یا موقعیت برتر میتواند بر تصمیمگیریها تأثیر منفی بگذارد و اهداف انسانی و اجتماعی را دچار انحراف کند. \n",
|
||||
"\n",
|
||||
"3. **خودشیفتگی**: در این شرایط، فرد به جای تمرکز بر نیازهای دیگران، بر خود و خواستههای شخصیاش تمرکز دارد. این ویژگی معمولاً با کمبود همدلی و توجه به دیگران همراه است.\n",
|
||||
"\n",
|
||||
"4. **اشرافیگری**: به معنای تمایل به زندگی در لوکس و تجملاتی است که ممکن است با رشد فردی و خدمات اجتماعی در تضاد باشد. \n",
|
||||
"\n",
|
||||
"5. **لذتجویی**: این ویژگی به گرایش به جستجوی لذتهای فوری و موقتی اشاره دارد که میتواند منجر به غفلت از مسئولیتهای اجتماعی و انسانی شود.\n",
|
||||
"\n",
|
||||
"6. **عدم سازماندهی نیروهای مردمی و جهادی**: این مفهوم نشاندهنده فقدان ساختار و هماهنگی در فعالیتهای اجتماعی است که میتواند منجر به ناکامی در تحقق اهداف جمعی گردد.\n",
|
||||
"\n",
|
||||
"7. **وابسته کردن نیروهای جهادی به برنامه و حمایت بنیاد**: این ویژگی به معنای ایجاد وابستگی به منابع و حمایتهای بیرونی است که میتواند استقلال و خودکفایی نیروهای جهادی را تحتالشعاع قرار دهد.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(res[0][1]['output_opposite'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "2039a0cd-9857-4ddd-8290-a0d689c389e6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.13"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,301 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "6e0b19d0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# this file is loading the articles and the value measures JSON file \n",
|
||||
"# for each article and value measure it will send to LLM for an analysis\n",
|
||||
"# user can select which list of measures to be used for the analysis (orginal or opposite)\n",
|
||||
"# the analysis and th model input are saved into a JSN file"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "dd37ecab",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"MAX_NUMBER_OF_DOCUMENTS = 300"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8edafda4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Loading Data and Measurs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "639f75cf",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Loading JSON data"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "1a2f9775",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"with open('Step1_output_list_of_processed_measures.json', encoding=\"utf8\") as f:\n",
|
||||
" list_of_processed_measures = json.load(f)\n",
|
||||
" \n",
|
||||
"list_of_original_measures = [l['output_original'] for l in list_of_processed_measures]\n",
|
||||
"list_of_opsitive_measures = [l['output_opposite'] for l in list_of_processed_measures]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1c94123f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Analyzer class and implementation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "b6025d8f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import joblib\n",
|
||||
"import time\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# positive\n",
|
||||
"PERSONALITY = \"\"\"\n",
|
||||
"شما یک تحلیلگر علمی و بیطرف هستید که در حوزه تحلیل محتوای کیفی تخصص دارید. وظیفه شما سنجش میزان رعایت یک معیار مشخص در یک متن علمی است.\n",
|
||||
"\n",
|
||||
"شما بر اساس یک «معیار رفتاری یا محتوایی» که به شما داده میشود و یک «متن علمی»، بررسی میکنید که تا چه حد این متن با معیار مذکور هماهنگ است. ارزیابی شما بدون تعصب، منصفانه، دقیق و تحلیلی است.\n",
|
||||
"\n",
|
||||
"شما در تحلیل خود:\n",
|
||||
"- ابتدا اجزای متن علمی را با مؤلفههای معیار تطبیق میدهید.\n",
|
||||
"- در صورت وجود تطابق یا عدم تطابق، به صورت شفاف و با مثال یا دلایل علمی تحلیل میکنید.\n",
|
||||
"- از قضاوت ارزشی پرهیز میکنید و لحن تحلیلی و عینی دارید.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"INTRO_QUARY = \"\"\"\n",
|
||||
"اکنون لطفاً متن زیر را بر اساس معیار ارائهشده ارزیابی کن. تحلیل خود را با ساختاری علمی، دقیق و بیطرف بنویس.\n",
|
||||
"معیار مورد سنجش:\n",
|
||||
"\n",
|
||||
"\"\"\"\n",
|
||||
" \n",
|
||||
"QUESTION = \"\"\"\n",
|
||||
"\n",
|
||||
"شما در تحلیل خود:\n",
|
||||
"- ابتدا اجزای متن علمی را با مؤلفههای معیار تطبیق میدهید.\n",
|
||||
"- در صورت وجود تطابق یا عدم تطابق، به صورت شفاف و با مثال یا دلایل علمی تحلیل میکنید.\n",
|
||||
"- از قضاوت ارزشی پرهیز میکنید و لحن تحلیلی و عینی دارید.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from LLMInterface import LLMInterface\n",
|
||||
"class ValueAnalyser(LLMInterface):\n",
|
||||
" def __init__(self, list_of_measures, personality=PERSONALITY, number_of_jobs=64, number_of_meaures_to_valuate=64):\n",
|
||||
" super().__init__(personality=personality)\n",
|
||||
" self.number_of_jobs = number_of_jobs\n",
|
||||
"\n",
|
||||
" self.list_of_measures = list_of_measures\n",
|
||||
" self.list_of_measures = self.list_of_measures[:number_of_meaures_to_valuate]\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" def convert_to_message(self, measure, query):\n",
|
||||
" intro_text = 'حالا به متن توجه کن: '\n",
|
||||
" return INTRO_QUARY +'\\n'+ measure +'\\n'+ intro_text +'\\n'+ query +'\\n'+ QUESTION\n",
|
||||
"\n",
|
||||
" def get_text_analysis(self, text):\n",
|
||||
" def func(i):\n",
|
||||
" measure = self.list_of_measures[i]\n",
|
||||
" query = self.convert_to_message(measure, text)\n",
|
||||
" output = self.get_reponse(query)\n",
|
||||
" return (measure, query, output, -1.)\n",
|
||||
"\n",
|
||||
" with joblib.parallel_backend(\"threading\"):\n",
|
||||
" output_analysis = [joblib.Parallel(n_jobs=self.number_of_jobs)(joblib.delayed(func)(i) for i in range(len(self.list_of_measures)))]\n",
|
||||
" return output_analysis"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "afbf2718",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Resuming from paper index 294 (0‑based)\n",
|
||||
"Processed 295/300 – بررسی تهدیدها و شناسایی فرصتهای محیطی صنعت لوازم خانگی در شرایط تحریم اقتصادی، در جهت تبیین مدل اقتصاد مقاومتی (با رویکرد مدیریت استراتژیک)\n",
|
||||
"Processed 296/300 – ارائه مدل مدیریت تکنولوژی در شرایط اقتصاد مقاومتی در صنایع با تکنولوژی پیچیده؛ مطالعه موردی: صنعت مخابرات\n",
|
||||
"Processed 297/300 – بررسی فرصت های ناشی از کلان روند های اقتصاد دیجیتال به منظور تدوین الگوی توسعه صنعتی ایران در راستای سیاست های کلی اقتصاد مقاومتی\n",
|
||||
"Processed 298/300 – نقش و کارکرد اداره امور مالیاتی در راستای اصلاح نظام درآمدی دولت با تأکید بر افزایش سهم درآمدهای مالیاتی مطرح در سیاستهای کلی اقتصاد مقاومتی\n",
|
||||
"Processed 299/300 – بررسی عدالت اقتصادی از منظر قرآن(در راستای تحقق اقتصاد مقاومتی)\n",
|
||||
"Processed 300/300 – نقش دولت در مدیریت واردات در راستای تحقق سیاستهای کلی اقتصاد مقاومتی\n",
|
||||
"Processing complete.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import time\n",
|
||||
"from typing import List, Dict, Any\n",
|
||||
"\n",
|
||||
"analyzer = ValueAnalyser(list_of_measures=list_of_original_measures)\n",
|
||||
"\n",
|
||||
"# Load the source data\n",
|
||||
"with open('Results.json', encoding='utf8') as f:\n",
|
||||
" data = json.load(f)\n",
|
||||
"\n",
|
||||
"TOTAL_PAPERS = len(data['results'])\n",
|
||||
"\n",
|
||||
"CHECKPOINT_FILE = 'checkpoint.txt'\n",
|
||||
"OUTPUT_FILE = 'step2-original.json'\n",
|
||||
"\n",
|
||||
"def load_checkpoint() -> int:\n",
|
||||
" \"\"\"Return the last successfully processed index (0‑based), or 0 if none.\"\"\"\n",
|
||||
" try:\n",
|
||||
" with open(CHECKPOINT_FILE, 'r') as f:\n",
|
||||
" return int(f.read().strip())\n",
|
||||
" except FileNotFoundError:\n",
|
||||
" return 0\n",
|
||||
"\n",
|
||||
"def save_checkpoint(index: int):\n",
|
||||
" \"\"\"Save the last completed index.\"\"\"\n",
|
||||
" with open(CHECKPOINT_FILE, 'w') as f:\n",
|
||||
" f.write(str(index))\n",
|
||||
"\n",
|
||||
"def load_existing_results() -> List[Dict[str, Any]]:\n",
|
||||
" \"\"\"Load previously saved results, or return empty list if file is missing/corrupt.\"\"\"\n",
|
||||
" try:\n",
|
||||
" with open(OUTPUT_FILE, 'r', encoding='utf8') as f:\n",
|
||||
" return json.load(f)\n",
|
||||
" except (FileNotFoundError, json.JSONDecodeError):\n",
|
||||
" return []\n",
|
||||
"\n",
|
||||
"def save_results(results: List[Dict[str, Any]]):\n",
|
||||
" \"\"\"Overwrite the output file with the full list of results.\"\"\"\n",
|
||||
" with open(OUTPUT_FILE, 'w', encoding='utf8') as f:\n",
|
||||
" json.dump(results, f, ensure_ascii=False, indent=2)\n",
|
||||
"\n",
|
||||
"# Resume from last checkpoint\n",
|
||||
"start_index = load_checkpoint()\n",
|
||||
"all_results = load_existing_results()\n",
|
||||
"\n",
|
||||
"# If you want to process a fixed range (e.g., 10 to 20), adjust start_index accordingly\n",
|
||||
"START = 0\n",
|
||||
"END = TOTAL_PAPERS # or 20, or any limit\n",
|
||||
"\n",
|
||||
"if start_index < START:\n",
|
||||
" start_index = START\n",
|
||||
"\n",
|
||||
"print(f\"Resuming from paper index {start_index} (0‑based)\")\n",
|
||||
"\n",
|
||||
"# Process each remaining paper\n",
|
||||
"for i in range(start_index, END):\n",
|
||||
" article = data['results'][i]\n",
|
||||
" retries = 3\n",
|
||||
" success = False\n",
|
||||
"\n",
|
||||
" for attempt in range(retries):\n",
|
||||
" try:\n",
|
||||
" # Simulate network call – your actual analysis may involve HTTP requests\n",
|
||||
" res = analyzer.get_text_analysis('\\n'.join(article))\n",
|
||||
" \n",
|
||||
" result_entry = {\n",
|
||||
" \"title\": article['title'],\n",
|
||||
" \"year\": article[\"year\"],\n",
|
||||
" \"grade\": article[\"grade\"],\n",
|
||||
" \"authors\": article[\"authors\"],\n",
|
||||
" \"universities\": article[\"universities\"],\n",
|
||||
" 'link': article['link'],\n",
|
||||
" 'abstract': article['abstract'],\n",
|
||||
" \"response\": res\n",
|
||||
" }\n",
|
||||
" \n",
|
||||
" all_results.append(result_entry)\n",
|
||||
" save_results(all_results) # save after each success\n",
|
||||
" save_checkpoint(i + 1) # next index to start from\n",
|
||||
" print(f\"Processed {i+1}/{END} – {article['title']}\")\n",
|
||||
" success = True\n",
|
||||
" break # exit retry loop\n",
|
||||
" \n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"Error on paper {i} (attempt {attempt+1}/{retries}): {e}\")\n",
|
||||
" if attempt < retries - 1:\n",
|
||||
" time.sleep(5) # wait before retry\n",
|
||||
" else:\n",
|
||||
" print(f\"Failed to process paper {i} after {retries} attempts. Skipping.\")\n",
|
||||
" # Optionally, save checkpoint as i+1 to skip this paper entirely\n",
|
||||
" save_checkpoint(i + 1)\n",
|
||||
" \n",
|
||||
" if not success:\n",
|
||||
" # Log the failure but continue with next paper\n",
|
||||
" continue\n",
|
||||
" \n",
|
||||
" # Small delay between papers to avoid overwhelming the server\n",
|
||||
" time.sleep(1)\n",
|
||||
"\n",
|
||||
"print(\"Processing complete.\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "baad4652-cc0b-4f0b-8e89-c1ec8bc95b3d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"300\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(TOTAL_PAPERS)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f8764ea0-1301-4696-982d-e5f6120e959c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.13"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,570 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1c94123f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Score estimation class and implementation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "e1b8267f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from openai import OpenAI\n",
|
||||
"#from transformers import pipeline\n",
|
||||
"import re\n",
|
||||
"import joblib\n",
|
||||
"import time\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# positive\n",
|
||||
"PERSONALITY = \"\"\"\n",
|
||||
"\n",
|
||||
"شما از یک مقیاس دهتایی برای اعلام میزان رعایت معیار استفاده میکنید.\n",
|
||||
"سپس عدد انتخابشده را به شکل خروجی پایتون بازمیگردانید.\n",
|
||||
"\n",
|
||||
"مقیاس رضایت شما بهصورت زیر تعریف میشود:\n",
|
||||
"9. رضایت کامل از رعایت معیار \n",
|
||||
"8. رضایت بسیار زیاد \n",
|
||||
"7. رضایت زیاد \n",
|
||||
"6. نسبتاً راضی \n",
|
||||
"5. رضایت متوسط \n",
|
||||
"4. رضایت اندک \n",
|
||||
"3. نارضایتی اندک \n",
|
||||
"2. نسبتاً ناراضی \n",
|
||||
"1. نارضایتی زیاد \n",
|
||||
"0. عدم رضایت کامل از رعایت معیار\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"INTRO_QUARY = \"\"\"\n",
|
||||
"اکنون لطفاً متن زیر را بررسی کن. سپس از مقیاس دهتایی برای اعلام میزان رعایت معیار استفاده کن و در نهایت، نتیجه را در قالب یک متغیر پایتونی به نام `satisfaction_index` ارائه بده.\n",
|
||||
"متن نحلیل :\n",
|
||||
"\n",
|
||||
"\"\"\"\n",
|
||||
" \n",
|
||||
"QUESTION = \"\"\"\n",
|
||||
"\n",
|
||||
"در پایان، پاسخ خود را اینگونه ارائه بده:\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"satisfaction_index = عدد انتخابی از 0 تا 9\n",
|
||||
"```\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"def extract_senmtiment_score(output):\n",
|
||||
" # If the output contains a code block, extract the list using regex\n",
|
||||
" import re\n",
|
||||
"\n",
|
||||
" # Get only the code block (text between ```python and ```)\n",
|
||||
" match = re.search(r\"```python\\n(.*?)```\", output, re.DOTALL)\n",
|
||||
" if match:\n",
|
||||
" code = match.group(1)\n",
|
||||
" exec_globals = {}\n",
|
||||
" try:\n",
|
||||
" exec(code, exec_globals) # Be cautious with exec; only use with trusted content\n",
|
||||
" return exec_globals[\"satisfaction_index\"]/9.\n",
|
||||
" except: \n",
|
||||
" print(\"No variables found.\")\n",
|
||||
" print(code)\n",
|
||||
" return 0\n",
|
||||
"\n",
|
||||
" else:\n",
|
||||
" print(\"No code block or questions found.\")\n",
|
||||
" print(match)\n",
|
||||
" \n",
|
||||
" return 0\n",
|
||||
"\n",
|
||||
"from LLMInterface import LLMInterface\n",
|
||||
"class ScoreSentimentAnalyser(LLMInterface):\n",
|
||||
" def __init__(self, personality=PERSONALITY, number_of_jobs=64, number_of_meaures_to_valuate=64):\n",
|
||||
" super().__init__(personality=personality)\n",
|
||||
" self.number_of_jobs = number_of_jobs\n",
|
||||
"\n",
|
||||
" def convert_to_message(self, query):\n",
|
||||
" return INTRO_QUARY +'\\n'+ query +'\\n'+ QUESTION\n",
|
||||
"\n",
|
||||
" def get_text_analysis(self, list_of_analysis):\n",
|
||||
" def func(i):\n",
|
||||
" analysis = list_of_analysis[i]\n",
|
||||
" query = self.convert_to_message(analysis)\n",
|
||||
" output = self.get_reponse(query)\n",
|
||||
" return extract_senmtiment_score(output)\n",
|
||||
"\n",
|
||||
" with joblib.parallel_backend(\"threading\"):\n",
|
||||
" output_analysis = [joblib.Parallel(n_jobs=self.number_of_jobs)(joblib.delayed(func)(i) for i in range(len(list_of_analysis)))]\n",
|
||||
" return output_analysis"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "f10c0a32",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"analyzer = ScoreSentimentAnalyser()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "e3e683d5",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Resuming from existing output file: Step3_output_article_response_original_3.json\n",
|
||||
"Resuming from index 290 (checkpoint found)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"CHECKPOINT_FILE = 'checkpoint_original.txt'\n",
|
||||
"OUTPUT_FILE = 'Step3_output_article_response_original_3.json'\n",
|
||||
"\n",
|
||||
"# 1. Load existing output if it exists (resume)\n",
|
||||
"if os.path.exists(OUTPUT_FILE):\n",
|
||||
" with open(OUTPUT_FILE, encoding='utf8') as f:\n",
|
||||
" article_response = json.load(f)\n",
|
||||
" print(f\"Resuming from existing output file: {OUTPUT_FILE}\")\n",
|
||||
"else:\n",
|
||||
" with open('step2-original.json', encoding='utf8') as f:\n",
|
||||
" article_response = json.load(f)\n",
|
||||
" print(\"Starting fresh from step2-original.json\")\n",
|
||||
"\n",
|
||||
"# 2. Read last processed index from checkpoint\n",
|
||||
"start = 0\n",
|
||||
"if os.path.exists(CHECKPOINT_FILE):\n",
|
||||
" with open(CHECKPOINT_FILE, 'r') as f:\n",
|
||||
" start = int(f.read().strip()) + 1\n",
|
||||
" print(f\"Resuming from index {start} (checkpoint found)\")\n",
|
||||
"\n",
|
||||
"end = len(article_response)\n",
|
||||
"\n",
|
||||
"# 3. Process from start to end\n",
|
||||
"for i in range(start, end):\n",
|
||||
" try:\n",
|
||||
" text = [l[2] for l in article_response[i]['response'][0]]\n",
|
||||
" scores = [l[3] for l in article_response[i]['response'][0]] # not used later, but kept\n",
|
||||
" res = analyzer.get_text_analysis(text)[0] # assumes res is list of same length as text\n",
|
||||
" \n",
|
||||
" # Update scores\n",
|
||||
" for j in range(len(article_response[i]['response'][0])):\n",
|
||||
" article_response[i]['response'][0][j][3] = res[j]\n",
|
||||
" \n",
|
||||
" # Save JSON after each successful update\n",
|
||||
" with open(OUTPUT_FILE, 'w', encoding='utf8') as f:\n",
|
||||
" json.dump(article_response, f, ensure_ascii=False)\n",
|
||||
" \n",
|
||||
" # Update checkpoint (last processed index)\n",
|
||||
" with open(CHECKPOINT_FILE, 'w') as f:\n",
|
||||
" f.write(str(i))\n",
|
||||
" \n",
|
||||
" print(f\"Processed and saved index {i}\")\n",
|
||||
" \n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"Error at index {i}: {e}\")\n",
|
||||
" # Do not update checkpoint – next run will retry this index\n",
|
||||
" break # stop the loop so you can investigate the error"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "271a77e4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Resuming from existing output file: Step3_output_article_response_opposite_2.json\n",
|
||||
"Processed and saved index 0\n",
|
||||
"Processed and saved index 1\n",
|
||||
"Processed and saved index 2\n",
|
||||
"Processed and saved index 3\n",
|
||||
"Processed and saved index 4\n",
|
||||
"Processed and saved index 5\n",
|
||||
"Processed and saved index 6\n",
|
||||
"Processed and saved index 7\n",
|
||||
"Processed and saved index 8\n",
|
||||
"Processed and saved index 9\n",
|
||||
"Processed and saved index 10\n",
|
||||
"Processed and saved index 11\n",
|
||||
"Processed and saved index 12\n",
|
||||
"Processed and saved index 13\n",
|
||||
"Processed and saved index 14\n",
|
||||
"Processed and saved index 15\n",
|
||||
"Processed and saved index 16\n",
|
||||
"Processed and saved index 17\n",
|
||||
"Processed and saved index 18\n",
|
||||
"Processed and saved index 19\n",
|
||||
"Processed and saved index 20\n",
|
||||
"Processed and saved index 21\n",
|
||||
"Processed and saved index 22\n",
|
||||
"Processed and saved index 23\n",
|
||||
"Processed and saved index 24\n",
|
||||
"Processed and saved index 25\n",
|
||||
"Processed and saved index 26\n",
|
||||
"Processed and saved index 27\n",
|
||||
"Processed and saved index 28\n",
|
||||
"Processed and saved index 29\n",
|
||||
"Processed and saved index 30\n",
|
||||
"Processed and saved index 31\n",
|
||||
"Processed and saved index 32\n",
|
||||
"Processed and saved index 33\n",
|
||||
"Processed and saved index 34\n",
|
||||
"Processed and saved index 35\n",
|
||||
"Processed and saved index 36\n",
|
||||
"Processed and saved index 37\n",
|
||||
"Processed and saved index 38\n",
|
||||
"Processed and saved index 39\n",
|
||||
"Processed and saved index 40\n",
|
||||
"Processed and saved index 41\n",
|
||||
"Processed and saved index 42\n",
|
||||
"Processed and saved index 43\n",
|
||||
"Processed and saved index 44\n",
|
||||
"Processed and saved index 45\n",
|
||||
"Processed and saved index 46\n",
|
||||
"Processed and saved index 47\n",
|
||||
"Processed and saved index 48\n",
|
||||
"Processed and saved index 49\n",
|
||||
"Processed and saved index 50\n",
|
||||
"Processed and saved index 51\n",
|
||||
"Processed and saved index 52\n",
|
||||
"Processed and saved index 53\n",
|
||||
"Processed and saved index 54\n",
|
||||
"Processed and saved index 55\n",
|
||||
"Processed and saved index 56\n",
|
||||
"Processed and saved index 57\n",
|
||||
"Processed and saved index 58\n",
|
||||
"Processed and saved index 59\n",
|
||||
"Processed and saved index 60\n",
|
||||
"Processed and saved index 61\n",
|
||||
"Processed and saved index 62\n",
|
||||
"Processed and saved index 63\n",
|
||||
"Processed and saved index 64\n",
|
||||
"Processed and saved index 65\n",
|
||||
"Processed and saved index 66\n",
|
||||
"Processed and saved index 67\n",
|
||||
"Processed and saved index 68\n",
|
||||
"Processed and saved index 69\n",
|
||||
"Processed and saved index 70\n",
|
||||
"Processed and saved index 71\n",
|
||||
"Processed and saved index 72\n",
|
||||
"Processed and saved index 73\n",
|
||||
"No code block or questions found.\n",
|
||||
"None\n",
|
||||
"Processed and saved index 74\n",
|
||||
"Processed and saved index 75\n",
|
||||
"Processed and saved index 76\n",
|
||||
"Processed and saved index 77\n",
|
||||
"Processed and saved index 78\n",
|
||||
"Processed and saved index 79\n",
|
||||
"Processed and saved index 80\n",
|
||||
"Processed and saved index 81\n",
|
||||
"Processed and saved index 82\n",
|
||||
"Processed and saved index 83\n",
|
||||
"Processed and saved index 84\n",
|
||||
"Processed and saved index 85\n",
|
||||
"Processed and saved index 86\n",
|
||||
"Processed and saved index 87\n",
|
||||
"Processed and saved index 88\n",
|
||||
"Processed and saved index 89\n",
|
||||
"Processed and saved index 90\n",
|
||||
"Processed and saved index 91\n",
|
||||
"Processed and saved index 92\n",
|
||||
"Processed and saved index 93\n",
|
||||
"Processed and saved index 94\n",
|
||||
"Processed and saved index 95\n",
|
||||
"Processed and saved index 96\n",
|
||||
"Processed and saved index 97\n",
|
||||
"Processed and saved index 98\n",
|
||||
"Processed and saved index 99\n",
|
||||
"Processed and saved index 100\n",
|
||||
"Processed and saved index 101\n",
|
||||
"Processed and saved index 102\n",
|
||||
"Processed and saved index 103\n",
|
||||
"Processed and saved index 104\n",
|
||||
"Processed and saved index 105\n",
|
||||
"Processed and saved index 106\n",
|
||||
"Processed and saved index 107\n",
|
||||
"Processed and saved index 108\n",
|
||||
"Processed and saved index 109\n",
|
||||
"Processed and saved index 110\n",
|
||||
"Processed and saved index 111\n",
|
||||
"Processed and saved index 112\n",
|
||||
"Processed and saved index 113\n",
|
||||
"Processed and saved index 114\n",
|
||||
"Processed and saved index 115\n",
|
||||
"Processed and saved index 116\n",
|
||||
"Processed and saved index 117\n",
|
||||
"Processed and saved index 118\n",
|
||||
"Processed and saved index 119\n",
|
||||
"Processed and saved index 120\n",
|
||||
"Processed and saved index 121\n",
|
||||
"Processed and saved index 122\n",
|
||||
"Processed and saved index 123\n",
|
||||
"Processed and saved index 124\n",
|
||||
"Processed and saved index 125\n",
|
||||
"Processed and saved index 126\n",
|
||||
"Processed and saved index 127\n",
|
||||
"Processed and saved index 128\n",
|
||||
"Processed and saved index 129\n",
|
||||
"Processed and saved index 130\n",
|
||||
"Processed and saved index 131\n",
|
||||
"Processed and saved index 132\n",
|
||||
"Processed and saved index 133\n",
|
||||
"Processed and saved index 134\n",
|
||||
"Processed and saved index 135\n",
|
||||
"Processed and saved index 136\n",
|
||||
"Processed and saved index 137\n",
|
||||
"Processed and saved index 138\n",
|
||||
"Processed and saved index 139\n",
|
||||
"Processed and saved index 140\n",
|
||||
"Processed and saved index 141\n",
|
||||
"Processed and saved index 142\n",
|
||||
"Processed and saved index 143\n",
|
||||
"Processed and saved index 144\n",
|
||||
"Processed and saved index 145\n",
|
||||
"Processed and saved index 146\n",
|
||||
"Processed and saved index 147\n",
|
||||
"Processed and saved index 148\n",
|
||||
"Processed and saved index 149\n",
|
||||
"Processed and saved index 150\n",
|
||||
"Processed and saved index 151\n",
|
||||
"Processed and saved index 152\n",
|
||||
"Processed and saved index 153\n",
|
||||
"Processed and saved index 154\n",
|
||||
"Processed and saved index 155\n",
|
||||
"Processed and saved index 156\n",
|
||||
"Processed and saved index 157\n",
|
||||
"Processed and saved index 158\n",
|
||||
"Processed and saved index 159\n",
|
||||
"Processed and saved index 160\n",
|
||||
"Processed and saved index 161\n",
|
||||
"Processed and saved index 162\n",
|
||||
"Processed and saved index 163\n",
|
||||
"Processed and saved index 164\n",
|
||||
"Processed and saved index 165\n",
|
||||
"Processed and saved index 166\n",
|
||||
"Processed and saved index 167\n",
|
||||
"Processed and saved index 168\n",
|
||||
"Processed and saved index 169\n",
|
||||
"Processed and saved index 170\n",
|
||||
"Processed and saved index 171\n",
|
||||
"Processed and saved index 172\n",
|
||||
"Processed and saved index 173\n",
|
||||
"Processed and saved index 174\n",
|
||||
"Processed and saved index 175\n",
|
||||
"Processed and saved index 176\n",
|
||||
"Processed and saved index 177\n",
|
||||
"Processed and saved index 178\n",
|
||||
"Processed and saved index 179\n",
|
||||
"Processed and saved index 180\n",
|
||||
"Processed and saved index 181\n",
|
||||
"Processed and saved index 182\n",
|
||||
"Processed and saved index 183\n",
|
||||
"Processed and saved index 184\n",
|
||||
"Processed and saved index 185\n",
|
||||
"Processed and saved index 186\n",
|
||||
"Processed and saved index 187\n",
|
||||
"Processed and saved index 188\n",
|
||||
"Processed and saved index 189\n",
|
||||
"Processed and saved index 190\n",
|
||||
"Processed and saved index 191\n",
|
||||
"Processed and saved index 192\n",
|
||||
"Processed and saved index 193\n",
|
||||
"Processed and saved index 194\n",
|
||||
"Processed and saved index 195\n",
|
||||
"Processed and saved index 196\n",
|
||||
"Processed and saved index 197\n",
|
||||
"Processed and saved index 198\n",
|
||||
"Processed and saved index 199\n",
|
||||
"Processed and saved index 200\n",
|
||||
"Processed and saved index 201\n",
|
||||
"Processed and saved index 202\n",
|
||||
"Processed and saved index 203\n",
|
||||
"Processed and saved index 204\n",
|
||||
"Processed and saved index 205\n",
|
||||
"Processed and saved index 206\n",
|
||||
"Processed and saved index 207\n",
|
||||
"Processed and saved index 208\n",
|
||||
"Processed and saved index 209\n",
|
||||
"Processed and saved index 210\n",
|
||||
"Processed and saved index 211\n",
|
||||
"Processed and saved index 212\n",
|
||||
"Processed and saved index 213\n",
|
||||
"Processed and saved index 214\n",
|
||||
"Processed and saved index 215\n",
|
||||
"Processed and saved index 216\n",
|
||||
"Processed and saved index 217\n",
|
||||
"Processed and saved index 218\n",
|
||||
"Processed and saved index 219\n",
|
||||
"Processed and saved index 220\n",
|
||||
"Processed and saved index 221\n",
|
||||
"Processed and saved index 222\n",
|
||||
"Processed and saved index 223\n",
|
||||
"Processed and saved index 224\n",
|
||||
"Processed and saved index 225\n",
|
||||
"Processed and saved index 226\n",
|
||||
"Processed and saved index 227\n",
|
||||
"Processed and saved index 228\n",
|
||||
"Processed and saved index 229\n",
|
||||
"Processed and saved index 230\n",
|
||||
"Processed and saved index 231\n",
|
||||
"Processed and saved index 232\n",
|
||||
"Processed and saved index 233\n",
|
||||
"Processed and saved index 234\n",
|
||||
"Processed and saved index 235\n",
|
||||
"Processed and saved index 236\n",
|
||||
"Processed and saved index 237\n",
|
||||
"Processed and saved index 238\n",
|
||||
"Processed and saved index 239\n",
|
||||
"Processed and saved index 240\n",
|
||||
"Processed and saved index 241\n",
|
||||
"Processed and saved index 242\n",
|
||||
"Processed and saved index 243\n",
|
||||
"Processed and saved index 244\n",
|
||||
"Processed and saved index 245\n",
|
||||
"Processed and saved index 246\n",
|
||||
"Processed and saved index 247\n",
|
||||
"Processed and saved index 248\n",
|
||||
"Processed and saved index 249\n",
|
||||
"Processed and saved index 250\n",
|
||||
"Processed and saved index 251\n",
|
||||
"Processed and saved index 252\n",
|
||||
"Processed and saved index 253\n",
|
||||
"Processed and saved index 254\n",
|
||||
"Processed and saved index 255\n",
|
||||
"Processed and saved index 256\n",
|
||||
"Processed and saved index 257\n",
|
||||
"Processed and saved index 258\n",
|
||||
"Processed and saved index 259\n",
|
||||
"Processed and saved index 260\n",
|
||||
"Processed and saved index 261\n",
|
||||
"Processed and saved index 262\n",
|
||||
"Processed and saved index 263\n",
|
||||
"Processed and saved index 264\n",
|
||||
"Processed and saved index 265\n",
|
||||
"Processed and saved index 266\n",
|
||||
"Processed and saved index 267\n",
|
||||
"Processed and saved index 268\n",
|
||||
"Processed and saved index 269\n",
|
||||
"Processed and saved index 270\n",
|
||||
"Processed and saved index 271\n",
|
||||
"Processed and saved index 272\n",
|
||||
"Processed and saved index 273\n",
|
||||
"Processed and saved index 274\n",
|
||||
"Processed and saved index 275\n",
|
||||
"Processed and saved index 276\n",
|
||||
"Processed and saved index 277\n",
|
||||
"Processed and saved index 278\n",
|
||||
"Processed and saved index 279\n",
|
||||
"Processed and saved index 280\n",
|
||||
"Processed and saved index 281\n",
|
||||
"Processed and saved index 282\n",
|
||||
"Processed and saved index 283\n",
|
||||
"Processed and saved index 284\n",
|
||||
"Processed and saved index 285\n",
|
||||
"Processed and saved index 286\n",
|
||||
"Processed and saved index 287\n",
|
||||
"Processed and saved index 288\n",
|
||||
"Processed and saved index 289\n",
|
||||
"Processed and saved index 290\n",
|
||||
"Processed and saved index 291\n",
|
||||
"Processed and saved index 292\n",
|
||||
"Processed and saved index 293\n",
|
||||
"Processed and saved index 294\n",
|
||||
"Processed and saved index 295\n",
|
||||
"Processed and saved index 296\n",
|
||||
"Processed and saved index 297\n",
|
||||
"Processed and saved index 298\n",
|
||||
"Processed and saved index 299\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"CHECKPOINT_FILE = 'checkpoint_opposite.txt'\n",
|
||||
"OUTPUT_FILE = 'Step3_output_article_response_opposite_2.json'\n",
|
||||
"\n",
|
||||
"# 1. Load existing output if it exists (resume)\n",
|
||||
"if os.path.exists(OUTPUT_FILE):\n",
|
||||
" with open(OUTPUT_FILE, encoding='utf8') as f:\n",
|
||||
" article_response = json.load(f)\n",
|
||||
" print(f\"Resuming from existing output file: {OUTPUT_FILE}\")\n",
|
||||
"else:\n",
|
||||
" with open('step2-opposite.json', encoding='utf8') as f:\n",
|
||||
" article_response = json.load(f)\n",
|
||||
" print(\"Starting fresh from step2-opposite.json\")\n",
|
||||
"\n",
|
||||
"# 2. Read last processed index from checkpoint\n",
|
||||
"start = 0\n",
|
||||
"if os.path.exists(CHECKPOINT_FILE):\n",
|
||||
" with open(CHECKPOINT_FILE, 'r') as f:\n",
|
||||
" start = int(f.read().strip()) + 1\n",
|
||||
" print(f\"Resuming from index {start} (checkpoint found)\")\n",
|
||||
"\n",
|
||||
"end = len(article_response)\n",
|
||||
"\n",
|
||||
"# 3. Process from start to end\n",
|
||||
"for i in range(start, end):\n",
|
||||
" try:\n",
|
||||
" text = [l[2] for l in article_response[i]['response'][0]]\n",
|
||||
" scores = [l[3] for l in article_response[i]['response'][0]]\n",
|
||||
" res = analyzer.get_text_analysis(text)[0]\n",
|
||||
" \n",
|
||||
" for j in range(len(article_response[i]['response'][0])):\n",
|
||||
" article_response[i]['response'][0][j][3] = res[j]\n",
|
||||
" \n",
|
||||
" # Save JSON incrementally\n",
|
||||
" with open(OUTPUT_FILE, 'w', encoding='utf8') as f:\n",
|
||||
" json.dump(article_response, f, ensure_ascii=False)\n",
|
||||
" \n",
|
||||
" # Update checkpoint\n",
|
||||
" with open(CHECKPOINT_FILE, 'w') as f:\n",
|
||||
" f.write(str(i))\n",
|
||||
" \n",
|
||||
" print(f\"Processed and saved index {i}\")\n",
|
||||
" \n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"Error at index {i}: {e}\")\n",
|
||||
" break"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.13"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "0e4e4b7d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"with open('Step3_output_article_response_original_3.json', encoding=\"utf8\") as f:\n",
|
||||
" data_pos = json.load(f)\n",
|
||||
"with open('Step3_output_article_response_opposite_2.json', encoding=\"utf8\") as f:\n",
|
||||
" data_neg = json.load(f)\n",
|
||||
" "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"id": "e7b83fa4",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"combined_list = []\n",
|
||||
"for art_id in range(len(data_pos)):\n",
|
||||
" entry = {}\n",
|
||||
" entry['article_id'] = art_id\n",
|
||||
" entry['article_name'] = data_pos[art_id]['title']\n",
|
||||
" entry['article_institute'] = data_pos[art_id]['universities']\n",
|
||||
" entry['article_abstract'] = data_pos[art_id]['abstract']\n",
|
||||
" \n",
|
||||
" entry['measures_positive'] = [l[0] for l in data_pos[art_id]['response'][0][:]]\n",
|
||||
" entry['responses_positive'] = [l[2] for l in data_pos[art_id]['response'][0][:]]\n",
|
||||
" entry['scores_positive'] = [l[3] for l in data_pos[art_id]['response'][0][:]]\n",
|
||||
" \n",
|
||||
" entry['measures_negative'] = [l[0] for l in data_neg[art_id]['response'][0][:]]\n",
|
||||
" entry['responses_negative'] = [l[2] for l in data_neg[art_id]['response'][0][:]]\n",
|
||||
" entry['scores_negative'] = [l[3] for l in data_neg[art_id]['response'][0][:]]\n",
|
||||
"\n",
|
||||
" combined_list += [entry]\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "24d0c9b5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"with open('output/Step4_article_response_all_combined_2.json', 'w', encoding=\"utf8\") as f:\n",
|
||||
" json.dump(combined_list, f, ensure_ascii=False)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "0cda7716",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import csv\n",
|
||||
"\n",
|
||||
"def write_csv(filename, score_lbl='scores_positive'):\n",
|
||||
" # Open the file in append mode\n",
|
||||
" with open(filename, mode='w', newline='', encoding=\"utf-8-sig\") as file:\n",
|
||||
" writer = csv.writer(file)\n",
|
||||
"\n",
|
||||
" labels = ['Value '+str(j) for j in range(64)]\n",
|
||||
"\n",
|
||||
" writer.writerow(['Institute'] + labels)\n",
|
||||
"\n",
|
||||
" for i in range(0, len(combined_list)):\n",
|
||||
" article = combined_list[i]\n",
|
||||
" uni = article['article_institute']\n",
|
||||
" rankings = article[score_lbl]\n",
|
||||
" writer.writerow([uni]+rankings)\n",
|
||||
" \n",
|
||||
"write_csv(filename='output/Step4_scores_positive_2.csv', score_lbl='scores_positive')\n",
|
||||
"write_csv(filename='output/Step4_scores_negative_2.csv', score_lbl='scores_negative')"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.13"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
Institute,Value 0,Value 1,Value 2,Value 3,Value 4,Value 5,Value 6,Value 7,Value 8,Value 9,Value 10,Value 11,Value 12,Value 13,Value 14,Value 15,Value 16,Value 17,Value 18,Value 19,Value 20,Value 21,Value 22,Value 23,Value 24,Value 25,Value 26,Value 27,Value 28,Value 29,Value 30,Value 31,Value 32,Value 33,Value 34,Value 35,Value 36,Value 37,Value 38,Value 39,Value 40,Value 41,Value 42,Value 43,Value 44,Value 45,Value 46,Value 47,Value 48,Value 49,Value 50,Value 51,Value 52,Value 53,Value 54,Value 55,Value 56,Value 57,Value 58,Value 59,Value 60,Value 61,Value 62,Value 63
|
||||
دانشگاه الزهرا (س),0.8888888888888888,0.6666666666666666,0.8888888888888888,0.7777777777777778,0.2222222222222222,0.8888888888888888,0.7777777777777778,0.5555555555555556
|
||||
دانشگاه تحصیلات تکمیلی صنعتی و فناوری پیشرفته کرمان,1.0,0.4444444444444444,0.2222222222222222,0.3333333333333333,0.2222222222222222,0.3333333333333333,0.5555555555555556,0.8888888888888888
|
||||
دانشگاه ارومیه,0.8888888888888888,0.5555555555555556,1.0,0.8888888888888888,0.0,1.0,0.2222222222222222,0.3333333333333333
|
||||
دانشگاه شهید بهشتی,1.0,0.8888888888888888,1.0,1.0,1.0,1.0,0.5555555555555556,0.8888888888888888
|
||||
دانشگاه تربیت مدرس,0.8888888888888888,0.6666666666666666,1.0,0.8888888888888888,0.7777777777777778,1.0,0.5555555555555556,0.5555555555555556
|
||||
موسسه آموزش عالی یاسین,0.8888888888888888,0.2222222222222222,0.6666666666666666,0.8888888888888888,0.6666666666666666,0.7777777777777778,0.5555555555555556,0.3333333333333333
|
||||
دانشگاه امام صادق علیه السلام,0.1111111111111111,0.2222222222222222,0.8888888888888888,1.0,1.0,0.2222222222222222,0.5555555555555556,0.4444444444444444
|
||||
دانشگاه الزهرا (س),1.0,0.1111111111111111,1.0,1.0,1.0,0.2222222222222222,0.2222222222222222,0.3333333333333333
|
||||
|
@@ -0,0 +1,9 @@
|
||||
Institute,Value 0,Value 1,Value 2,Value 3,Value 4,Value 5,Value 6,Value 7,Value 8,Value 9,Value 10,Value 11,Value 12,Value 13,Value 14,Value 15,Value 16,Value 17,Value 18,Value 19,Value 20,Value 21,Value 22,Value 23,Value 24,Value 25,Value 26,Value 27,Value 28,Value 29,Value 30,Value 31,Value 32,Value 33,Value 34,Value 35,Value 36,Value 37,Value 38,Value 39,Value 40,Value 41,Value 42,Value 43,Value 44,Value 45,Value 46,Value 47,Value 48,Value 49,Value 50,Value 51,Value 52,Value 53,Value 54,Value 55,Value 56,Value 57,Value 58,Value 59,Value 60,Value 61,Value 62,Value 63
|
||||
دانشگاه الزهرا (س),0.5555555555555556,0.5555555555555556,0.5555555555555556,0.7777777777777778,0.6666666666666666,0.5555555555555556,0.3333333333333333,0.6666666666666666
|
||||
دانشگاه تحصیلات تکمیلی صنعتی و فناوری پیشرفته کرمان,0.2222222222222222,0.4444444444444444,0.2222222222222222,0.2222222222222222,0.2222222222222222,0.3333333333333333,0.5555555555555556,0.2222222222222222
|
||||
دانشگاه ارومیه,0.2222222222222222,0.4444444444444444,0.2222222222222222,0.3333333333333333,0.2222222222222222,0.3333333333333333,0.2222222222222222,0.2222222222222222
|
||||
دانشگاه شهید بهشتی,0.5555555555555556,0.6666666666666666,0.6666666666666666,0.4444444444444444,0.4444444444444444,0.7777777777777778,0.4444444444444444,0.6666666666666666
|
||||
دانشگاه تربیت مدرس,0.4444444444444444,0.4444444444444444,0.5555555555555556,0.4444444444444444,0.3333333333333333,0.6666666666666666,0.5555555555555556,0.4444444444444444
|
||||
موسسه آموزش عالی یاسین,0.1111111111111111,0.4444444444444444,0.5555555555555556,0.3333333333333333,0.4444444444444444,0.3333333333333333,0.2222222222222222,0.3333333333333333
|
||||
دانشگاه امام صادق علیه السلام,0.4444444444444444,0.5555555555555556,0.7777777777777778,0.5555555555555556,0.4444444444444444,0.3333333333333333,0.3333333333333333,0.5555555555555556
|
||||
دانشگاه الزهرا (س),0.2222222222222222,0.4444444444444444,0.3333333333333333,0.2222222222222222,0.4444444444444444,0.3333333333333333,0.4444444444444444,0.3333333333333333
|
||||
|
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
300
|
||||
@@ -0,0 +1 @@
|
||||
299
|
||||
@@ -0,0 +1 @@
|
||||
289
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,9 @@
|
||||
Institute,Value 0,Value 1,Value 2,Value 3,Value 4,Value 5,Value 6,Value 7,Value 8,Value 9,Value 10,Value 11,Value 12,Value 13,Value 14,Value 15,Value 16,Value 17,Value 18,Value 19,Value 20,Value 21,Value 22,Value 23,Value 24,Value 25,Value 26,Value 27,Value 28,Value 29,Value 30,Value 31,Value 32,Value 33,Value 34,Value 35,Value 36,Value 37,Value 38,Value 39,Value 40,Value 41,Value 42,Value 43,Value 44,Value 45,Value 46,Value 47,Value 48,Value 49,Value 50,Value 51,Value 52,Value 53,Value 54,Value 55,Value 56,Value 57,Value 58,Value 59,Value 60,Value 61,Value 62,Value 63
|
||||
دانشگاه الزهرا (س),0.5555555555555556,0.8888888888888888,0.7777777777777778,0.6666666666666666,0.5555555555555556,0.8888888888888888,0.5555555555555556,0.8888888888888888
|
||||
دانشگاه تحصیلات تکمیلی صنعتی و فناوری پیشرفته کرمان,0.3333333333333333,0.3333333333333333,0.2222222222222222,0.3333333333333333,0.5555555555555556,0.2222222222222222,0.2222222222222222,0.5555555555555556
|
||||
دانشگاه ارومیه,0.8888888888888888,0.8888888888888888,0.4444444444444444,0.4444444444444444,0.7777777777777778,0.5555555555555556,0.4444444444444444,0.5555555555555556
|
||||
دانشگاه شهید بهشتی,1.0,1.0,0.4444444444444444,1.0,1.0,0.8888888888888888,0.6666666666666666,0.7777777777777778
|
||||
دانشگاه تربیت مدرس,0.8888888888888888,0.8888888888888888,0.8888888888888888,0.3333333333333333,0.8888888888888888,1.0,0.7777777777777778,0.3333333333333333
|
||||
موسسه آموزش عالی یاسین,1.0,1.0,0.0,0.8888888888888888,0.4444444444444444,1.0,0.5555555555555556,0.3333333333333333
|
||||
دانشگاه امام صادق علیه السلام,1.0,0.8888888888888888,0.8888888888888888,0.3333333333333333,1.0,1.0,0.7777777777777778,0.2222222222222222
|
||||
دانشگاه الزهرا (س),1.0,0.8888888888888888,1.0,0.0,0.4444444444444444,0.6666666666666666,0.5555555555555556,0.6666666666666666
|
||||
|
@@ -0,0 +1,291 @@
|
||||
Institute,Value 0,Value 1,Value 2,Value 3,Value 4,Value 5,Value 6,Value 7,Value 8,Value 9,Value 10,Value 11,Value 12,Value 13,Value 14,Value 15,Value 16,Value 17,Value 18,Value 19,Value 20,Value 21,Value 22,Value 23,Value 24,Value 25,Value 26,Value 27,Value 28,Value 29,Value 30,Value 31,Value 32,Value 33,Value 34,Value 35,Value 36,Value 37,Value 38,Value 39,Value 40,Value 41,Value 42,Value 43,Value 44,Value 45,Value 46,Value 47,Value 48,Value 49,Value 50,Value 51,Value 52,Value 53,Value 54,Value 55,Value 56,Value 57,Value 58,Value 59,Value 60,Value 61,Value 62,Value 63
|
||||
['دانشگاه تهران'],0.1111111111111111,1.0,0.1111111111111111,0.0,0.1111111111111111,1.0,0.0,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد مرودشت'],0.0,0.8888888888888888,0.0,0.2222222222222222,0.2222222222222222,0.2222222222222222,0.0,0.3333333333333333
|
||||
['موسسه آموزش عالی سارویه'],0.2222222222222222,0.1111111111111111,0.8888888888888888,0.2222222222222222,0.8888888888888888,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه علوم و معارف قرآن کریم'],1.0,0.4444444444444444,0.1111111111111111,0.0,0.2222222222222222,0.6666666666666666,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه پیام نور استان لرستان'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.3333333333333333,0.0,0.2222222222222222
|
||||
['دانشگاه تبریز'],0.6666666666666666,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.5555555555555556,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تربیت مدرس'],0.0,0.1111111111111111,0.2222222222222222,0.3333333333333333,0.1111111111111111,0.4444444444444444,0.0,0.2222222222222222
|
||||
['دانشگاه تبریز'],0.1111111111111111,0.0,0.2222222222222222,0.4444444444444444,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['جامعه المصطفی العالمیه'],0.3333333333333333,1.0,0.0,0.3333333333333333,0.0,0.2222222222222222,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد الکترونیکی'],0.1111111111111111,1.0,0.1111111111111111,0.0,0.2222222222222222,0.8888888888888888,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه شهید چمران اهواز'],1.0,0.5555555555555556,0.0,0.0,0.2222222222222222,1.0,0.0,0.3333333333333333
|
||||
['دانشگاه تهران'],0.1111111111111111,0.1111111111111111,0.3333333333333333,0.2222222222222222,0.1111111111111111,1.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه گیلان'],0.1111111111111111,1.0,0.0,0.8888888888888888,0.3333333333333333,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه گیلان'],0.4444444444444444,1.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه قم'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.8888888888888888,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد گچساران'],0.0,0.1111111111111111,1.0,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه ادیان و مذاهب'],0.0,0.2222222222222222,0.0,0.0,0.1111111111111111,0.5555555555555556,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه قم'],0.6666666666666666,1.0,1.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه قم'],0.3333333333333333,0.1111111111111111,0.0,0.0,0.2222222222222222,0.2222222222222222,0.0,0.3333333333333333
|
||||
['موسسه آموزش عالی بصیر - آبیک'],1.0,1.0,0.0,0.1111111111111111,0.6666666666666666,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد نراق'],0.3333333333333333,0.1111111111111111,0.2222222222222222,0.0,0.2222222222222222,0.5555555555555556,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],0.0,0.1111111111111111,0.2222222222222222,0.7777777777777778,0.2222222222222222,0.8888888888888888,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه شهید باهنر کرمان'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.6666666666666666,0.2222222222222222,0.3333333333333333,0.1111111111111111,0.4444444444444444
|
||||
['دانشگاه پیام نور استان سمنان'],0.8888888888888888,1.0,0.2222222222222222,0.2222222222222222,0.3333333333333333,0.2222222222222222,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد تهران مرکزی'],0.4444444444444444,1.0,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.0,0.0,0.4444444444444444
|
||||
['موسسه آموزش عالی ناصرخسرو'],0.0,1.0,0.0,0.1111111111111111,0.2222222222222222,0.6666666666666666,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه شهید مدنی آذربایجان'],0.2222222222222222,0.0,0.0,0.1111111111111111,0.1111111111111111,0.3333333333333333,0.0,0.3333333333333333
|
||||
['دانشگاه امام صادق علیه السلام'],1.0,0.1111111111111111,1.0,0.0,0.2222222222222222,0.0,0.0,0.1111111111111111
|
||||
['دانشگاه علوم قضایی و خدمات اداری'],0.8888888888888888,0.0,0.1111111111111111,0.8888888888888888,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.0,0.1111111111111111,0.1111111111111111,0.3333333333333333,0.3333333333333333,0.2222222222222222,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه قم'],0.8888888888888888,0.1111111111111111,1.0,0.0,0.2222222222222222,0.7777777777777778,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه خوارزمی'],0.1111111111111111,0.0,0.0,1.0,0.0,0.1111111111111111,0.0,0.1111111111111111
|
||||
['موسسه آموزش عالی نیما'],0.0,1.0,0.0,0.2222222222222222,0.1111111111111111,0.3333333333333333,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه مازندران'],0.5555555555555556,1.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.0
|
||||
['دانشگاه پیام نور استان مازندران'],1.0,0.1111111111111111,0.1111111111111111,0.0,0.4444444444444444,0.8888888888888888,0.0,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد مشکین\u200cشهر'],0.0,0.2222222222222222,0.1111111111111111,0.3333333333333333,0.1111111111111111,1.0,0.0,0.5555555555555556
|
||||
['دانشگاه آزاد اسلامی \u200cواحد مرودشت'],0.3333333333333333,0.0,0.1111111111111111,0.0,0.5555555555555556,0.1111111111111111,0.0,0.2222222222222222
|
||||
['دانشگاه شاهد'],0.6666666666666666,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.3333333333333333,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه تهران'],1.0,0.0,0.1111111111111111,0.3333333333333333,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان تهران'],0.1111111111111111,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.5555555555555556,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.0,1.0,0.1111111111111111,0.8888888888888888,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.8888888888888888,1.0,0.0,0.3333333333333333,0.0,0.0,0.0,0.2222222222222222
|
||||
['دانشگاه پیام نور استان گیلان'],0.2222222222222222,0.0,1.0,0.2222222222222222,0.1111111111111111,0.8888888888888888,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی \u200cواحد مرودشت'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.5555555555555556,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه آزاد اسلامی واحد صفادشت'],1.0,0.2222222222222222,0.1111111111111111,1.0,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه قم'],0.0,0.0,0.2222222222222222,0.5555555555555556,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه خوارزمی'],0.0,1.0,0.0,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان فارس'],0.0,0.0,0.2222222222222222,1.0,0.2222222222222222,1.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد نراق'],0.3333333333333333,0.6666666666666666,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.0,0.4444444444444444
|
||||
['موسسه آموزش عالی باختر ایلام'],0.1111111111111111,0.7777777777777778,0.1111111111111111,1.0,0.2222222222222222,0.3333333333333333,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد بهشهر'],0.8888888888888888,0.3333333333333333,0.0,1.0,0.1111111111111111,1.0,0.0,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد بندرعباس'],0.8888888888888888,0.1111111111111111,0.2222222222222222,1.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.4444444444444444
|
||||
['دانشگاه اردکان'],0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.4444444444444444,0.4444444444444444,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],1.0,0.0,0.0,0.0,0.4444444444444444,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه ولی\u200cعصر (عج) رفسنجان'],0.4444444444444444,1.0,0.1111111111111111,0.0,0.0,0.2222222222222222,0.0,0.0
|
||||
['دانشگاه پیام نور استان فارس'],0.2222222222222222,0.0,0.0,0.2222222222222222,0.2222222222222222,0.4444444444444444,0.0,0.1111111111111111
|
||||
['دانشگاه قم'],0.1111111111111111,0.8888888888888888,0.0,1.0,0.1111111111111111,0.5555555555555556,0.0,0.2222222222222222
|
||||
['دانشگاه خوارزمی'],0.0,0.0,0.1111111111111111,1.0,0.3333333333333333,1.0,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه شهید بهشتی'],1.0,0.0,0.0,0.0,0.4444444444444444,0.6666666666666666,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه گیلان'],0.3333333333333333,1.0,0.2222222222222222,0.8888888888888888,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد الکترونیکی'],0.0,0.0,0.1111111111111111,1.0,0.2222222222222222,0.0,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.6666666666666666,0.2222222222222222,0.0,1.0,0.2222222222222222,1.0,0.0,0.1111111111111111
|
||||
['موسسه آموزش عالی آمل'],1.0,0.0,0.0,0.1111111111111111,0.5555555555555556,0.1111111111111111,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه خوارزمی'],1.0,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه خوارزمی'],0.2222222222222222,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه شاهد'],1.0,0.1111111111111111,0.0,0.0,0.2222222222222222,0.3333333333333333,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه قم'],0.1111111111111111,0.6666666666666666,1.0,0.2222222222222222,0.3333333333333333,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['موسسه آموزش عالی عطار'],0.1111111111111111,0.2222222222222222,1.0,0.1111111111111111,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه قم'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.0,0.4444444444444444
|
||||
['دانشگاه مازندران'],1.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222,1.0,0.3333333333333333,0.1111111111111111
|
||||
['دانشگاه پیام نور استان گیلان'],1.0,0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['موسسه آموزش عالی خرد - بوشهر'],1.0,1.0,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه مازندران'],0.1111111111111111,0.1111111111111111,0.0,0.8888888888888888,0.2222222222222222,0.1111111111111111,0.0,0.2222222222222222
|
||||
['دانشگاه شاهد'],0.8888888888888888,0.0,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.0,0.2222222222222222,0.5555555555555556
|
||||
['دانشگاه پیام نور استان کرمانشاه'],0.0,0.1111111111111111,0.0,1.0,0.0,0,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه قم'],0.3333333333333333,0.0,0.3333333333333333,0.2222222222222222,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['موسسه آموزش عالی طلوع مهر - قم'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.6666666666666666,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان البرز'],0.3333333333333333,1.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد شهریار'],1.0,0.2222222222222222,0.2222222222222222,0.8888888888888888,0.3333333333333333,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشکده روابط بین الملل وزارت امورخارجه'],1.0,0.0,0.0,0.6666666666666666,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.2222222222222222,1.0,0.1111111111111111,0.2222222222222222,0.0,1.0,0.0,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد صفادشت'],1.0,0.0,0.0,1.0,0.0,0.0,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],1.0,0.0,0.3333333333333333,0.1111111111111111,0.2222222222222222,0.3333333333333333,0.3333333333333333,0.0
|
||||
['دانشگاه امام صادق علیه السلام'],1.0,0.0,0.1111111111111111,1.0,0.6666666666666666,0.1111111111111111,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه سیستان و بلوچستان'],0.0,0.4444444444444444,0.0,0.4444444444444444,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.3333333333333333
|
||||
['موسسه آموزش عالی آمل'],0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0,0.3333333333333333,0.1111111111111111,0.1111111111111111
|
||||
['پردیس نسیبه تهران'],1.0,0.1111111111111111,0.1111111111111111,0.6666666666666666,0.5555555555555556,0.1111111111111111,0.0,0.2222222222222222
|
||||
['دانشگاه خوارزمی'],1.0,0.0,1.0,1.0,0.2222222222222222,0.2222222222222222,0.0,0.1111111111111111
|
||||
['موسسه آموزش عالی مقدس اردبیلی'],0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],0.3333333333333333,0.0,0.1111111111111111,1.0,0.2222222222222222,0.4444444444444444,0.0,0.1111111111111111
|
||||
['دانشگاه قم'],1.0,0.0,0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه امام صادق علیه السلام'],1.0,0.1111111111111111,0.2222222222222222,0.0,0.2222222222222222,0.7777777777777778,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد دماوند'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.8888888888888888,0.1111111111111111,0.0,0.3333333333333333
|
||||
['دانشگاه پیام نور استان تهران'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی \u200cواحد اردبیل'],0.1111111111111111,0.1111111111111111,0.1111111111111111,1.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه زابل'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه قم'],0.2222222222222222,1.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.3333333333333333,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه تبریز'],0.1111111111111111,0.8888888888888888,0.8888888888888888,0.5555555555555556,0.2222222222222222,0.2222222222222222,0.0,0.2222222222222222
|
||||
['دانشگاه تبریز'],1.0,0.7777777777777778,0.0,0.1111111111111111,0.3333333333333333,0.0,0.1111111111111111,0.1111111111111111
|
||||
['موسسه آموزش عالی عطار'],1.0,1.0,0.0,0.0,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه خوارزمی'],1.0,0.8888888888888888,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],1.0,0.0,0.0,0.2222222222222222,0.1111111111111111,1.0,0.0,0.2222222222222222
|
||||
['دانشگاه صنعتی شاهرود'],0.2222222222222222,0.5555555555555556,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.5555555555555556,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان تهران'],1.0,0.1111111111111111,1.0,0.8888888888888888,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه علوم و معارف قرآن کریم'],1.0,0.1111111111111111,0.3333333333333333,0.8888888888888888,0.2222222222222222,0.2222222222222222,0.0,0.1111111111111111
|
||||
['دانشگاه قم'],0.8888888888888888,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.4444444444444444,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه یزد'],0.1111111111111111,0.1111111111111111,0.2222222222222222,0.3333333333333333,0.3333333333333333,0.2222222222222222,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه پیام نور استان تهران'],0.2222222222222222,0.1111111111111111,0.2222222222222222,0.0,0.4444444444444444,0.1111111111111111,0.0,0.2222222222222222
|
||||
['دانشگاه ایلام'],1.0,0.0,0.4444444444444444,0.8888888888888888,0.4444444444444444,0.2222222222222222,0.3333333333333333,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد قائم\u200cشهر'],1.0,0.1111111111111111,0.0,0.2222222222222222,0.2222222222222222,0.0,0.0,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.8888888888888888,0.2222222222222222,0.1111111111111111,0.0,0.3333333333333333,0.2222222222222222,0.0,0.1111111111111111
|
||||
['دانشگاه باقرالعلوم علیه السلام'],0.6666666666666666,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.8888888888888888,0.2222222222222222,0.2222222222222222
|
||||
['جامعه المصطفی العالمیه'],0.0,1.0,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.4444444444444444,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد شهریار'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه پیام نور استان البرز'],0.1111111111111111,1.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.0
|
||||
['دانشگاه تبریز'],0.7777777777777778,0.6666666666666666,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.0,0.0,0.2222222222222222
|
||||
['دانشگاه تهران'],0.1111111111111111,0.0,0.1111111111111111,0.0,0.3333333333333333,0.2222222222222222,0.0,0.2222222222222222
|
||||
['دانشگاه مازندران'],1.0,1.0,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0
|
||||
['دانشگاه گیلان'],0.8888888888888888,0.0,0.1111111111111111,0.5555555555555556,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان مرکزی'],0.1111111111111111,0.0,0.0,0.0,0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111
|
||||
['دانشگاه قم'],0.0,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.5555555555555556,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان سمنان'],0.0,0.8888888888888888,0.1111111111111111,0.0,0.1111111111111111,0.8888888888888888,0.0,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی \u200cواحد گچساران'],1.0,0.8888888888888888,0.3333333333333333,1.0,0.1111111111111111,0.3333333333333333,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,1.0,0.1111111111111111,0.2222222222222222,0.3333333333333333,0.2222222222222222,0.0,0.0
|
||||
['دانشگاه شاهد'],1.0,1.0,0.1111111111111111,1.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان یزد'],0.1111111111111111,0.2222222222222222,0.2222222222222222,0.8888888888888888,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه آیت\u200cالله حائری میبد'],0.1111111111111111,0.4444444444444444,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.4444444444444444
|
||||
['دانشگاه سیستان و بلوچستان'],0.1111111111111111,0.2222222222222222,0.2222222222222222,1.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه تبریز'],1.0,0.1111111111111111,0.0,1.0,0.4444444444444444,0.2222222222222222,0.2222222222222222,0.3333333333333333
|
||||
['دانشگاه زنجان'],0.1111111111111111,1.0,0.0,0.4444444444444444,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه یزد'],1.0,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.5555555555555556
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.0,1.0,0.2222222222222222,0.8888888888888888,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.5555555555555556
|
||||
['دانشگاه پیام نور استان خراسان رضوی'],0.0,0.1111111111111111,0.1111111111111111,0.3333333333333333,0.1111111111111111,1.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد دماوند'],0.0,0.1111111111111111,0.0,0.5555555555555556,0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111
|
||||
['موسسه آموزش عالی پارس'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.3333333333333333,0.0,0.1111111111111111
|
||||
['دانشگاه مازندران'],1.0,0.1111111111111111,0.0,0.8888888888888888,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه علوم و معارف قرآن کریم'],1.0,0.2222222222222222,0.0,1.0,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],1.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.4444444444444444,0.2222222222222222
|
||||
['دانشگاه پیام نور استان مازندران'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.8888888888888888,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد دماوند'],0.3333333333333333,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه قم'],0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه ارومیه'],0.8888888888888888,0.1111111111111111,1.0,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.4444444444444444,0.4444444444444444
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],1.0,1.0,0.1111111111111111,0.0,0.3333333333333333,0.8888888888888888,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان ایلام'],1.0,0.8888888888888888,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد ماکو'],1.0,0.6666666666666666,0.6666666666666666,0.1111111111111111,0.3333333333333333,0.2222222222222222,0.0,0.4444444444444444
|
||||
['دانشگاه علامه طباطبائی'],0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.1111111111111111,0.3333333333333333,1.0,0.0,0.2222222222222222,0.3333333333333333,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه ولی\u200cعصر (عج) رفسنجان'],0.3333333333333333,1.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,1.0,0.4444444444444444,0.2222222222222222
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.1111111111111111,0.0,0.1111111111111111,0.8888888888888888,0.2222222222222222,0.7777777777777778,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه تهران'],0.0,0.1111111111111111,0.1111111111111111,1.0,0.0,0.8888888888888888,0.2222222222222222,0.3333333333333333
|
||||
['دانشگاه آزاد اسلامی \u200cواحد قم'],1.0,0.1111111111111111,0.0,0.7777777777777778,0.1111111111111111,1.0,0.0,0.3333333333333333
|
||||
['دانشگاه علوم و معارف قرآن کریم'],1.0,0.0,0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,0.4444444444444444,0.2222222222222222,0.7777777777777778,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0
|
||||
['دانشگاه پیام نور استان مازندران'],0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.1111111111111111,1.0,0.1111111111111111,0.0
|
||||
['دانشگاه آزاد اسلامی واحد تهران مرکزی'],0.2222222222222222,0.1111111111111111,0.6666666666666666,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],0.1111111111111111,0.0,0.8888888888888888,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه علوم و معارف قرآن کریم'],1.0,0.1111111111111111,0.0,0.4444444444444444,0.2222222222222222,0.5555555555555556,0.2222222222222222,0.5555555555555556
|
||||
['دانشگاه شهید باهنر کرمان'],1.0,0.1111111111111111,0.0,0.1111111111111111,0.0,0.3333333333333333,0.2222222222222222,0.4444444444444444
|
||||
['دانشگاه ارومیه'],0.8888888888888888,0.2222222222222222,0.0,1.0,0.2222222222222222,0.2222222222222222,0.0,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد تهران مرکزی'],1.0,1.0,0.4444444444444444,0.3333333333333333,0.5555555555555556,0.3333333333333333,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],1.0,1.0,0.0,0.1111111111111111,0.3333333333333333,0.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],0.0,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,1.0,0.3333333333333333,0.0
|
||||
['دانشگاه ارومیه'],0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.3333333333333333,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه تربیت مدرس'],0.1111111111111111,0.2222222222222222,0.2222222222222222,0.2222222222222222,0.5555555555555556,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان تهران'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.2222222222222222,0.0,0.0,0.0,0.0,0.5555555555555556,0.0,0.3333333333333333
|
||||
['دانشگاه قم'],0.1111111111111111,1.0,0.0,1.0,0.1111111111111111,0.4444444444444444,0.0,0.1111111111111111
|
||||
['موسسه آموزش عالی پرندک'],0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.2222222222222222,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.3333333333333333,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه آیت\u200cالله حائری میبد'],1.0,0.2222222222222222,0.0,0.4444444444444444,0.2222222222222222,0.0,0.0,0.2222222222222222
|
||||
['دانشگاه قم'],0.2222222222222222,0.0,0.0,0.8888888888888888,0.0,0.0,0.0,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.2222222222222222,0.0,0.1111111111111111,0.2222222222222222,0.0,0.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه شهید باهنر کرمان'],1.0,0.0,0.1111111111111111,0.0,0.3333333333333333,0.2222222222222222,0.1111111111111111,0.0
|
||||
['دانشگاه آزاد اسلامی واحد مشکین\u200cشهر'],1.0,0.1111111111111111,0.0,0.6666666666666666,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['موسسه آموزش عالی هدف'],0.3333333333333333,0.1111111111111111,0.0,0.1111111111111111,0.4444444444444444,0.5555555555555556,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد نراق'],0.8888888888888888,0.1111111111111111,0.4444444444444444,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['پژوهشگاه مهندسی بحران\u200cهای طبیعی شاخص پژوه ـ اصفهان'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,1.0,0.3333333333333333,0.0,0.1111111111111111
|
||||
['جامعه المصطفی العالمیه'],1.0,0.2222222222222222,0.0,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.2222222222222222,0.3333333333333333,0.2222222222222222,0.0
|
||||
['دانشگاه تبریز'],0.3333333333333333,0.1111111111111111,0.1111111111111111,1.0,0.1111111111111111,0.0,0.0,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد تهران مرکزی'],0.0,0.0,0.0,0.5555555555555556,0.1111111111111111,0.8888888888888888,0.0,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد دماوند'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.8888888888888888,0.0,0.5555555555555556,0.0,0.2222222222222222
|
||||
['دانشگاه تهران'],0.0,0.1111111111111111,0.0,0.2222222222222222,0.3333333333333333,0.0,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه امیرالمومنین (ع)'],0.3333333333333333,0.5555555555555556,0.1111111111111111,0.2222222222222222,0.1111111111111111,1.0,0.2222222222222222,0.2222222222222222
|
||||
['موسسه آموزش عالی عطار'],0.0,0.8888888888888888,0.0,1.0,0.1111111111111111,0.2222222222222222,0.3333333333333333,0.2222222222222222
|
||||
['دانشگاه شمال'],0.1111111111111111,0.0,0.3333333333333333,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تبریز'],1.0,0.2222222222222222,0.1111111111111111,0.0,0.2222222222222222,0.3333333333333333,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.5555555555555556,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه ولی\u200cعصر (عج) رفسنجان'],0.0,1.0,0.1111111111111111,0.5555555555555556,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تهران'],0.6666666666666666,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان ایلام'],0.5555555555555556,0.7777777777777778,0.1111111111111111,1.0,0.2222222222222222,1.0,0.0,0.3333333333333333
|
||||
['دانشگاه آزاد اسلامی و احد ایلام'],1.0,0.1111111111111111,0.1111111111111111,0.0,0.4444444444444444,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه محقق اردبیلی'],0.0,1.0,0.0,0.1111111111111111,0.6666666666666666,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه قم'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.5555555555555556,0.0,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه علامه طباطبائی'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.3333333333333333,0.0,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد دماوند'],0.6666666666666666,0.0,0.0,1.0,0.2222222222222222,0.4444444444444444,0.0,0.2222222222222222
|
||||
['دانشگاه بین\u200cالمللی اهل بیت (ع)'],1.0,0.0,0.6666666666666666,0.2222222222222222,0.6666666666666666,0.2222222222222222,0.2222222222222222,0.2222222222222222
|
||||
['موسسه آموزش عالی پرندک'],0.1111111111111111,0.8888888888888888,0.0,1.0,0.3333333333333333,0.2222222222222222,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه آزاد اسلامی واحد مشکین\u200cشهر'],0.1111111111111111,0.1111111111111111,0.0,1.0,0.1111111111111111,0.6666666666666666,0.3333333333333333,0.2222222222222222
|
||||
['دانشگاه سیستان و بلوچستان'],1.0,0.0,0.1111111111111111,0.2222222222222222,0.3333333333333333,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان تهران'],0.2222222222222222,0.1111111111111111,0.6666666666666666,0.8888888888888888,0.0,0.0,0.2222222222222222,0.2222222222222222
|
||||
['موسسه آموزش عالی سبحان'],1.0,0.1111111111111111,0.8888888888888888,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.0
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],1.0,0.0,0.0,0.8888888888888888,0.2222222222222222,0.8888888888888888,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان تهران'],0.6666666666666666,0.0,0.1111111111111111,0.8888888888888888,0.1111111111111111,0.1111111111111111,0.3333333333333333,0.2222222222222222
|
||||
['دانشگاه پیام نور استان اصفهان'],0.2222222222222222,0.0,0.0,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.0
|
||||
['دانشگاه پیام نور استان اصفهان'],0.0,0.0,0.5555555555555556,1.0,0.1111111111111111,0.8888888888888888,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه شهید بهشتی'],0.2222222222222222,0.5555555555555556,0.1111111111111111,1.0,0.2222222222222222,0.3333333333333333,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه پیام نور استان اصفهان'],0.6666666666666666,1.0,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه علامه طباطبائی'],1.0,0.7777777777777778,0.0,1.0,0.2222222222222222,0.2222222222222222,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.0,1.0,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه تربیت مدرس'],0.1111111111111111,0.7777777777777778,1.0,0.1111111111111111,0.3333333333333333,0.3333333333333333,0.1111111111111111,0.3333333333333333
|
||||
['موسسه آموزش عالی مهر کرمان'],0.3333333333333333,0.1111111111111111,0.0,0.1111111111111111,0.3333333333333333,0.3333333333333333,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه قم'],1.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه پیام نور استان البرز'],0.2222222222222222,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه پیام نور استان تهران'],0.1111111111111111,0.0,1.0,0.1111111111111111,0.4444444444444444,0.2222222222222222,0.4444444444444444,0.0
|
||||
['دانشگاه آزاد اسلامی \u200cواحد قم'],1.0,1.0,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه فردوسی مشهد'],1.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد خلخال'],0.4444444444444444,0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222
|
||||
['دانشگاه اصفهان'],0.1111111111111111,0.2222222222222222,0.3333333333333333,0.8888888888888888,0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد ساری'],0.3333333333333333,0.3333333333333333,0.7777777777777778,0.0,0.5555555555555556,0.1111111111111111,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه تبریز'],0.2222222222222222,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.3333333333333333,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه تهران'],0.8888888888888888,0.1111111111111111,0.8888888888888888,0.8888888888888888,1.0,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان تهران'],1.0,1.0,0.1111111111111111,0.8888888888888888,0.5555555555555556,0.2222222222222222,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه پیام نور استان هرمزگان'],0.1111111111111111,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.0,0.1111111111111111
|
||||
['دانشگاه خوارزمی'],1.0,0.8888888888888888,0.0,1.0,0.2222222222222222,0.2222222222222222,0.0,0.0
|
||||
['دانشگاه آزاد اسلامی \u200cواحد مرودشت'],0.0,1.0,0.3333333333333333,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.7777777777777778,0.6666666666666666
|
||||
['موسسه آموزش عالی کار'],1.0,0.0,0.0,0.2222222222222222,0.3333333333333333,0.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],1.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.3333333333333333,0.2222222222222222,0.5555555555555556
|
||||
['دانشگاه امام صادق علیه السلام'],1.0,1.0,0.1111111111111111,0.3333333333333333,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه سیستان و بلوچستان'],0.2222222222222222,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.5555555555555556,0.0,0.1111111111111111,0.6666666666666666,0.4444444444444444,0.1111111111111111,0.1111111111111111,0.8888888888888888
|
||||
['دانشگاه شیراز'],0.0,0.0,0.2222222222222222,0.1111111111111111,0.4444444444444444,0.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه قم'],0.0,0.1111111111111111,0.1111111111111111,1.0,0.3333333333333333,0.1111111111111111,0.6666666666666666,0.2222222222222222
|
||||
['دانشگاه علامه طباطبائی'],1.0,1.0,0.6666666666666666,0.8888888888888888,0.2222222222222222,0.4444444444444444,0.0,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد تهران جنوب'],0.1111111111111111,1.0,0.1111111111111111,0.6666666666666666,0.1111111111111111,0.0,0.1111111111111111,0.4444444444444444
|
||||
['دانشگاه شاهد'],1.0,0.0,0.1111111111111111,0.2222222222222222,0.3333333333333333,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد چالوس'],1.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه شمال'],0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه یزد'],0.0,1.0,0.1111111111111111,0.1111111111111111,0.0,0.3333333333333333,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد ماکو'],0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.0,0.2222222222222222,0.0,0.4444444444444444
|
||||
['دانشگاه خلیج فارس'],0.2222222222222222,1.0,0.1111111111111111,0.1111111111111111,0.0,0.0,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه علامه طباطبائی'],0.2222222222222222,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه زنجان'],0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.3333333333333333,0.2222222222222222,0.5555555555555556
|
||||
['دانشگاه پیام نور استان سمنان'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0,0.2222222222222222,0.0,0.2222222222222222
|
||||
['دانشگاه صنعتی سجاد'],0.2222222222222222,0.0,0.0,1.0,0.2222222222222222,0.2222222222222222,0.2222222222222222,0.5555555555555556
|
||||
['موسسه آموزش عالی باختر ایلام'],0.8888888888888888,0.0,0.1111111111111111,0.8888888888888888,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان یزد'],0.8888888888888888,0.1111111111111111,0.1111111111111111,0.8888888888888888,0.0,0.4444444444444444,0.0,0.2222222222222222
|
||||
['دانشگاه پیام نور استان اردبیل'],0.0,0.1111111111111111,0.0,0.0,0.0,1.0,0.0,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.0,1.0,0.1111111111111111,0.4444444444444444,0.0,0.1111111111111111,0.0,0.4444444444444444
|
||||
['دانشگاه یزد'],0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.5555555555555556,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,1.0,0.0,0.2222222222222222,0.3333333333333333,0.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.8888888888888888,0.0,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان تهران'],1.0,1.0,0.1111111111111111,0.0,0.2222222222222222,0.6666666666666666,0.0,0.5555555555555556
|
||||
['دانشگاه امام صادق علیه السلام'],0.6666666666666666,0.0,0.2222222222222222,0.4444444444444444,0.1111111111111111,0.5555555555555556,0.0,0.1111111111111111
|
||||
['موسسه آموزش عالی پرندک'],0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان خراسان رضوی'],1.0,1.0,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه تهران'],1.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,1.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان تهران'],0.8888888888888888,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.5555555555555556,0.0,0.1111111111111111
|
||||
['دانشگاه پیام نور استان خراسان رضوی'],1.0,0.8888888888888888,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],1.0,0.8888888888888888,0.3333333333333333,1.0,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.2222222222222222,0.1111111111111111,0.1111111111111111,1.0,0.3333333333333333,0.2222222222222222,0.2222222222222222,0.1111111111111111
|
||||
['موسسه آموزش عالی هدف'],1.0,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه ارومیه'],1.0,0.1111111111111111,0.0,1.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد دماوند'],0.1111111111111111,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111,1.0,0.0,0.1111111111111111
|
||||
['موسسه آموزش عالی رجاء'],0.5555555555555556,0.2222222222222222,1.0,1.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد تهران مرکزی'],0.2222222222222222,0.2222222222222222,0.0,0.7777777777777778,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد لنجان'],1.0,0.1111111111111111,0.5555555555555556,0.2222222222222222,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان لرستان'],1.0,0.1111111111111111,0.0,0.8888888888888888,0.3333333333333333,0.4444444444444444,0.3333333333333333,0.1111111111111111
|
||||
['دانشگاه قم'],0.2222222222222222,1.0,0.2222222222222222,1.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['جامعه المصطفی العالمیه'],1.0,1.0,0.2222222222222222,0.1111111111111111,0.0,0.5555555555555556,0.5555555555555556,0.8888888888888888
|
||||
['دانشگاه آزاد اسلامی واحد الکترونیکی'],0.7777777777777778,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,1.0,0.0,0.3333333333333333
|
||||
['دانشگاه امام صادق علیه السلام'],1.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.8888888888888888,0.2222222222222222,0.0,0.0
|
||||
['دانشگاه خوارزمی'],0.1111111111111111,1.0,0.2222222222222222,0.8888888888888888,0.3333333333333333,0.1111111111111111,0.0,0.3333333333333333
|
||||
['دانشگاه ایلام'],0.0,0.1111111111111111,0.0,0.5555555555555556,0.2222222222222222,0.5555555555555556,0.0,0.1111111111111111
|
||||
['دانشگاه سیستان و بلوچستان'],1.0,1.0,1.0,0.0,0.2222222222222222,0.6666666666666666,0.0,0.2222222222222222
|
||||
['دانشگاه رازی'],0.8888888888888888,0.1111111111111111,0.0,0.0,0.3333333333333333,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.3333333333333333,0.4444444444444444
|
||||
['دانشگاه پیام نور استان لرستان'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.5555555555555556,0.2222222222222222,0.2222222222222222,0.4444444444444444
|
||||
['دانشگاه ارومیه'],0.1111111111111111,0.2222222222222222,0.0,0.8888888888888888,0.2222222222222222,0.2222222222222222,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد بندرعباس'],0.6666666666666666,1.0,0.0,0.1111111111111111,0.1111111111111111,0.3333333333333333,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.8888888888888888,0.0,0.3333333333333333,0.0,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,0.2222222222222222,0.0,0.8888888888888888,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد تهران مرکزی'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.3333333333333333,0.1111111111111111,0.8888888888888888,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه پیام نور استان تهران'],0.5555555555555556,0.1111111111111111,0.1111111111111111,1.0,0.2222222222222222,0.5555555555555556,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],1.0,0.4444444444444444,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد مرودشت'],0.8888888888888888,0.0,0.1111111111111111,0.6666666666666666,0.1111111111111111,1.0,0.0,0.0
|
||||
['دانشگاه الزهرا (س)'],1.0,1.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.0,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد مرودشت'],0.1111111111111111,0.6666666666666666,0.0,0.3333333333333333,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
|
@@ -0,0 +1,9 @@
|
||||
Institute,Value 0,Value 1,Value 2,Value 3,Value 4,Value 5,Value 6,Value 7,Value 8,Value 9,Value 10,Value 11,Value 12,Value 13,Value 14,Value 15,Value 16,Value 17,Value 18,Value 19,Value 20,Value 21,Value 22,Value 23,Value 24,Value 25,Value 26,Value 27,Value 28,Value 29,Value 30,Value 31,Value 32,Value 33,Value 34,Value 35,Value 36,Value 37,Value 38,Value 39,Value 40,Value 41,Value 42,Value 43,Value 44,Value 45,Value 46,Value 47,Value 48,Value 49,Value 50,Value 51,Value 52,Value 53,Value 54,Value 55,Value 56,Value 57,Value 58,Value 59,Value 60,Value 61,Value 62,Value 63
|
||||
دانشگاه الزهرا (س),0.2222222222222222,0.6666666666666666,0.4444444444444444,0.6666666666666666,0.5555555555555556,0.5555555555555556,0.7777777777777778,0.7777777777777778
|
||||
دانشگاه تحصیلات تکمیلی صنعتی و فناوری پیشرفته کرمان,0.2222222222222222,0.5555555555555556,0.3333333333333333,0.3333333333333333,0.3333333333333333,0.3333333333333333,0.3333333333333333,0.2222222222222222
|
||||
دانشگاه ارومیه,0.1111111111111111,0.4444444444444444,0.2222222222222222,0.3333333333333333,0.3333333333333333,0.3333333333333333,0.4444444444444444,0.2222222222222222
|
||||
دانشگاه شهید بهشتی,0.5555555555555556,0.7777777777777778,0.3333333333333333,0.5555555555555556,0.6666666666666666,0.6666666666666666,0.6666666666666666,0.8888888888888888
|
||||
دانشگاه تربیت مدرس,0.6666666666666666,0.5555555555555556,0.5555555555555556,0.4444444444444444,0.3333333333333333,0.5555555555555556,0.4444444444444444,0.5555555555555556
|
||||
موسسه آموزش عالی یاسین,0.2222222222222222,0.4444444444444444,0.2222222222222222,0.5555555555555556,0.3333333333333333,0.3333333333333333,0.4444444444444444,0.3333333333333333
|
||||
دانشگاه امام صادق علیه السلام,0.3333333333333333,0.4444444444444444,0.5555555555555556,0.6666666666666666,0.4444444444444444,0.4444444444444444,0.3333333333333333,0.5555555555555556
|
||||
دانشگاه الزهرا (س),0.3333333333333333,0.5555555555555556,0.4444444444444444,0.5555555555555556,0.3333333333333333,0.3333333333333333,0.4444444444444444,0.5555555555555556
|
||||
|
@@ -0,0 +1,291 @@
|
||||
Institute,Value 0,Value 1,Value 2,Value 3,Value 4,Value 5,Value 6,Value 7,Value 8,Value 9,Value 10,Value 11,Value 12,Value 13,Value 14,Value 15,Value 16,Value 17,Value 18,Value 19,Value 20,Value 21,Value 22,Value 23,Value 24,Value 25,Value 26,Value 27,Value 28,Value 29,Value 30,Value 31,Value 32,Value 33,Value 34,Value 35,Value 36,Value 37,Value 38,Value 39,Value 40,Value 41,Value 42,Value 43,Value 44,Value 45,Value 46,Value 47,Value 48,Value 49,Value 50,Value 51,Value 52,Value 53,Value 54,Value 55,Value 56,Value 57,Value 58,Value 59,Value 60,Value 61,Value 62,Value 63
|
||||
['دانشگاه تهران'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی \u200cواحد مرودشت'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.0,0.0,0.1111111111111111,0.2222222222222222
|
||||
['موسسه آموزش عالی سارویه'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه پیام نور استان لرستان'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه تبریز'],0.0,0.0,0.0,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه تربیت مدرس'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه تبریز'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.0,0.0,0.1111111111111111,0.1111111111111111
|
||||
['جامعه المصطفی العالمیه'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد الکترونیکی'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه شهید چمران اهواز'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تهران'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه گیلان'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.4444444444444444,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه گیلان'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.3333333333333333,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه قم'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0
|
||||
['دانشگاه آزاد اسلامی \u200cواحد گچساران'],0.1111111111111111,0.4444444444444444,0.1111111111111111,0.0,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه ادیان و مذاهب'],0.1111111111111111,0.2222222222222222,0.0,0.0,0.0,0.0,0.2222222222222222,0.0
|
||||
['دانشگاه قم'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه قم'],0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['موسسه آموزش عالی بصیر - آبیک'],0.1111111111111111,0.2222222222222222,0.0,0.2222222222222222,0.3333333333333333,0.1111111111111111,0.0,0.0
|
||||
['دانشگاه آزاد اسلامی واحد نراق'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه شهید باهنر کرمان'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان سمنان'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد تهران مرکزی'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['موسسه آموزش عالی ناصرخسرو'],0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.0
|
||||
['دانشگاه شهید مدنی آذربایجان'],0.2222222222222222,0.3333333333333333,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.6666666666666666,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.0,0.0,0.0
|
||||
['دانشگاه علوم قضایی و خدمات اداری'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.2222222222222222,0.0,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.5555555555555556,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه قم'],0.1111111111111111,0.3333333333333333,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه خوارزمی'],0.0,0.2222222222222222,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.0
|
||||
['موسسه آموزش عالی نیما'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه مازندران'],0.0,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان مازندران'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه آزاد اسلامی واحد مشکین\u200cشهر'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.3333333333333333,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد مرودشت'],0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.3333333333333333,0.1111111111111111
|
||||
['دانشگاه شاهد'],0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0,0.0
|
||||
['دانشگاه تهران'],0.1111111111111111,0.4444444444444444,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.0
|
||||
['دانشگاه پیام نور استان تهران'],0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.0,0.1111111111111111
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان گیلان'],0.1111111111111111,0.2222222222222222,0.0,0.0,0.0,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد مرودشت'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد صفادشت'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه قم'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه خوارزمی'],0.0,0.1111111111111111,0.2222222222222222,0.0,0.0,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه پیام نور استان فارس'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد نراق'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['موسسه آموزش عالی باختر ایلام'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد بهشهر'],0.1111111111111111,0.2222222222222222,0.0,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد بندرعباس'],0.0,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.0,0.0
|
||||
['دانشگاه اردکان'],0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.2222222222222222,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.1111111111111111,0.6666666666666666,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه ولی\u200cعصر (عج) رفسنجان'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.0,0.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان فارس'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه قم'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه خوارزمی'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.0,0.1111111111111111
|
||||
['دانشگاه شهید بهشتی'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.0,0.1111111111111111,0.0,0.0
|
||||
['دانشگاه گیلان'],0.0,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد الکترونیکی'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.2222222222222222
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.1111111111111111,0.2222222222222222,0.0,0.2222222222222222,0.0,0.0,0.2222222222222222,0.1111111111111111
|
||||
['موسسه آموزش عالی آمل'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0,0.0,0.1111111111111111
|
||||
['دانشگاه خوارزمی'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه خوارزمی'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه شاهد'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه قم'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0
|
||||
['موسسه آموزش عالی عطار'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه قم'],0.0,0.4444444444444444,0.0,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه مازندران'],0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0
|
||||
['دانشگاه پیام نور استان گیلان'],0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['موسسه آموزش عالی خرد - بوشهر'],0.1111111111111111,0.2222222222222222,0.0,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه مازندران'],0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.2222222222222222,0.2222222222222222,0.0
|
||||
['دانشگاه شاهد'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان کرمانشاه'],0.2222222222222222,0.2222222222222222,0.2222222222222222,0.0,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه قم'],0.0,0.2222222222222222,0,0.0,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['موسسه آموزش عالی طلوع مهر - قم'],0.0,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه پیام نور استان البرز'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه آزاد اسلامی واحد شهریار'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.0,0.2222222222222222,0.1111111111111111
|
||||
['دانشکده روابط بین الملل وزارت امورخارجه'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد صفادشت'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه پیام نور استان تهران'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه سیستان و بلوچستان'],0.0,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['موسسه آموزش عالی آمل'],0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['پردیس نسیبه تهران'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه خوارزمی'],0.2222222222222222,0.2222222222222222,0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['موسسه آموزش عالی مقدس اردبیلی'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0,0.3333333333333333,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه قم'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.0,0.0,0.2222222222222222,0.0
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد دماوند'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی \u200cواحد اردبیل'],0.0,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.0,0.0,0.0,0.0
|
||||
['دانشگاه زابل'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.0,0.2222222222222222,0.0
|
||||
['دانشگاه قم'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111,0.0,0.0
|
||||
['دانشگاه تبریز'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تبریز'],0.0,0.3333333333333333,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['موسسه آموزش عالی عطار'],0.0,0.2222222222222222,0.0,0.2222222222222222,0.0,0.0,0.0,0.2222222222222222
|
||||
['دانشگاه خوارزمی'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],0.0,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه صنعتی شاهرود'],0.1111111111111111,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.0,0.4444444444444444,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه قم'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه یزد'],0.2222222222222222,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.0,0.2222222222222222,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه ایلام'],0.0,0.2222222222222222,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد قائم\u200cشهر'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه باقرالعلوم علیه السلام'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['جامعه المصطفی العالمیه'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد شهریار'],0.1111111111111111,0.5555555555555556,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان البرز'],0.2222222222222222,0.4444444444444444,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.0
|
||||
['دانشگاه تبریز'],0.1111111111111111,0.2222222222222222,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه تهران'],0.2222222222222222,0.2222222222222222,0.1111111111111111,0.0,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه مازندران'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه گیلان'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0
|
||||
['دانشگاه پیام نور استان مرکزی'],0.0,0.2222222222222222,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه قم'],0.1111111111111111,0.2222222222222222,0.0,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه پیام نور استان سمنان'],0.0,0.3333333333333333,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد گچساران'],0.0,0.2222222222222222,0.1111111111111111,0.0,0.0,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه شاهد'],0.0,0.2222222222222222,0.0,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان یزد'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه آیت\u200cالله حائری میبد'],0.0,0.3333333333333333,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه سیستان و بلوچستان'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تبریز'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه زنجان'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.0
|
||||
['دانشگاه یزد'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه پیام نور استان خراسان رضوی'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.2222222222222222,0.0,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد دماوند'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['موسسه آموزش عالی پارس'],0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه مازندران'],0.0,0.1111111111111111,0.0,0.0,0.0,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.0
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه پیام نور استان مازندران'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0,0.0
|
||||
['دانشگاه آزاد اسلامی واحد دماوند'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.0,0.1111111111111111,0.0
|
||||
['دانشگاه قم'],0.1111111111111111,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه ارومیه'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان ایلام'],0.0,0.2222222222222222,0.0,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد ماکو'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه علامه طباطبائی'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.0,0.0,0.5555555555555556,0.2222222222222222
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه ولی\u200cعصر (عج) رفسنجان'],0.3333333333333333,0.3333333333333333,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه تهران'],0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد قم'],0.1111111111111111,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.3333333333333333
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.1111111111111111,0.1111111111111111,0.0,0.0,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان مازندران'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد تهران مرکزی'],0.1111111111111111,0.2222222222222222,0.0,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه شهید باهنر کرمان'],0.1111111111111111,0.0,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه ارومیه'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.0,0.0,0.3333333333333333,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد تهران مرکزی'],0.0,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه ارومیه'],0.0,0.3333333333333333,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222
|
||||
['دانشگاه تربیت مدرس'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه قم'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.3333333333333333,0.1111111111111111,0.1111111111111111
|
||||
['موسسه آموزش عالی پرندک'],0.1111111111111111,0.2222222222222222,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان تهران'],0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.2222222222222222
|
||||
['دانشگاه آیت\u200cالله حائری میبد'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه قم'],0.0,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.0
|
||||
['دانشگاه پیام نور استان تهران'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه شهید باهنر کرمان'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد مشکین\u200cشهر'],0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['موسسه آموزش عالی هدف'],0.0,0.0,0.1111111111111111,0.2222222222222222,0.0,0.0,0.1111111111111111,0.0
|
||||
['دانشگاه آزاد اسلامی واحد نراق'],0.0,0.3333333333333333,0.0,0.0,0.0,0.1111111111111111,0.1111111111111111,0.3333333333333333
|
||||
['پژوهشگاه مهندسی بحران\u200cهای طبیعی شاخص پژوه ـ اصفهان'],0.0,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['جامعه المصطفی العالمیه'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.3333333333333333,0.1111111111111111,0.0,0.0,0.2222222222222222,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه تبریز'],0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد تهران مرکزی'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.0,0.1111111111111111,0.0
|
||||
['دانشگاه آزاد اسلامی واحد دماوند'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تهران'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امیرالمومنین (ع)'],0.0,0.0,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['موسسه آموزش عالی عطار'],0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه شمال'],0.1111111111111111,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تبریز'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.0,0.0,0.1111111111111111
|
||||
['دانشگاه ولی\u200cعصر (عج) رفسنجان'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تهران'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه پیام نور استان ایلام'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.0,0.0,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی و احد ایلام'],0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه محقق اردبیلی'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.0
|
||||
['دانشگاه قم'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه علامه طباطبائی'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه آزاد اسلامی واحد دماوند'],0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.3333333333333333,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه بین\u200cالمللی اهل بیت (ع)'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['موسسه آموزش عالی پرندک'],0.0,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.3333333333333333
|
||||
['دانشگاه آزاد اسلامی واحد مشکین\u200cشهر'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه سیستان و بلوچستان'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه پیام نور استان تهران'],0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111
|
||||
['موسسه آموزش عالی سبحان'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه آزاد اسلامی واحد گرمی'],0.2222222222222222,0.0,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.0,0.0,0.1111111111111111,0.0
|
||||
['دانشگاه پیام نور استان اصفهان'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه پیام نور استان اصفهان'],0.2222222222222222,0.4444444444444444,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه شهید بهشتی'],0.1111111111111111,0.1111111111111111,0.0,0.0,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان اصفهان'],0.1111111111111111,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.0,0.0
|
||||
['دانشگاه علامه طباطبائی'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه علوم و معارف قرآن کریم'],0.0,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تربیت مدرس'],0.0,0.2222222222222222,0.0,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['موسسه آموزش عالی مهر کرمان'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه قم'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان البرز'],0.1111111111111111,0.3333333333333333,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد قم'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه فردوسی مشهد'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی \u200cواحد خلخال'],0.0,0.5555555555555556,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه اصفهان'],0.0,0.2222222222222222,0.0,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد ساری'],0.0,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تبریز'],0.0,0.0,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تهران'],0.0,0.4444444444444444,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222,0.0
|
||||
['دانشگاه پیام نور استان تهران'],0.0,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.0,0.2222222222222222
|
||||
['دانشگاه پیام نور استان هرمزگان'],0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه خوارزمی'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی \u200cواحد مرودشت'],0.0,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111
|
||||
['موسسه آموزش عالی کار'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.1111111111111111,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.4444444444444444,0.2222222222222222
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه سیستان و بلوچستان'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه شیراز'],0.1111111111111111,0.3333333333333333,0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه قم'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه علامه طباطبائی'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.3333333333333333,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد تهران جنوب'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه شاهد'],0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد چالوس'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه شمال'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه یزد'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد ماکو'],0.0,0.5555555555555556,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه خلیج فارس'],0.1111111111111111,0.2222222222222222,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه علامه طباطبائی'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه زنجان'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه پیام نور استان سمنان'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0,0.0
|
||||
['دانشگاه صنعتی سجاد'],0.1111111111111111,0.3333333333333333,0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['موسسه آموزش عالی باختر ایلام'],0.3333333333333333,0.5555555555555556,0.0,0.3333333333333333,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان یزد'],0.0,0.1111111111111111,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0
|
||||
['دانشگاه پیام نور استان اردبیل'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0
|
||||
['دانشگاه پیام نور استان تهران'],0.1111111111111111,0.6666666666666666,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه یزد'],0.0,0.2222222222222222,0.0,0.0,0.0,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.5555555555555556,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.3333333333333333,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.4444444444444444,0.0,0.1111111111111111,0.2222222222222222,0.0,0.2222222222222222,0.1111111111111111
|
||||
['موسسه آموزش عالی پرندک'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان خراسان رضوی'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه تهران'],0.1111111111111111,0.1111111111111111,0.0,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه پیام نور استان خراسان رضوی'],0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0
|
||||
['دانشگاه صدا و سیمای جمهوری اسلامی ایران'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222
|
||||
['موسسه آموزش عالی هدف'],0.1111111111111111,0.1111111111111111,0.0,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.2222222222222222
|
||||
['دانشگاه ارومیه'],0.1111111111111111,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.5555555555555556
|
||||
['دانشگاه آزاد اسلامی واحد دماوند'],0.0,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.0,0.2222222222222222,0.2222222222222222
|
||||
['موسسه آموزش عالی رجاء'],0.1111111111111111,0.1111111111111111,0.0,0.0,0.5555555555555556,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد تهران مرکزی'],0.0,0.3333333333333333,0.0,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه آزاد اسلامی واحد لنجان'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه پیام نور استان لرستان'],0.0,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.3333333333333333,0.1111111111111111
|
||||
['دانشگاه قم'],0.0,0.2222222222222222,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['جامعه المصطفی العالمیه'],0.0,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.0
|
||||
['دانشگاه آزاد اسلامی واحد الکترونیکی'],0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111,0.0,0.2222222222222222
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه خوارزمی'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه ایلام'],0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه سیستان و بلوچستان'],0.1111111111111111,0.2222222222222222,0.0,0.0,0.0,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه رازی'],0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.6666666666666666,0.0,0.0,0.0,0.1111111111111111,0.0,0.1111111111111111
|
||||
['دانشگاه پیام نور استان لرستان'],0.2222222222222222,0.1111111111111111,0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه ارومیه'],0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد بندرعباس'],0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.0
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.4444444444444444
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی واحد تهران مرکزی'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.1111111111111111
|
||||
['دانشگاه پیام نور استان تهران'],0.0,0.6666666666666666,0.1111111111111111,0.1111111111111111,0.0,0.1111111111111111,0.1111111111111111,0.2222222222222222
|
||||
['دانشگاه امام صادق علیه السلام'],0.0,0.2222222222222222,0.1111111111111111,0.2222222222222222,0.0,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد مرودشت'],0.0,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه الزهرا (س)'],0.1111111111111111,0.2222222222222222,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
['دانشگاه آزاد اسلامی \u200cواحد مرودشت'],0.0,0.1111111111111111,0.0,0.0,0.3333333333333333,0.1111111111111111,0.1111111111111111,0.1111111111111111
|
||||
|
File diff suppressed because one or more lines are too long
@@ -0,0 +1,573 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>نمایشگر معیار مقالات</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
200: '#bfdbfe',
|
||||
300: '#93c5fd',
|
||||
400: '#60a5fa',
|
||||
500: '#3b82f6',
|
||||
600: '#2563eb',
|
||||
700: '#1d4ed8',
|
||||
800: '#1e40af',
|
||||
900: '#1e3a8a',
|
||||
},
|
||||
secondary: {
|
||||
50: '#f0fdf4',
|
||||
100: '#dcfce7',
|
||||
200: '#bbf7d0',
|
||||
300: '#86efac',
|
||||
400: '#4ade80',
|
||||
500: '#22c55e',
|
||||
600: '#16a34a',
|
||||
700: '#15803d',
|
||||
800: '#166534',
|
||||
900: '#14532d',
|
||||
},
|
||||
accent: {
|
||||
50: '#fdf2f8',
|
||||
100: '#fce7f3',
|
||||
200: '#fbcfe8',
|
||||
300: '#f9a8d4',
|
||||
400: '#f472b6',
|
||||
500: '#ec4899',
|
||||
600: '#db2777',
|
||||
700: '#be185d',
|
||||
800: '#9d174d',
|
||||
900: '#831843',
|
||||
}
|
||||
},
|
||||
fontFamily: {
|
||||
'vazir': ['Vazir', 'Tahoma', 'sans-serif'],
|
||||
},
|
||||
boxShadow: {
|
||||
'soft': '0 4px 20px rgba(0, 0, 0, 0.08)',
|
||||
'medium': '0 8px 30px rgba(0, 0, 0, 0.12)',
|
||||
'hard': '0 15px 50px rgba(0, 0, 0, 0.2)',
|
||||
},
|
||||
animation: {
|
||||
'float': 'float 6s ease-in-out infinite',
|
||||
'glow': 'glow 2s ease-in-out infinite alternate',
|
||||
},
|
||||
keyframes: {
|
||||
float: {
|
||||
'0%, 100%': { transform: 'translateY(0)' },
|
||||
'50%': { transform: 'translateY(-10px)' },
|
||||
},
|
||||
glow: {
|
||||
'0%': { boxShadow: '0 0 5px rgba(59, 130, 246, 0.5)' },
|
||||
'100%': { boxShadow: '0 0 20px rgba(59, 130, 246, 0.8)' },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Vazir&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Vazir', Tahoma, sans-serif;
|
||||
}
|
||||
|
||||
/* استایل زیبا برای select با نمایش چند گزینه */
|
||||
select[size] {
|
||||
background-image: none;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
select[size] option {
|
||||
padding: 0.75rem 1rem;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
select[size] option:hover {
|
||||
background-color: #eff6ff;
|
||||
}
|
||||
|
||||
select[size] option:checked {
|
||||
background-color: #3b82f6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* گرادیان های زیبا */
|
||||
.gradient-bg {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.gradient-card {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
}
|
||||
|
||||
.gradient-success {
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
}
|
||||
|
||||
.gradient-danger {
|
||||
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
||||
}
|
||||
|
||||
/* انیمیشن های اضافی */
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-slide-in {
|
||||
animation: slideIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
/* استایل برای اسکرول بار زیبا */
|
||||
select[size]::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
select[size]::-webkit-scrollbar-track {
|
||||
background: #f1f5f9;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
select[size]::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
select[size]::-webkit-scrollbar-thumb:hover {
|
||||
background: #94a3b8;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="font-vazir bg-gradient-to-br from-blue-50 to-indigo-100 min-h-screen">
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<!-- هدر زیبا -->
|
||||
<header class="text-center mb-12 animate-slide-in">
|
||||
<div class="inline-block p-1 rounded-2xl bg-gradient-to-r from-primary-500 to-accent-500 mb-6 shadow-hard">
|
||||
<div class="bg-white rounded-xl py-4 px-8">
|
||||
<h1 class="text-4xl font-bold bg-gradient-to-r from-primary-600 to-accent-600 bg-clip-text text-transparent">
|
||||
<i class="fas fa-file-alt text-primary-500 mr-3"></i>
|
||||
نمایشگر معیار مقالات
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xl text-gray-600 max-w-2xl mx-auto leading-relaxed">
|
||||
ابزاری پیشرفته برای ارزیابی و نمایش معیارهای مقالات علمی با قابلیتهای تحلیلی کامل
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- کارت بارگذاری فایل -->
|
||||
<div class="max-w-4xl mx-auto mb-8 animate-slide-in" style="animation-delay: 0.1s;">
|
||||
<div class="bg-white rounded-2xl shadow-soft overflow-hidden border border-gray-100 transition-all duration-300 hover:shadow-medium">
|
||||
<div class="p-1 bg-gradient-to-r from-primary-500 to-accent-500">
|
||||
<div class="bg-white rounded-xl p-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-6 flex items-center">
|
||||
<i class="fas fa-folder-open text-primary-500 ml-3 text-xl"></i>
|
||||
بارگذاری فایل داده
|
||||
</h2>
|
||||
<div class="mb-4">
|
||||
<label for="fileInput" class="block text-lg font-medium text-gray-700 mb-3">فایل JSON حاوی اطلاعات مقالات</label>
|
||||
<div class="border-2 border-dashed border-gray-300 rounded-xl p-8 text-center transition-all duration-300 hover:border-primary-400 hover:bg-primary-50 cursor-pointer">
|
||||
<input type="file" id="fileInput" accept=".json" class="absolute top-0 right-0 w-full h-full opacity-0 cursor-pointer" />
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<i class="fas fa-cloud-upload-alt text-4xl text-primary-500 mb-4"></i>
|
||||
<span class="text-xl font-medium text-gray-700">برای بارگذاری فایل JSON اینجا کلیک کنید</span>
|
||||
<p class="text-gray-500 mt-2">یا فایل را به این منطقه بکشید و رها کنید</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- کارت انتخاب مقاله -->
|
||||
<div class="max-w-4xl mx-auto mb-8 hidden" id="articleSelectionCard">
|
||||
<div class="bg-white rounded-2xl shadow-soft overflow-hidden border border-gray-100 transition-all duration-300 hover:shadow-medium">
|
||||
<div class="p-1 bg-gradient-to-r from-secondary-500 to-primary-500">
|
||||
<div class="bg-white rounded-xl p-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-6 flex items-center">
|
||||
<i class="fas fa-book text-secondary-500 ml-3 text-xl"></i>
|
||||
انتخاب مقاله
|
||||
</h2>
|
||||
<div class="mb-4">
|
||||
<label for="articleSelect" class="block text-lg font-medium text-gray-700 mb-3">مقاله مورد نظر</label>
|
||||
<select
|
||||
id="articleSelect"
|
||||
size="6"
|
||||
class="w-full border-2 border-gray-300 rounded-xl p-2 focus:border-primary-500 focus:ring-2 focus:ring-primary-200 transition-all duration-300 bg-white shadow-sm"
|
||||
>
|
||||
<option value="" disabled selected>لطفا مقاله را انتخاب کنید</option>
|
||||
</select>
|
||||
<p class="text-sm text-gray-500 mt-2">حداقل 6 مقاله به طور همزمان نمایش داده میشود</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- کارت اطلاعات مقاله -->
|
||||
<div class="max-w-4xl mx-auto mb-8 hidden" id="articleInfoCard">
|
||||
<div class="bg-white rounded-2xl shadow-soft overflow-hidden border border-gray-100 transition-all duration-300 hover:shadow-medium">
|
||||
<div class="p-1 bg-gradient-to-r from-accent-500 to-purple-500">
|
||||
<div class="bg-white rounded-xl p-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-6 flex items-center">
|
||||
<i class="fas fa-info-circle text-accent-500 ml-3 text-xl"></i>
|
||||
اطلاعات مقاله
|
||||
</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="info-item">
|
||||
<div class="info-label text-lg font-bold text-primary-600 mb-2 flex items-center">
|
||||
<i class="fas fa-file-signature ml-2"></i>
|
||||
نام مقاله
|
||||
</div>
|
||||
<div class="info-value bg-gray-50 p-4 rounded-xl border-r-4 border-primary-500 min-h-[60px] flex items-center text-gray-700" id="articleName">-</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label text-lg font-bold text-primary-600 mb-2 flex items-center">
|
||||
<i class="fas fa-university ml-2"></i>
|
||||
نام مجموعه
|
||||
</div>
|
||||
<div class="info-value bg-gray-50 p-4 rounded-xl border-r-4 border-primary-500 min-h-[60px] flex items-center text-gray-700" id="articleInstitute">-</div>
|
||||
</div>
|
||||
<div class="info-item md:col-span-2">
|
||||
<div class="info-label text-lg font-bold text-primary-600 mb-2 flex items-center">
|
||||
<i class="fas fa-align-left ml-2"></i>
|
||||
چکیده
|
||||
</div>
|
||||
<div class="info-value bg-gray-50 p-4 rounded-xl border-r-4 border-primary-500 min-h-[100px] text-gray-700 leading-relaxed" id="articleAbstract">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- کارت انتخاب معیار -->
|
||||
<div class="max-w-4xl mx-auto mb-8 hidden" id="measureSelectionCard">
|
||||
<div class="bg-white rounded-2xl shadow-soft overflow-hidden border border-gray-100 transition-all duration-300 hover:shadow-medium">
|
||||
<div class="p-1 bg-gradient-to-r from-yellow-500 to-orange-500">
|
||||
<div class="bg-white rounded-xl p-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-6 flex items-center">
|
||||
<i class="fas fa-tasks text-yellow-500 ml-3 text-xl"></i>
|
||||
انتخاب معیار
|
||||
</h2>
|
||||
<div class="mb-4">
|
||||
<label for="responseIndexSelect" class="block text-lg font-medium text-gray-700 mb-3">شماره معیار مورد نظر</label>
|
||||
<select
|
||||
id="responseIndexSelect"
|
||||
size="6"
|
||||
class="w-full border-2 border-gray-300 rounded-xl p-2 focus:border-primary-500 focus:ring-2 focus:ring-primary-200 transition-all duration-300 bg-white shadow-sm"
|
||||
>
|
||||
<option value="" disabled selected>لطفا معیار را انتخاب کنید</option>
|
||||
</select>
|
||||
<p class="text-sm text-gray-500 mt-2">حداقل 6 معیار به طور همزمان نمایش داده میشود</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- کارت جزئیات معیار -->
|
||||
<div class="max-w-6xl mx-auto mb-8 hidden" id="measuresCard">
|
||||
<div class="bg-white rounded-2xl shadow-soft overflow-hidden border border-gray-100 transition-all duration-300 hover:shadow-medium">
|
||||
<div class="p-1 bg-gradient-to-r from-green-500 to-blue-500">
|
||||
<div class="bg-white rounded-xl p-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-6 flex items-center">
|
||||
<i class="fas fa-chart-bar text-green-500 ml-3 text-xl"></i>
|
||||
جزئیات معیار
|
||||
</h2>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<!-- معیار مطلوب -->
|
||||
<div class="measure-box p-6 rounded-2xl bg-gradient-to-br from-green-50 to-blue-50 border-t-4 border-green-500 shadow-soft transition-all duration-300 hover:shadow-medium">
|
||||
<div class="measure-header flex items-center mb-6">
|
||||
<div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center mr-4">
|
||||
<i class="fas fa-thumbs-up text-green-600 text-xl"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="measure-title font-bold text-xl text-gray-800">معیار مطلوب</div>
|
||||
<div class="text-green-600 text-sm">Positive Measure</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="measure-content mb-5">
|
||||
<div class="info-label font-bold text-green-700 mb-2 flex items-center">
|
||||
<i class="fas fa-ruler ml-2"></i>
|
||||
معیار:
|
||||
</div>
|
||||
<div class="info-value bg-white p-4 rounded-xl border border-green-200 text-gray-700 min-h-[80px] shadow-sm" id="posMeasure">-</div>
|
||||
</div>
|
||||
<div class="measure-content mb-5">
|
||||
<div class="info-label font-bold text-green-700 mb-2 flex items-center">
|
||||
<i class="fas fa-comment-dots ml-2"></i>
|
||||
پاسخ:
|
||||
</div>
|
||||
<div class="info-value bg-white p-4 rounded-xl border border-green-200 text-gray-700 min-h-[80px] shadow-sm" id="posResponse">-</div>
|
||||
</div>
|
||||
<div class="measure-content">
|
||||
<div class="info-label font-bold text-green-700 mb-2 flex items-center">
|
||||
<i class="fas fa-star ml-2"></i>
|
||||
امتیاز رعایت:
|
||||
</div>
|
||||
<div class="score inline-flex items-center px-4 py-2 rounded-full text-white font-bold text-lg bg-gradient-to-r from-green-500 to-green-600 shadow-md" id="posScore">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- معیار معکوس -->
|
||||
<div class="measure-box p-6 rounded-2xl bg-gradient-to-br from-red-50 to-pink-50 border-t-4 border-red-500 shadow-soft transition-all duration-300 hover:shadow-medium">
|
||||
<div class="measure-header flex items-center mb-6">
|
||||
<div class="w-12 h-12 rounded-full bg-red-100 flex items-center justify-center mr-4">
|
||||
<i class="fas fa-thumbs-down text-red-600 text-xl"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="measure-title font-bold text-xl text-gray-800">معیار معکوس</div>
|
||||
<div class="text-red-600 text-sm">Negative Measure</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="measure-content mb-5">
|
||||
<div class="info-label font-bold text-red-700 mb-2 flex items-center">
|
||||
<i class="fas fa-ruler ml-2"></i>
|
||||
معیار:
|
||||
</div>
|
||||
<div class="info-value bg-white p-4 rounded-xl border border-red-200 text-gray-700 min-h-[80px] shadow-sm" id="negMeasure">-</div>
|
||||
</div>
|
||||
<div class="measure-content mb-5">
|
||||
<div class="info-label font-bold text-red-700 mb-2 flex items-center">
|
||||
<i class="fas fa-comment-dots ml-2"></i>
|
||||
پاسخ:
|
||||
</div>
|
||||
<div class="info-value bg-white p-4 rounded-xl border border-red-200 text-gray-700 min-h-[80px] shadow-sm" id="negResponse">-</div>
|
||||
</div>
|
||||
<div class="measure-content">
|
||||
<div class="info-label font-bold text-red-700 mb-2 flex items-center">
|
||||
<i class="fas fa-star ml-2"></i>
|
||||
امتیاز رعایت:
|
||||
</div>
|
||||
<div class="score inline-flex items-center px-4 py-2 rounded-full text-white font-bold text-lg bg-gradient-to-r from-red-500 to-red-600 shadow-md" id="negScore">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- وضعیت خالی -->
|
||||
<div class="max-w-2xl mx-auto" id="emptyState">
|
||||
<div class="bg-white rounded-2xl shadow-soft overflow-hidden border border-gray-100 p-8 text-center">
|
||||
<div class="w-24 h-24 rounded-full bg-gradient-to-r from-primary-100 to-accent-100 flex items-center justify-center mx-auto mb-6 animate-float">
|
||||
<i class="fas fa-file-import text-4xl text-primary-500"></i>
|
||||
</div>
|
||||
<h3 class="text-2xl font-bold text-gray-800 mb-4">هنوز فایلی بارگذاری نشده است</h3>
|
||||
<p class="text-gray-600 text-lg mb-6">لطفاً یک فایل JSON حاوی اطلاعات مقالات را بارگذاری کنید</p>
|
||||
<div class="w-16 h-1 bg-gradient-to-r from-primary-500 to-accent-500 rounded-full mx-auto"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- فوتر -->
|
||||
<footer class="text-center mt-12 pt-8 border-t border-gray-200">
|
||||
<p class="text-gray-600">
|
||||
نمایشگر معیار مقالات - طراحی شده با
|
||||
<i class="fas fa-heart text-red-500 mx-1 animate-pulse"></i>
|
||||
و قدرت Tailwind CSS
|
||||
</p>
|
||||
<div class="flex justify-center space-x-4 space-x-reverse mt-4">
|
||||
<div class="w-2 h-2 rounded-full bg-primary-500"></div>
|
||||
<div class="w-2 h-2 rounded-full bg-secondary-500"></div>
|
||||
<div class="w-2 h-2 rounded-full bg-accent-500"></div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let articles = [];
|
||||
|
||||
document.getElementById('fileInput').addEventListener('change', function (e) {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
// نمایش وضعیت بارگذاری
|
||||
document.getElementById('emptyState').classList.add('hidden');
|
||||
|
||||
const loadingHTML = `
|
||||
<div class="text-center py-12">
|
||||
<div class="w-16 h-16 border-4 border-primary-500 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
|
||||
<p class="text-lg text-gray-700">در حال بارگذاری و پردازش فایل...</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// ذخیره المنت اصلی قبل از جایگزینی
|
||||
const articleSelectionCard = document.getElementById('articleSelectionCard');
|
||||
const originalContent = articleSelectionCard.innerHTML;
|
||||
articleSelectionCard.innerHTML = loadingHTML;
|
||||
articleSelectionCard.classList.remove('hidden');
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
try {
|
||||
articles = JSON.parse(e.target.result);
|
||||
setTimeout(() => {
|
||||
// بازگرداندن محتوای اصلی
|
||||
articleSelectionCard.innerHTML = originalContent;
|
||||
|
||||
populateArticleDropdown();
|
||||
document.getElementById('emptyState').classList.add('hidden');
|
||||
}, 800);
|
||||
} catch (err) {
|
||||
alert("خطا در خواندن فایل JSON: " + err.message);
|
||||
resetUI();
|
||||
}
|
||||
};
|
||||
reader.onerror = function() {
|
||||
alert("خطا در خواندن فایل");
|
||||
resetUI();
|
||||
};
|
||||
reader.readAsText(file);
|
||||
});
|
||||
|
||||
function resetUI() {
|
||||
document.getElementById('emptyState').classList.remove('hidden');
|
||||
document.getElementById('articleSelectionCard').classList.add('hidden');
|
||||
document.getElementById('articleInfoCard').classList.add('hidden');
|
||||
document.getElementById('measureSelectionCard').classList.add('hidden');
|
||||
document.getElementById('measuresCard').classList.add('hidden');
|
||||
}
|
||||
|
||||
function populateArticleDropdown() {
|
||||
const select = document.getElementById('articleSelect');
|
||||
|
||||
if (!select) {
|
||||
console.error("Element with ID 'articleSelect' not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
// حذف گزینه پیشفرض
|
||||
select.innerHTML = '';
|
||||
|
||||
if (articles.length === 0) {
|
||||
const option = document.createElement('option');
|
||||
option.textContent = 'مقالهای یافت نشد';
|
||||
option.disabled = true;
|
||||
select.appendChild(option);
|
||||
return;
|
||||
}
|
||||
|
||||
articles.forEach((article, index) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = index;
|
||||
|
||||
// ایجاد متن قابل نمایش برای مقاله
|
||||
const articleId = article["article id"] !== undefined ? article["article id"] : index;
|
||||
const articleName = article["article name"] || 'بدون نام';
|
||||
|
||||
option.textContent = `مقاله ${1 + articleId}: ${articleName}`;
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
// تنظیم event listener برای تغییر انتخاب
|
||||
select.addEventListener('change', () => {
|
||||
const idx = select.value;
|
||||
if (articles[idx]) {
|
||||
loadArticle(articles[idx]);
|
||||
}
|
||||
});
|
||||
|
||||
// انتخاب و نمایش اولین مقاله به صورت پیشفرض
|
||||
if (articles.length > 0) {
|
||||
select.selectedIndex = 0;
|
||||
loadArticle(articles[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function loadArticle(article) {
|
||||
// نمایش کارت اطلاعات مقاله
|
||||
document.getElementById('articleInfoCard').classList.remove('hidden');
|
||||
|
||||
document.getElementById('articleName').textContent = article["article name"] || 'نامشخص';
|
||||
document.getElementById('articleInstitute').textContent = article["article institute"] || 'نامشخص';
|
||||
document.getElementById('articleAbstract').textContent = article["article abstract"] || 'چکیدهای موجود نیست';
|
||||
|
||||
const responseCount = Math.max(
|
||||
article.measures_positive?.length || 0,
|
||||
article.responses_positive?.length || 0,
|
||||
article.scores_positive?.length || 0
|
||||
);
|
||||
|
||||
const responseSelect = document.getElementById('responseIndexSelect');
|
||||
|
||||
if (!responseSelect) {
|
||||
console.error("Element with ID 'responseIndexSelect' not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
// حذف گزینههای قبلی
|
||||
responseSelect.innerHTML = '';
|
||||
|
||||
for (let i = 0; i < responseCount; i++) {
|
||||
const option = document.createElement('option');
|
||||
option.value = i;
|
||||
option.textContent = `معیار ${i + 1}`;
|
||||
responseSelect.appendChild(option);
|
||||
}
|
||||
|
||||
// تنظیم event listener برای تغییر انتخاب معیار
|
||||
responseSelect.addEventListener('change', () => {
|
||||
const i = parseInt(responseSelect.value);
|
||||
displayResponse(article, i);
|
||||
});
|
||||
|
||||
// نمایش کارت انتخاب معیار
|
||||
document.getElementById('measureSelectionCard').classList.remove('hidden');
|
||||
|
||||
if (responseCount > 0) {
|
||||
// انتخاب اولین معیار به صورت پیشفرض
|
||||
responseSelect.selectedIndex = 0;
|
||||
displayResponse(article, 0);
|
||||
document.getElementById('measuresCard').classList.remove('hidden');
|
||||
} else {
|
||||
// اگر معیاری وجود ندارد
|
||||
const option = document.createElement('option');
|
||||
option.textContent = 'معیاری یافت نشد';
|
||||
option.disabled = true;
|
||||
responseSelect.appendChild(option);
|
||||
document.getElementById('measuresCard').classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function displayResponse(article, i) {
|
||||
const posMeasure = article.measures_positive?.[i] || 'معیاری تعریف نشده';
|
||||
const posResponse = article.responses_positive?.[i] || 'پاسخی موجود نیست';
|
||||
const posScore = article.scores_positive?.[i] !== undefined ? article.scores_positive?.[i] : 'نامشخص';
|
||||
|
||||
const negMeasure = article.measures_negative?.[i] || 'معیاری تعریف نشده';
|
||||
const negResponse = article.responses_negative?.[i] || 'پاسخی موجود نیست';
|
||||
const negScore = article.scores_negative?.[i] !== undefined ? article.scores_negative?.[i] : 'نامشخص';
|
||||
|
||||
document.getElementById('posMeasure').innerHTML = marked.parse(posMeasure);
|
||||
document.getElementById('posResponse').innerHTML = marked.parse(posResponse);
|
||||
document.getElementById('posScore').textContent = posScore;
|
||||
|
||||
document.getElementById('negMeasure').innerHTML = marked.parse(negMeasure);
|
||||
document.getElementById('negResponse').innerHTML = marked.parse(negResponse);
|
||||
document.getElementById('negScore').textContent = negScore;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc \
|
||||
g++ \
|
||||
build-essential \
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy requirements first for better caching
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir --upgrade pip && \
|
||||
pip install --no-cache-dir wheel setuptools && \
|
||||
pip install --no-cache-dir -r requirements.txt && \
|
||||
pip install fastapi==0.111.0
|
||||
pip install torch==2.1.0 --index-url https://download.pytorch.org/whl/cpu
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Create necessary directories
|
||||
RUN mkdir -p /app/faiss_index /root/.cache/huggingface
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8000
|
||||
|
||||
# Run the application
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
@@ -0,0 +1,371 @@
|
||||
from typing_extensions import TypedDict, List
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
from pydantic import BaseModel, Field
|
||||
from langgraph.store.memory import InMemoryStore
|
||||
from langmem import create_memory_store_manager
|
||||
from langchain_openai import ChatOpenAI
|
||||
import json
|
||||
from pathlib import Path
|
||||
import asyncio
|
||||
import threading
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from functools import lru_cache
|
||||
import pickle
|
||||
from dataclasses import dataclass
|
||||
from queue import Queue
|
||||
import logging
|
||||
|
||||
from rag.rag import RAG
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Episode(BaseModel):
|
||||
observation: str = Field(..., description="The context and setup - what happened")
|
||||
thoughts: str = Field(
|
||||
...,
|
||||
description="Internal reasoning process and observations of the agent in the episode that let it arrive"
|
||||
' at the correct action and result. "I ..."',
|
||||
)
|
||||
action: str = Field(
|
||||
...,
|
||||
description="What was done, how, and in what format. (Include whatever is salient to the success of the action). I ..",
|
||||
)
|
||||
result: str = Field(
|
||||
...,
|
||||
description="Outcome and retrospective. What did you do well? What could you do better next time? I ...",
|
||||
)
|
||||
|
||||
class StateAgent(TypedDict):
|
||||
query: List[List[str]]
|
||||
top_k: int
|
||||
need_correction: bool
|
||||
is_correct: str
|
||||
|
||||
class AsyncBackgroundProcessor:
|
||||
"""Background processor for async operations"""
|
||||
def __init__(self, max_workers=2):
|
||||
self.executor = ThreadPoolExecutor(max_workers=max_workers)
|
||||
self.store_queue = Queue()
|
||||
self.running = True
|
||||
self.worker_thread = threading.Thread(target=self._process_queue, daemon=True)
|
||||
self.worker_thread.start()
|
||||
|
||||
def _process_queue(self):
|
||||
"""Process store operations in background"""
|
||||
while self.running:
|
||||
try:
|
||||
if not self.store_queue.empty():
|
||||
func, args, kwargs = self.store_queue.get(timeout=0.1)
|
||||
try:
|
||||
func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing background task: {e}")
|
||||
else:
|
||||
time.sleep(0.01)
|
||||
except Exception as e:
|
||||
logger.error(f"Queue processing error: {e}")
|
||||
|
||||
def submit(self, func, *args, **kwargs):
|
||||
"""Submit task to background queue"""
|
||||
self.store_queue.put((func, args, kwargs))
|
||||
|
||||
def shutdown(self):
|
||||
"""Shutdown background processor"""
|
||||
self.running = False
|
||||
self.executor.shutdown(wait=True)
|
||||
if self.worker_thread.is_alive():
|
||||
self.worker_thread.join(timeout=5)
|
||||
|
||||
class Agent:
|
||||
def __init__(self, Config):
|
||||
if not Config.API_KEY:
|
||||
raise RuntimeError("Missing API_KEY. Set it in your .env or environment as OPENAI_API_KEY/API_KEY.")
|
||||
os.environ.setdefault("OPENAI_API_KEY", Config.API_KEY)
|
||||
os.environ.setdefault("OPENAI_BASE_URL", "https://api.avalai.ir/v1")
|
||||
|
||||
# Initialize components
|
||||
self.rag = RAG(
|
||||
Config.MODEL_NAME,
|
||||
Config.RETREIVER_NAME,
|
||||
Config.VECTOR_PATH,
|
||||
Config.DATA_PATH,
|
||||
Config.DEVICE
|
||||
)
|
||||
self.memory_llm = self.rag.generator
|
||||
|
||||
# Load store from file first to avoid duplicate loading
|
||||
self.dump_file = Config.DUMP_FILE if hasattr(Config, 'DUMP_FILE') else "episodes_dump.json"
|
||||
self.episode_cache = {} # Cache for episodes
|
||||
self.search_cache = {} # Cache for search results
|
||||
|
||||
# Initialize store with optimized settings
|
||||
self.store = InMemoryStore(
|
||||
index={
|
||||
"dims": 1536,
|
||||
"embed": "openai:text-embedding-3-small",
|
||||
"metric": "cosine", # Use cosine similarity
|
||||
}
|
||||
)
|
||||
|
||||
# Background processor for async operations
|
||||
self.bg_processor = AsyncBackgroundProcessor(max_workers=2)
|
||||
|
||||
# Try to load store from file
|
||||
if Path(self.dump_file).exists():
|
||||
try:
|
||||
self._fast_load_store(filepath=self.dump_file)
|
||||
logger.info(f"Loaded {len(self.episode_cache)} episodes from cache")
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not load previous content: {e}")
|
||||
|
||||
# Initialize chat with optimized settings
|
||||
chat = ChatOpenAI(
|
||||
model=self.memory_llm.model_name,
|
||||
openai_api_key=os.environ["OPENAI_API_KEY"],
|
||||
base_url=os.environ.get("OPENAI_BASE_URL", "https://api.avalai.ir/v1"),
|
||||
temperature=0.2,
|
||||
max_tokens=512,
|
||||
timeout=30, # Add timeout
|
||||
max_retries=2, # Limit retries
|
||||
)
|
||||
|
||||
# Initialize memory manager
|
||||
self.manager = create_memory_store_manager(
|
||||
chat,
|
||||
namespace=("memories", "episodes"),
|
||||
schemas=[Episode],
|
||||
instructions="Extract exceptional examples of noteworthy problem-solving scenarios",
|
||||
enable_inserts=True,
|
||||
store=self.store,
|
||||
)
|
||||
|
||||
# Track store size for periodic dumping
|
||||
self.operations_since_last_dump = 0
|
||||
self.dump_threshold = 10 # Dump every 10 operations
|
||||
|
||||
def _fast_load_store(self, filepath: str = "episodes_dump.json"):
|
||||
"""Fast loading of store data with caching"""
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Bulk load episodes into cache
|
||||
for key, episode_data in data.items():
|
||||
if 'content' in episode_data['value']:
|
||||
self.episode_cache[key] = episode_data['value']['content']
|
||||
|
||||
logger.info(f"Loaded {len(self.episode_cache)} episodes into cache")
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading store: {e}")
|
||||
raise
|
||||
|
||||
@lru_cache(maxsize=100)
|
||||
def _cached_search(self, query: str, limit: int):
|
||||
"""Cached search function"""
|
||||
cache_key = f"{query[:50]}_{limit}"
|
||||
|
||||
if cache_key in self.search_cache:
|
||||
return self.search_cache[cache_key]
|
||||
|
||||
# Perform actual search
|
||||
results = self.store.search(
|
||||
("memories", "episodes"),
|
||||
query=query,
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
# Cache the results
|
||||
self.search_cache[cache_key] = results
|
||||
return results
|
||||
|
||||
def _generate_system_message(self, similar_episodes):
|
||||
"""Generate system message from similar episodes"""
|
||||
system_message = "You are a helpful assistant."
|
||||
|
||||
if similar_episodes:
|
||||
system_message += "\n\n### EPISODIC MEMORY:"
|
||||
for i, item in enumerate(similar_episodes, start=1):
|
||||
episode = item.value["content"]
|
||||
system_message += f"\n\nEpisode {i}:"
|
||||
system_message += f"\nObservation: {episode.get('observation', 'N/A')}"
|
||||
system_message += f"\nThought: {episode.get('thoughts', 'N/A')}"
|
||||
system_message += f"\nAction: {episode.get('action', 'N/A')}"
|
||||
system_message += f"\nResult: {episode.get('result', 'N/A')}"
|
||||
|
||||
return system_message
|
||||
|
||||
def app(self, messages: list, top_k: int, feedback: bool = False):
|
||||
"""Optimized app method with async processing"""
|
||||
logger.info('Starting processing...')
|
||||
|
||||
# Get user message
|
||||
user_message = messages[-1]["content"]
|
||||
|
||||
# 1. Search for similar episodes
|
||||
logger.info('Searching for similar episodes...')
|
||||
similar = self._cached_search(user_message, top_k)
|
||||
|
||||
# 2. Generate response
|
||||
logger.info('Generating response...')
|
||||
system_message = self._generate_system_message(similar)
|
||||
|
||||
# Use thread pool for async generation
|
||||
def generate_response():
|
||||
return self.memory_llm.generate([
|
||||
{"role": "system", "content": system_message},
|
||||
*messages[:-1],
|
||||
{"role": "user", "content": user_message}
|
||||
])
|
||||
|
||||
# Run in thread pool to avoid blocking
|
||||
with ThreadPoolExecutor(max_workers=1) as executor:
|
||||
future = executor.submit(generate_response)
|
||||
response = future.result(timeout=30) # 30 second timeout
|
||||
|
||||
logger.info('Response generated')
|
||||
|
||||
# 3. Prepare messages for background processing
|
||||
messages_with_assistant = [
|
||||
*messages,
|
||||
{"role": "assistant", "content": response}
|
||||
]
|
||||
|
||||
# 4. Submit store operation to background processor (non-blocking)
|
||||
if feedback:
|
||||
self.bg_processor.submit(
|
||||
self._process_store_operation,
|
||||
messages_with_assistant,
|
||||
user_message,
|
||||
response
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
def _process_store_operation(self, messages_with_assistant, user_message, response):
|
||||
"""Process store operation in background"""
|
||||
try:
|
||||
logger.info('Processing store operation in background...')
|
||||
self.manager.invoke({"messages": messages_with_assistant})
|
||||
logger.info('Store operation completed')
|
||||
|
||||
# Increment operation counter
|
||||
self.operations_since_last_dump += 1
|
||||
|
||||
# Periodic dumping
|
||||
if self.operations_since_last_dump >= self.dump_threshold:
|
||||
self._background_dump_store()
|
||||
self.operations_since_last_dump = 0
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Store operation failed: {e}")
|
||||
# Fallback: add correction directly
|
||||
self._add_correction_direct(
|
||||
user_message,
|
||||
response
|
||||
)
|
||||
|
||||
def _add_correction_direct(self, observation: str, result: str):
|
||||
"""Add correction directly without manager"""
|
||||
episode = Episode(
|
||||
observation=observation,
|
||||
thoughts="Saved via fallback due to upstream error.",
|
||||
action="Generated assistant reply and stored episode directly.",
|
||||
result=result,
|
||||
)
|
||||
key = f"episode:{uuid.uuid4()}"
|
||||
self.store.put(
|
||||
("memories", "episodes"),
|
||||
key=key,
|
||||
value={"content": episode.model_dump()}
|
||||
)
|
||||
|
||||
# Update cache
|
||||
self.episode_cache[key] = episode.model_dump()
|
||||
|
||||
# Clear search cache since we added new data
|
||||
self.search_cache.clear()
|
||||
|
||||
# Schedule background dump
|
||||
self.bg_processor.submit(self._background_dump_store)
|
||||
|
||||
def _background_dump_store(self, filepath: str = None):
|
||||
"""Dump store to file in background"""
|
||||
if filepath is None:
|
||||
filepath = self.dump_file
|
||||
|
||||
try:
|
||||
# Get all items efficiently
|
||||
items = []
|
||||
batch_size = 50
|
||||
|
||||
# Try to get items in batches if store supports it
|
||||
try:
|
||||
# Try batch retrieval if available
|
||||
items = list(self.store.search(
|
||||
("memories", "episodes"),
|
||||
query="", # Empty query to get all
|
||||
limit=1000 # Limit to prevent memory issues
|
||||
))
|
||||
except:
|
||||
# Fallback to cache
|
||||
items = [{
|
||||
'key': key,
|
||||
'namespace': ("memories", "episodes"),
|
||||
'value': {'content': value}
|
||||
} for key, value in self.episode_cache.items()]
|
||||
|
||||
# Prepare data for JSON
|
||||
all_data = {}
|
||||
for item in items:
|
||||
if hasattr(item, 'key'):
|
||||
key = item.key
|
||||
value = item.value
|
||||
else:
|
||||
key = item['key']
|
||||
value = item['value']
|
||||
|
||||
all_data[key] = {
|
||||
"namespace": ("memories", "episodes"),
|
||||
"value": value
|
||||
}
|
||||
|
||||
# Write to file
|
||||
temp_file = f"{filepath}.tmp"
|
||||
with open(temp_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(all_data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
# Atomic replace
|
||||
os.replace(temp_file, filepath)
|
||||
|
||||
logger.info(f"Successfully dumped {len(all_data)} episodes")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error dumping store: {e}")
|
||||
|
||||
def search_episodes(self, query: str, limit: int = 5):
|
||||
"""Optimized search method"""
|
||||
return self._cached_search(query, limit)
|
||||
|
||||
def shutdown(self):
|
||||
"""Clean shutdown"""
|
||||
# Dump store before shutdown
|
||||
self._background_dump_store()
|
||||
|
||||
# Shutdown background processor
|
||||
self.bg_processor.shutdown()
|
||||
|
||||
logger.info("Agent shutdown complete")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from utils.config import Config
|
||||
|
||||
agent = Agent(Config)
|
||||
result = agent.app([{"role": "user", "content": "hi how are you?"}], 5)
|
||||
print(f"Response: {result}")
|
||||
|
||||
agent.shutdown()
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,16 @@
|
||||
from utils.app import Application
|
||||
import signal
|
||||
import threading
|
||||
|
||||
|
||||
def main():
|
||||
app = Application()
|
||||
try:
|
||||
app.run_fastapi()
|
||||
except KeyboardInterrupt:
|
||||
print("\nFastAPI server interrupted.")
|
||||
finally:
|
||||
print("Application shutdown complete.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,148 @@
|
||||
from datetime import datetime
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_login import UserMixin
|
||||
from flask_bcrypt import Bcrypt
|
||||
|
||||
db = SQLAlchemy()
|
||||
bcrypt = Bcrypt()
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
__tablename__ = 'users'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(80), unique=True, nullable=False)
|
||||
email = db.Column(db.String(120), unique=True, nullable=False)
|
||||
password_hash = db.Column(db.String(255), nullable=False)
|
||||
|
||||
# فیلدهای جدید برای پنل ادمین
|
||||
role = db.Column(db.String(20), default='user') # 'admin', 'user'
|
||||
is_active = db.Column(db.Boolean, default=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
def set_password(self, password):
|
||||
"""Hash and set the password"""
|
||||
self.password_hash = bcrypt.generate_password_hash(password).decode('utf-8')
|
||||
|
||||
def check_password(self, password):
|
||||
"""Check if the provided password matches the hash"""
|
||||
return bcrypt.check_password_hash(self.password_hash, password)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<User {self.username}>'
|
||||
|
||||
class DataFile(db.Model):
|
||||
__tablename__ = 'data_files'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
filename = db.Column(db.String(255), nullable=False)
|
||||
original_filename = db.Column(db.String(255), nullable=False)
|
||||
file_path = db.Column(db.String(500), nullable=False)
|
||||
file_size = db.Column(db.Integer, nullable=False)
|
||||
file_type = db.Column(db.String(50), nullable=False) # 'json', 'pdf', etc.
|
||||
uploader_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
status = db.Column(db.String(20), default='pending') # 'pending', 'processing', 'completed', 'error'
|
||||
description = db.Column(db.Text)
|
||||
uploaded_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
processed_at = db.Column(db.DateTime)
|
||||
|
||||
# ارتباط با آپلودکننده
|
||||
uploader = db.relationship('User', backref='uploaded_files')
|
||||
|
||||
class Article(db.Model):
|
||||
__tablename__ = 'articles'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
article_id = db.Column(db.String(255), nullable=False)
|
||||
name = db.Column(db.String(500), nullable=False)
|
||||
institute = db.Column(db.String(500))
|
||||
abstract = db.Column(db.Text)
|
||||
|
||||
# فیلدهای معیارها
|
||||
measures_positive = db.Column(db.Text)
|
||||
measures_negative = db.Column(db.Text)
|
||||
responses_positive = db.Column(db.Text)
|
||||
responses_negative = db.Column(db.Text)
|
||||
scores_positive = db.Column(db.Text)
|
||||
scores_negative = db.Column(db.Text)
|
||||
|
||||
# فیلدهای جدید برای پنل ادمین
|
||||
pdf_path = db.Column(db.String(500))
|
||||
data_file_id = db.Column(db.Integer, db.ForeignKey('data_files.id'))
|
||||
|
||||
# وضعیت تصحیح
|
||||
correction_status = db.Column(db.String(20), default='pending') # 'pending', 'in_progress', 'completed', 'reviewed'
|
||||
corrected_by = db.Column(db.Integer, db.ForeignKey('users.id'))
|
||||
corrected_at = db.Column(db.DateTime)
|
||||
reviewer_id = db.Column(db.Integer, db.ForeignKey('users.id'))
|
||||
reviewed_at = db.Column(db.DateTime)
|
||||
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# ارتباطهای موجود - مشخص کردن foreign_keys به صورت صریح
|
||||
user = db.relationship('User', foreign_keys=[user_id], backref=db.backref('articles', lazy=True))
|
||||
|
||||
# ارتباطهای جدید برای پنل ادمین
|
||||
corrector = db.relationship('User', foreign_keys=[corrected_by], backref='corrected_articles')
|
||||
reviewer = db.relationship('User', foreign_keys=[reviewer_id], backref='reviewed_articles')
|
||||
data_file = db.relationship('DataFile', backref='articles')
|
||||
|
||||
class ArticleResponse(db.Model):
|
||||
__tablename__ = 'article_responses'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
article_id = db.Column(db.Integer, db.ForeignKey('articles.id'), nullable=False)
|
||||
measure_index = db.Column(db.Integer, nullable=False)
|
||||
measure_type = db.Column(db.String(10), nullable=False)
|
||||
original_response = db.Column(db.Text)
|
||||
edited_response = db.Column(db.Text)
|
||||
ai_generated_response = db.Column(db.Text)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
article = db.relationship('Article', backref=db.backref('responses', lazy=True))
|
||||
|
||||
class ChatSession(db.Model):
|
||||
__tablename__ = 'chat_sessions'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
|
||||
# فیلد جدید برای ارتباط با مقاله
|
||||
article_id = db.Column(db.Integer, db.ForeignKey('articles.id'))
|
||||
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# ارتباطهای موجود
|
||||
user = db.relationship('User', backref=db.backref('chat_sessions', lazy=True))
|
||||
messages = db.relationship('Message', backref='session', lazy=True, cascade='all, delete-orphan')
|
||||
|
||||
# ارتباط جدید
|
||||
article = db.relationship('Article', backref='chat_sessions')
|
||||
|
||||
class Message(db.Model):
|
||||
__tablename__ = 'messages'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
session_id = db.Column(db.Integer, db.ForeignKey('chat_sessions.id'), nullable=False)
|
||||
role = db.Column(db.String(20), nullable=False)
|
||||
content = db.Column(db.Text, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
class CorrectionLog(db.Model):
|
||||
__tablename__ = 'correction_logs'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
article_id = db.Column(db.Integer, db.ForeignKey('articles.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
action = db.Column(db.String(50), nullable=False) # 'created', 'updated', 'corrected', 'reviewed'
|
||||
field_name = db.Column(db.String(100))
|
||||
old_value = db.Column(db.Text)
|
||||
new_value = db.Column(db.Text)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
# ارتباطها
|
||||
article = db.relationship('Article', backref='correction_logs')
|
||||
user = db.relationship('User', backref='correction_actions')
|
||||
@@ -0,0 +1,26 @@
|
||||
from utils.config import Config
|
||||
from openai import OpenAI
|
||||
|
||||
|
||||
class Generator:
|
||||
def __init__(self, model_name):
|
||||
super().__init__()
|
||||
self.model_name = model_name
|
||||
self.model = self.config(model_name)
|
||||
|
||||
def generate(self, messages, top_k=None, top_p=None, do_sample=None, max_length=None):
|
||||
print("sending prompt to deepseek")
|
||||
response = self.model.chat.completions.create(
|
||||
model=self.model_name,
|
||||
messages=messages,
|
||||
stream=False
|
||||
)
|
||||
return response.choices[0].message.content
|
||||
|
||||
def config(self, model_name):
|
||||
return OpenAI(api_key=Config.API_KEY, base_url='https://api.avalai.ir/v1')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generator = Generator('deepseek-v3.1')
|
||||
print(generator.generate("hi how are you?"))
|
||||
@@ -0,0 +1,92 @@
|
||||
from rag.generator import Generator
|
||||
from rag.retriever import Retriver
|
||||
import numpy as np
|
||||
|
||||
|
||||
class RAG:
|
||||
def __init__(self, model_name, retriever_name, vector_path, data_path, device):
|
||||
self.model_name = model_name
|
||||
self.retriever_name = retriever_name
|
||||
self.vector_path = vector_path
|
||||
self.data_path = data_path
|
||||
self.generator = Generator(model_name)
|
||||
# self.retriever = Retriver(vector_path, data_path, retriever_name, device)
|
||||
|
||||
def make_prompt(self, query, top_k):
|
||||
results = self._retrieve(query, top_k)
|
||||
if results:
|
||||
data = [res.page_content.strip() for res in results]
|
||||
else:
|
||||
data = ["شما محتوایی بارگداری نکرده اید."]
|
||||
prompt = f"""
|
||||
محتوای ارایه شده : {data}
|
||||
سوال : {query}
|
||||
|
||||
لطفا بر اساس محتوای ارایه شده به سوال کامل و دقیق جواب بده دقت کن که بیشتر جواب تولید کنی و دقیق باشی
|
||||
"""
|
||||
sanitized_results = []
|
||||
for res in results:
|
||||
meta = getattr(res, "metadata", {})
|
||||
if isinstance(meta, dict):
|
||||
clean_meta = {}
|
||||
for k, v in meta.items():
|
||||
if isinstance(v, np.generic):
|
||||
clean_meta[k] = v.item()
|
||||
else:
|
||||
clean_meta[k] = v
|
||||
else:
|
||||
clean_meta = {}
|
||||
|
||||
sanitized_doc = {
|
||||
"page_content": res.page_content,
|
||||
"metadata": clean_meta
|
||||
}
|
||||
sanitized_results.append(sanitized_doc)
|
||||
|
||||
return prompt, sanitized_results
|
||||
|
||||
def query(self, query, top_k):
|
||||
results = self._retrieve(query, top_k)
|
||||
if results:
|
||||
data = [res.page_content.strip() for res in results]
|
||||
resp = self.make_response(query, data)
|
||||
else:
|
||||
data = ["شما محتوایی بارگداری نکرده اید."]
|
||||
resp = self.make_response(query, data)
|
||||
|
||||
sanitized_results = []
|
||||
for res in results:
|
||||
meta = getattr(res, "metadata", {})
|
||||
if isinstance(meta, dict):
|
||||
clean_meta = {}
|
||||
for k, v in meta.items():
|
||||
if isinstance(v, np.generic):
|
||||
clean_meta[k] = v.item()
|
||||
else:
|
||||
clean_meta[k] = v
|
||||
else:
|
||||
clean_meta = {}
|
||||
|
||||
sanitized_doc = {
|
||||
"page_content": res.page_content,
|
||||
"metadata": clean_meta
|
||||
}
|
||||
sanitized_results.append(sanitized_doc)
|
||||
|
||||
return sanitized_results, resp
|
||||
|
||||
def make_response(self, query, data):
|
||||
prompt = f"""
|
||||
محتوای ارایه شده : {data}
|
||||
سوال : {query}
|
||||
|
||||
لطفا بر اساس محتوای ارایه شده به سوال کامل و دقیق جواب بده دقت کن که بیشتر جواب تولید کنی و دقیق باشی
|
||||
"""
|
||||
return self.generator.generate(prompt)
|
||||
|
||||
def _retrieve(self, query, top_k):
|
||||
return self.retriever.retrieve(query, top_k)
|
||||
|
||||
def update_retriever(self):
|
||||
"""Retriever را با اسناد جدید در data_path دوباره بارگذاری میکند."""
|
||||
self.retriever.reload_documents()
|
||||
@@ -0,0 +1,106 @@
|
||||
from langchain_community.document_loaders import DirectoryLoader, TextLoader, PyPDFLoader
|
||||
from langchain_huggingface import HuggingFaceEmbeddings
|
||||
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
||||
from langchain_community.vectorstores import FAISS
|
||||
from langchain_core.documents import Document
|
||||
from typing import List, Dict, Any
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
class Retriver:
|
||||
def __init__(self, vector_path, data_path, retriever_name, device):
|
||||
self.vector_path = vector_path
|
||||
self.data_path = data_path
|
||||
self.embedding_model = HuggingFaceEmbeddings(
|
||||
model_name=retriever_name,
|
||||
model_kwargs={"device": device},
|
||||
encode_kwargs={"normalize_embeddings": True}
|
||||
)
|
||||
self.vectorstore = self.load_vectorstore(self.embedding_model, self.vector_path)
|
||||
if self.vectorstore is None:
|
||||
documents = self.load_documents(data_path)
|
||||
chunks = self.split_documents(documents)
|
||||
if chunks:
|
||||
self.vectorstore = self.create_retriever(chunks, self.embedding_model)
|
||||
self.save_vectorstore(self.vectorstore, self.vector_path)
|
||||
|
||||
def get_data_dir(self, data_dir=None):
|
||||
import sys, os
|
||||
if data_dir is not None:
|
||||
return data_dir
|
||||
if getattr(sys, 'frozen', False):
|
||||
exe_dir = os.path.dirname(sys.executable)
|
||||
else:
|
||||
exe_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
data_dir_path = os.path.join(exe_dir, 'data')
|
||||
return data_dir_path
|
||||
|
||||
def load_documents(self, data_dir=None):
|
||||
data_dir = self.get_data_dir(data_dir)
|
||||
pdf_loader = DirectoryLoader(data_dir, glob="*.pdf", loader_cls=PyPDFLoader)
|
||||
txt_loader = DirectoryLoader(data_dir, glob="*.txt", loader_cls=TextLoader)
|
||||
pdf_docs = pdf_loader.load()
|
||||
txt_docs = txt_loader.load()
|
||||
all_docs = pdf_docs + txt_docs
|
||||
return all_docs
|
||||
|
||||
def retrieve(self, query, pdf_path, top_k=10):
|
||||
if not self.vectorstore:
|
||||
return []
|
||||
|
||||
filter_dict = {"source": pdf_path}
|
||||
|
||||
try:
|
||||
docs_and_scores = self.vectorstore.similarity_search_with_score(
|
||||
query,
|
||||
k=top_k,
|
||||
filter=filter_dict
|
||||
)
|
||||
except TypeError:
|
||||
docs_and_scores = self.vectorstore.similarity_search_with_score(query, k=top_k*2)
|
||||
docs_and_scores = [(doc, score) for doc, score in docs_and_scores
|
||||
if doc.metadata.get("source") == pdf_path]
|
||||
|
||||
for doc, score in docs_and_scores:
|
||||
doc.metadata["score"] = float(score)
|
||||
|
||||
return [doc for doc, _ in docs_and_scores[:top_k]]
|
||||
|
||||
def split_documents(self, documents, chunk_size=1000, chunk_overlap=200):
|
||||
text_splitter = RecursiveCharacterTextSplitter(
|
||||
chunk_size=chunk_size,
|
||||
chunk_overlap=chunk_overlap,
|
||||
separators=["\n\n", "\n", " ", ""]
|
||||
)
|
||||
chunks = text_splitter.split_documents(documents)
|
||||
return chunks
|
||||
|
||||
def create_retriever(self, documents, embedding_model):
|
||||
vectorstore = FAISS.from_documents(documents, embedding_model)
|
||||
return vectorstore
|
||||
|
||||
def save_vectorstore(self, vectorstore, path="./faiss_index"):
|
||||
vectorstore.save_local(path)
|
||||
|
||||
def load_vectorstore(self, embedding_model, path="./faiss_index"):
|
||||
if os.path.exists(os.path.join(path, "index.faiss")):
|
||||
vectorstore = FAISS.load_local(path, embedding_model, allow_dangerous_deserialization=True)
|
||||
return vectorstore
|
||||
else:
|
||||
return None
|
||||
|
||||
def reload_documents(self):
|
||||
"""
|
||||
تمام اسناد موجود در self.data_path را دوباره بارگذاری کرده،
|
||||
vectorstore را بازسازی و ذخیره میکند.
|
||||
"""
|
||||
print("🔄 Reloading documents and rebuilding vectorstore...")
|
||||
documents = self.load_documents(self.data_path)
|
||||
chunks = self.split_documents(documents)
|
||||
if chunks:
|
||||
self.vectorstore = self.create_retriever(chunks, self.embedding_model)
|
||||
self.save_vectorstore(self.vectorstore, self.vector_path)
|
||||
else:
|
||||
self.vectorstore = None
|
||||
print("⚠️ No documents found to reload.")
|
||||
@@ -0,0 +1,119 @@
|
||||
# Python Package Requirements (generated from conda 'nlp' environment)
|
||||
# Note: System/Conda-specific packages (like cuda, blas, compilers) have been filtered out.
|
||||
# It is recommended to use within a Python virtual environment.
|
||||
|
||||
# Core ML & Data Science
|
||||
numpy==1.26.4
|
||||
pandas==2.3.3
|
||||
scipy==1.16.2
|
||||
scikit-learn==1.7.2
|
||||
|
||||
# Deep Learning Frameworks & GPU
|
||||
torch==2.7.1
|
||||
transformers==4.57.1
|
||||
datasets==4.3.0
|
||||
accelerate==1.11.0
|
||||
safetensors==0.6.2
|
||||
bitsandbytes==0.48.1
|
||||
tokenizers==0.22.1
|
||||
faiss-cpu==1.9.0
|
||||
hnswlib==0.8.0
|
||||
|
||||
# LangChain Ecosystem
|
||||
langchain==0.3.27
|
||||
langchain-core==0.3.79
|
||||
langchain-community==0.3.27
|
||||
langchain-openai==0.3.35
|
||||
langchain-anthropic==0.3.22
|
||||
langchain-huggingface==0.3.1
|
||||
langchain-text-splitters==0.3.11
|
||||
langgraph==1.0.1
|
||||
langgraph-checkpoint==3.0.1
|
||||
langgraph-sdk==0.2.9
|
||||
langsmith==0.4.32
|
||||
|
||||
# LLM Libraries & APIs
|
||||
openai==2.8.0
|
||||
anthropic==0.73.0
|
||||
tiktoken==0.12.0
|
||||
huggingface-hub==0.36.0
|
||||
sentence-transformers==5.1.2
|
||||
llama-cpp-python==0.3.16
|
||||
|
||||
# Web & Async
|
||||
fastapi==0.111.0
|
||||
uvicorn==0.29.0
|
||||
starlette==0.37.2
|
||||
pydantic==2.12.3
|
||||
pydantic-core==2.41.4
|
||||
pydantic-settings==2.11.0
|
||||
httpx==0.28.1
|
||||
aiohttp==3.13.1
|
||||
websockets==15.0.1
|
||||
|
||||
# Database
|
||||
sqlalchemy==2.0.44
|
||||
psycopg2-binary==2.9.11
|
||||
pyarrow==22.0.0
|
||||
|
||||
# Flask Ecosystem (if you need it)
|
||||
flask==3.0.3
|
||||
flask-sqlalchemy==3.1.1
|
||||
flask-login==0.6.3
|
||||
flask-bcrypt==1.0.1
|
||||
|
||||
# Utilities & Helpers
|
||||
click==8.3.0
|
||||
colorama==0.4.6
|
||||
loguru==0.7.3
|
||||
tqdm==4.67.1
|
||||
tenacity==9.1.2
|
||||
python-dotenv==1.0.1
|
||||
pyyaml==6.0.3
|
||||
orjson==3.11.4
|
||||
rich==14.2.0
|
||||
typer==0.20.0
|
||||
watchfiles==1.1.1
|
||||
filelock==3.20.0
|
||||
fsspec==2025.9.0
|
||||
jiter==0.12.0
|
||||
sqlite-vec==0.1.6
|
||||
diskcache==5.6.3
|
||||
docstring-parser==0.17.0
|
||||
|
||||
# Jupyter & Notebook (optional)
|
||||
ipython==9.5.0
|
||||
jupyterlab==4.4.7
|
||||
ipykernel==6.30.1
|
||||
traitlets==5.14.3
|
||||
|
||||
# Document Processing
|
||||
pypdf==6.1.3
|
||||
pdfplumber==0.11.5
|
||||
pymupdf==1.24.1
|
||||
python-docx==1.2.0
|
||||
beautifulsoup4==4.13.5
|
||||
lxml==6.0.2
|
||||
|
||||
# Other Dependencies
|
||||
pillow==11.3.0
|
||||
pybind11==3.0.1
|
||||
optree==0.17.0
|
||||
typing-extensions==4.15.0
|
||||
importlib-metadata==8.7.0
|
||||
packaging==25.0
|
||||
requests==2.32.3
|
||||
urllib3==2.5.0
|
||||
certifi==2025.11.12
|
||||
charset-normalizer==3.4.4
|
||||
idna==3.11
|
||||
cryptography==46.0.3
|
||||
bcrypt==5.0.0
|
||||
argon2-cffi==21.3.0
|
||||
python-multipart==0.0.20
|
||||
trustcall==0.0.39
|
||||
dydantic==0.0.8
|
||||
langmem==0.0.30
|
||||
distro==1.9.0
|
||||
webencodings==0.5.1
|
||||
zstandard==0.25.0
|
||||
@@ -0,0 +1,185 @@
|
||||
from fastapi import FastAPI, HTTPException, File, UploadFile
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict, Any, Optional
|
||||
import numpy as np
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from agent.espisodic_agent import Agent
|
||||
from utils.config import Config
|
||||
|
||||
|
||||
app = FastAPI(title="YARA RAG API", description="Backend API for YARA RAG assistant")
|
||||
|
||||
agent = Agent(Config)
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
os.makedirs(Config.DATA_PATH, exist_ok=True)
|
||||
|
||||
class ChunkMetadata(BaseModel):
|
||||
score: Optional[float] = None
|
||||
source: Optional[str] = None
|
||||
page: Optional[int] = None
|
||||
|
||||
class Chunk(BaseModel):
|
||||
page_content: str
|
||||
metadata: ChunkMetadata
|
||||
|
||||
class QueryRequest(BaseModel):
|
||||
message: str
|
||||
# pdf_path: str
|
||||
|
||||
class QueryResponse(BaseModel):
|
||||
response: str
|
||||
|
||||
|
||||
def sanitize_metadata(metadata: Any) -> Dict[str, Any]:
|
||||
if not isinstance(metadata, dict):
|
||||
return {}
|
||||
|
||||
clean_meta = {}
|
||||
for k, v in metadata.items():
|
||||
if isinstance(v, np.generic):
|
||||
v = v.item()
|
||||
if k == "page":
|
||||
if v is None:
|
||||
clean_meta[k] = None
|
||||
else:
|
||||
try:
|
||||
clean_meta[k] = int(v)
|
||||
except (ValueError, TypeError):
|
||||
clean_meta[k] = None
|
||||
elif k == "score":
|
||||
if v is None:
|
||||
clean_meta[k] = None
|
||||
else:
|
||||
try:
|
||||
clean_meta[k] = float(v)
|
||||
except (ValueError, TypeError):
|
||||
clean_meta[k] = None
|
||||
elif k == "source":
|
||||
clean_meta[k] = str(v) if v is not None else None
|
||||
else:
|
||||
if isinstance(v, (int, float, str, bool, type(None))):
|
||||
clean_meta[k] = v
|
||||
else:
|
||||
clean_meta[k] = str(v)
|
||||
|
||||
return clean_meta
|
||||
|
||||
|
||||
@app.post("/query", response_model=QueryResponse)
|
||||
async def query_rag(request: QueryRequest):
|
||||
if not request.message.strip():
|
||||
raise HTTPException(status_code=400, detail="Message cannot be empty")
|
||||
|
||||
try:
|
||||
print('query callled')
|
||||
full_response = agent.app([{"role": "user", "content": request.message}], 5)
|
||||
print("finisehed")
|
||||
|
||||
return QueryResponse(response=format_response_as_markdown(full_response))
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"RAG processing failed: {str(e)}")
|
||||
|
||||
def format_response_as_markdown(response_text: str) -> str:
|
||||
"""
|
||||
فرمت کردن پاسخ به صورت Markdown برای نمایش زیباتر
|
||||
"""
|
||||
if not response_text:
|
||||
return response_text
|
||||
|
||||
response_text = response_text.strip()
|
||||
|
||||
if any(char in response_text for char in ['#', '*', '`', '-']):
|
||||
return response_text
|
||||
|
||||
paragraphs = [p.strip() for p in response_text.split('\n') if p.strip()]
|
||||
|
||||
formatted_paragraphs = []
|
||||
for i, paragraph in enumerate(paragraphs):
|
||||
if len(paragraph.split()) > 10:
|
||||
formatted_paragraphs.append(paragraph)
|
||||
else:
|
||||
if paragraph.endswith(':') or any(keyword in paragraph.lower() for keyword in ['نکته', 'توجه', 'مهم', 'خلاصه']):
|
||||
formatted_paragraphs.append(f"**{paragraph}**")
|
||||
else:
|
||||
formatted_paragraphs.append(paragraph)
|
||||
|
||||
final_paragraphs = []
|
||||
for paragraph in formatted_paragraphs:
|
||||
if any(indicator in paragraph for indicator in ['•', '- ', '۱.', '۲.', '۳.']):
|
||||
final_paragraphs.append(paragraph)
|
||||
elif len(paragraph.split()) <= 8 and not paragraph.endswith(('.', ':', ';')):
|
||||
final_paragraphs.append(f"- {paragraph}")
|
||||
else:
|
||||
final_paragraphs.append(paragraph)
|
||||
|
||||
return '\n\n'.join(final_paragraphs)
|
||||
|
||||
@app.post("/format_response")
|
||||
async def format_response(request: dict):
|
||||
"""
|
||||
فرمت کردن پاسخ به صورت Markdown
|
||||
"""
|
||||
try:
|
||||
response_text = request.get("response", "")
|
||||
formatted_response = format_response_as_markdown(response_text)
|
||||
|
||||
return {
|
||||
"original": response_text,
|
||||
"formatted": formatted_response,
|
||||
"status": "success"
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"original": request.get("response", ""),
|
||||
"formatted": request.get("response", ""),
|
||||
"status": "error",
|
||||
"message": str(e)
|
||||
}
|
||||
|
||||
@app.post("/upload")
|
||||
async def upload_files(files: List[UploadFile] = File(...)):
|
||||
if not files:
|
||||
raise HTTPException(status_code=400, detail="No files uploaded")
|
||||
|
||||
saved_files = []
|
||||
try:
|
||||
for file in files:
|
||||
if not file.filename.lower().endswith(".pdf"):
|
||||
raise HTTPException(status_code=400, detail=f"Only PDF files allowed: {file.filename}")
|
||||
|
||||
file_path = os.path.join(Config.DATA_PATH, file.filename)
|
||||
with open(file_path, "wb") as f:
|
||||
shutil.copyfileobj(file.file, f)
|
||||
saved_files.append(file.filename)
|
||||
|
||||
rag.update_retriever()
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"{len(saved_files)} PDF file(s) uploaded and retriever updated.",
|
||||
"files": saved_files
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Upload or update failed: {str(e)}")
|
||||
|
||||
@app.get("/health")
|
||||
def health_check():
|
||||
return {"status": "OK", "model_loaded": rag is not None}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
@@ -0,0 +1,20 @@
|
||||
from service.rag_api import app as api
|
||||
import uvicorn
|
||||
from werkzeug.serving import make_server
|
||||
|
||||
class Application:
|
||||
def run_fastapi(self):
|
||||
print("FastAPI server starting on http://0.0.0.0:8000")
|
||||
config = uvicorn.Config(
|
||||
api,
|
||||
host="0.0.0.0",
|
||||
port=8000,
|
||||
log_level="info",
|
||||
)
|
||||
self.uvicorn_server = uvicorn.Server(config)
|
||||
self.uvicorn_server.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = Application()
|
||||
app.run_fastapi()
|
||||
@@ -0,0 +1,33 @@
|
||||
import os
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
load_dotenv()
|
||||
|
||||
# Set Hugging Face mirror for better connectivity
|
||||
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
|
||||
|
||||
class Config:
|
||||
API_KEY = os.getenv("API_KEY")
|
||||
MODEL_NAME = os.getenv("MODEL_NAME")
|
||||
RETREIVER_NAME = os.getenv("RETREIVER_NAME")
|
||||
VECTOR_PATH = os.getenv("VECTOR_PATH")
|
||||
DATA_PATH = os.getenv("DATA_PATH")
|
||||
DEVICE = os.getenv('DEVICE')
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY')
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///yara.db'
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
RAG_API_URL = os.environ.get('RAG_API_URL') or 'http://localhost:8000/query'
|
||||
SESSION_COOKIE_SECURE = os.environ.get('FLASK_ENV') == 'production'
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
UPLOAD_FOLDER = 'uploads'
|
||||
# MAX_CONTENT_LENGTH = 50 * 1024 * 1024
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(Config.API_KEY)
|
||||
print(Config.DATA_PATH)
|
||||
print(Config.RETREIVER_NAME)
|
||||
print(Config.DEVICE)
|
||||
@@ -0,0 +1,52 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./backend:/app
|
||||
- model_cache:/root/.cache/huggingface
|
||||
- faiss_index:/app/faiss_index
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- PYTHONPATH=/app
|
||||
- TRANSFORMERS_CACHE=/root/.cache/huggingface
|
||||
- FAISS_INDEX_PATH=/app/faiss_index
|
||||
networks:
|
||||
- app-network
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "5000:5000"
|
||||
volumes:
|
||||
- ./frontend:/app
|
||||
environment:
|
||||
- FLASK_ENV=production
|
||||
- BACKEND_URL=http://backend:8000
|
||||
- PYTHONUNBUFFERED=1
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- app-network
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
app-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
model_cache:
|
||||
faiss_index:
|
||||
@@ -0,0 +1,33 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Set environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV FLASK_APP=app.py
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc \
|
||||
g++ \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy requirements and install dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application
|
||||
COPY . .
|
||||
|
||||
# Create non-root user
|
||||
RUN useradd -m -u 1000 flaskuser && \
|
||||
chown -R flaskuser:flaskuser /app
|
||||
USER flaskuser
|
||||
|
||||
# Expose port
|
||||
EXPOSE 5000
|
||||
|
||||
# Run with gunicorn
|
||||
CMD ["gunicorn", "--timeout", "300", "--workers", "3", "--bind", "0.0.0.0:5000", "--preload", "app:app"]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
from flask import Flask
|
||||
from flask_login import LoginManager
|
||||
from model.models import db, User, bcrypt
|
||||
from auth import auth
|
||||
from admin import admin_blueprint
|
||||
from dashboard import dashboard_blueprint
|
||||
from config import Config
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(Config)
|
||||
|
||||
print(f"DEBUG: SECRET_KEY = {app.config.get('SECRET_KEY')}")
|
||||
|
||||
db.init_app(app)
|
||||
bcrypt.init_app(app)
|
||||
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = 'auth.login'
|
||||
login_manager.login_message = "Please log in to access this page."
|
||||
|
||||
app.config['SESSION_COOKIE_NAME'] = 'flask_session'
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
user = db.session.get(User, int(user_id))
|
||||
print(f"DEBUG: Loading user {user_id} -> {user}")
|
||||
return user
|
||||
|
||||
app.register_blueprint(admin_blueprint)
|
||||
app.register_blueprint(auth, url_prefix='/auth')
|
||||
app.register_blueprint(dashboard_blueprint)
|
||||
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
|
||||
return app
|
||||
|
||||
app = create_app()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000, debug=False)
|
||||
@@ -0,0 +1,44 @@
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, flash
|
||||
from flask_login import login_user, logout_user, login_required
|
||||
from model.models import db, User
|
||||
|
||||
auth = Blueprint('auth', __name__)
|
||||
|
||||
@auth.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if user and user.check_password(password):
|
||||
login_user(user)
|
||||
if user.role == "admin":
|
||||
return redirect(url_for('admin.admin_dashboard'))
|
||||
return redirect(url_for('dashboard.dashboard'))
|
||||
flash('نام کاربری یا رمز عبور اشتباه است.', 'error')
|
||||
return render_template('index.html')
|
||||
|
||||
@auth.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
if request.method == 'POST':
|
||||
username = request.form['username']
|
||||
email = request.form['email']
|
||||
password = request.form['password']
|
||||
|
||||
if User.query.filter_by(username=username).first():
|
||||
flash('این نام کاربری قبلاً استفاده شده.', 'error')
|
||||
return render_template('index.html')
|
||||
|
||||
user = User(username=username, email=email)
|
||||
user.set_password(password)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
flash('ثبتنام با موفقیت انجام شد. وارد شوید.', 'success')
|
||||
return render_template('index.html')
|
||||
return render_template('index.html')
|
||||
|
||||
@auth.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
return render_template('index.html')
|
||||
@@ -0,0 +1,20 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
load_dotenv(os.path.join(os.path.dirname(__file__), '.env'))
|
||||
|
||||
class Config:
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-fallback-key-for-testing-12345'
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///yara.db'
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
RAG_API_URL = os.environ.get('RAG_API_URL') or 'http://localhost:8000/'
|
||||
|
||||
SESSION_COOKIE_SECURE = False
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||
SESSION_COOKIE_DOMAIN = None
|
||||
|
||||
UPLOAD_FOLDER = 'uploads'
|
||||
|
||||
PERMANENT_SESSION_LIFETIME = 86400*10
|
||||
@@ -0,0 +1,833 @@
|
||||
from flask import Blueprint, render_template, request, jsonify, Response, stream_with_context, session
|
||||
from flask_login import login_required, current_user
|
||||
from model.models import db, ChatSession, Message, Article, ArticleResponse, CorrectionLog
|
||||
from datetime import datetime
|
||||
import requests
|
||||
import time
|
||||
import json
|
||||
import uuid
|
||||
import os
|
||||
|
||||
dashboard_blueprint = Blueprint('dashboard', __name__)
|
||||
|
||||
RAG_API_URL = "http://localhost:8000/"
|
||||
|
||||
@dashboard_blueprint.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@dashboard_blueprint.route('/dashboard')
|
||||
@login_required
|
||||
def dashboard():
|
||||
session_obj = ChatSession.query.filter_by(user_id=current_user.id).order_by(ChatSession.created_at.desc()).first()
|
||||
if not session_obj:
|
||||
session_obj = ChatSession(user_id=current_user.id)
|
||||
db.session.add(session_obj)
|
||||
db.session.commit()
|
||||
|
||||
messages = Message.query.filter_by(session_id=session_obj.id).order_by(Message.created_at).all()
|
||||
messages_data = [
|
||||
{
|
||||
"id": msg.id,
|
||||
"session_id": msg.session_id,
|
||||
"role": msg.role,
|
||||
"content": msg.content,
|
||||
"created_at": msg.created_at.isoformat() if msg.created_at else None
|
||||
}
|
||||
for msg in messages
|
||||
]
|
||||
|
||||
return render_template('dashboard/dashboard.html', messages=messages_data)
|
||||
|
||||
@dashboard_blueprint.route('/send_message', methods=['POST'])
|
||||
@login_required
|
||||
def send_message():
|
||||
user_message = request.json.get('message', '').strip()
|
||||
article_id = request.json.get('article_id', '')
|
||||
|
||||
if not user_message:
|
||||
return jsonify({"error": "پیام خالی است"}), 400
|
||||
|
||||
# Get PDF path from database based on article_id
|
||||
pdf_path = ""
|
||||
if article_id:
|
||||
article = Article.query.filter_by(id=article_id, user_id=current_user.id).first()
|
||||
if article and article.pdf_path:
|
||||
pdf_path = article.pdf_path
|
||||
|
||||
session_obj = ChatSession.query.filter_by(user_id=current_user.id).order_by(ChatSession.created_at.desc()).first()
|
||||
if not session_obj:
|
||||
session_obj = ChatSession(user_id=current_user.id)
|
||||
db.session.add(session_obj)
|
||||
db.session.commit()
|
||||
|
||||
user_msg = Message(session_id=session_obj.id, role='user', content=user_message)
|
||||
db.session.add(user_msg)
|
||||
db.session.commit()
|
||||
|
||||
try:
|
||||
response = requests.post("http://localhost:8000/query", json={"message": user_message, "pdf_path": pdf_path}, timeout=60)
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Backend error: {response.text}")
|
||||
|
||||
result = response.json()
|
||||
full_response = result["response"]
|
||||
|
||||
assistant_msg = Message(session_id=session_obj.id, role='assistant', content=full_response)
|
||||
db.session.add(assistant_msg)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
"content": full_response})
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"خطا در ارتباط با سرور: {str(e)}"
|
||||
err_msg = Message(session_id=session_obj.id, role='assistant', content=error_msg)
|
||||
db.session.add(err_msg)
|
||||
db.session.commit()
|
||||
return jsonify({"content": error_msg, "chunks": []}), 500
|
||||
|
||||
@dashboard_blueprint.route('/clear_chat', methods=['POST'])
|
||||
@login_required
|
||||
def clear_chat():
|
||||
new_session = ChatSession(user_id=current_user.id)
|
||||
db.session.add(new_session)
|
||||
db.session.commit()
|
||||
return jsonify({"status": "cleared", "new_session_id": new_session.id})
|
||||
|
||||
@dashboard_blueprint.route('/api/current_user')
|
||||
@login_required
|
||||
def get_current_user():
|
||||
"""دریافت اطلاعات کاربر جاری"""
|
||||
return jsonify({
|
||||
'id': current_user.id,
|
||||
'username': current_user.username,
|
||||
'email': getattr(current_user, 'email', ''),
|
||||
'name': getattr(current_user, 'name', current_user.username),
|
||||
'role': getattr(current_user, 'role', 'ویراستار')
|
||||
})
|
||||
|
||||
@dashboard_blueprint.route('/articles')
|
||||
@login_required
|
||||
def articles_dashboard():
|
||||
"""صفحه اصلی نمایشگر معیار مقالات"""
|
||||
user_data = {
|
||||
'name': getattr(current_user, 'name', current_user.username),
|
||||
'email': getattr(current_user, 'email', ''),
|
||||
'role': getattr(current_user, 'role', 'ویراستار')
|
||||
}
|
||||
return render_template('dashboard/articles_dashboard.html', user_data=user_data)
|
||||
|
||||
@dashboard_blueprint.route('/api/generate_response', methods=['POST'])
|
||||
@login_required
|
||||
def generate_response():
|
||||
"""تولید پاسخ جدید با هوش مصنوعی برای یک معیار"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'error': 'دادههای JSON دریافت نشد'}), 400
|
||||
|
||||
article = data.get('article')
|
||||
user_edited_response = data.get('user_edited_response')
|
||||
measure_text = data.get('measure_text', '')
|
||||
measure_type = data.get('measure_type', '')
|
||||
|
||||
if not article:
|
||||
return jsonify({'error': 'دادههای مقاله دریافت نشد'}), 400
|
||||
|
||||
if not user_edited_response:
|
||||
return jsonify({'error': 'پاسخ ویرایش شده دریافت نشد'}), 400
|
||||
|
||||
article_id = (
|
||||
article.get('id') or
|
||||
article.get('article_id') or
|
||||
article.get('article id')
|
||||
)
|
||||
|
||||
if article_id is None:
|
||||
return jsonify({'error': 'شناسه مقاله در دادهها یافت نشد'}), 400
|
||||
|
||||
# Get PDF path from database
|
||||
pdf_path = ""
|
||||
if article.get('id'):
|
||||
db_article = Article.query.filter_by(id=article['id'], user_id=current_user.id).first()
|
||||
if db_article and db_article.pdf_path:
|
||||
pdf_path = db_article.pdf_path
|
||||
else:
|
||||
# Fallback to article name if no PDF path in database
|
||||
pdf_path = article.get('name', '').strip() + ".pdf"
|
||||
|
||||
# Find the index of the measure text and get the current response
|
||||
response_measure = ""
|
||||
try:
|
||||
if measure_type == 'positive':
|
||||
measures = article.get('measures_positive', [])
|
||||
responses = article.get('responses_positive', [])
|
||||
if measure_text in measures:
|
||||
index = measures.index(measure_text)
|
||||
response_measure = responses[index] if index < len(responses) else ""
|
||||
else:
|
||||
measures = article.get('measures_negative', [])
|
||||
responses = article.get('responses_negative', [])
|
||||
if measure_text in measures:
|
||||
index = measures.index(measure_text)
|
||||
response_measure = responses[index] if index < len(responses) else ""
|
||||
except (ValueError, IndexError):
|
||||
response_measure = ""
|
||||
|
||||
prompt = f"""
|
||||
چکیده مقاله: {article.get('abstract', '')}
|
||||
معیار {measure_type}: {measure_text}
|
||||
|
||||
پاسخ قبلی: {response_measure}
|
||||
|
||||
پاسخ ویرایش شده توسط کاربر: {user_edited_response}
|
||||
|
||||
لطفاً بر اساس معیار فوق و پاسخ ویرایش شده کاربر، یک پاسخ بهبود یافته و حرفهایتر ارائه دهید که:
|
||||
1. کاملاً با معیار داده شده مرتبط باشد
|
||||
2. ساختار منظمتری داشته باشد
|
||||
3. از لحاظ علمی دقیق و معتبر باشد
|
||||
4. از پاسخ کاربر به عنوان پایه استفاده کند اما آن را ارتقا دهد
|
||||
5. مستقیماً به معیار اشاره شده پاسخ دهد
|
||||
|
||||
پاسخ بهبود یافته:
|
||||
"""
|
||||
|
||||
try:
|
||||
rag_response = requests.post(
|
||||
"http://localhost:8000/query",
|
||||
json={
|
||||
"message": prompt,
|
||||
"pdf_path": pdf_path
|
||||
},
|
||||
timeout=30000
|
||||
)
|
||||
|
||||
if rag_response.status_code == 200:
|
||||
result = rag_response.json()
|
||||
ai_response = result.get("response", user_edited_response)
|
||||
else:
|
||||
print(f"RAG error: {rag_response.status_code} - {rag_response.text}")
|
||||
ai_response = user_edited_response
|
||||
|
||||
except Exception as e:
|
||||
print(f"خطا در ارتباط با RAG: {e}")
|
||||
ai_response = user_edited_response
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'ai_response': ai_response,
|
||||
'message': 'پاسخ با موفقیت توسط هوش مصنوعی تولید شد'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in generate_response: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return jsonify({'error': f'خطا در تولید پاسخ: {str(e)}'}), 500
|
||||
|
||||
@dashboard_blueprint.route('/api/delete_article/<int:article_id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def delete_article(article_id):
|
||||
"""حذف مقاله"""
|
||||
article = Article.query.filter_by(id=article_id, user_id=current_user.id).first()
|
||||
if not article:
|
||||
return jsonify({'error': 'مقاله یافت نشد'}), 404
|
||||
|
||||
try:
|
||||
# Delete related responses first
|
||||
ArticleResponse.query.filter_by(article_id=article_id).delete()
|
||||
db.session.delete(article)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({'success': True, 'message': 'مقاله با موفقیت حذف شد'})
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'error': f'خطا در حذف مقاله: {str(e)}'}), 500
|
||||
|
||||
@dashboard_blueprint.route('/api/upload_articles', methods=['POST'])
|
||||
@login_required
|
||||
def upload_articles():
|
||||
"""آپلود فایل JSON مقالات"""
|
||||
if 'file' not in request.files:
|
||||
return jsonify({'error': 'فایلی انتخاب نشده است'}), 400
|
||||
|
||||
file = request.files['file']
|
||||
if file.filename == '':
|
||||
return jsonify({'error': 'فایلی انتخاب نشده است'}), 400
|
||||
|
||||
if not file.filename.endswith('.json'):
|
||||
return jsonify({'error': 'فقط فایلهای JSON پشتیبانی میشوند'}), 400
|
||||
|
||||
try:
|
||||
articles_data = json.load(file)
|
||||
uploaded_count = 0
|
||||
|
||||
for article_data in articles_data:
|
||||
existing_article = Article.query.filter_by(
|
||||
user_id=current_user.id,
|
||||
article_id=article_data.get('article id', str(uuid.uuid4()))
|
||||
).first()
|
||||
|
||||
if existing_article:
|
||||
continue
|
||||
|
||||
# Extract PDF path from article data or create from name
|
||||
pdf_path = article_data.get('pdf_path', '')
|
||||
if not pdf_path and 'article name' in article_data:
|
||||
pdf_path = f"{article_data['article name'].strip()}.pdf"
|
||||
|
||||
article = Article(
|
||||
user_id=current_user.id,
|
||||
article_id=article_data.get('article id', str(uuid.uuid4())),
|
||||
name=article_data.get('article name', 'بدون نام'),
|
||||
institute=article_data.get('article institute', 'نامشخص'),
|
||||
abstract=article_data.get('article abstract', 'چکیدهای موجود نیست'),
|
||||
pdf_path=pdf_path, # Store PDF path
|
||||
measures_positive=json.dumps(article_data.get('measures_positive', []), ensure_ascii=False),
|
||||
measures_negative=json.dumps(article_data.get('measures_negative', []), ensure_ascii=False),
|
||||
responses_positive=json.dumps(article_data.get('responses_positive', []), ensure_ascii=False),
|
||||
responses_negative=json.dumps(article_data.get('responses_negative', []), ensure_ascii=False),
|
||||
scores_positive=json.dumps(article_data.get('scores_positive', []), ensure_ascii=False),
|
||||
scores_negative=json.dumps(article_data.get('scores_negative', []), ensure_ascii=False)
|
||||
)
|
||||
db.session.add(article)
|
||||
uploaded_count += 1
|
||||
|
||||
db.session.commit()
|
||||
|
||||
new_articles = Article.query.filter_by(user_id=current_user.id).all()
|
||||
articles_list = []
|
||||
for article in new_articles:
|
||||
articles_list.append({
|
||||
'id': article.id,
|
||||
'article_id': article.article_id,
|
||||
'name': article.name,
|
||||
'institute': article.institute,
|
||||
'abstract': article.abstract,
|
||||
'pdf_path': article.pdf_path, # Include PDF path in response
|
||||
'measures_positive': json.loads(article.measures_positive) if article.measures_positive else [],
|
||||
'measures_negative': json.loads(article.measures_negative) if article.measures_negative else [],
|
||||
'responses_positive': json.loads(article.responses_positive) if article.responses_positive else [],
|
||||
'responses_negative': json.loads(article.responses_negative) if article.responses_negative else [],
|
||||
'scores_positive': json.loads(article.scores_positive) if article.scores_positive else [],
|
||||
'scores_negative': json.loads(article.scores_negative) if article.scores_negative else []
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
'message': f'{uploaded_count} مقاله با موفقیت آپلود شد',
|
||||
'articles': articles_list
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in upload_articles: {str(e)}")
|
||||
return jsonify({'error': f'خطا در پردازش فایل: {str(e)}'}), 500
|
||||
|
||||
@dashboard_blueprint.route('/my_articles')
|
||||
@login_required
|
||||
def my_articles():
|
||||
"""مقالات تخصیص داده شده به کاربر"""
|
||||
from model.models import Article
|
||||
|
||||
status_filter = request.args.get('status', 'all')
|
||||
query = Article.query.filter_by(user_id=current_user.id)
|
||||
|
||||
if status_filter != 'all':
|
||||
query = query.filter_by(correction_status=status_filter)
|
||||
|
||||
articles = query.order_by(Article.updated_at.desc()).all()
|
||||
|
||||
return render_template('user/my_articles.html',
|
||||
articles=articles,
|
||||
status_filter=status_filter)
|
||||
|
||||
@dashboard_blueprint.route('/correct_article/<int:article_id>')
|
||||
@login_required
|
||||
def correct_article(article_id):
|
||||
"""صفحه تصحیح مقاله"""
|
||||
from model.models import Article, CorrectionLog, db
|
||||
|
||||
article = Article.query.filter_by(id=article_id, user_id=current_user.id).first_or_404()
|
||||
|
||||
if article.correction_status == 'pending':
|
||||
article.correction_status = 'in_progress'
|
||||
article.corrected_by = current_user.id
|
||||
db.session.commit()
|
||||
|
||||
return render_template('user/correct_article.html', article=article)
|
||||
|
||||
@dashboard_blueprint.route('/api/save_correction', methods=['POST'])
|
||||
@login_required
|
||||
def save_correction():
|
||||
"""ذخیره تصحیح کاربر"""
|
||||
from model.models import Article, CorrectionLog, db
|
||||
import json
|
||||
|
||||
data = request.json
|
||||
article_id = data.get('article_id')
|
||||
field = data.get('field')
|
||||
new_value = data.get('value')
|
||||
|
||||
article = Article.query.filter_by(id=article_id, user_id=current_user.id).first_or_404()
|
||||
|
||||
try:
|
||||
# ذخیره مقدار قدیمی برای لاگ
|
||||
old_value = getattr(article, field, '')
|
||||
|
||||
# بهروزرسانی فیلد
|
||||
setattr(article, field, new_value)
|
||||
article.correction_status = 'completed'
|
||||
article.corrected_at = datetime.utcnow()
|
||||
|
||||
# ایجاد لاگ
|
||||
log = CorrectionLog(
|
||||
article_id=article.id,
|
||||
user_id=current_user.id,
|
||||
action='corrected',
|
||||
field_name=field,
|
||||
old_value=old_value,
|
||||
new_value=new_value
|
||||
)
|
||||
db.session.add(log)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({'success': True, 'message': 'تغییرات ذخیره شد'})
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
@dashboard_blueprint.route('/api/update_measure_completion', methods=['POST'])
|
||||
@login_required
|
||||
def update_measure_completion():
|
||||
"""بهروزرسانی وضعیت تکمیل یک معیار"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'error': 'دادههای JSON دریافت نشد'}), 400
|
||||
|
||||
article_id = data.get('article_id')
|
||||
measure_index = data.get('measure_index')
|
||||
is_completed = data.get('is_completed')
|
||||
|
||||
if article_id is None:
|
||||
return jsonify({'error': 'شناسه مقاله ارسال نشد'}), 400
|
||||
|
||||
if measure_index is None:
|
||||
return jsonify({'error': 'شماره معیار ارسال نشد'}), 400
|
||||
|
||||
if is_completed is None:
|
||||
return jsonify({'error': 'وضعیت تکمیل ارسال نشد'}), 400
|
||||
|
||||
# پیدا کردن مقاله
|
||||
article = Article.query.filter_by(id=article_id, user_id=current_user.id).first()
|
||||
if not article:
|
||||
return jsonify({'error': 'مقاله یافت نشد'}), 404
|
||||
|
||||
# دریافت وضعیتهای تکمیل فعلی
|
||||
measures_completed = []
|
||||
if article.measures_completed:
|
||||
try:
|
||||
measures_completed = json.loads(article.measures_completed)
|
||||
except json.JSONDecodeError:
|
||||
measures_completed = []
|
||||
|
||||
# اطمینان از اندازه آرایه
|
||||
max_measures = max(
|
||||
len(json.loads(article.measures_positive)) if article.measures_positive else 0,
|
||||
len(json.loads(article.measures_negative)) if article.measures_negative else 0
|
||||
)
|
||||
|
||||
while len(measures_completed) <= measure_index:
|
||||
measures_completed.append(False)
|
||||
|
||||
# بهروزرسانی وضعیت معیار
|
||||
measures_completed[measure_index] = bool(is_completed)
|
||||
|
||||
# ذخیره در دیتابیس
|
||||
article.measures_completed = json.dumps(measures_completed, ensure_ascii=False)
|
||||
|
||||
# محاسبه و بهروزرسانی وضعیت کلی مقاله
|
||||
total_measures = max_measures
|
||||
completed_count = sum(measures_completed[:total_measures]) if total_measures > 0 else 0
|
||||
|
||||
if total_measures == 0:
|
||||
article.correction_status = 'pending'
|
||||
elif completed_count == 0:
|
||||
article.correction_status = 'pending'
|
||||
elif completed_count < total_measures:
|
||||
article.correction_status = 'in_progress'
|
||||
else:
|
||||
article.correction_status = 'completed'
|
||||
|
||||
article.updated_at = datetime.utcnow()
|
||||
db.session.commit()
|
||||
|
||||
# ایجاد لاگ برای ذخیره تغییرات
|
||||
try:
|
||||
log = CorrectionLog(
|
||||
article_id=article.id,
|
||||
user_id=current_user.id,
|
||||
action='measure_completion_updated',
|
||||
field_name=f'measure_{measure_index}_completed',
|
||||
old_value='False' if not is_completed else 'True',
|
||||
new_value='True' if is_completed else 'False',
|
||||
details=json.dumps({
|
||||
'measure_index': measure_index,
|
||||
'is_completed': is_completed,
|
||||
'total_completed': completed_count,
|
||||
'total_measures': total_measures
|
||||
}, ensure_ascii=False)
|
||||
)
|
||||
db.session.add(log)
|
||||
db.session.commit()
|
||||
except Exception as log_error:
|
||||
print(f"خطا در ذخیره لاگ: {log_error}")
|
||||
# در صورت خطا در لاگ، commit اصلی را rollback نکن
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'وضعیت تکمیل معیار با موفقیت بهروزرسانی شد',
|
||||
'data': {
|
||||
'article_id': article.id,
|
||||
'measure_index': measure_index,
|
||||
'is_completed': is_completed,
|
||||
'total_completed': completed_count,
|
||||
'total_measures': total_measures,
|
||||
'article_status': article.correction_status
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in update_measure_completion: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
db.session.rollback()
|
||||
return jsonify({'error': f'خطا در بهروزرسانی وضعیت تکمیل: {str(e)}'}), 500
|
||||
|
||||
@dashboard_blueprint.route('/api/articles')
|
||||
@login_required
|
||||
def get_articles():
|
||||
"""دریافت لیست مقالات کاربر"""
|
||||
articles = Article.query.filter_by(user_id=current_user.id).all()
|
||||
|
||||
articles_list = []
|
||||
for article in articles:
|
||||
# محاسبه وضعیت بر اساس معیارهای تکمیل شده
|
||||
measures_completed = []
|
||||
if article.measures_completed:
|
||||
try:
|
||||
measures_completed = json.loads(article.measures_completed)
|
||||
except json.JSONDecodeError:
|
||||
measures_completed = []
|
||||
|
||||
# محاسبه وضعیت کلی
|
||||
max_measures = max(
|
||||
len(json.loads(article.measures_positive)) if article.measures_positive else 0,
|
||||
len(json.loads(article.measures_negative)) if article.measures_negative else 0
|
||||
)
|
||||
|
||||
completed_count = sum(measures_completed[:max_measures]) if max_measures > 0 else 0
|
||||
|
||||
# تعیین وضعیت مقاله
|
||||
if max_measures == 0:
|
||||
article_status = 'pending'
|
||||
elif completed_count == 0:
|
||||
article_status = 'pending'
|
||||
elif completed_count < max_measures:
|
||||
article_status = 'in_progress'
|
||||
else:
|
||||
article_status = 'completed'
|
||||
|
||||
articles_list.append({
|
||||
'id': article.id,
|
||||
'article_id': article.article_id,
|
||||
'name': article.name,
|
||||
'institute': article.institute,
|
||||
'abstract': article.abstract,
|
||||
'pdf_path': article.pdf_path,
|
||||
'measures_positive': json.loads(article.measures_positive) if article.measures_positive else [],
|
||||
'measures_negative': json.loads(article.measures_negative) if article.measures_negative else [],
|
||||
'responses_positive': json.loads(article.responses_positive) if article.responses_positive else [],
|
||||
'responses_negative': json.loads(article.responses_negative) if article.responses_negative else [],
|
||||
'scores_positive': json.loads(article.scores_positive) if article.scores_positive else [],
|
||||
'scores_negative': json.loads(article.scores_negative) if article.scores_negative else [],
|
||||
'measures_completed': measures_completed, # وضعیت تکمیل هر معیار
|
||||
'status': article_status, # وضعیت کلی مقاله
|
||||
'correction_status': article.correction_status,
|
||||
'measures_count': max_measures, # تعداد کل معیارها
|
||||
'completed_measures': completed_count # تعداد معیارهای تکمیل شده
|
||||
})
|
||||
|
||||
return jsonify(articles_list)
|
||||
|
||||
@dashboard_blueprint.route('/api/update_response', methods=['POST'])
|
||||
@login_required
|
||||
def update_response():
|
||||
"""ذخیره مستقیم پاسخ ویرایش شده و امتیازها"""
|
||||
data = request.json
|
||||
article_id = data.get('article_id')
|
||||
measure_index = data.get('measure_index')
|
||||
measure_type = data.get('measure_type')
|
||||
edited_response = data.get('edited_response')
|
||||
|
||||
# دریافت امتیازهای جدید (اگر ارسال شده باشند)
|
||||
positive_score = data.get('positive_score')
|
||||
negative_score = data.get('negative_score')
|
||||
score = data.get('score') # امتیاز کلی برای backward compatibility
|
||||
|
||||
if not all([article_id, measure_index is not None, measure_type, edited_response]):
|
||||
return jsonify({'error': 'دادههای ناقص ارسال شده است'}), 400
|
||||
|
||||
try:
|
||||
article = Article.query.filter_by(id=article_id, user_id=current_user.id).first()
|
||||
if not article:
|
||||
return jsonify({'error': 'مقاله یافت نشد'}), 404
|
||||
|
||||
# ذخیره پاسخ
|
||||
if measure_type == 'positive':
|
||||
responses = json.loads(article.responses_positive) if article.responses_positive else []
|
||||
if len(responses) > measure_index:
|
||||
responses[measure_index] = edited_response
|
||||
else:
|
||||
while len(responses) <= measure_index:
|
||||
responses.append("")
|
||||
responses[measure_index] = edited_response
|
||||
article.responses_positive = json.dumps(responses, ensure_ascii=False)
|
||||
|
||||
# ذخیره امتیاز مثبت اگر ارسال شده باشد
|
||||
if positive_score is not None:
|
||||
scores = json.loads(article.scores_positive) if article.scores_positive else []
|
||||
if len(scores) > measure_index:
|
||||
scores[measure_index] = float(positive_score)
|
||||
else:
|
||||
while len(scores) <= measure_index:
|
||||
scores.append(0.0)
|
||||
scores[measure_index] = float(positive_score)
|
||||
article.scores_positive = json.dumps(scores, ensure_ascii=False)
|
||||
else:
|
||||
responses = json.loads(article.responses_negative) if article.responses_negative else []
|
||||
if len(responses) > measure_index:
|
||||
responses[measure_index] = edited_response
|
||||
else:
|
||||
while len(responses) <= measure_index:
|
||||
responses.append("")
|
||||
responses[measure_index] = edited_response
|
||||
article.responses_negative = json.dumps(responses, ensure_ascii=False)
|
||||
|
||||
# ذخیره امتیاز منفی اگر ارسال شده باشد
|
||||
if negative_score is not None:
|
||||
scores = json.loads(article.scores_negative) if article.scores_negative else []
|
||||
if len(scores) > measure_index:
|
||||
scores[measure_index] = float(negative_score)
|
||||
else:
|
||||
while len(scores) <= measure_index:
|
||||
scores.append(0.0)
|
||||
scores[measure_index] = float(negative_score)
|
||||
article.scores_negative = json.dumps(scores, ensure_ascii=False)
|
||||
|
||||
# همچنین ذخیره امتیاز کلی برای backward compatibility
|
||||
if score is not None and measure_type == 'positive':
|
||||
scores = json.loads(article.scores_positive) if article.scores_positive else []
|
||||
if len(scores) > measure_index:
|
||||
scores[measure_index] = float(score)
|
||||
else:
|
||||
while len(scores) <= measure_index:
|
||||
scores.append(0.0)
|
||||
scores[measure_index] = float(score)
|
||||
article.scores_positive = json.dumps(scores, ensure_ascii=False)
|
||||
elif score is not None and measure_type == 'negative':
|
||||
scores = json.loads(article.scores_negative) if article.scores_negative else []
|
||||
if len(scores) > measure_index:
|
||||
scores[measure_index] = float(score)
|
||||
else:
|
||||
while len(scores) <= measure_index:
|
||||
scores.append(0.0)
|
||||
scores[measure_index] = float(score)
|
||||
article.scores_negative = json.dumps(scores, ensure_ascii=False)
|
||||
|
||||
# بررسی تکمیل خودکار معیار
|
||||
responses_positive = json.loads(article.responses_positive) if article.responses_positive else []
|
||||
responses_negative = json.loads(article.responses_negative) if article.responses_negative else []
|
||||
measures_completed = json.loads(article.measures_completed) if article.measures_completed else []
|
||||
|
||||
# اگر هر دو پاسخ برای این معیار پر شدهاند، به صورت خودکار تکمیل را علامت بزن
|
||||
if measure_index < len(responses_positive) and measure_index < len(responses_negative):
|
||||
pos_response = responses_positive[measure_index] if measure_index < len(responses_positive) else ""
|
||||
neg_response = responses_negative[measure_index] if measure_index < len(responses_negative) else ""
|
||||
|
||||
if pos_response.strip() and pos_response != "پاسخی موجود نیست" and \
|
||||
neg_response.strip() and neg_response != "پاسخی موجود نیست":
|
||||
|
||||
# اطمینان از اندازه آرایه
|
||||
while len(measures_completed) <= measure_index:
|
||||
measures_completed.append(False)
|
||||
|
||||
measures_completed[measure_index] = True
|
||||
article.measures_completed = json.dumps(measures_completed, ensure_ascii=False)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# ایجاد لاگ
|
||||
try:
|
||||
log = CorrectionLog(
|
||||
article_id=article.id,
|
||||
user_id=current_user.id,
|
||||
action='response_updated',
|
||||
field_name=f'{measure_type}_response_{measure_index}',
|
||||
old_value='',
|
||||
new_value=edited_response[:200], # فقط 200 کاراکتر اول
|
||||
details=json.dumps({
|
||||
'measure_index': measure_index,
|
||||
'measure_type': measure_type,
|
||||
'positive_score': positive_score,
|
||||
'negative_score': negative_score
|
||||
}, ensure_ascii=False)
|
||||
)
|
||||
db.session.add(log)
|
||||
db.session.commit()
|
||||
except Exception as log_error:
|
||||
print(f"خطا در ذخیره لاگ: {log_error}")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'پاسخ با موفقیت بهروزرسانی شد'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in update_response: {str(e)}")
|
||||
db.session.rollback()
|
||||
return jsonify({'error': f'خطا در بهروزرسانی پاسخ: {str(e)}'}), 500
|
||||
|
||||
@dashboard_blueprint.route('/api/article/<int:article_id>')
|
||||
@login_required
|
||||
def get_article_details(article_id):
|
||||
"""دریافت جزئیات یک مقاله خاص"""
|
||||
article = Article.query.filter_by(id=article_id, user_id=current_user.id).first()
|
||||
|
||||
if not article:
|
||||
return jsonify({'error': 'مقاله یافت نشد'}), 404
|
||||
|
||||
# محاسبه وضعیت بر اساس معیارهای تکمیل شده
|
||||
measures_completed = []
|
||||
if article.measures_completed:
|
||||
try:
|
||||
measures_completed = json.loads(article.measures_completed)
|
||||
except json.JSONDecodeError:
|
||||
measures_completed = []
|
||||
|
||||
# محاسبه وضعیت کلی
|
||||
max_measures = max(
|
||||
len(json.loads(article.measures_positive)) if article.measures_positive else 0,
|
||||
len(json.loads(article.measures_negative)) if article.measures_negative else 0
|
||||
)
|
||||
|
||||
completed_count = sum(measures_completed[:max_measures]) if max_measures > 0 else 0
|
||||
|
||||
# تعیین وضعیت مقاله
|
||||
if max_measures == 0:
|
||||
article_status = 'pending'
|
||||
elif completed_count == 0:
|
||||
article_status = 'pending'
|
||||
elif completed_count < max_measures:
|
||||
article_status = 'in_progress'
|
||||
else:
|
||||
article_status = 'completed'
|
||||
|
||||
article_data = {
|
||||
'id': article.id,
|
||||
'article_id': article.article_id,
|
||||
'name': article.name,
|
||||
'institute': article.institute,
|
||||
'abstract': article.abstract,
|
||||
'pdf_path': article.pdf_path,
|
||||
'measures_positive': json.loads(article.measures_positive) if article.measures_positive else [],
|
||||
'measures_negative': json.loads(article.measures_negative) if article.measures_negative else [],
|
||||
'responses_positive': json.loads(article.responses_positive) if article.responses_positive else [],
|
||||
'responses_negative': json.loads(article.responses_negative) if article.responses_negative else [],
|
||||
'scores_positive': json.loads(article.scores_positive) if article.scores_positive else [],
|
||||
'scores_negative': json.loads(article.scores_negative) if article.scores_negative else [],
|
||||
'measures_completed': measures_completed,
|
||||
'status': article_status,
|
||||
'correction_status': article.correction_status,
|
||||
'measures_count': max_measures,
|
||||
'completed_measures': completed_count,
|
||||
'progress_percentage': round((completed_count / max_measures) * 100, 2) if max_measures > 0 else 0,
|
||||
'created_at': article.created_at.isoformat() if article.created_at else None,
|
||||
'updated_at': article.updated_at.isoformat() if article.updated_at else None
|
||||
}
|
||||
|
||||
return jsonify(article_data)
|
||||
|
||||
@dashboard_blueprint.route('/api/sync_article_status/<int:article_id>', methods=['POST'])
|
||||
@login_required
|
||||
def sync_article_status(article_id):
|
||||
"""همگامسازی وضعیت مقاله بر اساس پاسخها و وضعیت تکمیل"""
|
||||
article = Article.query.filter_by(id=article_id, user_id=current_user.id).first()
|
||||
|
||||
if not article:
|
||||
return jsonify({'error': 'مقاله یافت نشد'}), 404
|
||||
|
||||
try:
|
||||
# دریافت دادههای فعلی
|
||||
measures_positive = json.loads(article.measures_positive) if article.measures_positive else []
|
||||
measures_negative = json.loads(article.measures_negative) if article.measures_negative else []
|
||||
responses_positive = json.loads(article.responses_positive) if article.responses_positive else []
|
||||
responses_negative = json.loads(article.responses_negative) if article.responses_negative else []
|
||||
measures_completed = json.loads(article.measures_completed) if article.measures_completed else []
|
||||
|
||||
max_measures = max(len(measures_positive), len(measures_negative))
|
||||
|
||||
# بررسی وضعیت تکمیل بر اساس پاسخها
|
||||
updated_measures_completed = []
|
||||
for i in range(max_measures):
|
||||
# اگر از قبل تکمیل علامت خورده، آن را حفظ کن
|
||||
if i < len(measures_completed) and measures_completed[i]:
|
||||
updated_measures_completed.append(True)
|
||||
continue
|
||||
|
||||
# بررسی پاسخها
|
||||
pos_response = responses_positive[i] if i < len(responses_positive) else ""
|
||||
neg_response = responses_negative[i] if i < len(responses_negative) else ""
|
||||
|
||||
is_pos_valid = pos_response and pos_response.strip() and pos_response != "پاسخی موجود نیست"
|
||||
is_neg_valid = neg_response and neg_response.strip() and neg_response != "پاسخی موجود نیست"
|
||||
|
||||
updated_measures_completed.append(is_pos_valid and is_neg_valid)
|
||||
|
||||
# ذخیره وضعیت بهروزرسانی شده
|
||||
article.measures_completed = json.dumps(updated_measures_completed, ensure_ascii=False)
|
||||
|
||||
# محاسبه وضعیت کلی
|
||||
completed_count = sum(updated_measures_completed)
|
||||
|
||||
if max_measures == 0:
|
||||
article.correction_status = 'pending'
|
||||
elif completed_count == 0:
|
||||
article.correction_status = 'pending'
|
||||
elif completed_count < max_measures:
|
||||
article.correction_status = 'in_progress'
|
||||
else:
|
||||
article.correction_status = 'completed'
|
||||
|
||||
article.updated_at = datetime.utcnow()
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'وضعیت مقاله همگامسازی شد',
|
||||
'data': {
|
||||
'article_id': article.id,
|
||||
'total_measures': max_measures,
|
||||
'completed_measures': completed_count,
|
||||
'article_status': article.correction_status,
|
||||
'progress_percentage': round((completed_count / max_measures) * 100, 2) if max_measures > 0 else 0
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in sync_article_status: {str(e)}")
|
||||
db.session.rollback()
|
||||
return jsonify({'error': f'خطا در همگامسازی وضعیت: {str(e)}'}), 500
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
from datetime import datetime
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_login import UserMixin
|
||||
from flask_bcrypt import Bcrypt
|
||||
|
||||
db = SQLAlchemy()
|
||||
bcrypt = Bcrypt()
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
__tablename__ = 'users'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(80), unique=True, nullable=False)
|
||||
email = db.Column(db.String(120), unique=True, nullable=False)
|
||||
password_hash = db.Column(db.String(255), nullable=False)
|
||||
|
||||
role = db.Column(db.String(20), default='user') # 'admin', 'user'
|
||||
is_active = db.Column(db.Boolean, default=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
def set_password(self, password):
|
||||
"""Hash and set the password"""
|
||||
self.password_hash = bcrypt.generate_password_hash(password).decode('utf-8')
|
||||
|
||||
def check_password(self, password):
|
||||
"""Check if the provided password matches the hash"""
|
||||
return bcrypt.check_password_hash(self.password_hash, password)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<User {self.username}>'
|
||||
|
||||
class DataFile(db.Model):
|
||||
__tablename__ = 'data_files'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
filename = db.Column(db.String(255), nullable=False)
|
||||
original_filename = db.Column(db.String(255), nullable=False)
|
||||
file_path = db.Column(db.String(500), nullable=False)
|
||||
file_size = db.Column(db.Integer, nullable=False)
|
||||
file_type = db.Column(db.String(50), nullable=False) # 'json', 'pdf', etc.
|
||||
uploader_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
status = db.Column(db.String(20), default='pending') # 'pending', 'processing', 'completed', 'error'
|
||||
description = db.Column(db.Text)
|
||||
uploaded_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
processed_at = db.Column(db.DateTime)
|
||||
|
||||
uploader = db.relationship('User', backref='uploaded_files')
|
||||
|
||||
class Article(db.Model):
|
||||
__tablename__ = 'articles'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
article_id = db.Column(db.String(255), nullable=False)
|
||||
name = db.Column(db.String(500), nullable=False)
|
||||
institute = db.Column(db.String(500))
|
||||
abstract = db.Column(db.Text)
|
||||
|
||||
measures_positive = db.Column(db.Text)
|
||||
measures_negative = db.Column(db.Text)
|
||||
responses_positive = db.Column(db.Text)
|
||||
responses_negative = db.Column(db.Text)
|
||||
scores_positive = db.Column(db.Text)
|
||||
scores_negative = db.Column(db.Text)
|
||||
|
||||
measures_completed = db.Column(db.Text)
|
||||
|
||||
pdf_path = db.Column(db.String(500))
|
||||
data_file_id = db.Column(db.Integer, db.ForeignKey('data_files.id'))
|
||||
word_count = db.Column(db.Integer)
|
||||
page_count = db.Column(db.Integer)
|
||||
file_size = db.Column(db.Integer)
|
||||
|
||||
correction_status = db.Column(db.String(20), default='pending')
|
||||
corrected_by = db.Column(db.Integer, db.ForeignKey('users.id'))
|
||||
corrected_at = db.Column(db.DateTime)
|
||||
reviewer_id = db.Column(db.Integer, db.ForeignKey('users.id'))
|
||||
reviewed_at = db.Column(db.DateTime)
|
||||
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
user = db.relationship('User', foreign_keys=[user_id], backref=db.backref('articles', lazy=True))
|
||||
|
||||
corrector = db.relationship('User', foreign_keys=[corrected_by], backref='corrected_articles')
|
||||
reviewer = db.relationship('User', foreign_keys=[reviewer_id], backref='reviewed_articles')
|
||||
data_file = db.relationship('DataFile', backref='articles')
|
||||
|
||||
class ArticleResponse(db.Model):
|
||||
__tablename__ = 'article_responses'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
article_id = db.Column(db.Integer, db.ForeignKey('articles.id'), nullable=False)
|
||||
measure_index = db.Column(db.Integer, nullable=False)
|
||||
measure_type = db.Column(db.String(10), nullable=False)
|
||||
original_response = db.Column(db.Text)
|
||||
edited_response = db.Column(db.Text)
|
||||
ai_generated_response = db.Column(db.Text)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
article = db.relationship('Article', backref=db.backref('responses', lazy=True))
|
||||
|
||||
class ChatSession(db.Model):
|
||||
__tablename__ = 'chat_sessions'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
|
||||
article_id = db.Column(db.Integer, db.ForeignKey('articles.id'))
|
||||
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
user = db.relationship('User', backref=db.backref('chat_sessions', lazy=True))
|
||||
messages = db.relationship('Message', backref='session', lazy=True, cascade='all, delete-orphan')
|
||||
|
||||
article = db.relationship('Article', backref='chat_sessions')
|
||||
|
||||
class Message(db.Model):
|
||||
__tablename__ = 'messages'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
session_id = db.Column(db.Integer, db.ForeignKey('chat_sessions.id'), nullable=False)
|
||||
role = db.Column(db.String(20), nullable=False)
|
||||
content = db.Column(db.Text, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
class CorrectionLog(db.Model):
|
||||
__tablename__ = 'correction_logs'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
article_id = db.Column(db.Integer, db.ForeignKey('articles.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
action = db.Column(db.String(50), nullable=False) # 'created', 'updated', 'corrected', 'reviewed'
|
||||
field_name = db.Column(db.String(100))
|
||||
old_value = db.Column(db.Text)
|
||||
new_value = db.Column(db.Text)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
# ارتباطها
|
||||
article = db.relationship('Article', backref='correction_logs')
|
||||
user = db.relationship('User', backref='correction_actions')
|
||||
@@ -0,0 +1,10 @@
|
||||
flask==3.0.0
|
||||
gunicorn==21.2.0
|
||||
requests==2.31.0
|
||||
jinja2==3.1.2
|
||||
werkzeug==3.0.1
|
||||
flask-cors==4.0.0
|
||||
flask_login
|
||||
flask_sqlalchemy
|
||||
flask_bcrypt
|
||||
python-dotenv
|
||||
@@ -0,0 +1,34 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__)))
|
||||
|
||||
from app import create_app
|
||||
from model.models import db, User
|
||||
|
||||
def setup_admin():
|
||||
app = create_app()
|
||||
|
||||
with app.app_context():
|
||||
try:
|
||||
db.drop_all()
|
||||
|
||||
db.create_all()
|
||||
|
||||
admin_user = User(
|
||||
username='admin',
|
||||
email='admin@admin.com',
|
||||
role='admin'
|
||||
)
|
||||
admin_user.set_password('admin123')
|
||||
|
||||
db.session.add(admin_user)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
except Exception as e:
|
||||
print(f'❌ خطا در راهاندازی: {e}')
|
||||
db.session.rollback()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setup_admin()
|
||||
File diff suppressed because one or more lines are too long
Vendored
+9
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 261 KiB |
@@ -0,0 +1,257 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const chatContainer = document.getElementById('chatContainer');
|
||||
const userInput = document.getElementById('userInput');
|
||||
const sendBtn = document.getElementById('sendBtn');
|
||||
const clearBtn = document.getElementById('clearChat');
|
||||
const topKSlider = document.getElementById('top_k_slider');
|
||||
const topKValueDisplay = document.getElementById('top_k_value');
|
||||
|
||||
let currentTopK = parseInt(topKSlider.value) || 5;
|
||||
|
||||
topKSlider.addEventListener('input', function () {
|
||||
currentTopK = parseInt(this.value);
|
||||
topKValueDisplay.textContent = currentTopK;
|
||||
});
|
||||
|
||||
function markdownToHtml(text) {
|
||||
if (typeof text !== 'string') return text;
|
||||
|
||||
text = text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
|
||||
text = text.replace(/\*(.*?)\*/g, '<em>$1</em>');
|
||||
text = text.replace(/`(.*?)`/g, '<code>$1</code>');
|
||||
text = text.replace(/^\s*\*\s+(.*)$/gm, '<li>$1</li>');
|
||||
if (text.includes('<li>')) {
|
||||
text = text.replace(/(<li>.*<\/li>)/gs, '<ul>$1</ul>');
|
||||
}
|
||||
text = text.replace(/\n/g, '<br>');
|
||||
return text;
|
||||
}
|
||||
|
||||
function addMessage(role, content, isStreaming = false) {
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = `flex ${role === 'user' ? 'justify-end' : 'justify-start'} mb-4 animate-fadeIn`;
|
||||
|
||||
if (role === 'assistant' && !isStreaming) {
|
||||
messageDiv.innerHTML = `
|
||||
<div class="max-w-[85%] flex gap-3">
|
||||
<div class="w-9 h-9 rounded-full bg-gradient-to-br from-iran-green to-green-600 flex items-center justify-center text-white">🤖</div>
|
||||
<div class="bg-white border border-gray-200 p-4 rounded-2xl rounded-bl-none shadow-sm break-words">
|
||||
<strong class="text-iran-green block mb-1">یارا</strong>
|
||||
${simpleMarkdownToHtml(content)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
} else if (role === 'user') {
|
||||
messageDiv.innerHTML = `
|
||||
<div class="max-w-[85%] flex gap-3">
|
||||
<div class="bg-blue-500 text-white p-4 rounded-2xl rounded-br-none shadow-sm break-words">
|
||||
${simpleMarkdownToHtml(content)}
|
||||
</div>
|
||||
<div class="w-9 h-9 rounded-full bg-blue-600 flex items-center justify-center text-white">👤</div>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
messageDiv.innerHTML = `
|
||||
<div class="max-w-[85%] flex gap-3">
|
||||
<div class="w-9 h-9 rounded-full bg-gradient-to-br from-iran-green to-green-600 flex items-center justify-center text-white">🤖</div>
|
||||
<div class="bg-white border border-gray-200 p-4 rounded-2xl rounded-bl-none shadow-sm break-words">
|
||||
<strong class="text-iran-green block mb-1">یارا</strong>
|
||||
${content}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
chatContainer.appendChild(messageDiv);
|
||||
chatContainer.scrollTop = chatContainer.scrollHeight;
|
||||
}
|
||||
|
||||
function addTypingIndicator() {
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = 'chat-message assistant-message';
|
||||
messageDiv.id = 'typingPlaceholder';
|
||||
|
||||
const avatarDiv = document.createElement('div');
|
||||
avatarDiv.className = 'avatar';
|
||||
avatarDiv.textContent = '🤖';
|
||||
|
||||
const contentDiv = document.createElement('div');
|
||||
contentDiv.className = 'message-content';
|
||||
contentDiv.innerHTML = `<strong>یارا:</strong> <span class="typing-dots">در حال پاسخدهی...</span>`;
|
||||
|
||||
messageDiv.appendChild(avatarDiv);
|
||||
messageDiv.appendChild(contentDiv);
|
||||
chatContainer.appendChild(messageDiv);
|
||||
chatContainer.scrollTop = chatContainer.scrollHeight;
|
||||
}
|
||||
|
||||
function updateTypingContent(text) {
|
||||
const placeholder = document.getElementById('typingPlaceholder');
|
||||
if (placeholder) {
|
||||
// ✅ تبدیل Markdown به HTML حتی در حالت استریم
|
||||
placeholder.querySelector('.message-content').innerHTML = `<strong>یارا:</strong> ${markdownToHtml(text)}`;
|
||||
}
|
||||
}
|
||||
|
||||
function removeTypingIndicator() {
|
||||
const placeholder = document.getElementById('typingPlaceholder');
|
||||
if (placeholder) placeholder.remove();
|
||||
}
|
||||
|
||||
function displayChunks(chunks) {
|
||||
const existingSection = document.querySelector('.chunks-section');
|
||||
if (existingSection) existingSection.remove();
|
||||
|
||||
if (!chunks || chunks.length === 0) return;
|
||||
|
||||
const section = document.createElement('div');
|
||||
section.className = 'chunks-section';
|
||||
|
||||
// ✅ جعبه توضیحی با Markdown
|
||||
const explanation = document.createElement('div');
|
||||
explanation.className = 'explanation-box';
|
||||
explanation.innerHTML = `
|
||||
<p>🧠 <strong>پاسخ من بر اساس این اسناد تولید شده:</strong></p>
|
||||
<ul>
|
||||
${chunks.map((chunk, i) => {
|
||||
const source = chunk.metadata?.source || 'نامشخص';
|
||||
const page = chunk.metadata?.page || '?';
|
||||
const score = chunk.metadata?.score?.toFixed(2) || 'N/A';
|
||||
return `<li>📄 <strong>${source}</strong> (صفحه ${page}) — امتیاز: ${score}</li>`;
|
||||
}).join('')}
|
||||
</ul>
|
||||
`;
|
||||
section.appendChild(explanation);
|
||||
|
||||
// ✅ جزئیات چانکها با Markdown
|
||||
const detailsHeader = document.createElement('h4');
|
||||
detailsHeader.textContent = '📄 محتوای کامل اسناد مرتبط:';
|
||||
section.appendChild(detailsHeader);
|
||||
|
||||
chunks.forEach(chunk => {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'chunk-card';
|
||||
|
||||
const score = chunk.metadata?.score !== undefined ? chunk.metadata.score.toFixed(2) : 'N/A';
|
||||
const source = chunk.metadata?.source || 'نامشخص';
|
||||
const page = chunk.metadata?.page || 'نامشخص';
|
||||
|
||||
// ✅ تبدیل Markdown در محتوای چانکها
|
||||
card.innerHTML = `
|
||||
<div class="chunk-meta">
|
||||
<span class="chunk-score">امتیاز شباهت: ${score}</span>
|
||||
<span class="source-tag">📄 منبع: ${source}</span>
|
||||
<span class="source-tag">📄 صفحه: ${page}</span>
|
||||
</div>
|
||||
<div class="chunk-content">${markdownToHtml(truncateText(chunk.page_content, 400))}</div>
|
||||
`;
|
||||
section.appendChild(card);
|
||||
});
|
||||
|
||||
chatContainer.insertAdjacentElement('afterend', section);
|
||||
}
|
||||
|
||||
function truncateText(text, maxLength) {
|
||||
if (text.length <= maxLength) return text;
|
||||
return text.slice(0, maxLength) + '...';
|
||||
}
|
||||
|
||||
async function sendMessage() {
|
||||
const message = userInput.value.trim();
|
||||
if (!message) return;
|
||||
|
||||
addMessage('user', message);
|
||||
userInput.value = '';
|
||||
addTypingIndicator();
|
||||
|
||||
try {
|
||||
const response = await fetch('/send_message', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
message: message,
|
||||
top_k: currentTopK
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
let buffer = '';
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
|
||||
let boundary = buffer.indexOf('\n\n');
|
||||
while (boundary !== -1) {
|
||||
let line = buffer.slice(0, boundary).trim();
|
||||
buffer = buffer.slice(boundary + 2);
|
||||
|
||||
if (line.startsWith('data: ')) {
|
||||
try {
|
||||
const jsonStr = line.slice(6); // remove "data: "
|
||||
const data = JSON.parse(jsonStr);
|
||||
|
||||
if (data.type === 'stream') {
|
||||
updateTypingContent(data.content);
|
||||
} else if (data.type === 'final') {
|
||||
removeTypingIndicator();
|
||||
addMessage('assistant', data.content);
|
||||
} else if (data.type === 'chunks') {
|
||||
displayChunks(data.content);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("JSON parse error:", e);
|
||||
}
|
||||
}
|
||||
|
||||
boundary = buffer.indexOf('\n\n');
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Fetch error:", error);
|
||||
removeTypingIndicator();
|
||||
addMessage('assistant', "❌ خطایی در ارتباط با سرور رخ داد. لطفاً دوباره تلاش کنید.");
|
||||
}
|
||||
}
|
||||
|
||||
sendBtn.addEventListener('click', sendMessage);
|
||||
|
||||
userInput.addEventListener('keypress', function (e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
|
||||
clearBtn.addEventListener('click', async function () {
|
||||
try {
|
||||
const response = await fetch('/clear_chat', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
chatContainer.innerHTML = '';
|
||||
const existingSection = document.querySelector('.chunks-section');
|
||||
if (existingSection) existingSection.remove();
|
||||
|
||||
addMessage('assistant', "سلام! 😊 من یارا هستم — دستیار هوشمند شما. چطور میتونم کمکتون کنم؟");
|
||||
} else {
|
||||
alert("خطا در پاککردن گفتگو");
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Clear chat error:", e);
|
||||
alert("اتصال به سرور برقرار نشد");
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,493 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Vazirmatn', 'Segoe UI', Tahoma, sans-serif;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
color: #2c3e50;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* ✅ Fix: app-container should be ROW to place sidebar next to main */
|
||||
.app-container {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
flex-direction: row; /* 👈 Was column — now fixed to row */
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
width: 300px;
|
||||
background: #fff;
|
||||
padding: 2rem;
|
||||
box-shadow: 2px 0 10px rgba(0,0,0,0.05);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
overflow-y: auto;
|
||||
flex-shrink: 0; /* Prevent shrinking on small screens */
|
||||
}
|
||||
|
||||
.sidebar img {
|
||||
width: 100%;
|
||||
border-radius: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.sidebar h2 {
|
||||
color: #1a237e;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.caption {
|
||||
color: #78909c;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
background: #e3f2fd;
|
||||
padding: 1rem;
|
||||
border-radius: 1rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.info-box ul {
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
/* Main Content Wrapper (now a COLUMN inside the row) */
|
||||
.main-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1; /* Takes remaining vertical space */
|
||||
padding: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto; /* Allow scrolling if content is long */
|
||||
}
|
||||
|
||||
.main-header {
|
||||
background: linear-gradient(120deg, #1a237e, #3949ab);
|
||||
padding: 1.5rem;
|
||||
border-radius: 1.5rem;
|
||||
color: white;
|
||||
margin-bottom: 2rem;
|
||||
box-shadow: 0 8px 30px rgba(26, 35, 126, 0.2);
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.header-content .emoji {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.header-content h1 {
|
||||
margin: 0;
|
||||
font-size: 2.2rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.header-content p {
|
||||
margin: 0.5rem 0 0 0;
|
||||
opacity: 0.9;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* Chat Container */
|
||||
.chat-container {
|
||||
flex: 1; /* Fill available space in .main */
|
||||
overflow-y: auto;
|
||||
padding: 1rem 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.2rem;
|
||||
min-height: 0; /* Important: allows scrolling inside flex child */
|
||||
}
|
||||
|
||||
.chat-message {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
padding: 1.5rem;
|
||||
border-radius: 1.5rem;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.06);
|
||||
animation: fadeInUp 0.6s ease-out;
|
||||
max-width: 85%;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 40px;
|
||||
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.assistant-message .avatar {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { box-shadow: 0 0 0 0 rgba(85, 139, 47, 0.4); }
|
||||
70% { box-shadow: 0 0 0 10px rgba(85, 139, 47, 0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(85, 139, 47, 0); }
|
||||
}
|
||||
|
||||
.user-message {
|
||||
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
|
||||
border-right: 5px solid #1565c0;
|
||||
margin-right: auto;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.assistant-message {
|
||||
background: linear-gradient(135deg, #f1f8e9 0%, #dcedc8 100%);
|
||||
border-right: 5px solid #558b2f;
|
||||
margin-left: auto;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Typing indicator */
|
||||
.typing-indicator {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: #558b2f;
|
||||
margin: 0 3px;
|
||||
animation: typing 1s infinite;
|
||||
}
|
||||
|
||||
.typing-indicator:nth-child(2) { animation-delay: 0.2s; }
|
||||
.typing-indicator:nth-child(3) { animation-delay: 0.4s; }
|
||||
|
||||
@keyframes typing {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-5px); }
|
||||
}
|
||||
|
||||
/* Retrieved Chunks */
|
||||
.chunks-section {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.chunks-section h3 {
|
||||
color: #1a237e;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.chunk-card {
|
||||
background: white;
|
||||
padding: 1.5rem;
|
||||
border-radius: 1.2rem;
|
||||
margin: 1rem 0;
|
||||
border: 1px solid #e0e0e0;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.04);
|
||||
transition: all 0.3s ease;
|
||||
border-left: 4px solid #ff8a65;
|
||||
}
|
||||
|
||||
.chunk-card:hover {
|
||||
box-shadow: 0 8px 20px rgba(0,0,0,0.08);
|
||||
transform: translateX(8px);
|
||||
border-left-color: #e64a19;
|
||||
}
|
||||
|
||||
.chunk-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.7rem;
|
||||
margin-bottom: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chunk-score {
|
||||
font-weight: 800;
|
||||
color: #e64a19;
|
||||
font-size: 1.2rem;
|
||||
background: linear-gradient(135deg, #fff3e0, #ffe0b2);
|
||||
padding: 0.4rem 1rem;
|
||||
border-radius: 1.5rem;
|
||||
display: inline-block;
|
||||
box-shadow: 0 2px 5px rgba(230, 74, 25, 0.15);
|
||||
}
|
||||
|
||||
.source-tag {
|
||||
background: linear-gradient(135deg, #e3f2fd, #bbdefb);
|
||||
color: #0d47a1;
|
||||
padding: 0.4rem 1rem;
|
||||
border-radius: 1.5rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
box-shadow: 0 2px 4px rgba(13, 71, 161, 0.1);
|
||||
}
|
||||
|
||||
.chunk-content {
|
||||
line-height: 1.8;
|
||||
font-size: 1.05rem;
|
||||
color: #455a64;
|
||||
background: #fafafa;
|
||||
padding: 1rem;
|
||||
border-radius: 0.8rem;
|
||||
border-right: 3px solid #e0e0e0;
|
||||
}
|
||||
|
||||
/* Input Area */
|
||||
.input-area {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-top: 1rem;
|
||||
align-items: flex-end;
|
||||
padding: 1rem 0;
|
||||
background: #fff; /* Optional: prevent overlap with gradient */
|
||||
}
|
||||
|
||||
.input-area textarea {
|
||||
flex: 1;
|
||||
border-radius: 2rem;
|
||||
padding: 1rem 1.5rem;
|
||||
font-size: 1.1rem;
|
||||
border: 2px solid #cfd8dc;
|
||||
box-shadow: inset 0 2px 5px rgba(0,0,0,0.05);
|
||||
min-height: 100px;
|
||||
resize: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.input-area textarea:focus {
|
||||
outline: none;
|
||||
border-color: #4caf50;
|
||||
box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.25), inset 0 2px 5px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.input-area button {
|
||||
border-radius: 2rem;
|
||||
padding: 0.7rem 2rem;
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
background: linear-gradient(135deg, #4caf50, #2e7d32);
|
||||
color: white;
|
||||
border: none;
|
||||
box-shadow: 0 5px 15px rgba(46, 125, 50, 0.3);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.input-area button:hover {
|
||||
transform: translateY(-3px) scale(1.03);
|
||||
box-shadow: 0 8px 25px rgba(46, 125, 50, 0.5);
|
||||
background: linear-gradient(135deg, #66bb6a, #388e3c);
|
||||
}
|
||||
|
||||
/* Footer — now inside main-wrapper, pushed down by flex:1 on .main */
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 2rem 0;
|
||||
color: #78909c;
|
||||
font-size: 0.95rem;
|
||||
border-top: 1px solid #eceff1;
|
||||
background: #fff;
|
||||
margin-top: auto; /* Works now because main-wrapper is column */
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #1e88e5;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn-secondary {
|
||||
background: linear-gradient(135deg, #ff7043, #d84315);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.7rem 1rem;
|
||||
border-radius: 2rem;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: linear-gradient(135deg, #ff8a65, #e64a19);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* === زیباسازی تنظیمات عددی === */
|
||||
.setting-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin: 1.5rem 0;
|
||||
padding: 0.8rem 0;
|
||||
}
|
||||
|
||||
.setting-label {
|
||||
font-weight: 600;
|
||||
color: #1a237e;
|
||||
font-size: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.number-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 2px solid #cfd8dc;
|
||||
border-radius: 2rem;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.number-input:focus-within {
|
||||
border-color: #4caf50;
|
||||
box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.25);
|
||||
}
|
||||
|
||||
.number-input input {
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
background: #fafafa;
|
||||
border: none;
|
||||
padding: 0.7rem 0;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.number-input button {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: none;
|
||||
background: #e3f2fd;
|
||||
color: #1565c0;
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.number-input button:hover {
|
||||
background: #bbdefb;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.number-input button:active {
|
||||
background: #90caf9;
|
||||
}
|
||||
|
||||
.number-input button.minus {
|
||||
border-left: 1px solid #bbdefb;
|
||||
}
|
||||
|
||||
.number-input button.plus {
|
||||
border-right: 1px solid #bbdefb;
|
||||
}
|
||||
|
||||
.slider {
|
||||
width: 150px;
|
||||
height: 8px;
|
||||
border-radius: 1rem;
|
||||
background: #e0e0e0;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background: #4caf50;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background: #4caf50;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.typing-dots::after {
|
||||
content: '...';
|
||||
animation: blink 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%, 100% { opacity: 0.2; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
.explanation-box {
|
||||
background: #f0f7ff;
|
||||
border: 1px solid #bee3f8;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin: 20px 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: #2c5282;
|
||||
}
|
||||
|
||||
.explanation-box ul {
|
||||
margin: 8px 0 0 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.explanation-box li {
|
||||
margin: 4px 0;
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px dashed #a0aec0;
|
||||
}
|
||||
|
||||
.explanation-box strong {
|
||||
color: #2b6cb0;
|
||||
}
|
||||
|
||||
.chunks-section {
|
||||
display: block !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
.chunk-card {
|
||||
display: block !important;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"liveServer.settings.port": 5501
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>جزئیات مقاله - پنل مدیریت</title>
|
||||
<script src="/static/3.4.17.es"></script>
|
||||
<link rel="stylesheet" href="/static/all.min.css">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Vazir&display=swap');
|
||||
body { font-family: 'Vazir', Tahoma, sans-serif; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 min-h-screen">
|
||||
<div class="flex">
|
||||
<aside class="w-64 bg-green-800 text-white min-h-screen">
|
||||
<div class="p-4">
|
||||
<h1 class="text-xl font-bold text-center border-b border-green-700 pb-4">
|
||||
<i class="fas fa-cogs ml-2"></i>
|
||||
پنل مدیریت
|
||||
</h1>
|
||||
<nav class="mt-8">
|
||||
<ul class="space-y-2">
|
||||
<li><a href="{{ url_for('admin.admin_dashboard') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-tachometer-alt ml-3"></i>داشبورد</a></li>
|
||||
<li><a href="{{ url_for('admin.files_management') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-file-upload ml-3"></i>مدیریت فایلها</a></li>
|
||||
<li><a href="{{ url_for('admin.articles_management') }}" class="flex items-center p-3 rounded-lg bg-green-700 transition-colors"><i class="fas fa-book ml-3"></i>مدیریت مقالات</a></li>
|
||||
<li><a href="{{ url_for('admin.users_management') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-users ml-3"></i>مدیریت کاربران</a></li>
|
||||
<li><a href="{{ url_for('admin.corrections_management') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-edit ml-3"></i>نظارت بر تصحیحها</a></li>
|
||||
<li class="pt-4 border-t border-green-700 mt-4"><a href="{{ url_for('dashboard.index') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-arrow-right ml-3"></i>بازگشت به سایت</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="flex-1 p-6">
|
||||
<header class="bg-white rounded-lg shadow-sm p-4 mb-6">
|
||||
<div class="flex justify-between items-center">
|
||||
<h2 class="text-2xl font-bold text-gray-800">جزئیات مقاله</h2>
|
||||
<div class="flex items-center space-x-4 space-x-reverse">
|
||||
<span class="text-gray-600">خوش آمدید، {{ current_user.username }}</span>
|
||||
<div class="w-8 h-8 bg-green-500 rounded-full flex items-center justify-center text-white">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="bg-white rounded-lg shadow-sm p-6">
|
||||
<h3 class="text-xl font-bold text-gray-800 mb-4">{{ article.name }}</h3>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">موسسه</label>
|
||||
<p class="mt-1 text-gray-900">{{ article.institute }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">چکیده</label>
|
||||
<p class="mt-1 text-gray-900">{{ article.abstract }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">وضعیت تصحیح</label>
|
||||
<span class="mt-1 px-2 py-1 text-xs rounded-full
|
||||
{% if article.correction_status == 'completed' %}bg-green-100 text-green-800
|
||||
{% elif article.correction_status == 'in_progress' %}bg-yellow-100 text-yellow-800
|
||||
{% else %}bg-red-100 text-red-800{% endif %}">
|
||||
{{ article.correction_status }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,971 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>مدیریت مقالات - پنل مدیریت</title>
|
||||
<script src="/static/3.4.17.es"></script>
|
||||
<link rel="stylesheet" href="/static/all.min.css">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Vazir&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Vazir', Tahoma, sans-serif;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-in;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.truncate-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.assigned-badge {
|
||||
font-size: 0.7rem;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.debug-info {
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
background: #f5f5f5;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin: 10px 0;
|
||||
border-right: 3px solid #f39c12;
|
||||
}
|
||||
|
||||
/* استایل برای مودال */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.modal-overlay.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
width: 90%;
|
||||
max-width: 700px;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
transform: translateY(-20px);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.modal-overlay.active .modal-content {
|
||||
transform: translateY(0);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-50 min-h-screen">
|
||||
<div class="flex">
|
||||
<!-- نوار کناری -->
|
||||
<aside class="w-64 bg-green-800 text-white min-h-screen">
|
||||
<div class="p-4">
|
||||
<h1 class="text-xl font-bold text-center border-b border-green-700 pb-4">
|
||||
<i class="fas fa-cogs ml-2"></i>
|
||||
پنل مدیریت
|
||||
</h1>
|
||||
<nav class="mt-8">
|
||||
<ul class="space-y-2">
|
||||
<li><a href="{{ url_for('admin.admin_dashboard') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i
|
||||
class="fas fa-tachometer-alt ml-3"></i>داشبورد</a></li>
|
||||
<li><a href="{{ url_for('admin.files_management') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i
|
||||
class="fas fa-file-upload ml-3"></i>مدیریت فایلها</a></li>
|
||||
<li><a href="{{ url_for('admin.articles_management') }}"
|
||||
class="flex items-center p-3 rounded-lg bg-green-700 transition-colors"><i
|
||||
class="fas fa-book ml-3"></i>مدیریت مقالات</a></li>
|
||||
<li><a href="{{ url_for('admin.users_management') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i
|
||||
class="fas fa-users ml-3"></i>مدیریت کاربران</a></li>
|
||||
<li><a href="{{ url_for('admin.corrections_management') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i
|
||||
class="fas fa-edit ml-3"></i>نظارت بر تصحیحها</a></li>
|
||||
<li class="pt-4 border-t border-green-700 mt-4"><a href="{{ url_for('dashboard.index') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i
|
||||
class="fas fa-arrow-right ml-3"></i>بازگشت به سایت</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- محتوای اصلی -->
|
||||
<main class="flex-1 p-6">
|
||||
<!-- هدر صفحه -->
|
||||
<header class="bg-white rounded-2xl shadow-sm p-6 mb-6 fade-in">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-gray-800">مدیریت مقالات</h2>
|
||||
<p class="text-gray-600 mt-1">مدیریت و بازبینی مقالات سیستم</p>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4 space-x-reverse">
|
||||
<div
|
||||
class="w-10 h-10 bg-gradient-to-r from-green-500 to-emerald-600 rounded-full flex items-center justify-center text-white shadow-lg">
|
||||
<i class="fas fa-user text-sm"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- فیلترها و جستجو -->
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 mb-6 fade-in">
|
||||
<div class="flex flex-col md:flex-row md:items-center md:justify-between space-y-4 md:space-y-0">
|
||||
<div class="flex items-center space-x-4 space-x-reverse">
|
||||
<span class="text-sm font-medium text-gray-700">فیلتر بر اساس وضعیت:</span>
|
||||
<div class="flex space-x-2 space-x-reverse">
|
||||
<a href="{{ url_for('admin.articles_management') }}?status=all"
|
||||
class="px-3 py-1 text-sm rounded-full {% if status_filter == 'all' %}bg-green-100 text-green-800{% else %}bg-gray-100 text-gray-600 hover:bg-gray-200{% endif %} transition-colors">
|
||||
همه
|
||||
</a>
|
||||
<a href="{{ url_for('admin.articles_management') }}?status=pending"
|
||||
class="px-3 py-1 text-sm rounded-full {% if status_filter == 'pending' %}bg-red-100 text-red-800{% else %}bg-gray-100 text-gray-600 hover:bg-gray-200{% endif %} transition-colors">
|
||||
در انتظار
|
||||
</a>
|
||||
<a href="{{ url_for('admin.articles_management') }}?status=in_progress"
|
||||
class="px-3 py-1 text-sm rounded-full {% if status_filter == 'in_progress' %}bg-yellow-100 text-yellow-800{% else %}bg-gray-100 text-gray-600 hover:bg-gray-200{% endif %} transition-colors">
|
||||
در حال تصحیح
|
||||
</a>
|
||||
<a href="{{ url_for('admin.articles_management') }}?status=completed"
|
||||
class="px-3 py-1 text-sm rounded-full {% if status_filter == 'completed' %}bg-green-100 text-green-800{% else %}bg-gray-100 text-gray-600 hover:bg-gray-200{% endif %} transition-colors">
|
||||
تکمیل شده
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" id="searchInput" placeholder="جستجو در مقالات..."
|
||||
class="w-full md:w-64 px-4 py-2 pr-10 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500">
|
||||
<i class="fas fa-search absolute right-3 top-3 text-gray-400"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- لیست مقالات -->
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 fade-in">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-xl font-bold text-gray-800 flex items-center">
|
||||
<i class="fas fa-list ml-2 text-blue-600"></i>
|
||||
لیست مقالات
|
||||
</h3>
|
||||
<div class="text-sm text-gray-500">
|
||||
{% if articles %}
|
||||
صفحه {{ articles.page }} از {{ articles.pages }} | مجموع: {{ articles.total }} مقاله
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- بخش دیباگ برای بررسی دادهها -->
|
||||
{% if debug_mode %}
|
||||
<div class="debug-info mb-4">
|
||||
<strong>اطلاعات دیباگ:</strong><br>
|
||||
- مقالات موجود: {{ articles.total if articles else 0 }}<br>
|
||||
- تعداد آیتمها: {{ articles.items|length if articles and articles.items else 0 }}<br>
|
||||
- وضعیت فیلتر: {{ status_filter }}<br>
|
||||
- تعداد کاربران ارسال شده: {{ users|length if users else 0 }}<br>
|
||||
{% if articles and articles.items %}
|
||||
- نمونه از اولین مقاله:<br>
|
||||
ID: {{ articles.items[0].id }}<br>
|
||||
نام: {{ articles.items[0].name }}<br>
|
||||
user_id: {{ articles.items[0].user_id }}<br>
|
||||
correction_status: {{ articles.items[0].correction_status }}<br>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if articles and articles.items %}
|
||||
<!-- ساخت دیکشنری کاربران برای دسترسی آسان -->
|
||||
{% if users %}
|
||||
{% set user_dict = {} %}
|
||||
{% for user in users %}
|
||||
{% set _ = user_dict.update({user.id: user}) %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm text-right text-gray-700">
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-4 py-3">عنوان مقاله</th>
|
||||
<th class="px-4 py-3">موسسه</th>
|
||||
<th class="px-4 py-3">وضعیت تصحیح</th>
|
||||
<th class="px-4 py-3">کاربر اختصاصیافته</th>
|
||||
<th class="px-4 py-3">تاریخ ایجاد</th>
|
||||
<th class="px-4 py-3">عملیات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="articlesTableBody">
|
||||
{% for article in articles.items %}
|
||||
<tr class="border-b hover:bg-gray-50 transition-colors">
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex items-start">
|
||||
<i class="fas fa-file-alt text-gray-400 ml-3 mt-1"></i>
|
||||
<div>
|
||||
<!-- استفاده از فیلدهای مختلف برای عنوان -->
|
||||
<p class="font-medium text-gray-900 truncate-2">
|
||||
{{ article.name or article.title or article.filename or 'بدون عنوان' }}
|
||||
</p>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
ID: {{ article.id }}
|
||||
{% if article.article_id %}({{ article.article_id }}){% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<p class="text-gray-600">{{ article.institute or 'نامشخص' }}</p>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
{% set correction_status = article.correction_status or article.status or 'pending' %}
|
||||
<span class="px-3 py-1 text-xs rounded-full
|
||||
{% if correction_status == 'completed' %}bg-green-100 text-green-800 border border-green-200
|
||||
{% elif correction_status == 'in_progress' %}bg-yellow-100 text-yellow-800 border border-yellow-200
|
||||
{% else %}bg-red-100 text-red-800 border border-red-200{% endif %}">
|
||||
<i class="fas
|
||||
{% if correction_status == 'completed' %}fa-check-circle
|
||||
{% elif correction_status == 'in_progress' %}fa-spinner
|
||||
{% else %}fa-clock{% endif %} ml-1">
|
||||
</i>
|
||||
{% if correction_status == 'completed' %}تکمیل شده
|
||||
{% elif correction_status == 'in_progress' %}در حال تصحیح
|
||||
{% else %}در انتظار تصحیح{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
{% if article.user_id %}
|
||||
{% if users and user_dict %}
|
||||
{% set assigned_user = user_dict.get(article.user_id) %}
|
||||
{% if assigned_user %}
|
||||
<div class="flex items-center">
|
||||
<i class="fas fa-user-check text-green-600 ml-2"></i>
|
||||
<div>
|
||||
<span class="text-sm font-medium">{{ assigned_user.fullname or assigned_user.username or assigned_user.email }}</span>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
{% if assigned_user.email %}{{ assigned_user.email }}{% endif %}
|
||||
{% if assigned_user.role %} ({{ assigned_user.role }}){% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="flex items-center">
|
||||
<i class="fas fa-user-clock text-yellow-600 ml-2"></i>
|
||||
<span class="text-sm">کاربر ID: {{ article.user_id }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<!-- اگر لیست کاربران موجود نیست -->
|
||||
<div class="flex items-center">
|
||||
<i class="fas fa-user-clock text-blue-600 ml-2"></i>
|
||||
<span class="text-sm">کاربر ID: {{ article.user_id }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="flex items-center">
|
||||
<i class="fas fa-user-times text-gray-400 ml-2"></i>
|
||||
<span class="text-sm text-gray-500">اختصاص داده نشده</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- دکمه اختصاص مقاله -->
|
||||
<button onclick="assignArticle({{ article.id }})"
|
||||
class="mt-2 inline-flex items-center text-xs text-blue-600 hover:text-blue-800 transition-colors">
|
||||
<i class="fas fa-user-plus ml-1"></i>
|
||||
{% if article.user_id %}تغییر{% else %}اختصاص{% endif %}
|
||||
</button>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
{% if article.created_at %}
|
||||
<div class="text-sm">
|
||||
<p>{{ article.created_at.strftime('%Y/%m/%d') }}</p>
|
||||
<p class="text-gray-500">{{ article.created_at.strftime('%H:%M') }}</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-gray-500">نامشخص</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex items-center space-x-2 space-x-reverse">
|
||||
<!-- تغییر لینک مشاهده جزئیات به استفاده از مودال -->
|
||||
<button onclick="showArticleDetails({{ article.id }})"
|
||||
class="text-blue-600 hover:text-blue-800 transition-colors"
|
||||
title="مشاهده جزئیات">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
{% if article.pdf_path %}
|
||||
<a href="{{ article.pdf_path }}"
|
||||
class="text-green-600 hover:text-green-800 transition-colors"
|
||||
title="دانلود مقاله" target="_blank">
|
||||
<i class="fas fa-download"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="text-gray-400 cursor-not-allowed" title="فایل PDF موجود نیست">
|
||||
<i class="fas fa-download"></i>
|
||||
</span>
|
||||
{% endif %}
|
||||
<form action="{{ url_for('admin.delete_article', article_id=article.id) }}"
|
||||
method="POST"
|
||||
onsubmit="return confirmDeleteArticle(event, {{ article.id }}, '{{ article.name or 'بدون عنوان' }}');"
|
||||
class="inline">
|
||||
<button type="submit"
|
||||
class="text-red-600 hover:text-red-800 transition-colors"
|
||||
title="حذف مقاله">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- صفحهبندی -->
|
||||
{% if articles.pages > 1 %}
|
||||
<div class="flex justify-center items-center space-x-2 space-x-reverse mt-6">
|
||||
{% if articles.has_prev %}
|
||||
<a href="{{ url_for('admin.articles_management', page=articles.prev_num, status=status_filter) }}"
|
||||
class="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% for page_num in articles.iter_pages(left_edge=2, left_current=2, right_current=2, right_edge=2) %}
|
||||
{% if page_num %}
|
||||
{% if page_num == articles.page %}
|
||||
<span class="px-4 py-2 bg-green-600 text-white rounded-lg">{{ page_num }}</span>
|
||||
{% else %}
|
||||
<a href="{{ url_for('admin.articles_management', page=page_num, status=status_filter) }}"
|
||||
class="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
{{ page_num }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="px-2">...</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if articles.has_next %}
|
||||
<a href="{{ url_for('admin.articles_management', page=articles.next_num, status=status_filter) }}"
|
||||
class="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
<div class="text-center py-8">
|
||||
<i class="fas fa-book-open text-4xl text-gray-400 mb-4"></i>
|
||||
<h4 class="text-lg font-medium text-gray-700">هیچ مقالهای یافت نشد</h4>
|
||||
<p class="text-gray-500 mt-2">
|
||||
{% if status_filter != 'all' %}
|
||||
هیچ مقالهای با وضعیت "{{ status_filter }}" وجود ندارد
|
||||
{% else %}
|
||||
هنوز مقالهای در سیستم ثبت نشده است
|
||||
{% endif %}
|
||||
</p>
|
||||
<a href="{{ url_for('admin.files_management') }}"
|
||||
class="mt-4 inline-flex items-center px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors">
|
||||
<i class="fas fa-upload ml-2"></i>
|
||||
آپلود فایل جدید
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- مودال اختصاص مقاله -->
|
||||
<div id="assignModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full hidden z-50">
|
||||
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
|
||||
<div class="mt-3">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">اختصاص مقاله</h3>
|
||||
<form id="assignForm" method="POST">
|
||||
<input type="hidden" id="articleId" name="article_id">
|
||||
<div class="mb-4">
|
||||
<label for="userId" class="block text-sm font-medium text-gray-700 mb-2">انتخاب کاربر:</label>
|
||||
<select id="userId" name="user_id"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500">
|
||||
<option value="">-- انتخاب کاربر --</option>
|
||||
{% if users %}
|
||||
{% for user in users %}
|
||||
<option value="{{ user.id }}">
|
||||
{{ user.fullname or user.username }} - {{ user.email }}
|
||||
{% if user.role %}({{ user.role }}){% endif %}
|
||||
</option>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<option value="1">مدیر سیستم</option>
|
||||
<option value="2">کاربر نمونه ۱</option>
|
||||
<option value="3">کاربر نمونه ۲</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex justify-end space-x-3 space-x-reverse mt-6">
|
||||
<button type="button" onclick="closeAssignModal()"
|
||||
class="px-4 py-2 bg-gray-300 text-gray-700 rounded-md hover:bg-gray-400 transition-colors">
|
||||
انصراف
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors">
|
||||
اختصاص
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- مودال نمایش جزئیات مقاله -->
|
||||
<div id="articleDetailModal" class="modal-overlay">
|
||||
<div class="modal-content">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-xl font-bold text-gray-800 flex items-center">
|
||||
<i class="fas fa-file-alt text-blue-600 ml-2"></i>
|
||||
جزئیات مقاله
|
||||
</h3>
|
||||
<button onclick="closeArticleDetailModal()"
|
||||
class="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<i class="fas fa-times text-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="articleDetailContent">
|
||||
<!-- محتوای جزئیات مقاله به صورت داینامیک لود میشود -->
|
||||
<div class="text-center py-8">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-green-600 mx-auto mb-4"></div>
|
||||
<p class="text-gray-600">در حال بارگذاری اطلاعات مقاله...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end mt-6 pt-4 border-t border-gray-200">
|
||||
<button onclick="closeArticleDetailModal()"
|
||||
class="px-5 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors">
|
||||
بستن
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// تابع تأیید حذف مقاله
|
||||
function confirmDeleteArticle(event, articleId, articleName) {
|
||||
event.preventDefault();
|
||||
const confirmation = confirm(`آیا از حذف مقاله "${articleName}" با شناسه ${articleId} اطمینان دارید؟\nاین عمل غیرقابل بازگشت است.`);
|
||||
|
||||
if (confirmation) {
|
||||
event.target.closest('form').submit();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// توابع مربوط به مودال اختصاص مقاله
|
||||
function assignArticle(articleId) {
|
||||
console.log('اختصاص مقاله با ID:', articleId);
|
||||
document.getElementById('articleId').value = articleId;
|
||||
document.getElementById('assignModal').classList.remove('hidden');
|
||||
|
||||
// برای UX بهتر، اسکرول به بالای مودال
|
||||
document.getElementById('assignModal').scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
function closeAssignModal() {
|
||||
document.getElementById('assignModal').classList.add('hidden');
|
||||
// بازنشانی فرم
|
||||
document.getElementById('assignForm').reset();
|
||||
}
|
||||
|
||||
// ارسال فرم اختصاص مقاله
|
||||
document.getElementById('assignForm').addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const articleId = document.getElementById('articleId').value;
|
||||
const userId = document.getElementById('userId').value;
|
||||
|
||||
if (!userId) {
|
||||
alert('لطفاً یک کاربر انتخاب کنید');
|
||||
return;
|
||||
}
|
||||
|
||||
// نمایش پیام در حال پردازش
|
||||
const submitBtn = this.querySelector('button[type="submit"]');
|
||||
const originalText = submitBtn.innerHTML;
|
||||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin ml-1"></i> در حال پردازش...';
|
||||
submitBtn.disabled = true;
|
||||
|
||||
console.log('ارسال درخواست اختصاص:', { article_id: articleId, user_id: userId });
|
||||
|
||||
// ارسال درخواست به سرور
|
||||
fetch('{{ url_for("admin.assign_article") }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
article_id: articleId,
|
||||
user_id: userId
|
||||
})
|
||||
})
|
||||
.then(response => {
|
||||
console.log('پاسخ دریافت شد:', response);
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('دادههای پاسخ:', data);
|
||||
if (data.success) {
|
||||
alert(data.message);
|
||||
closeAssignModal();
|
||||
// تأخیر کوتاه قبل از ریلود برای نمایش پیام
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
alert('خطا: ' + data.message);
|
||||
submitBtn.innerHTML = originalText;
|
||||
submitBtn.disabled = false;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('خطا در ارتباط با سرور: ' + error.message);
|
||||
submitBtn.innerHTML = originalText;
|
||||
submitBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
// توابع مربوط به مودال نمایش جزئیات مقاله
|
||||
function showArticleDetails(articleId) {
|
||||
console.log('نمایش جزئیات مقاله با ID:', articleId);
|
||||
|
||||
// نمایش مودال
|
||||
const modal = document.getElementById('articleDetailModal');
|
||||
modal.classList.add('active');
|
||||
|
||||
// نمایش اسکلت لودینگ
|
||||
document.getElementById('articleDetailContent').innerHTML = `
|
||||
<div class="text-center py-8">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-green-600 mx-auto mb-4"></div>
|
||||
<p class="text-gray-600">در حال بارگذاری اطلاعات مقاله...</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// درخواست اطلاعات مقاله از سرور
|
||||
setTimeout(() => {
|
||||
loadArticleDetails(articleId);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function loadArticleDetails(articleId) {
|
||||
// درخواست به API برای دریافت جزئیات مقاله
|
||||
fetch(`/admin/api/article/${articleId}/details`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('مقاله یافت نشد');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || 'خطا در دریافت اطلاعات');
|
||||
}
|
||||
|
||||
const article = data.article;
|
||||
|
||||
// وضعیت تصحیح
|
||||
let statusBadge = '';
|
||||
if (article.correction_status === 'completed') {
|
||||
statusBadge = '<span class="px-3 py-1 text-xs rounded-full bg-green-100 text-green-800 border border-green-200">تکمیل شده</span>';
|
||||
} else if (article.correction_status === 'in_progress') {
|
||||
statusBadge = '<span class="px-3 py-1 text-xs rounded-full bg-yellow-100 text-yellow-800 border border-yellow-200">در حال تصحیح</span>';
|
||||
} else {
|
||||
statusBadge = '<span class="px-3 py-1 text-xs rounded-full bg-red-100 text-red-800 border border-red-200">در انتظار تصحیح</span>';
|
||||
}
|
||||
|
||||
// کاربر اختصاصیافته
|
||||
let assignedUserHtml = '';
|
||||
if (article.assigned_user) {
|
||||
assignedUserHtml = `
|
||||
<div class="flex items-center">
|
||||
<div class="w-8 h-8 bg-green-100 rounded-full flex items-center justify-center ml-2">
|
||||
<i class="fas fa-user text-green-600 text-sm"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-gray-900 font-medium">${article.assigned_user.fullname || article.assigned_user.username}</p>
|
||||
<p class="text-xs text-gray-500">${article.assigned_user.email || ''} ${article.assigned_user.role ? '(' + article.assigned_user.role + ')' : ''}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
assignedUserHtml = `
|
||||
<div class="flex items-center">
|
||||
<div class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center ml-2">
|
||||
<i class="fas fa-user-times text-gray-400 text-sm"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-gray-900 font-medium">اختصاص داده نشده</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// تصحیحها
|
||||
let correctionsHtml = '';
|
||||
if (article.corrections && article.corrections.length > 0) {
|
||||
correctionsHtml = `
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">تاریخچه تصحیحها:</label>
|
||||
<div class="space-y-2">
|
||||
${article.corrections.map(correction => `
|
||||
<div class="p-3 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-700">
|
||||
${correction.user ? correction.user.fullname || correction.user.username : 'کاربر نامشخص'}
|
||||
</span>
|
||||
<span class="px-2 py-1 text-xs rounded-full
|
||||
${correction.status === 'completed' ? 'bg-green-100 text-green-800' :
|
||||
correction.status === 'in_progress' ? 'bg-yellow-100 text-yellow-800' :
|
||||
'bg-blue-100 text-blue-800'}">
|
||||
${correction.status === 'completed' ? 'تکمیل شده' :
|
||||
correction.status === 'in_progress' ? 'در حال تصحیح' : 'اختصاص داده شده'}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
${correction.created_at || ''}
|
||||
${correction.updated_at ? ' - بروزرسانی: ' + correction.updated_at : ''}
|
||||
</p>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// متادیتا
|
||||
let metadataHtml = '';
|
||||
if (article.metadata) {
|
||||
const metadata = article.metadata;
|
||||
metadataHtml = `
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
${metadata.page_count ? `
|
||||
<div class="text-center p-3 bg-blue-50 rounded-lg">
|
||||
<p class="text-sm text-gray-600">تعداد صفحات</p>
|
||||
<p class="text-lg font-bold text-blue-700">${metadata.page_count}</p>
|
||||
</div>
|
||||
` : ''}
|
||||
${metadata.word_count ? `
|
||||
<div class="text-center p-3 bg-green-50 rounded-lg">
|
||||
<p class="text-sm text-gray-600">تعداد کلمات</p>
|
||||
<p class="text-lg font-bold text-green-700">${metadata.word_count}</p>
|
||||
</div>
|
||||
` : ''}
|
||||
${metadata.file_size ? `
|
||||
<div class="text-center p-3 bg-purple-50 rounded-lg">
|
||||
<p class="text-sm text-gray-600">حجم فایل</p>
|
||||
<p class="text-lg font-bold text-purple-700">${formatFileSize(metadata.file_size)}</p>
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="text-center p-3 bg-yellow-50 rounded-lg">
|
||||
<p class="text-sm text-gray-600">تعداد تصحیحها</p>
|
||||
<p class="text-lg font-bold text-yellow-700">${article.corrections_count || 0}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const articleContent = `
|
||||
<div class="space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">عنوان مقاله:</label>
|
||||
<div class="p-3 bg-gray-50 rounded-lg">
|
||||
<p class="text-gray-900 font-medium">${article.title || 'بدون عنوان'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">شناسه مقاله:</label>
|
||||
<div class="p-3 bg-gray-50 rounded-lg">
|
||||
<p class="text-gray-900">${article.id}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">موسسه:</label>
|
||||
<div class="p-3 bg-gray-50 rounded-lg">
|
||||
<p class="text-gray-900">${article.institute || 'نامشخص'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">وضعیت تصحیح:</label>
|
||||
<div class="p-3 bg-gray-50 rounded-lg">
|
||||
${statusBadge}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${metadataHtml}
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">کاربر اختصاصیافته:</label>
|
||||
<div class="p-3 bg-gray-50 rounded-lg">
|
||||
${assignedUserHtml}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">تاریخ ایجاد:</label>
|
||||
<div class="p-3 bg-gray-50 rounded-lg">
|
||||
<p class="text-gray-900">${article.created_at || 'نامشخص'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">آخرین بروزرسانی:</label>
|
||||
<div class="p-3 bg-gray-50 rounded-lg">
|
||||
<p class="text-gray-900">${article.updated_at || 'نامشخص'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">نام فایل اصلی:</label>
|
||||
<div class="p-3 bg-gray-50 rounded-lg">
|
||||
<p class="text-gray-900">${article.original_filename || 'نامشخص'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">مسیر فایل:</label>
|
||||
<div class="p-3 bg-gray-50 rounded-lg">
|
||||
<p class="text-gray-900 truncate">${article.file_path || 'نامشخص'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${correctionsHtml}
|
||||
|
||||
<div class="flex flex-wrap gap-3 space-x-reverse mt-6">
|
||||
<button onclick="assignArticle(${article.id})"
|
||||
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">
|
||||
<i class="fas fa-user-plus ml-1"></i>
|
||||
${article.assigned_user ? 'تغییر کاربر' : 'اختصاص به کاربر'}
|
||||
</button>
|
||||
${article.pdf_path ? `
|
||||
<a href="${article.pdf_path}"
|
||||
target="_blank"
|
||||
class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors">
|
||||
<i class="fas fa-download ml-1"></i>
|
||||
دانلود PDF
|
||||
</a>
|
||||
` : ''}
|
||||
<button onclick="updateArticleStatus(${article.id}, 'pending')"
|
||||
class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors">
|
||||
<i class="fas fa-clock ml-1"></i>
|
||||
در انتظار
|
||||
</button>
|
||||
<button onclick="updateArticleStatus(${article.id}, 'in_progress')"
|
||||
class="px-4 py-2 bg-yellow-600 text-white rounded-lg hover:bg-yellow-700 transition-colors">
|
||||
<i class="fas fa-spinner ml-1"></i>
|
||||
در حال تصحیح
|
||||
</button>
|
||||
<button onclick="updateArticleStatus(${article.id}, 'completed')"
|
||||
class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors">
|
||||
<i class="fas fa-check ml-1"></i>
|
||||
تکمیل شده
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.getElementById('articleDetailContent').innerHTML = articleContent;
|
||||
})
|
||||
.catch(error => {
|
||||
document.getElementById('articleDetailContent').innerHTML = `
|
||||
<div class="text-center py-8">
|
||||
<i class="fas fa-exclamation-triangle text-4xl text-yellow-500 mb-4"></i>
|
||||
<h4 class="text-lg font-medium text-gray-700">خطا در بارگذاری اطلاعات</h4>
|
||||
<p class="text-gray-500 mt-2">${error.message}</p>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
// تابع کمکی برای فرمتبندی حجم فایل
|
||||
function formatFileSize(bytes) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
// تابع برای بهروزرسانی وضعیت مقاله
|
||||
function updateArticleStatus(articleId, status) {
|
||||
if (!confirm('آیا از تغییر وضعیت مقاله اطمینان دارید؟')) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`/admin/api/article/${articleId}/status`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ status: status })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert(data.message);
|
||||
// بستن مودال و رفرش صفحه
|
||||
closeArticleDetailModal();
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
alert('خطا: ' + data.error);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('خطا در ارتباط با سرور: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function closeArticleDetailModal() {
|
||||
const modal = document.getElementById('articleDetailModal');
|
||||
modal.classList.remove('active');
|
||||
// پاک کردن محتوا بعد از بستن مودال
|
||||
setTimeout(() => {
|
||||
document.getElementById('articleDetailContent').innerHTML = `
|
||||
<div class="text-center py-8">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-green-600 mx-auto mb-4"></div>
|
||||
<p class="text-gray-600">در حال بارگذاری اطلاعات مقاله...</p>
|
||||
</div>
|
||||
`;
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// بستن مودالها با کلیک خارج از آنها
|
||||
window.onclick = function (event) {
|
||||
const assignModal = document.getElementById('assignModal');
|
||||
const articleDetailModal = document.getElementById('articleDetailModal');
|
||||
|
||||
if (event.target === assignModal) {
|
||||
closeAssignModal();
|
||||
}
|
||||
|
||||
if (event.target === articleDetailModal) {
|
||||
closeArticleDetailModal();
|
||||
}
|
||||
}
|
||||
|
||||
// بستن مودالها با کلید ESC
|
||||
document.addEventListener('keydown', function (event) {
|
||||
if (event.key === 'Escape') {
|
||||
closeAssignModal();
|
||||
closeArticleDetailModal();
|
||||
}
|
||||
});
|
||||
|
||||
// جستجوی مقالات
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const articlesTableBody = document.getElementById('articlesTableBody');
|
||||
|
||||
if (searchInput && articlesTableBody) {
|
||||
searchInput.addEventListener('input', function (e) {
|
||||
const searchTerm = e.target.value.toLowerCase();
|
||||
const rows = articlesTableBody.querySelectorAll('tr');
|
||||
|
||||
rows.forEach(row => {
|
||||
const title = row.querySelector('td:first-child .font-medium').textContent.toLowerCase();
|
||||
const institute = row.querySelector('td:nth-child(2)').textContent.toLowerCase();
|
||||
const assignedUser = row.querySelector('td:nth-child(4)').textContent.toLowerCase();
|
||||
|
||||
if (title.includes(searchTerm) || institute.includes(searchTerm) || assignedUser.includes(searchTerm)) {
|
||||
row.style.display = '';
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// انیمیشن
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const fadeElements = document.querySelectorAll('.fade-in');
|
||||
fadeElements.forEach((element, index) => {
|
||||
element.style.animationDelay = `${index * 0.1}s`;
|
||||
});
|
||||
});
|
||||
|
||||
// خودکار پنهان کردن پیامها
|
||||
setTimeout(() => {
|
||||
const flashMessages = document.querySelectorAll('.fixed div');
|
||||
flashMessages.forEach(msg => {
|
||||
if (msg.classList.contains('alert')) {
|
||||
msg.style.transition = 'all 0.5s ease';
|
||||
msg.style.opacity = '0';
|
||||
msg.style.transform = 'translateX(-100%)';
|
||||
setTimeout(() => msg.remove(), 500);
|
||||
}
|
||||
});
|
||||
}, 5000);
|
||||
|
||||
// افزودن event listener برای بررسی وضعیت
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// بررسی وجود مقالات
|
||||
const articleCount = {{ articles.total if articles else 0 }};
|
||||
console.log('تعداد مقالات:', articleCount);
|
||||
|
||||
// بررسی وجود کاربران
|
||||
const userCount = {{ users|length if users else 0 }};
|
||||
console.log('تعداد کاربران:', userCount);
|
||||
|
||||
// بررسی endpoint اختصاص
|
||||
console.log('Endpoint اختصاص:', '{{ url_for("admin.assign_article") }}');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,323 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>نظارت بر تصحیحها - پنل مدیریت</title>
|
||||
<script src="/static/3.4.17.es"></script>
|
||||
<link rel="stylesheet" href="/static/all.min.css">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Vazir&display=swap');
|
||||
body { font-family: 'Vazir', Tahoma, sans-serif; }
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-in;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
background-color: #e5e7eb;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 min-h-screen">
|
||||
<div class="flex">
|
||||
<!-- نوار کناری -->
|
||||
<aside class="w-64 bg-green-800 text-white min-h-screen">
|
||||
<div class="p-4">
|
||||
<h1 class="text-xl font-bold text-center border-b border-green-700 pb-4">
|
||||
<i class="fas fa-cogs ml-2"></i>
|
||||
پنل مدیریت
|
||||
</h1>
|
||||
<nav class="mt-8">
|
||||
<ul class="space-y-2">
|
||||
<li><a href="{{ url_for('admin.admin_dashboard') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-tachometer-alt ml-3"></i>داشبورد</a></li>
|
||||
<li><a href="{{ url_for('admin.files_management') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-file-upload ml-3"></i>مدیریت فایلها</a></li>
|
||||
<li><a href="{{ url_for('admin.articles_management') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-book ml-3"></i>مدیریت مقالات</a></li>
|
||||
<li><a href="{{ url_for('admin.users_management') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-users ml-3"></i>مدیریت کاربران</a></li>
|
||||
<li><a href="{{ url_for('admin.corrections_management') }}" class="flex items-center p-3 rounded-lg bg-green-700 transition-colors"><i class="fas fa-edit ml-3"></i>نظارت بر تصحیحها</a></li>
|
||||
<li class="pt-4 border-t border-green-700 mt-4"><a href="{{ url_for('dashboard.index') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-arrow-right ml-3"></i>بازگشت به سایت</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- محتوای اصلی -->
|
||||
<main class="flex-1 p-6">
|
||||
<!-- هدر صفحه -->
|
||||
<header class="bg-white rounded-2xl shadow-sm p-6 mb-6 fade-in">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-gray-800">نظارت بر تصحیح مقالات</h2>
|
||||
<p class="text-gray-600 mt-1">پیگیری وضعیت تصحیح و بازبینی مقالات</p>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4 space-x-reverse">
|
||||
<div class="w-10 h-10 bg-gradient-to-r from-green-500 to-emerald-600 rounded-full flex items-center justify-center text-white shadow-lg">
|
||||
<i class="fas fa-user text-sm"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- آمار تصحیحها -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6">
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 fade-in">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-gray-500 text-sm">کل مقالات</p>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ stats.total }}</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center">
|
||||
<i class="fas fa-book text-blue-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 fade-in">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-gray-500 text-sm">در انتظار تصحیح</p>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ stats.pending }}</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-yellow-100 rounded-full flex items-center justify-center">
|
||||
<i class="fas fa-clock text-yellow-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 fade-in">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-gray-500 text-sm">در حال تصحیح</p>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ stats.in_progress }}</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-orange-100 rounded-full flex items-center justify-center">
|
||||
<i class="fas fa-spinner text-orange-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 fade-in">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-gray-500 text-sm">تکمیل شده</p>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ stats.completed }}</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center">
|
||||
<i class="fas fa-check-circle text-green-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<a href="/admin/report">
|
||||
<button class="relative px-8 py-4 font-bold text-white rounded-full bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-300 ease-in-out focus:outline-none focus:ring-4 focus:ring-purple-300 mb-3">
|
||||
<span class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
گزارش جامع دانشگاه ها
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<!-- نوار پیشرفت -->
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 mb-6 fade-in">
|
||||
<h3 class="text-lg font-bold text-gray-800 mb-4 flex items-center">
|
||||
<i class="fas fa-chart-line ml-2 text-green-600"></i>
|
||||
پیشرفت کلی تصحیح مقالات
|
||||
</h3>
|
||||
|
||||
{% if stats.total > 0 %}
|
||||
<div class="space-y-4">
|
||||
<div class="flex justify-between text-sm text-gray-600">
|
||||
<span>درصد پیشرفت</span>
|
||||
<span>{{ "%.1f"|format((stats.completed / stats.total) * 100) }}%</span>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill bg-green-500"
|
||||
style="width: {{ (stats.completed / stats.total) * 100 }}%"></div>
|
||||
</div>
|
||||
<div class="flex justify-between text-xs text-gray-500">
|
||||
<span>{{ stats.completed }} مقاله تکمیل شده</span>
|
||||
<span>{{ stats.total - stats.completed }} مقاله باقیمانده</span>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-gray-500 text-center py-4">هیچ مقالهای برای نمایش وجود ندارد</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- توزیع وضعیتها -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
||||
<!-- نمودار دایرهای وضعیتها -->
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 fade-in">
|
||||
<h3 class="text-lg font-bold text-gray-800 mb-4 flex items-center">
|
||||
<i class="fas fa-chart-pie ml-2 text-purple-600"></i>
|
||||
توزیع وضعیت تصحیح
|
||||
</h3>
|
||||
<div class="flex justify-center items-center h-64">
|
||||
<div class="relative w-48 h-48">
|
||||
<!-- نمودار دایرهای ساده -->
|
||||
<svg viewBox="0 0 42 42" class="w-full h-full">
|
||||
{% set total = stats.total %}
|
||||
{% if total > 0 %}
|
||||
{% set completed_angle = (stats.completed / total) * 360 %}
|
||||
{% set in_progress_angle = (stats.in_progress / total) * 360 %}
|
||||
{% set pending_angle = (stats.pending / total) * 360 %}
|
||||
|
||||
<circle cx="21" cy="21" r="15.9155" fill="transparent"
|
||||
stroke="#10b981" stroke-width="8"
|
||||
stroke-dasharray="{{ completed_angle / 360 * 100 }} {{ 100 - completed_angle / 360 * 100 }}"
|
||||
stroke-dashoffset="25"/>
|
||||
|
||||
<circle cx="21" cy="21" r="15.9155" fill="transparent"
|
||||
stroke="#f59e0b" stroke-width="8"
|
||||
stroke-dasharray="{{ in_progress_angle / 360 * 100 }} {{ 100 - in_progress_angle / 360 * 100 }}"
|
||||
stroke-dashoffset="{{ 25 - completed_angle / 360 * 100 }}"/>
|
||||
|
||||
<circle cx="21" cy="21" r="15.9155" fill="transparent"
|
||||
stroke="#ef4444" stroke-width="8"
|
||||
stroke-dasharray="{{ pending_angle / 360 * 100 }} {{ 100 - pending_angle / 360 * 100 }}"
|
||||
stroke-dashoffset="{{ 25 - (completed_angle + in_progress_angle) / 360 * 100 }}"/>
|
||||
{% endif %}
|
||||
</svg>
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<div class="text-center">
|
||||
<p class="text-2xl font-bold text-gray-800">
|
||||
{% if total > 0 %}{{ "%.0f"|format((stats.completed / total) * 100) }}%{% else %}0%{% endif %}
|
||||
</p>
|
||||
<p class="text-xs text-gray-500">تکمیل شده</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-4 mt-4 text-center">
|
||||
<div>
|
||||
<div class="w-3 h-3 bg-green-500 rounded-full mx-auto mb-1"></div>
|
||||
<p class="text-xs text-gray-600">تکمیل شده</p>
|
||||
<p class="font-bold">{{ stats.completed }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="w-3 h-3 bg-yellow-500 rounded-full mx-auto mb-1"></div>
|
||||
<p class="text-xs text-gray-600">در حال تصحیح</p>
|
||||
<p class="font-bold">{{ stats.in_progress }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="w-3 h-3 bg-red-500 rounded-full mx-auto mb-1"></div>
|
||||
<p class="text-xs text-gray-600">در انتظار</p>
|
||||
<p class="font-bold">{{ stats.pending }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- مقالات نیازمند بازبینی -->
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 fade-in">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-bold text-gray-800 flex items-center">
|
||||
<i class="fas fa-eye ml-2 text-blue-600"></i>
|
||||
مقالات نیازمند بازبینی
|
||||
</h3>
|
||||
<a href="{{ url_for('admin.articles_management') }}?status=completed"
|
||||
class="text-blue-600 hover:text-blue-700 text-sm">
|
||||
مشاهده همه
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
{% if articles_for_review %}
|
||||
{% for article in articles_for_review %}
|
||||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<div class="flex items-center">
|
||||
<i class="fas fa-file-alt text-gray-400 ml-3"></i>
|
||||
<div>
|
||||
<p class="font-medium text-gray-800 text-sm">{{ article.name[:50] }}{% if article.name|length > 50 %}...{% endif %}</p>
|
||||
<p class="text-xs text-gray-500">
|
||||
{% if article.updated_at %}
|
||||
آخرین بروزرسانی: {{ article.updated_at.strftime('%Y/%m/%d %H:%M') }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ url_for('admin.article_detail', article_id=article.id) }}"
|
||||
class="text-blue-600 hover:text-blue-800 text-sm">
|
||||
<i class="fas fa-external-link-alt"></i>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="text-center py-8">
|
||||
<i class="fas fa-check-circle text-3xl text-green-400 mb-3"></i>
|
||||
<p class="text-gray-500">همه مقالات بازبینی شدهاند</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- اقدامات سریع -->
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 fade-in">
|
||||
<h3 class="text-lg font-bold text-gray-800 mb-4 flex items-center">
|
||||
<i class="fas fa-bolt ml-2 text-yellow-600"></i>
|
||||
اقدامات سریع
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<a href="{{ url_for('admin.articles_management') }}?status=pending"
|
||||
class="p-4 bg-red-50 border border-red-200 rounded-lg hover:bg-red-100 transition-colors text-center group">
|
||||
<i class="fas fa-clock text-2xl text-red-500 mb-2 group-hover:scale-110 transition-transform"></i>
|
||||
<p class="font-medium text-red-800">مقالات در انتظار</p>
|
||||
<p class="text-sm text-red-600">{{ stats.pending }} مقاله</p>
|
||||
</a>
|
||||
|
||||
<a href="{{ url_for('admin.articles_management') }}?status=in_progress"
|
||||
class="p-4 bg-yellow-50 border border-yellow-200 rounded-lg hover:bg-yellow-100 transition-colors text-center group">
|
||||
<i class="fas fa-spinner text-2xl text-yellow-500 mb-2 group-hover:scale-110 transition-transform"></i>
|
||||
<p class="font-medium text-yellow-800">در حال تصحیح</p>
|
||||
<p class="text-sm text-yellow-600">{{ stats.in_progress }} مقاله</p>
|
||||
</a>
|
||||
|
||||
<a href="{{ url_for('admin.files_management') }}"
|
||||
class="p-4 bg-blue-50 border border-blue-200 rounded-lg hover:bg-blue-100 transition-colors text-center group">
|
||||
<i class="fas fa-upload text-2xl text-blue-500 mb-2 group-hover:scale-110 transition-transform"></i>
|
||||
<p class="font-medium text-blue-800">آپلود فایل جدید</p>
|
||||
<p class="text-sm text-blue-600">افزودن مقالات</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// انیمیشن
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const fadeElements = document.querySelectorAll('.fade-in');
|
||||
fadeElements.forEach((element, index) => {
|
||||
element.style.animationDelay = `${index * 0.1}s`;
|
||||
});
|
||||
});
|
||||
|
||||
// خودکار پنهان کردن پیامها
|
||||
setTimeout(() => {
|
||||
const flashMessages = document.querySelectorAll('.fixed div');
|
||||
flashMessages.forEach(msg => {
|
||||
msg.style.transition = 'all 0.5s ease';
|
||||
msg.style.opacity = '0';
|
||||
msg.style.transform = 'translateX(-100%)';
|
||||
setTimeout(() => msg.remove(), 500);
|
||||
});
|
||||
}, 5000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,264 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>پنل مدیریت - نمایشگر معیار مقالات</title>
|
||||
<script src="/static/3.4.17.es"></script>
|
||||
<link rel="stylesheet" href="/static/all.min.css">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Vazir&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Vazir', Tahoma, sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-50 min-h-screen">
|
||||
<!-- نوار کناری -->
|
||||
<div class="flex">
|
||||
<aside class="w-64 bg-green-800 text-white min-h-screen">
|
||||
<div class="p-4">
|
||||
<h1 class="text-xl font-bold text-center border-b border-green-700 pb-4">
|
||||
<i class="fas fa-cogs ml-2"></i>
|
||||
پنل مدیریت
|
||||
</h1>
|
||||
|
||||
<!-- بخش منوی نوار کناری با اصلاحات -->
|
||||
<nav class="mt-8">
|
||||
<ul class="space-y-2">
|
||||
<li>
|
||||
<a href="{{ url_for('admin.admin_dashboard') }}"
|
||||
class="flex items-center p-3 rounded-lg bg-green-700 transition-colors">
|
||||
<i class="fas fa-tachometer-alt ml-3"></i>
|
||||
داشبورد
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('admin.files_management') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors">
|
||||
<i class="fas fa-file-upload ml-3"></i>
|
||||
مدیریت فایلها
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('admin.articles_management') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors">
|
||||
<i class="fas fa-book ml-3"></i>
|
||||
مدیریت مقالات
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('admin.users_management') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors">
|
||||
<i class="fas fa-users ml-3"></i>
|
||||
مدیریت کاربران
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('admin.corrections_management') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors">
|
||||
<i class="fas fa-edit ml-3"></i>
|
||||
نظارت بر تصحیحها
|
||||
</a>
|
||||
</li>
|
||||
<li class="pt-4 border-t border-green-700 mt-4">
|
||||
<a href="{{ url_for('dashboard.index') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors">
|
||||
<i class="fas fa-arrow-right ml-3"></i>
|
||||
بازگشت به سایت
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- محتوای اصلی -->
|
||||
<main class="flex-1 p-6">
|
||||
<!-- هدر -->
|
||||
<header class="bg-white rounded-lg shadow-sm p-4 mb-6">
|
||||
<div class="flex justify-between items-center">
|
||||
<h2 class="text-2xl font-bold text-gray-800">داشبورد مدیریت</h2>
|
||||
<div class="flex items-center space-x-4 space-x-reverse">
|
||||
<span class="text-gray-600">خوش آمدید، {{ current_user.username }}</span>
|
||||
<div class="w-8 h-8 bg-green-500 rounded-full flex items-center justify-center text-white">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- کارتهای آمار -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
<div class="bg-white rounded-lg shadow-sm p-6 border-r-4 border-blue-500 stats-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-gray-500 text-sm">کل کاربران</p>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ stats.total_users }}</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center">
|
||||
<i class="fas fa-users text-blue-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow-sm p-6 border-r-4 border-green-500 stats-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-gray-500 text-sm">کل مقالات</p>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ stats.total_articles }}</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center">
|
||||
<i class="fas fa-book text-green-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow-sm p-6 border-r-4 border-yellow-500 stats-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-gray-500 text-sm">در انتظار تصحیح</p>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ stats.pending_corrections }}</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-yellow-100 rounded-full flex items-center justify-center">
|
||||
<i class="fas fa-clock text-yellow-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow-sm p-6 border-r-4 border-purple-500 stats-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-gray-500 text-sm">فایلهای داده</p>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ stats.total_files }}</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-purple-100 rounded-full flex items-center justify-center">
|
||||
<i class="fas fa-file-upload text-purple-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- بخشهای دیگر -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<!-- فایلهای اخیر -->
|
||||
<div class="bg-white rounded-lg shadow-sm p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-bold text-gray-800">آخرین فایلهای آپلود شده</h3>
|
||||
<a href="{{ url_for('admin.files_management') }}"
|
||||
class="text-green-600 hover:text-green-700 text-sm">
|
||||
مشاهده همه
|
||||
</a>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
{% if recent_files %}
|
||||
{% for file in recent_files %}
|
||||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||||
<div class="flex items-center">
|
||||
<i class="fas fa-file text-gray-400 ml-3"></i>
|
||||
<div>
|
||||
<p class="font-medium text-gray-800">{{
|
||||
file.original_filename|default(file.filename, true) }}</p>
|
||||
<p class="text-xs text-gray-500">
|
||||
{% if file.uploaded_at %}
|
||||
{{ file.uploaded_at.strftime('%Y/%m/%d') }}
|
||||
{% else %}
|
||||
تاریخ نامشخص
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="px-2 py-1 text-xs rounded-full
|
||||
{% if file.status == 'completed' %}bg-green-100 text-green-800
|
||||
{% elif file.status == 'processing' %}bg-yellow-100 text-yellow-800
|
||||
{% else %}bg-red-100 text-red-800{% endif %}">
|
||||
{% if file.status == 'completed' %}تکمیل شده
|
||||
{% elif file.status == 'processing' %}در حال پردازش
|
||||
{% else %}خطا{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class="text-gray-500 text-center py-4">هیچ فایلی یافت نشد</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- کاربران اخیر -->
|
||||
<div class="bg-white rounded-lg shadow-sm p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-bold text-gray-800">کاربران جدید</h3>
|
||||
<a href="{{ url_for('admin.users_management') }}"
|
||||
class="text-green-600 hover:text-green-700 text-sm">
|
||||
مشاهده همه
|
||||
</a>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
{% if recent_users %}
|
||||
{% for user in recent_users %}
|
||||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||||
<div class="flex items-center">
|
||||
<div class="w-8 h-8 bg-green-100 rounded-full flex items-center justify-center ml-3">
|
||||
<i class="fas fa-user text-green-500 text-sm"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium text-gray-800">{{ user.username }}</p>
|
||||
<p class="text-xs text-gray-500">{{ user.email }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="px-2 py-1 text-xs rounded-full
|
||||
{% if user.is_active %}bg-green-100 text-green-800
|
||||
{% else %}bg-red-100 text-red-800{% endif %}">
|
||||
{{ 'فعال' if user.is_active else 'غیرفعال' }}
|
||||
</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class="text-gray-500 text-center py-4">هیچ کاربری یافت نشد</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- اسکریپتها -->
|
||||
<script>
|
||||
// بهروزرسانی آمار هر 30 ثانیه
|
||||
function updateStats() {
|
||||
fetch('/admin/api/stats')
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network error');
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
// بهروزرسانی کارتهای آمار
|
||||
document.querySelectorAll('.stats-card').forEach((card, index) => {
|
||||
const valueElement = card.querySelector('.text-2xl');
|
||||
if (valueElement) {
|
||||
const values = [
|
||||
data.total_users,
|
||||
data.total_articles,
|
||||
data.pending_corrections,
|
||||
data.total_files
|
||||
];
|
||||
if (values[index] !== undefined) {
|
||||
valueElement.textContent = values[index];
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(error => console.error('Error updating stats:', error));
|
||||
}
|
||||
|
||||
// فقط اگر کاربر در صفحه داشبورد است، آمار را بهروزرسانی کن
|
||||
if (window.location.pathname === '/admin/' || window.location.pathname === '/admin') {
|
||||
setInterval(updateStats, 30000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,373 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>مدیریت فایلها - پنل مدیریت</title>
|
||||
<script src="/static/3.4.17.es"></script>
|
||||
<link rel="stylesheet" href="/static/all.min.css">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Vazir&display=swap');
|
||||
body { font-family: 'Vazir', Tahoma, sans-serif; }
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-in;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.upload-area {
|
||||
border: 2px dashed #d1d5db;
|
||||
border-radius: 0.75rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.upload-area.dragover {
|
||||
border-color: #10b981;
|
||||
background-color: #f0fdf4;
|
||||
}
|
||||
|
||||
.file-size {
|
||||
font-family: monospace;
|
||||
background-color: #f3f4f6;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 min-h-screen">
|
||||
<div class="flex">
|
||||
<!-- نوار کناری -->
|
||||
<aside class="w-64 bg-green-800 text-white min-h-screen">
|
||||
<div class="p-4">
|
||||
<h1 class="text-xl font-bold text-center border-b border-green-700 pb-4">
|
||||
<i class="fas fa-cogs ml-2"></i>
|
||||
پنل مدیریت
|
||||
</h1>
|
||||
<nav class="mt-8">
|
||||
<ul class="space-y-2">
|
||||
<li><a href="{{ url_for('admin.admin_dashboard') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-tachometer-alt ml-3"></i>داشبورد</a></li>
|
||||
<li><a href="{{ url_for('admin.files_management') }}" class="flex items-center p-3 rounded-lg bg-green-700 transition-colors"><i class="fas fa-file-upload ml-3"></i>مدیریت فایلها</a></li>
|
||||
<li><a href="{{ url_for('admin.articles_management') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-book ml-3"></i>مدیریت مقالات</a></li>
|
||||
<li><a href="{{ url_for('admin.users_management') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-users ml-3"></i>مدیریت کاربران</a></li>
|
||||
<li><a href="{{ url_for('admin.corrections_management') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-edit ml-3"></i>نظارت بر تصحیحها</a></li>
|
||||
<li class="pt-4 border-t border-green-700 mt-4"><a href="{{ url_for('dashboard.index') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-arrow-right ml-3"></i>بازگشت به سایت</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- محتوای اصلی -->
|
||||
<main class="flex-1 p-6">
|
||||
<!-- هدر صفحه -->
|
||||
<header class="bg-white rounded-2xl shadow-sm p-6 mb-6 fade-in">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-gray-800">مدیریت فایلهای داده</h2>
|
||||
<p class="text-gray-600 mt-1">آپلود و مدیریت فایلهای JSON حاوی مقالات</p>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4 space-x-reverse">
|
||||
<div class="w-10 h-10 bg-gradient-to-r from-green-500 to-emerald-600 rounded-full flex items-center justify-center text-white shadow-lg">
|
||||
<i class="fas fa-user text-sm"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- بخش آپلود فایل -->
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 mb-6 fade-in">
|
||||
<h3 class="text-xl font-bold text-gray-800 mb-4 flex items-center">
|
||||
<i class="fas fa-cloud-upload-alt ml-2 text-green-600"></i>
|
||||
آپلود فایل جدید
|
||||
</h3>
|
||||
|
||||
<form id="uploadForm" action="{{ url_for('admin.upload_file') }}" method="POST" enctype="multipart/form-data" class="space-y-4">
|
||||
<div id="uploadArea" class="upload-area p-8 text-center cursor-pointer">
|
||||
<i class="fas fa-file-upload text-4xl text-gray-400 mb-4"></i>
|
||||
<p class="text-gray-600 mb-2">فایل JSON خود را اینجا رها کنید یا برای انتخاب کلیک کنید</p>
|
||||
<p class="text-sm text-gray-500">فرمتهای مجاز: JSON | حداکثر حجم: 50 مگابایت</p>
|
||||
<input type="file" name="file" id="fileInput" accept=".json" class="hidden" required>
|
||||
<button type="button" onclick="document.getElementById('fileInput').click()"
|
||||
class="mt-4 bg-green-600 text-white px-6 py-2 rounded-lg hover:bg-green-700 transition-colors">
|
||||
انتخاب فایل
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="fileInfo" class="hidden p-4 bg-green-50 border border-green-200 rounded-lg">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<i class="fas fa-file text-green-600 ml-3"></i>
|
||||
<div>
|
||||
<p id="fileName" class="font-medium text-green-800"></p>
|
||||
<p id="fileSize" class="text-sm text-green-600"></p>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" onclick="clearFile()" class="text-red-500 hover:text-red-700">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="description" class="block text-sm font-medium text-gray-700 mb-2">توضیحات (اختیاری)</label>
|
||||
<textarea name="description" id="description" rows="3"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500"
|
||||
placeholder="توضیحات درباره محتوای فایل..."></textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="uploadBtn"
|
||||
class="w-full bg-green-600 text-white py-3 px-4 rounded-lg hover:bg-green-700 transition-colors flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
<i class="fas fa-upload ml-2"></i>
|
||||
آپلود و پردازش فایل
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- لیست فایلها -->
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 fade-in">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-xl font-bold text-gray-800 flex items-center">
|
||||
<i class="fas fa-list ml-2 text-blue-600"></i>
|
||||
فایلهای آپلود شده
|
||||
</h3>
|
||||
<div class="text-sm text-gray-500">
|
||||
مجموع: {{ files.total }} فایل
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if files.items %}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm text-right text-gray-700">
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-4 py-3">نام فایل</th>
|
||||
<th class="px-4 py-3">حجم</th>
|
||||
<th class="px-4 py-3">وضعیت</th>
|
||||
<th class="px-4 py-3">تاریخ آپلود</th>
|
||||
<th class="px-4 py-3">عملیات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for file in files.items %}
|
||||
<tr class="border-b hover:bg-gray-50 transition-colors">
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex items-center">
|
||||
<i class="fas fa-file-json text-yellow-500 ml-3"></i>
|
||||
<div>
|
||||
<p class="font-medium text-gray-900">{{ file.original_filename }}</p>
|
||||
{% if file.description %}
|
||||
<p class="text-xs text-gray-500">{{ file.description }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="file-size">
|
||||
{% if file.file_size %}
|
||||
{{ "%.2f"|format(file.file_size / 1024 / 1024) }} مگابایت
|
||||
{% else %}
|
||||
نامشخص
|
||||
{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="px-3 py-1 text-xs rounded-full
|
||||
{% if file.status == 'completed' %}bg-green-100 text-green-800
|
||||
{% elif file.status == 'processing' %}bg-yellow-100 text-yellow-800
|
||||
{% elif file.status == 'error' %}bg-red-100 text-red-800
|
||||
{% else %}bg-gray-100 text-gray-800{% endif %}">
|
||||
<i class="fas
|
||||
{% if file.status == 'completed' %}fa-check-circle
|
||||
{% elif file.status == 'processing' %}fa-spinner fa-spin
|
||||
{% elif file.status == 'error' %}fa-exclamation-circle
|
||||
{% else %}fa-question-circle{% endif %} ml-1">
|
||||
</i>
|
||||
{% if file.status == 'completed' %}تکمیل شده
|
||||
{% elif file.status == 'processing' %}در حال پردازش
|
||||
{% elif file.status == 'error' %}خطا
|
||||
{% else %}نامشخص{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
{% if file.uploaded_at %}
|
||||
<div class="text-sm">
|
||||
<p>{{ file.uploaded_at.strftime('%Y/%m/%d') }}</p>
|
||||
<p class="text-gray-500">{{ file.uploaded_at.strftime('%H:%M') }}</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-gray-500">نامشخص</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<form action="{{ url_for('admin.delete_file', file_id=file.id) }}" method="POST"
|
||||
onsubmit="return confirm('آیا از حذف این فایل اطمینان دارید؟ این عمل غیرقابل بازگشت است.');"
|
||||
class="inline">
|
||||
<button type="submit" class="text-red-600 hover:text-red-800 transition-colors">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- صفحهبندی -->
|
||||
{% if files.pages > 1 %}
|
||||
<div class="flex justify-center items-center space-x-2 space-x-reverse mt-6">
|
||||
{% if files.has_prev %}
|
||||
<a href="{{ url_for('admin.files_management', page=files.prev_num) }}"
|
||||
class="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% for page_num in files.iter_pages() %}
|
||||
{% if page_num %}
|
||||
{% if page_num == files.page %}
|
||||
<span class="px-4 py-2 bg-green-600 text-white rounded-lg">{{ page_num }}</span>
|
||||
{% else %}
|
||||
<a href="{{ url_for('admin.files_management', page=page_num) }}"
|
||||
class="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
{{ page_num }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="px-2">...</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if files.has_next %}
|
||||
<a href="{{ url_for('admin.files_management', page=files.next_num) }}"
|
||||
class="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
<div class="text-center py-8">
|
||||
<i class="fas fa-folder-open text-4xl text-gray-400 mb-4"></i>
|
||||
<h4 class="text-lg font-medium text-gray-700">هیچ فایلی یافت نشد</h4>
|
||||
<p class="text-gray-500 mt-2">هنوز فایلی آپلود نکردهاید</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// مدیریت آپلود فایل
|
||||
const uploadArea = document.getElementById('uploadArea');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const fileInfo = document.getElementById('fileInfo');
|
||||
const fileName = document.getElementById('fileName');
|
||||
const fileSize = document.getElementById('fileSize');
|
||||
const uploadBtn = document.getElementById('uploadBtn');
|
||||
const uploadForm = document.getElementById('uploadForm');
|
||||
|
||||
// رویدادهای درگ و دراپ
|
||||
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||
uploadArea.addEventListener(eventName, preventDefaults, false);
|
||||
});
|
||||
|
||||
function preventDefaults(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
['dragenter', 'dragover'].forEach(eventName => {
|
||||
uploadArea.addEventListener(eventName, highlight, false);
|
||||
});
|
||||
|
||||
['dragleave', 'drop'].forEach(eventName => {
|
||||
uploadArea.addEventListener(eventName, unhighlight, false);
|
||||
});
|
||||
|
||||
function highlight() {
|
||||
uploadArea.classList.add('dragover');
|
||||
}
|
||||
|
||||
function unhighlight() {
|
||||
uploadArea.classList.remove('dragover');
|
||||
}
|
||||
|
||||
uploadArea.addEventListener('drop', handleDrop, false);
|
||||
|
||||
function handleDrop(e) {
|
||||
const dt = e.dataTransfer;
|
||||
const files = dt.files;
|
||||
handleFiles(files);
|
||||
}
|
||||
|
||||
fileInput.addEventListener('change', function() {
|
||||
handleFiles(this.files);
|
||||
});
|
||||
|
||||
function handleFiles(files) {
|
||||
if (files.length > 0) {
|
||||
const file = files[0];
|
||||
|
||||
// بررسی فرمت فایل
|
||||
if (!file.name.toLowerCase().endsWith('.json')) {
|
||||
alert('لطفاً فقط فایلهای JSON آپلود کنید.');
|
||||
return;
|
||||
}
|
||||
|
||||
// بررسی حجم فایل (50MB)
|
||||
if (file.size > 50 * 1024 * 1024) {
|
||||
alert('حجم فایل باید کمتر از 50 مگابایت باشد.');
|
||||
return;
|
||||
}
|
||||
|
||||
// نمایش اطلاعات فایل
|
||||
fileName.textContent = file.name;
|
||||
fileSize.textContent = formatFileSize(file.size);
|
||||
fileInfo.classList.remove('hidden');
|
||||
uploadBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function clearFile() {
|
||||
fileInput.value = '';
|
||||
fileInfo.classList.add('hidden');
|
||||
uploadBtn.disabled = true;
|
||||
}
|
||||
|
||||
function formatFileSize(bytes) {
|
||||
if (bytes === 0) return '0 بایت';
|
||||
const k = 1024;
|
||||
const sizes = ['بایت', 'کیلوبایت', 'مگابایت', 'گیگابایت'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
// مدیریت ارسال فرم
|
||||
uploadForm.addEventListener('submit', function(e) {
|
||||
const file = fileInput.files[0];
|
||||
if (!file) {
|
||||
e.preventDefault();
|
||||
alert('لطفاً یک فایل انتخاب کنید.');
|
||||
return;
|
||||
}
|
||||
|
||||
uploadBtn.disabled = true;
|
||||
uploadBtn.innerHTML = '<i class="fas fa-spinner fa-spin ml-2"></i>در حال آپلود...';
|
||||
});
|
||||
|
||||
// انیمیشن
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const fadeElements = document.querySelectorAll('.fade-in');
|
||||
fadeElements.forEach((element, index) => {
|
||||
element.style.animationDelay = `${index * 0.1}s`;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,261 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>مدیریت کاربران - پنل مدیریت</title>
|
||||
<script src="/static/3.4.17.es"></script>
|
||||
<link rel="stylesheet" href="/static/all.min.css">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Vazir&display=swap');
|
||||
body { font-family: 'Vazir', Tahoma, sans-serif; }
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-in;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 min-h-screen">
|
||||
<div class="flex">
|
||||
<!-- نوار کناری -->
|
||||
<aside class="w-64 bg-green-800 text-white min-h-screen">
|
||||
<div class="p-4">
|
||||
<h1 class="text-xl font-bold text-center border-b border-green-700 pb-4">
|
||||
<i class="fas fa-cogs ml-2"></i>
|
||||
پنل مدیریت
|
||||
</h1>
|
||||
<nav class="mt-8">
|
||||
<ul class="space-y-2">
|
||||
<li><a href="{{ url_for('admin.admin_dashboard') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-tachometer-alt ml-3"></i>داشبورد</a></li>
|
||||
<li><a href="{{ url_for('admin.files_management') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-file-upload ml-3"></i>مدیریت فایلها</a></li>
|
||||
<li><a href="{{ url_for('admin.articles_management') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-book ml-3"></i>مدیریت مقالات</a></li>
|
||||
<li><a href="{{ url_for('admin.users_management') }}" class="flex items-center p-3 rounded-lg bg-green-700 transition-colors"><i class="fas fa-users ml-3"></i>مدیریت کاربران</a></li>
|
||||
<li><a href="{{ url_for('admin.corrections_management') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-edit ml-3"></i>نظارت بر تصحیحها</a></li>
|
||||
<li class="pt-4 border-t border-green-700 mt-4"><a href="{{ url_for('dashboard.index') }}" class="flex items-center p-3 rounded-lg hover:bg-green-700 transition-colors"><i class="fas fa-arrow-right ml-3"></i>بازگشت به سایت</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- محتوای اصلی -->
|
||||
<main class="flex-1 p-6">
|
||||
<!-- هدر صفحه -->
|
||||
<header class="bg-white rounded-2xl shadow-sm p-6 mb-6 fade-in">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-gray-800">مدیریت کاربران</h2>
|
||||
<p class="text-gray-600 mt-1">مدیریت حسابهای کاربری و سطوح دسترسی</p>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4 space-x-reverse">
|
||||
<div class="w-10 h-10 bg-gradient-to-r from-green-500 to-emerald-600 rounded-full flex items-center justify-center text-white shadow-lg">
|
||||
<i class="fas fa-user text-sm"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- آمار کاربران -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 text-center fade-in">
|
||||
<div class="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-3">
|
||||
<i class="fas fa-users text-blue-500 text-xl"></i>
|
||||
</div>
|
||||
<p class="text-gray-500 text-sm">کل کاربران</p>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ users.total }}</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 text-center fade-in">
|
||||
<div class="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-3">
|
||||
<i class="fas fa-user-check text-green-500 text-xl"></i>
|
||||
</div>
|
||||
<p class="text-gray-500 text-sm">کاربران فعال</p>
|
||||
<p class="text-2xl font-bold text-gray-800" id="activeUsersCount">...</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 text-center fade-in">
|
||||
<div class="w-12 h-12 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-3">
|
||||
<i class="fas fa-user-slash text-red-500 text-xl"></i>
|
||||
</div>
|
||||
<p class="text-gray-500 text-sm">کاربران غیرفعال</p>
|
||||
<p class="text-2xl font-bold text-gray-800" id="inactiveUsersCount">...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- لیست کاربران -->
|
||||
<div class="bg-white rounded-2xl shadow-sm p-6 fade-in">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-xl font-bold text-gray-800 flex items-center">
|
||||
<i class="fas fa-list ml-2 text-blue-600"></i>
|
||||
لیست کاربران
|
||||
</h3>
|
||||
<div class="text-sm text-gray-500">
|
||||
صفحه {{ users.page }} از {{ users.pages }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if users.items %}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm text-right text-gray-700">
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-4 py-3">کاربر</th>
|
||||
<th class="px-4 py-3">ایمیل</th>
|
||||
<th class="px-4 py-3">نقش</th>
|
||||
<th class="px-4 py-3">وضعیت</th>
|
||||
<th class="px-4 py-3">تاریخ ثبتنام</th>
|
||||
<th class="px-4 py-3">عملیات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users.items %}
|
||||
<tr class="border-b hover:bg-gray-50 transition-colors">
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex items-center">
|
||||
<div class="w-8 h-8 bg-gradient-to-r from-green-400 to-blue-500 rounded-full flex items-center justify-center text-white text-sm font-bold ml-3">
|
||||
{{ user.username[0]|upper }}
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium text-gray-900">{{ user.username }}</p>
|
||||
<p class="text-xs text-gray-500">ID: {{ user.id }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<p class="text-gray-600">{{ user.email }}</p>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="px-3 py-1 text-xs rounded-full
|
||||
{% if user.role == 'admin' %}bg-purple-100 text-purple-800
|
||||
{% else %}bg-blue-100 text-blue-800{% endif %}">
|
||||
<i class="fas
|
||||
{% if user.role == 'admin' %}fa-crown
|
||||
{% else %}fa-user{% endif %} ml-1">
|
||||
</i>
|
||||
{{ 'مدیر' if user.role == 'admin' else 'کاربر عادی' }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="px-3 py-1 text-xs rounded-full
|
||||
{% if user.is_active %}bg-green-100 text-green-800
|
||||
{% else %}bg-red-100 text-red-800{% endif %}">
|
||||
<i class="fas
|
||||
{% if user.is_active %}fa-check-circle
|
||||
{% else %}fa-times-circle{% endif %} ml-1">
|
||||
</i>
|
||||
{{ 'فعال' if user.is_active else 'غیرفعال' }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
{% if user.created_at %}
|
||||
<div class="text-sm">
|
||||
<p>{{ user.created_at.strftime('%Y/%m/%d') }}</p>
|
||||
<p class="text-gray-500">{{ user.created_at.strftime('%H:%M') }}</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-gray-500">نامشخص</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
{% if user.id != current_user.id %}
|
||||
<form action="{{ url_for('admin.toggle_user', user_id=user.id) }}" method="POST"
|
||||
onsubmit="return confirm('آیا از {{ 'غیرفعال' if user.is_active else 'فعال' }} کردن این کاربر اطمینان دارید؟');"
|
||||
class="inline">
|
||||
<button type="submit"
|
||||
class="{% if user.is_active %}text-red-600 hover:text-red-800{% else %}text-green-600 hover:text-green-800{% endif %} transition-colors ml-3">
|
||||
<i class="fas {% if user.is_active %}fa-user-slash{% else %}fa-user-check{% endif %}"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<span class="text-gray-400 text-sm">حساب شما</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- صفحهبندی -->
|
||||
{% if users.pages > 1 %}
|
||||
<div class="flex justify-center items-center space-x-2 space-x-reverse mt-6">
|
||||
{% if users.has_prev %}
|
||||
<a href="{{ url_for('admin.users_management', page=users.prev_num) }}"
|
||||
class="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% for page_num in users.iter_pages() %}
|
||||
{% if page_num %}
|
||||
{% if page_num == users.page %}
|
||||
<span class="px-4 py-2 bg-green-600 text-white rounded-lg">{{ page_num }}</span>
|
||||
{% else %}
|
||||
<a href="{{ url_for('admin.users_management', page=page_num) }}"
|
||||
class="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
{{ page_num }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="px-2">...</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if users.has_next %}
|
||||
<a href="{{ url_for('admin.users_management', page=users.next_num) }}"
|
||||
class="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
<div class="text-center py-8">
|
||||
<i class="fas fa-users text-4xl text-gray-400 mb-4"></i>
|
||||
<h4 class="text-lg font-medium text-gray-700">هیچ کاربری یافت نشد</h4>
|
||||
<p class="text-gray-500 mt-2">هنوز کاربری ثبتنام نکرده است</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// محاسبه آمار کاربران
|
||||
function calculateUserStats() {
|
||||
const activeUsers = document.querySelectorAll('tbody tr').length -
|
||||
document.querySelectorAll('tbody tr:has(.bg-red-100)').length;
|
||||
const inactiveUsers = document.querySelectorAll('tbody tr:has(.bg-red-100)').length;
|
||||
|
||||
document.getElementById('activeUsersCount').textContent = activeUsers;
|
||||
document.getElementById('inactiveUsersCount').textContent = inactiveUsers;
|
||||
}
|
||||
|
||||
// انیمیشن
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const fadeElements = document.querySelectorAll('.fade-in');
|
||||
fadeElements.forEach((element, index) => {
|
||||
element.style.animationDelay = `${index * 0.1}s`;
|
||||
});
|
||||
|
||||
calculateUserStats();
|
||||
});
|
||||
|
||||
// خودکار پنهان کردن پیامها
|
||||
setTimeout(() => {
|
||||
const flashMessages = document.querySelectorAll('.fixed div');
|
||||
flashMessages.forEach(msg => {
|
||||
msg.style.transition = 'all 0.5s ease';
|
||||
msg.style.opacity = '0';
|
||||
msg.style.transform = 'translateX(-100%)';
|
||||
setTimeout(() => msg.remove(), 500);
|
||||
});
|
||||
}, 5000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,805 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>معیارسنج</title>
|
||||
<link rel="stylesheet" href="/static/all.min.css">
|
||||
<link href="https://cdn.jsdelivr.net/gh/rastikerdar/vazirmatn@v33.003/Vazirmatn-font-face.css" rel="stylesheet">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Vazirmatn', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
--glass-bg: rgba(255, 255, 255, 0.08);
|
||||
--glass-border: rgba(255, 255, 255, 0.1);
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--primary-gradient);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* گرادیان پویا و بهینهشده */
|
||||
.gradient-bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
background:
|
||||
radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.15) 0%, transparent 40%),
|
||||
radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.1) 0%, transparent 40%),
|
||||
radial-gradient(circle at 40% 40%, rgba(120, 219, 255, 0.1) 0%, transparent 40%);
|
||||
animation: gradientShift 20s ease-in-out infinite alternate;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@keyframes gradientShift {
|
||||
0% {
|
||||
transform: scale(1) rotate(0deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1) rotate(180deg);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1) rotate(360deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
/* نوار ناوبری بهینهشده */
|
||||
nav {
|
||||
padding: 1.5rem 2rem;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.auth-buttons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 30px;
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
font-size: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
color: var(--text-primary);
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.btn:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* بخش اصلی بهینهشده */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.site-title {
|
||||
font-size: clamp(3rem, 8vw, 5rem);
|
||||
font-weight: 800;
|
||||
letter-spacing: -1px;
|
||||
line-height: 1.1;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #e6e6e6 50%, #b3b3b3 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
text-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
||||
animation: titleEntrance 1s ease-out;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@keyframes titleEntrance {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px) scale(0.9);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.floating {
|
||||
animation: floating 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes floating {
|
||||
0%, 100% {
|
||||
transform: translateY(0px) rotate(0deg);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-25px) rotate(1deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* صفحات ورود و ثبتنام بهینهشده */
|
||||
.auth-page {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 100;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease-out;
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.auth-page.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-radius: 24px;
|
||||
padding: 40px;
|
||||
width: 90%;
|
||||
max-width: 450px;
|
||||
border: 1px solid var(--glass-border);
|
||||
box-shadow:
|
||||
0 20px 40px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
transform: translateY(30px) scale(0.95);
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.auth-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||||
}
|
||||
|
||||
.auth-page.active .auth-card {
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
|
||||
.auth-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.auth-header h2 {
|
||||
font-size: 2.2rem;
|
||||
margin-bottom: 10px;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #e6e6e6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.auth-header p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-primary);
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.3s;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
opacity: 1;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 16px 20px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 12px;
|
||||
color: var(--text-primary);
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s;
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.form-control::placeholder {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.auth-btn {
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.1));
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 12px;
|
||||
color: var(--text-primary);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.auth-btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: left 0.6s;
|
||||
}
|
||||
|
||||
.auth-btn:hover {
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.2));
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.auth-btn:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.auth-btn.loading {
|
||||
opacity: 0.8;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* پیامها */
|
||||
.message {
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
display: none;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
background: linear-gradient(135deg, rgba(231, 76, 60, 0.2), rgba(192, 57, 43, 0.2));
|
||||
border: 1px solid rgba(231, 76, 60, 0.3);
|
||||
color: #ff9c8e;
|
||||
}
|
||||
|
||||
.success {
|
||||
background: linear-gradient(135deg, rgba(46, 204, 113, 0.2), rgba(39, 174, 96, 0.2));
|
||||
border: 1px solid rgba(46, 204, 113, 0.3);
|
||||
color: #a3ffce;
|
||||
}
|
||||
|
||||
/* افکتهای داینامیک */
|
||||
.floating-particles {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.particle {
|
||||
position: absolute;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 50%;
|
||||
animation: floatParticle 15s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes floatParticle {
|
||||
0% {
|
||||
transform: translateY(100vh) translateX(0) rotate(0deg);
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
90% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-100px) translateX(100px) rotate(360deg);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* رسپانسیو */
|
||||
@media (max-width: 768px) {
|
||||
nav {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.auth-buttons {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
padding: 30px 20px;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.site-title {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.site-title {
|
||||
font-size: 2.8rem;
|
||||
}
|
||||
|
||||
.auth-header h2 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
padding: 14px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* اسکرول بار زیبا */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- گرادیان پسزمینه -->
|
||||
<div class="gradient-bg"></div>
|
||||
|
||||
<!-- ذرات شناور -->
|
||||
<div class="floating-particles" id="particles"></div>
|
||||
|
||||
<!-- نوار ناوبری -->
|
||||
<nav>
|
||||
<div class="auth-buttons">
|
||||
<button class="btn btn-primary" onclick="showLogin()">
|
||||
<i class="fas fa-sign-in-alt"></i>
|
||||
ورود
|
||||
</button>
|
||||
<button class="btn btn-outline" onclick="showRegister()">
|
||||
<i class="fas fa-user-plus"></i>
|
||||
ثبت نام
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- محتوای اصلی -->
|
||||
<main class="main-content">
|
||||
<h1 class="site-title floating">معیارسنج</h1>
|
||||
</main>
|
||||
|
||||
<!-- صفحه ورود -->
|
||||
<div id="login-page" class="auth-page">
|
||||
<div class="auth-card">
|
||||
<button class="close-btn" onclick="hideAuthPages()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
<div class="auth-header">
|
||||
<h2>ورود به سامانه</h2>
|
||||
<p>لطفا اطلاعات حساب کاربری خود را وارد کنید</p>
|
||||
</div>
|
||||
|
||||
<!-- پیامها -->
|
||||
<div id="login-message" class="message"></div>
|
||||
|
||||
<form id="login-form">
|
||||
<div class="form-group">
|
||||
<input type="text" id="login-username" class="form-control" placeholder="نام کاربری" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" id="login-password" class="form-control" placeholder="رمز عبور" required>
|
||||
</div>
|
||||
<button type="submit" class="auth-btn" id="login-submit">
|
||||
<span id="login-text">ورود به سامانه</span>
|
||||
<span id="login-loading" style="display: none;">
|
||||
<i class="fas fa-spinner fa-spin"></i> در حال ورود...
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- صفحه ثبت نام -->
|
||||
<div id="register-page" class="auth-page">
|
||||
<div class="auth-card">
|
||||
<button class="close-btn" onclick="hideAuthPages()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
<div class="auth-header">
|
||||
<h2>ثبت نام در سامانه</h2>
|
||||
<p>فرم زیر را برای ایجاد حساب کاربری تکمیل کنید</p>
|
||||
</div>
|
||||
|
||||
<!-- پیامها -->
|
||||
<div id="register-message" class="message"></div>
|
||||
|
||||
<form id="register-form">
|
||||
<div class="form-group">
|
||||
<input type="text" id="register-username" class="form-control" placeholder="نام کاربری" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="email" id="register-email" class="form-control" placeholder="ایمیل" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" id="register-password" class="form-control" placeholder="رمز عبور" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" id="register-confirm-password" class="form-control"
|
||||
placeholder="تکرار رمز عبور" required>
|
||||
</div>
|
||||
<button type="submit" class="auth-btn" id="register-submit">
|
||||
<span id="register-text">ثبت نام در سامانه</span>
|
||||
<span id="register-loading" style="display: none;">
|
||||
<i class="fas fa-spinner fa-spin"></i> در حال ثبت نام...
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ایجاد ذرات شناور
|
||||
function createParticles() {
|
||||
const particlesContainer = document.getElementById('particles');
|
||||
const particleCount = 20;
|
||||
|
||||
for (let i = 0; i < particleCount; i++) {
|
||||
const particle = document.createElement('div');
|
||||
particle.classList.add('particle');
|
||||
|
||||
// اندازه تصادفی
|
||||
const size = Math.random() * 60 + 10;
|
||||
particle.style.width = `${size}px`;
|
||||
particle.style.height = `${size}px`;
|
||||
|
||||
// موقعیت تصادفی
|
||||
particle.style.left = `${Math.random() * 100}%`;
|
||||
particle.style.top = `${Math.random() * 100}%`;
|
||||
|
||||
// تاخیر و مدت زمان انیمیشن تصادفی
|
||||
const delay = Math.random() * 5;
|
||||
const duration = 15 + Math.random() * 10;
|
||||
particle.style.animationDelay = `${delay}s`;
|
||||
particle.style.animationDuration = `${duration}s`;
|
||||
|
||||
particlesContainer.appendChild(particle);
|
||||
}
|
||||
}
|
||||
|
||||
// فراخوانی پس از لود صفحه
|
||||
document.addEventListener('DOMContentLoaded', createParticles);
|
||||
|
||||
// توابع نمایش/پنهان کردن صفحات
|
||||
function showLogin() {
|
||||
document.getElementById('login-page').classList.add('active');
|
||||
document.getElementById('login-form').reset();
|
||||
hideMessage('login-message');
|
||||
}
|
||||
|
||||
function showRegister() {
|
||||
document.getElementById('register-page').classList.add('active');
|
||||
document.getElementById('register-form').reset();
|
||||
hideMessage('register-message');
|
||||
}
|
||||
|
||||
function hideAuthPages() {
|
||||
document.querySelectorAll('.auth-page').forEach(page => {
|
||||
page.classList.remove('active');
|
||||
});
|
||||
}
|
||||
|
||||
// بستن صفحات با کلیک خارج از کارت
|
||||
document.querySelectorAll('.auth-page').forEach(page => {
|
||||
page.addEventListener('click', function (e) {
|
||||
if (e.target === this) {
|
||||
hideAuthPages();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// مدیریت فرم لاگین
|
||||
document.getElementById('login-form').addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
loginUser();
|
||||
});
|
||||
|
||||
// مدیریت فرم ثبت نام
|
||||
document.getElementById('register-form').addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
registerUser();
|
||||
});
|
||||
|
||||
// تابع نمایش پیام
|
||||
function showMessage(elementId, message, type) {
|
||||
const messageElement = document.getElementById(elementId);
|
||||
messageElement.textContent = message;
|
||||
messageElement.className = `message ${type}`;
|
||||
messageElement.style.display = 'block';
|
||||
}
|
||||
|
||||
// تابع پنهان کردن پیام
|
||||
function hideMessage(elementId) {
|
||||
const messageElement = document.getElementById(elementId);
|
||||
messageElement.style.display = 'none';
|
||||
}
|
||||
|
||||
async function loginUser() {
|
||||
const username = document.getElementById('login-username').value;
|
||||
const password = document.getElementById('login-password').value;
|
||||
|
||||
if (!username || !password) {
|
||||
showMessage('login-message', 'لطفا تمام فیلدها را پر کنید', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('login-text').style.display = 'none';
|
||||
document.getElementById('login-loading').style.display = 'inline';
|
||||
document.getElementById('login-submit').classList.add('loading');
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('username', username);
|
||||
formData.append('password', password);
|
||||
|
||||
const response = await fetch('/auth/login', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (response.redirected) {
|
||||
window.location.href = response.url;
|
||||
} else {
|
||||
const html = await response.text();
|
||||
showMessage('login-message', 'نام کاربری یا رمز عبور اشتباه است', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
showMessage('login-message', 'خطا در ارتباط با سرور', 'error');
|
||||
} finally {
|
||||
document.getElementById('login-text').style.display = 'inline';
|
||||
document.getElementById('login-loading').style.display = 'none';
|
||||
document.getElementById('login-submit').classList.remove('loading');
|
||||
}
|
||||
}
|
||||
|
||||
async function registerUser() {
|
||||
const username = document.getElementById('register-username').value;
|
||||
const email = document.getElementById('register-email').value;
|
||||
const password = document.getElementById('register-password').value;
|
||||
const confirmPassword = document.getElementById('register-confirm-password').value;
|
||||
|
||||
if (!username || !email || !password || !confirmPassword) {
|
||||
showMessage('register-message', 'لطفا تمام فیلدها را پر کنید', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
showMessage('register-message', 'رمز عبور و تکرار آن مطابقت ندارند', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
showMessage('register-message', 'رمز عبور باید حداقل 6 کاراکتر باشد', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
showMessage('register-message', 'لطفا یک ایمیل معتبر وارد کنید', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('register-text').style.display = 'none';
|
||||
document.getElementById('register-loading').style.display = 'inline';
|
||||
document.getElementById('register-submit').classList.add('loading');
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('username', username);
|
||||
formData.append('email', email);
|
||||
formData.append('password', password);
|
||||
|
||||
const response = await fetch('/auth/register', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const responseText = await response.text();
|
||||
|
||||
let responseData;
|
||||
try {
|
||||
responseData = JSON.parse(responseText);
|
||||
} catch (e) {
|
||||
console.log('Response is not JSON:', responseText);
|
||||
if (response.ok && (responseText.includes('success') || responseText.includes('موفق'))) {
|
||||
showMessage('register-message', 'ثبت نام با موفقیت انجام شد!', 'success');
|
||||
setTimeout(() => {
|
||||
hideAuthPages();
|
||||
showLogin();
|
||||
}, 2000);
|
||||
} else {
|
||||
showMessage('register-message', 'پاسخ غیرمنتظره از سرور', 'error');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
if (responseData.success) {
|
||||
showMessage('register-message', 'ثبت نام با موفقیت انجام شد!', 'success');
|
||||
setTimeout(() => {
|
||||
hideAuthPages();
|
||||
showLogin();
|
||||
}, 2000);
|
||||
} else {
|
||||
showMessage('register-message', responseData.message || 'خطا در ثبت نام', 'error');
|
||||
}
|
||||
} else {
|
||||
let errorMessage = 'خطا در ثبت نام';
|
||||
if (response.status === 400) {
|
||||
errorMessage = 'اطلاعات وارد شده معتبر نیست';
|
||||
} else if (response.status === 409) {
|
||||
errorMessage = 'نام کاربری یا ایمیل قبلاً استفاده شده است';
|
||||
} else if (response.status === 500) {
|
||||
errorMessage = 'خطای سرور لطفاً بعداً تلاش کنید';
|
||||
}
|
||||
showMessage('register-message', responseData?.message || errorMessage, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Network error:', error);
|
||||
showMessage('register-message', 'خطا در ارتباط با سرور', 'error');
|
||||
} finally {
|
||||
document.getElementById('register-text').style.display = 'inline';
|
||||
document.getElementById('register-loading').style.display = 'none';
|
||||
document.getElementById('register-submit').classList.remove('loading');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,615 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>معیارسنج</title>
|
||||
<link rel="stylesheet" href="/static/all.min.css">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Vazirmatn', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background: url('static/images/bg.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-attachment: fixed;
|
||||
color: white;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* افکتهای گرادیان پویا */
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background:
|
||||
radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 25%),
|
||||
radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.2) 0%, transparent 25%),
|
||||
radial-gradient(circle at 40% 40%, rgba(120, 219, 255, 0.2) 0%, transparent 25%);
|
||||
z-index: -1;
|
||||
animation: gradientShift 15s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes gradientShift {
|
||||
0% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
/* نوار ناوبری */
|
||||
nav {
|
||||
padding: 1.5rem 2rem;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.auth-buttons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 30px;
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s;
|
||||
font-size: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: rgba(207, 206, 206, 0);
|
||||
color: rgb(255, 255, 255);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
color: white;
|
||||
border: 2px solid rgba(255, 255, 255, 0.308);
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* بخش اصلی با نام سامانه */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.site-title {
|
||||
font-size: 5rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: -2px;
|
||||
line-height: 1.1;
|
||||
text-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||
background: linear-gradient(to right, #fff, #f0f0f0);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
animation: titleGlow 3s ease-in-out infinite alternate;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@keyframes titleGlow {
|
||||
from {
|
||||
text-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
to {
|
||||
text-shadow: 0 5px 25px rgba(0, 0, 0, 0.4), 0 0 30px rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* صفحات ورود و ثبت نام */
|
||||
.auth-page {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 100;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.auth-page.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(15px);
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
width: 90%;
|
||||
max-width: 450px;
|
||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
transform: translateY(20px);
|
||||
transition: transform 0.4s;
|
||||
}
|
||||
|
||||
.auth-page.active .auth-card {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.auth-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.auth-header h2 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 15px 20px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.form-control::placeholder {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.auth-btn {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.auth-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.auth-btn.loading {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.floating {
|
||||
animation: floating 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes floating {
|
||||
0% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
|
||||
/* استایل برای پیامها */
|
||||
.message {
|
||||
padding: 10px 15px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.error {
|
||||
background: rgba(231, 76, 60, 0.2);
|
||||
border: 1px solid rgba(231, 76, 60, 0.5);
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
.success {
|
||||
background: rgba(46, 204, 113, 0.2);
|
||||
border: 1px solid rgba(46, 204, 113, 0.5);
|
||||
color: #2ecc71;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.site-title {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 10px 20px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.site-title {
|
||||
font-size: 2.8rem;
|
||||
}
|
||||
|
||||
.auth-buttons {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
padding: 30px 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- نوار ناوبری -->
|
||||
<nav>
|
||||
<div class="auth-buttons">
|
||||
<button class="btn btn-primary" onclick="showLogin()">
|
||||
<i class="fas fa-sign-in-alt"></i>
|
||||
ورود
|
||||
</button>
|
||||
<button class="btn btn-outline" onclick="showRegister()">
|
||||
<i class="fas fa-user-plus"></i>
|
||||
ثبت نام
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- محتوای اصلی -->
|
||||
<main class="main-content">
|
||||
<h1 class="site-title floating">معیارسنج</h1>
|
||||
</main>
|
||||
|
||||
<!-- صفحه ورود -->
|
||||
<div id="login-page" class="auth-page">
|
||||
<div class="auth-card">
|
||||
<button class="close-btn" onclick="hideAuthPages()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
<div class="auth-header">
|
||||
<h2>ورود به سامانه</h2>
|
||||
<p>لطفا اطلاعات حساب کاربری خود را وارد کنید</p>
|
||||
</div>
|
||||
|
||||
<!-- پیامها -->
|
||||
<div id="login-message" class="message"></div>
|
||||
|
||||
<form id="login-form">
|
||||
<div class="form-group">
|
||||
<input type="text" id="login-username" class="form-control" placeholder="نام کاربری" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" id="login-password" class="form-control" placeholder="رمز عبور" required>
|
||||
</div>
|
||||
<button type="submit" class="auth-btn" id="login-submit">
|
||||
<span id="login-text">ورود به سامانه</span>
|
||||
<span id="login-loading" style="display: none;">
|
||||
<i class="fas fa-spinner fa-spin"></i> در حال ورود...
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- صفحه ثبت نام -->
|
||||
<div id="register-page" class="auth-page">
|
||||
<div class="auth-card">
|
||||
<button class="close-btn" onclick="hideAuthPages()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
<div class="auth-header">
|
||||
<h2>ثبت نام در سامانه</h2>
|
||||
<p>فرم زیر را برای ایجاد حساب کاربری تکمیل کنید</p>
|
||||
</div>
|
||||
|
||||
<!-- پیامها -->
|
||||
<div id="register-message" class="message"></div>
|
||||
|
||||
<form id="register-form">
|
||||
<div class="form-group">
|
||||
<input type="text" id="register-username" class="form-control" placeholder="نام کاربری" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="email" id="register-email" class="form-control" placeholder="ایمیل" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" id="register-password" class="form-control" placeholder="رمز عبور" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" id="register-confirm-password" class="form-control"
|
||||
placeholder="تکرار رمز عبور" required>
|
||||
</div>
|
||||
<button type="submit" class="auth-btn" id="register-submit">
|
||||
<span id="register-text">ثبت نام در سامانه</span>
|
||||
<span id="register-loading" style="display: none;">
|
||||
<i class="fas fa-spinner fa-spin"></i> در حال ثبت نام...
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// توابع نمایش/پنهان کردن صفحات
|
||||
function showLogin() {
|
||||
document.getElementById('login-page').classList.add('active');
|
||||
// پاک کردن فرم و پیامها
|
||||
document.getElementById('login-form').reset();
|
||||
hideMessage('login-message');
|
||||
}
|
||||
|
||||
function showRegister() {
|
||||
document.getElementById('register-page').classList.add('active');
|
||||
// پاک کردن فرم و پیامها
|
||||
document.getElementById('register-form').reset();
|
||||
hideMessage('register-message');
|
||||
}
|
||||
|
||||
function hideAuthPages() {
|
||||
document.querySelectorAll('.auth-page').forEach(page => {
|
||||
page.classList.remove('active');
|
||||
});
|
||||
}
|
||||
|
||||
// بستن صفحات با کلیک خارج از کارت
|
||||
document.querySelectorAll('.auth-page').forEach(page => {
|
||||
page.addEventListener('click', function (e) {
|
||||
if (e.target === this) {
|
||||
hideAuthPages();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// مدیریت فرم لاگین
|
||||
document.getElementById('login-form').addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
loginUser();
|
||||
});
|
||||
|
||||
// مدیریت فرم ثبت نام
|
||||
document.getElementById('register-form').addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
registerUser();
|
||||
});
|
||||
|
||||
// تابع نمایش پیام
|
||||
function showMessage(elementId, message, type) {
|
||||
const messageElement = document.getElementById(elementId);
|
||||
messageElement.textContent = message;
|
||||
messageElement.className = `message ${type}`;
|
||||
messageElement.style.display = 'block';
|
||||
}
|
||||
|
||||
// تابع پنهان کردن پیام
|
||||
function hideMessage(elementId) {
|
||||
const messageElement = document.getElementById(elementId);
|
||||
messageElement.style.display = 'none';
|
||||
}
|
||||
|
||||
async function loginUser() {
|
||||
const username = document.getElementById('login-username').value;
|
||||
const password = document.getElementById('login-password').value;
|
||||
|
||||
if (!username || !password) {
|
||||
showMessage('login-message', 'لطفا تمام فیلدها را پر کنید', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('login-text').style.display = 'none';
|
||||
document.getElementById('login-loading').style.display = 'inline';
|
||||
document.getElementById('login-submit').classList.add('loading');
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('username', username);
|
||||
formData.append('password', password);
|
||||
|
||||
const response = await fetch('/auth/login', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
console.log(response)
|
||||
if (response.redirected) {
|
||||
window.location.href = response.url;
|
||||
} else {
|
||||
const html = await response.text();
|
||||
showMessage('login-message', 'نام کاربری یا رمز عبور اشتباه است', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
showMessage('login-message', 'خطا در ارتباط با سرور', 'error');
|
||||
} finally {
|
||||
document.getElementById('login-text').style.display = 'inline';
|
||||
document.getElementById('login-loading').style.display = 'none';
|
||||
document.getElementById('login-submit').classList.remove('loading');
|
||||
}
|
||||
}
|
||||
|
||||
async function registerUser() {
|
||||
const username = document.getElementById('register-username').value;
|
||||
const email = document.getElementById('register-email').value;
|
||||
const password = document.getElementById('register-password').value;
|
||||
const confirmPassword = document.getElementById('register-confirm-password').value;
|
||||
|
||||
// Fixed validation - removed fullname reference
|
||||
if (!username || !email || !password || !confirmPassword) {
|
||||
showMessage('register-message', 'لطفا تمام فیلدها را پر کنید', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
showMessage('register-message', 'رمز عبور و تکرار آن مطابقت ندارند', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
showMessage('register-message', 'رمز عبور باید حداقل 6 کاراکتر باشد', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Email validation
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
showMessage('register-message', 'لطفا یک ایمیل معتبر وارد کنید', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('register-text').style.display = 'none';
|
||||
document.getElementById('register-loading').style.display = 'inline';
|
||||
document.getElementById('register-submit').classList.add('loading');
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('username', username);
|
||||
formData.append('email', email);
|
||||
formData.append('password', password);
|
||||
|
||||
const response = await fetch('/auth/register', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
// Read the response once and store it
|
||||
const responseText = await response.text();
|
||||
|
||||
let responseData;
|
||||
try {
|
||||
responseData = JSON.parse(responseText);
|
||||
} catch (e) {
|
||||
console.log('Response is not JSON:', responseText);
|
||||
// If it's not JSON, check if it's HTML or other content
|
||||
if (response.ok && responseText.includes('success') || responseText.includes('موفق')) {
|
||||
showMessage('register-message', 'ثبت نام با موفقیت انجام شد!', 'success');
|
||||
setTimeout(() => {
|
||||
hideAuthPages();
|
||||
showLogin();
|
||||
}, 2000);
|
||||
} else {
|
||||
showMessage('register-message', 'پاسخ غیرمنتظره از سرور', 'error');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Now handle the parsed JSON data
|
||||
if (response.ok) {
|
||||
if (responseData.success) {
|
||||
showMessage('register-message', 'ثبت نام با موفقیت انجام شد!', 'success');
|
||||
|
||||
setTimeout(() => {
|
||||
hideAuthPages();
|
||||
showLogin();
|
||||
}, 2000);
|
||||
} else {
|
||||
showMessage('register-message', responseData.message || 'خطا در ثبت نام', 'error');
|
||||
}
|
||||
} else {
|
||||
// Handle different HTTP status codes
|
||||
let errorMessage = 'خطا در ثبت نام';
|
||||
|
||||
if (response.status === 400) {
|
||||
errorMessage = 'اطلاعات وارد شده معتبر نیست';
|
||||
} else if (response.status === 409) {
|
||||
errorMessage = 'نام کاربری یا ایمیل قبلاً استفاده شده است';
|
||||
} else if (response.status === 500) {
|
||||
errorMessage = 'خطای سرور لطفاً بعداً تلاش کنید';
|
||||
}
|
||||
|
||||
showMessage('register-message', responseData?.message || errorMessage, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Network error:', error);
|
||||
showMessage('register-message', 'خطا در ارتباط با سرور', 'error');
|
||||
} finally {
|
||||
document.getElementById('register-text').style.display = 'inline';
|
||||
document.getElementById('register-loading').style.display = 'none';
|
||||
document.getElementById('register-submit').classList.remove('loading');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user