chore: 更新项目文档与忽略规则,移除旧镜像归档跟踪

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-11 15:03:20 +08:00
co-authored by Claude
parent 4ef15545e3
commit 3d3eeea1f7
6 changed files with 169 additions and 7 deletions
+13 -4
View File
@@ -8,6 +8,7 @@ instance/
.pytest_cache/
.mypy_cache/
user_input/
# Note: user_input/广寒计划/Timeline.md is intentionally tracked (mission timeline source)
# Temp files
~$*
@@ -20,19 +21,27 @@ scripts/update_vulture_wiki.py
# Reference docs & reports
docs/
# Tests (E2E/screenshot-based)
tests/
# Local E2E tests remain ignored; keep new regression coverage trackable.
tests/*
!tests/test_first_stage_fixes.py
# Demo screenshots
demo/test_screenshots/
# Playwright output and logs
output/playwright/
# Generated output: deployment bundles, screenshot caches, Playwright logs
output/
.playwright-cli/
.superpowers/
# KSP screenshots and upload cache
data/KSP/
# Docker image archives (rebuilt per release; deploy docs are the source of truth)
docker_image/*.tar
docker_image/*.tar.gz
# Verification screenshots
artifacts/
# Misc
test1
+12
View File
@@ -43,6 +43,18 @@ python scripts/wiki_update_page.py --path "home/Space_Shuttles/Vulture_Shuttle"
Add `--create-if-missing` for new pages. HTML files use `<style>` blocks (extracted to page CSS) and wiki-infobox table patterns. Images use `<div class="image-placeholder">` when the actual render is not yet available.
### Mission gallery release gate (mandatory)
- Mission gallery interaction is provided only by `scripts/wiki_mission_gallery_runtime.js`, published with `scripts/wiki_publish_mission_gallery_runtime.py`. Do not put gallery JavaScript in page `scriptJs` or inline `<script>` blocks.
- After any gallery content, CSS, runtime, or publishing change, run both commands below. A gallery release is incomplete if either command fails or if screenshots were not visually inspected:
```powershell
py -3.12 -m pytest tests/test_mission_galleries.py -q -p no:cacheprovider
py -3.12 scripts/wiki_verify_mission_galleries.py --screenshot-dir output/playwright/mission-gallery-e2e
```
- The E2E gate must cover every mission gallery, not a sample. See `docs/wiki-mission-gallery-quality-gate.md` for the incident root cause and checklist.
## Database
- PostgreSQL is authoritative for mission logs and timelines
Binary file not shown.
Binary file not shown.
+4 -3
View File
@@ -14,9 +14,10 @@ MOON_PERIOD = timedelta(days=27.32166) # 月球自转周期
TRANSIT_DAYS = timedelta(days=5) # 地月转移耗时
# 参考着陆窗口(UTC)—— 发射窗口 = 着陆窗口 − 5 天地月转移
# 基于 2024 年 10 月精确窗口校准
REF_LANDING_A = datetime(2024, 10, 21, 4, 55, tzinfo=timezone.utc)
REF_LANDING_B = datetime(2024, 10, 29, 3, 15, tzinfo=timezone.utc)
# A窗: 2025-05-14 04:09 着陆 校准 (k=8)
# B窗: 2025-03-15 02:22 着陆 校准 (k=5)
REF_LANDING_A = datetime(2024, 10, 7, 14, 23, tzinfo=timezone.utc)
REF_LANDING_B = datetime(2024, 10, 29, 11, 46, tzinfo=timezone.utc)
REF_LAUNCH_A = REF_LANDING_A - TRANSIT_DAYS
REF_LAUNCH_B = REF_LANDING_B - TRANSIT_DAYS
+140
View File
@@ -0,0 +1,140 @@
from __future__ import annotations
import tempfile
import unittest
from datetime import datetime, timezone
from pathlib import Path
from openpyxl import Workbook
from app import create_app
from app.config import Config
from app.extensions import db
from app.models import Asset, AssetLogEntry, SimulationSetting
from app.services.log_book_importer import import_log_book_data
class TestConfig(Config):
TESTING = True
SECRET_KEY = "test"
SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"
class FirstStageFixTests(unittest.TestCase):
def setUp(self) -> None:
self.app = create_app(TestConfig)
self.client = self.app.test_client()
with self.app.app_context():
db.create_all()
def tearDown(self) -> None:
with self.app.app_context():
db.session.remove()
db.drop_all()
def test_log_book_import_uses_state_nodes_and_imports_sub_log_events(self) -> None:
workbook = Workbook()
sheet = workbook.active
sheet.title = "ST-01"
sheet.append(["Log", "Start", "End", "Detail", "Sub log", None, None, "Location"])
sheet.append(
[
"Mars transfer",
datetime(2060, 1, 1),
datetime(2060, 1, 10),
"Outbound mission",
"2060-01-02 Reached Mars orbit",
None,
None,
"LEO",
]
)
with tempfile.TemporaryDirectory() as temp_dir:
path = Path(temp_dir) / "log_book.xlsx"
workbook.save(path)
with self.app.app_context():
summary = import_log_book_data(path)
entries = db.session.query(AssetLogEntry).order_by(AssetLogEntry.start_at).all()
self.assertEqual(summary.imported_assets[0].state_entries, 1)
self.assertEqual(summary.imported_assets[0].event_entries, 1)
self.assertEqual([entry.entry_kind for entry in entries], ["state", "event"])
self.assertEqual(entries[0].state_nodes[0].target_location, "LEO")
self.assertEqual(entries[0].state_nodes[0].state_label, "Mars transfer")
self.assertEqual(entries[1].state_nodes[0].target_location, "Mars")
def test_dashboard_attributes_only_events_within_thirty_days(self) -> None:
with self.app.app_context():
db.session.add(SimulationSetting(key="current_time", value="2060-03-12T09:00"))
alpha = Asset(name="Alpha", asset_type="Vehicle")
beta = Asset(name="Beta", asset_type="Vehicle")
alpha.log_entries.extend(
[
AssetLogEntry(
entry_kind="event",
title="Alpha event",
start_at=datetime(2060, 3, 20, tzinfo=timezone.utc),
),
AssetLogEntry(
entry_kind="event",
title="Too far away",
start_at=datetime(2060, 5, 1, tzinfo=timezone.utc),
),
]
)
beta.log_entries.append(
AssetLogEntry(
entry_kind="event",
title="Beta event",
start_at=datetime(2060, 3, 21, tzinfo=timezone.utc),
)
)
db.session.add_all([alpha, beta])
db.session.commit()
response = self.client.get("/")
page = response.get_data(as_text=True)
self.assertEqual(response.status_code, 200)
self.assertIn("Alpha: Alpha event", page)
self.assertIn("Beta: Beta event", page)
self.assertNotIn("Beta: Alpha event", page)
self.assertNotIn("Too far away", page)
def test_simulation_time_endpoint_persists_through_model(self) -> None:
response = self.client.post(
"/api/v1/sim-time",
json={"sim_time": "2061-04-05T06:07"},
)
self.assertEqual(response.status_code, 200)
with self.app.app_context():
setting = db.session.get(SimulationSetting, "current_time")
self.assertIsNotNone(setting)
self.assertEqual(setting.value, "2061-04-05T06:07")
def test_location_and_timeline_json_escape_script_terminators(self) -> None:
payload = "Probe </script><script>alert(1)</script>"
with self.app.app_context():
asset = Asset(name=payload, asset_type="Station", home_region="LEO")
asset.log_entries.append(
AssetLogEntry(
entry_kind="event",
title=payload,
start_at=datetime(2060, 3, 1, tzinfo=timezone.utc),
)
)
db.session.add(asset)
db.session.commit()
for path in ("/mission-preview/location-board", "/mission-preview/timeline"):
response = self.client.get(path)
page = response.get_data(as_text=True)
self.assertEqual(response.status_code, 200)
self.assertNotIn(payload, page)
self.assertIn("\\u003c/script\\u003e", page)
if __name__ == "__main__":
unittest.main()