359 lines
13 KiB
Python
359 lines
13 KiB
Python
"""把现站公开摘录写入「关于协会」相关模块,供测试环境查看。
|
||
|
||
数据来自仓库 docs/参考/现站/基础数据。重复执行会清掉关于协会单篇、专家委员会
|
||
与三类栏目文章后重写。口令只从环境变量读取。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from html import escape, unescape
|
||
from pathlib import Path
|
||
|
||
from sqlalchemy import delete, select
|
||
|
||
from app.db import Base, SessionLocal, get_engine
|
||
from app.models.about import (
|
||
AboutExpert,
|
||
AboutExpertAttachment,
|
||
AboutPage,
|
||
AboutPageAttachment,
|
||
create_expert,
|
||
save_about_page,
|
||
)
|
||
from app.models.content import AdminFile
|
||
from app.models.home import (
|
||
HomeArticle,
|
||
HomeArticleAttachment,
|
||
create_article,
|
||
)
|
||
|
||
ABOUT_COLUMN_CATEGORIES = ("annual_reports", "work_briefs", "qualifications")
|
||
from app.services.storage import ensure_buckets, make_object_key, upload_object
|
||
|
||
ROOT = Path(__file__).resolve().parents[3]
|
||
SOURCE = ROOT / "docs" / "参考" / "现站" / "基础数据"
|
||
ABOUT_DIR = SOURCE / "协会介绍"
|
||
NEWS = SOURCE / "新闻动态"
|
||
CONTACT = SOURCE / "联系我们" / "contact.json"
|
||
|
||
|
||
def _paragraphs_from_lines(lines: list[str]) -> str:
|
||
parts = []
|
||
for line in lines:
|
||
line = line.strip()
|
||
if not line:
|
||
continue
|
||
parts.append(f"<p>{escape(line)}</p>")
|
||
return "".join(parts)
|
||
|
||
|
||
def _paragraphs(path: Path) -> str:
|
||
text = unescape(path.read_text(encoding="utf-8"))
|
||
return _paragraphs_from_lines(text.splitlines())
|
||
|
||
|
||
def _content_type(path: Path) -> str:
|
||
suffix = path.suffix.lower()
|
||
if suffix == ".png":
|
||
return "image/png"
|
||
if suffix == ".webp":
|
||
return "image/webp"
|
||
if suffix == ".gif":
|
||
return "image/gif"
|
||
if suffix == ".pdf":
|
||
return "application/pdf"
|
||
return "image/jpeg"
|
||
|
||
|
||
def _store_public(db, path: Path, filename: str | None = None) -> AdminFile:
|
||
name = filename or f"about-sample-{path.name}"
|
||
row = db.scalar(
|
||
select(AdminFile).where(AdminFile.bucket == "public", AdminFile.filename == name)
|
||
)
|
||
if row is not None:
|
||
return row
|
||
data = path.read_bytes()
|
||
key = make_object_key(name)
|
||
upload_object("public", key, data, _content_type(path))
|
||
row = AdminFile(
|
||
bucket="public",
|
||
object_key=key,
|
||
filename=name,
|
||
content_type=_content_type(path),
|
||
size=len(data),
|
||
created_by=None,
|
||
)
|
||
db.add(row)
|
||
db.commit()
|
||
db.refresh(row)
|
||
return row
|
||
|
||
|
||
def _clear_about(db) -> None:
|
||
article_ids = list(
|
||
db.scalars(
|
||
select(HomeArticle.id).where(HomeArticle.category.in_(ABOUT_COLUMN_CATEGORIES))
|
||
).all()
|
||
)
|
||
if article_ids:
|
||
db.execute(
|
||
delete(HomeArticleAttachment).where(HomeArticleAttachment.article_id.in_(article_ids))
|
||
)
|
||
db.execute(delete(HomeArticle).where(HomeArticle.id.in_(article_ids)))
|
||
db.execute(delete(AboutExpertAttachment))
|
||
db.execute(delete(AboutExpert))
|
||
db.execute(delete(AboutPageAttachment))
|
||
db.execute(delete(AboutPage))
|
||
db.commit()
|
||
|
||
|
||
def _intro_html() -> str:
|
||
page = (ABOUT_DIR / "栏目页.txt").read_text(encoding="utf-8")
|
||
lines = []
|
||
for line in page.splitlines():
|
||
line = line.strip()
|
||
if not line:
|
||
continue
|
||
if line.startswith("来源:") or line in {
|
||
"计时中",
|
||
"首页",
|
||
"协会介绍",
|
||
"新闻中心",
|
||
"通知公告",
|
||
"新闻动态",
|
||
"政策法规",
|
||
"执业注册",
|
||
"继续教育",
|
||
"机构备案",
|
||
"会员服务",
|
||
"案例中心",
|
||
"联系我们",
|
||
"关于我们",
|
||
"当前位置 :",
|
||
"地址:",
|
||
"电话:",
|
||
"手机:",
|
||
"邮箱:",
|
||
"版权所有:",
|
||
"甘肃省房地产价格评估行业协会",
|
||
}:
|
||
continue
|
||
if line.startswith("|||") or line in {"넳", "넲", "끠", "搜索"}:
|
||
continue
|
||
if line.startswith("首页 ") or "甘肃省兰州市" in line and len(line) < 40:
|
||
if line.startswith("甘肃省兰州市") or line.startswith("0931") or "@" in line:
|
||
continue
|
||
continue
|
||
if line.startswith("甘肃省房地产价格评估行业协会(以下简称我会)"):
|
||
lines.append(line)
|
||
elif line.startswith("我会"):
|
||
lines.append(line)
|
||
lines.append("整理自现站公开页面:http://www.gsrea.org.cn/xhjs")
|
||
return _paragraphs_from_lines(lines)
|
||
|
||
|
||
def _org_html() -> str:
|
||
lines = [
|
||
"我会日常办事机构为协会秘书处,秘书处下设综合办公室,在理事会领导下负责处理日常事务。",
|
||
"理事会共有理事若干人,会员单位若干家(以现站公开信息为准)。",
|
||
"协会下设五个专业委员会:",
|
||
"一、专家委员会:负责重大疑难估价技术咨询与行业标准解读。",
|
||
"二、自律委员会:围绕投诉争议、执业风险提示与行业诚信建设开展工作。",
|
||
"三、技术委员会:组织估价技术研讨、典型案例交流与报告质量提升。",
|
||
"四、培训交流委员会:衔接继续教育、培训计划与对外交流。",
|
||
"五、信息化建设委员会:推进行业信息化与资料库、案例库建设。",
|
||
"整理自现站协会介绍:http://www.gsrea.org.cn/xhjs",
|
||
]
|
||
return _paragraphs_from_lines(lines)
|
||
|
||
|
||
def _contact_html() -> str:
|
||
import json
|
||
|
||
contact = json.loads(CONTACT.read_text(encoding="utf-8")) if CONTACT.is_file() else {}
|
||
address = contact.get("address") or "甘肃省兰州市城关区张掖路63号"
|
||
phone = contact.get("phone") or "0931-8126195"
|
||
email = contact.get("email") or "gsrea2018@163.com"
|
||
lines = [
|
||
"来访路线 / 电话联系方式",
|
||
f"办公地址:{address}",
|
||
f"电话:{phone}",
|
||
f"邮箱:{email}",
|
||
"来访请先电话预约。公交与停车指引可现场或电话咨询秘书处。",
|
||
"合作洽谈",
|
||
"结构合作、学术合作、商务合作意向,请将单位介绍与合作方向发送至协会邮箱,秘书处将协调对接。",
|
||
"加入协会指南",
|
||
"我会的会员分为个人会员和单位会员。个人会员分为房地产估价师执业会员、房地产价格评估经纪人执业会员;单位会员分为团体会员、理事单位会员和会长(秘书长)单位会员。",
|
||
"凡承认《甘肃省房地产价格评估行业协会章程》、具备会员条件的个人和单位均可申请入会。入会一般程序:提交入会申请书 → 理事会讨论通过 → 颁发会员证。",
|
||
"会费标准与管理办法以协会现行文件为准,正式文本可在会员服务栏目或向秘书处索取。",
|
||
f"整理自现站联系我们:{contact.get('url') or 'http://www.gsrea.org.cn/lxwm'}",
|
||
]
|
||
return _paragraphs_from_lines(lines)
|
||
|
||
|
||
def _news_article(article_id: int) -> tuple[str, str]:
|
||
folder = NEWS / "文章" / str(article_id)
|
||
meta = __import__("json").loads((folder / "meta.json").read_text(encoding="utf-8"))
|
||
body = _paragraphs(folder / "content.txt")
|
||
body += f"<p>整理自现站公开页面:{escape(meta.get('url') or '')}</p>"
|
||
return meta.get("title") or str(article_id), body
|
||
|
||
|
||
def seed() -> None:
|
||
import app.models.about # noqa: F401
|
||
import app.models.content # noqa: F401
|
||
import app.models.home # noqa: F401
|
||
|
||
ensure_buckets()
|
||
engine = get_engine()
|
||
Base.metadata.create_all(bind=engine)
|
||
db = SessionLocal()
|
||
try:
|
||
_clear_about(db)
|
||
|
||
save_about_page(
|
||
db,
|
||
"intro",
|
||
{"published": True, "bodyHtml": _intro_html(), "attachmentFileIds": []},
|
||
)
|
||
save_about_page(
|
||
db,
|
||
"org",
|
||
{"published": True, "bodyHtml": _org_html(), "attachmentFileIds": []},
|
||
)
|
||
save_about_page(
|
||
db,
|
||
"contact",
|
||
{"published": True, "bodyHtml": _contact_html(), "attachmentFileIds": []},
|
||
)
|
||
|
||
committees = [
|
||
("专家委员会", "负责重大疑难估价技术咨询与行业标准解读,服务会员机构与估价师。", 1),
|
||
("自律委员会", "围绕投诉争议、执业风险提示与行业诚信建设,推动自律公约落地。", 2),
|
||
("技术委员会", "组织估价技术研讨、典型案例交流与报告质量提升活动。", 3),
|
||
("培训交流委员会", "衔接继续教育、培训计划与对外交流,提升从业人员专业水平。", 4),
|
||
("信息化建设委员会", "推进行业信息化、交易案例库与资料库建设。", 5),
|
||
]
|
||
for name, blurb, order in committees:
|
||
create_expert(
|
||
db,
|
||
{
|
||
"name": name,
|
||
"bodyHtml": _paragraphs_from_lines(
|
||
[
|
||
blurb,
|
||
"成员名单与正式任命以协会最新公示为准。",
|
||
"整理自现站协会介绍公开信息。",
|
||
]
|
||
),
|
||
"sortOrder": order,
|
||
"published": True,
|
||
"attachmentFileIds": [],
|
||
},
|
||
)
|
||
|
||
# 年度报告 / 工作计划:理事会工作报告相关现站新闻
|
||
report_title, report_body = _news_article(8560786)
|
||
create_article(
|
||
db,
|
||
{
|
||
"category": "annual_reports",
|
||
"title": report_title[:200],
|
||
"publishedAt": "2025-07-24T16:00:00",
|
||
"published": True,
|
||
"bodyHtml": report_body,
|
||
"imageFileId": None,
|
||
"attachmentFileIds": [],
|
||
},
|
||
)
|
||
create_article(
|
||
db,
|
||
{
|
||
"category": "annual_reports",
|
||
"title": "2018年度工作报告及2019年工作计划(会议公开摘要)",
|
||
"publishedAt": "2019-04-26T16:00:00",
|
||
"published": True,
|
||
"bodyHtml": _paragraphs(
|
||
ABOUT_DIR / "文章" / "1209332" / "content.txt"
|
||
)
|
||
+ "<p>整理自现站:http://www.gsrea.org.cn/newsinfo/1209332.html</p>",
|
||
"imageFileId": None,
|
||
"attachmentFileIds": [],
|
||
},
|
||
)
|
||
|
||
# 工作简报:用现站新闻形态条目
|
||
for article_id, published_at in (
|
||
(8560786, "2025-07-24T16:00:00"),
|
||
(7911765, "2024-12-31T10:00:00"),
|
||
):
|
||
title, body = _news_article(article_id)
|
||
create_article(
|
||
db,
|
||
{
|
||
"category": "work_briefs",
|
||
"title": f"工作简报 · {title[:180]}",
|
||
"publishedAt": published_at,
|
||
"published": True,
|
||
"bodyHtml": body,
|
||
"imageFileId": None,
|
||
"attachmentFileIds": [],
|
||
},
|
||
)
|
||
|
||
# 资质荣誉:喜报 + 表彰图片
|
||
honor_title, honor_body = _news_article(7911765)
|
||
honor_images = []
|
||
for picture in sorted((ABOUT_DIR / "文章" / "1209332" / "images").glob("*.jpg"))[:2]:
|
||
honor_images.append(_store_public(db, picture, f"about-honor-{picture.name}").id)
|
||
image_tags = "".join(
|
||
f'<p><img src="{escape(_public_url(db, file_id))}" alt="协会荣誉示意"></p>'
|
||
for file_id in honor_images
|
||
)
|
||
create_article(
|
||
db,
|
||
{
|
||
"category": "qualifications",
|
||
"title": honor_title[:200],
|
||
"publishedAt": "2024-12-31T10:00:00",
|
||
"published": True,
|
||
"bodyHtml": honor_body + image_tags,
|
||
"imageFileId": honor_images[0] if honor_images else None,
|
||
"attachmentFileIds": honor_images[1:] if len(honor_images) > 1 else [],
|
||
},
|
||
)
|
||
create_article(
|
||
db,
|
||
{
|
||
"category": "qualifications",
|
||
"title": "优秀报告评选表彰与行业交流(公开纪要)",
|
||
"publishedAt": "2019-04-26T12:00:00",
|
||
"published": True,
|
||
"bodyHtml": _paragraphs(ABOUT_DIR / "文章" / "1209332" / "content.txt")
|
||
+ "<p>整理自现站表彰会议公开报道。</p>",
|
||
"imageFileId": honor_images[0] if honor_images else None,
|
||
"attachmentFileIds": [],
|
||
},
|
||
)
|
||
|
||
print(
|
||
"关于协会测试数据已写入:协会简介/组织机构/联系合作各 1 篇,"
|
||
f"专家委员会 {len(committees)} 条,"
|
||
"年度报告/工作计划 2 条,工作简报 2 条,资质荣誉 2 条。"
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
def _public_url(db, file_id: int) -> str:
|
||
from app.services.storage import public_object_url
|
||
|
||
row = db.get(AdminFile, file_id)
|
||
if row is None or not row.object_key:
|
||
return ""
|
||
return public_object_url(row.object_key)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
seed()
|