Files
gsfdc/server/app/commands/seed_about_sample.py

359 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""把现站公开摘录写入「关于协会」相关模块,供测试环境查看。
数据来自仓库 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()