feat:[FL-211][高程管理扁平化为文件记录]
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -9,7 +9,7 @@ from sqlalchemy.orm import Session, sessionmaker
|
||||
|
||||
from app.core.database import Base
|
||||
from app.models.atp_model import AtpModel, AtpModelVersion, AtpSimulationRun
|
||||
from app.models.elevation import ElevationDataImportJob, ElevationDataset
|
||||
from app.models.elevation import ElevationDataImportJob, ElevationDataset, ElevationFileRecord
|
||||
from app.models.user import User
|
||||
from app.models.wine import WineRun
|
||||
from app.schemas.atp_model import AtpSimulationRunRequest
|
||||
@@ -233,6 +233,53 @@ def test_queue_dataset_terrain_build_reuses_existing_running_task(monkeypatch) -
|
||||
session.close()
|
||||
|
||||
|
||||
def test_file_record_terrain_layer_and_tile_read_from_record_storage(monkeypatch) -> None:
|
||||
testing_session = _build_sessionmaker(ElevationFileRecord.__table__)
|
||||
session: Session = testing_session()
|
||||
try:
|
||||
record = ElevationFileRecord(
|
||||
id="abcdef1234567890abcdef1234567890",
|
||||
file_name="terrain.tif",
|
||||
file_path="/elevation/records/ab/cd/terrain.tif",
|
||||
file_format="tif",
|
||||
file_size=128,
|
||||
mount_code="default",
|
||||
status="active",
|
||||
terrain_status="ready",
|
||||
terrain_root_path="/elevation/terrain/records/ab/cd/abcdef1234567890abcdef1234567890",
|
||||
terrain_url_template="/api/v1/elevation/records/abcdef1234567890abcdef1234567890/terrain/{z}/{x}/{y}.terrain?v=1.0.0",
|
||||
terrain_min_zoom=0,
|
||||
terrain_max_zoom=0,
|
||||
)
|
||||
session.add(record)
|
||||
session.commit()
|
||||
|
||||
driver = _MemoryStorageDriver()
|
||||
layer_payload = b'{"tilejson":"2.1.0","format":"heightmap-1.0","version":"1.0.0","scheme":"tms","projection":"EPSG:4326","tiles":["{z}/{x}/{y}.terrain?v=1.0.0"],"minzoom":0,"maxzoom":0}'
|
||||
driver.write_file(
|
||||
"/elevation/terrain/records/ab/cd/abcdef1234567890abcdef1234567890/layer.json",
|
||||
content=layer_payload,
|
||||
content_type="application/json",
|
||||
)
|
||||
driver.write_file(
|
||||
"/elevation/terrain/records/ab/cd/abcdef1234567890abcdef1234567890/0/0/0.terrain",
|
||||
content=b"tile-bytes",
|
||||
content_type="application/octet-stream",
|
||||
)
|
||||
|
||||
monkeypatch.setattr(elevation_service, "_require_mount", lambda *_args, **_kwargs: SimpleNamespace(code="default"))
|
||||
monkeypatch.setattr(elevation_service, "_build_driver_or_400", lambda *_args, **_kwargs: driver)
|
||||
|
||||
layer = elevation_service.get_file_record_terrain_layer(session, record_id=record.id)
|
||||
tile = elevation_service.get_file_record_terrain_tile(session, record_id=record.id, z=0, x=0, y=0)
|
||||
|
||||
assert layer.maxzoom == 0
|
||||
assert layer.tiles == ["{z}/{x}/{y}.terrain?v=1.0.0"]
|
||||
assert tile == b"tile-bytes"
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
def test_import_dataset_data_files_queue_job_and_worker_keeps_preferred_raster(monkeypatch) -> None:
|
||||
testing_session = _build_sessionmaker(ElevationDataset.__table__, ElevationDataImportJob.__table__)
|
||||
session: Session = testing_session()
|
||||
@@ -296,7 +343,10 @@ def test_import_dataset_data_files_queue_job_and_worker_keeps_preferred_raster(m
|
||||
assert first.job.uploaded_file_count == 1
|
||||
assert first.job.analysis_task_queued is False
|
||||
assert import_calls == [(first.job.id, actor.id)]
|
||||
assert any(path.endswith(".img") and "/.imports/" in path for path in driver.files)
|
||||
saved_pending_job = session.get(ElevationDataImportJob, first.job.id)
|
||||
assert saved_pending_job is not None
|
||||
assert saved_pending_job.staged_files_json[0]["filename"] == "terrain.img"
|
||||
assert "content_base64" in saved_pending_job.staged_files_json[0]
|
||||
|
||||
second = elevation_service.import_dataset_data_files(
|
||||
session,
|
||||
|
||||
@@ -7,7 +7,7 @@ from sqlalchemy.orm import Session, sessionmaker
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.core.database import Base
|
||||
from app.models.elevation import ElevationApplyJob, ElevationDataset
|
||||
from app.models.elevation import ElevationApplyJob, ElevationDataset, ElevationFileRecord
|
||||
from app.models.line import Line
|
||||
from app.models.line_tower import LineTower
|
||||
from app.models.tower_profile import TowerProfile
|
||||
@@ -28,6 +28,7 @@ def _build_session_factory() -> sessionmaker[Session]:
|
||||
LineTower.__table__,
|
||||
TowerProfile.__table__,
|
||||
ElevationDataset.__table__,
|
||||
ElevationFileRecord.__table__,
|
||||
ElevationApplyJob.__table__,
|
||||
],
|
||||
)
|
||||
@@ -81,6 +82,53 @@ def test_create_apply_job_dispatches_actor_user_id(monkeypatch) -> None:
|
||||
session.close()
|
||||
|
||||
|
||||
def test_create_apply_job_accepts_file_record_id(monkeypatch) -> None:
|
||||
testing_session = _build_session_factory()
|
||||
session = testing_session()
|
||||
try:
|
||||
line = Line(code="L-APPLY-003", name="文件记录回填线路", voltage_kv=220, lightning_param_json={})
|
||||
record = ElevationFileRecord(
|
||||
file_name="record.csv",
|
||||
file_path="/elevation/records/ab/cd/record.csv",
|
||||
file_format="csv",
|
||||
file_size=32,
|
||||
mount_code="default",
|
||||
status="active",
|
||||
)
|
||||
session.add_all([line, record])
|
||||
session.flush()
|
||||
session.add(LineTower(line_id=line.id, seq_no=1, tower_no="T1", longitude=120.0, latitude=30.0))
|
||||
session.commit()
|
||||
|
||||
dispatched: dict[str, str | None] = {}
|
||||
|
||||
def _fake_dispatch(*, job_id: str, actor_user_id: str | None) -> SimpleNamespace:
|
||||
dispatched["job_id"] = job_id
|
||||
dispatched["actor_user_id"] = actor_user_id
|
||||
return SimpleNamespace(id="celery-task-file-record")
|
||||
|
||||
monkeypatch.setattr(elevation_service, "_dispatch_elevation_apply_task", _fake_dispatch)
|
||||
monkeypatch.setattr(elevation_service, "_publish_elevation_change", lambda *args, **kwargs: None)
|
||||
|
||||
response = elevation_service.create_apply_job(
|
||||
session,
|
||||
ElevationApplyJobCreateRequest(line_id=line.id, file_record_id=record.id, mode="overwrite_all"),
|
||||
actor=SimpleNamespace(id="tester"),
|
||||
)
|
||||
|
||||
saved_job = session.get(ElevationApplyJob, response.job.id)
|
||||
assert response.queued is True
|
||||
assert dispatched == {"job_id": response.job.id, "actor_user_id": "tester"}
|
||||
assert saved_job is not None
|
||||
assert saved_job.file_record_id == record.id
|
||||
assert saved_job.dataset_id is None
|
||||
assert saved_job.task_id == "celery-task-file-record"
|
||||
assert response.job.file_record_id == record.id
|
||||
assert response.job.file_record_name == "record.csv"
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
def test_execute_apply_job_uses_saved_actor_for_preparation_source(monkeypatch) -> None:
|
||||
testing_session = _build_session_factory()
|
||||
session = testing_session()
|
||||
@@ -160,3 +208,83 @@ def test_execute_apply_job_uses_saved_actor_for_preparation_source(monkeypatch)
|
||||
verification_session.close()
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
def test_execute_apply_job_uses_file_record_source(monkeypatch) -> None:
|
||||
testing_session = _build_session_factory()
|
||||
session = testing_session()
|
||||
try:
|
||||
line = Line(code="L-APPLY-004", name="文件记录高程回填线路", voltage_kv=110, lightning_param_json={})
|
||||
record = ElevationFileRecord(
|
||||
file_name="record.csv",
|
||||
file_path="/elevation/records/ab/cd/record.csv",
|
||||
file_format="csv",
|
||||
file_size=32,
|
||||
mount_code="default",
|
||||
status="active",
|
||||
)
|
||||
session.add_all([line, record])
|
||||
session.flush()
|
||||
|
||||
meter_to_lat = 1 / 111_320.0
|
||||
session.add_all(
|
||||
[
|
||||
LineTower(line_id=line.id, seq_no=1, tower_no="P1", longitude=120.0, latitude=30.0 + 300 * meter_to_lat),
|
||||
LineTower(line_id=line.id, seq_no=2, tower_no="P2", longitude=120.0, latitude=30.0 + 600 * meter_to_lat),
|
||||
LineTower(line_id=line.id, seq_no=3, tower_no="P3", longitude=120.0, latitude=30.0 + 900 * meter_to_lat),
|
||||
]
|
||||
)
|
||||
session.flush()
|
||||
|
||||
job = ElevationApplyJob(
|
||||
line_id=line.id,
|
||||
file_record_id=record.id,
|
||||
mode="overwrite_all",
|
||||
status="pending",
|
||||
total_tower_count=3,
|
||||
create_user="tester",
|
||||
update_user="tester",
|
||||
)
|
||||
session.add(job)
|
||||
session.commit()
|
||||
|
||||
points = [
|
||||
elevation_service.ElevationSamplePoint(
|
||||
lon=120.0,
|
||||
lat=30.0 + distance_m * meter_to_lat,
|
||||
altitude_m=100.0 + distance_m * 0.12,
|
||||
)
|
||||
for distance_m in range(0, 1251, 50)
|
||||
]
|
||||
|
||||
monkeypatch.setattr(elevation_service, "SessionLocal", testing_session)
|
||||
monkeypatch.setattr(elevation_service, "_load_dataset_points", lambda *_args, **_kwargs: (points, []))
|
||||
monkeypatch.setattr(elevation_service, "_publish_elevation_change", lambda *args, **kwargs: None)
|
||||
monkeypatch.setattr(elevation_service, "_publish_line_change", lambda *args, **kwargs: None)
|
||||
|
||||
elevation_service.execute_apply_job(job.id)
|
||||
|
||||
verification_session = testing_session()
|
||||
try:
|
||||
saved_job = verification_session.get(ElevationApplyJob, job.id)
|
||||
saved_line = verification_session.get(Line, line.id)
|
||||
towers = verification_session.execute(
|
||||
select(LineTower).where(LineTower.line_id == line.id).order_by(LineTower.seq_no.asc())
|
||||
).scalars().all()
|
||||
|
||||
assert saved_job is not None
|
||||
assert saved_job.status == "success"
|
||||
assert saved_line is not None
|
||||
assert saved_line.update_user == "tester"
|
||||
assert all(tower.altitude_m is not None for tower in towers)
|
||||
|
||||
source = saved_line.lightning_param_json["preparation_sources"]["ground_slope"]
|
||||
assert source["prepared_by_user_id"] == "tester"
|
||||
assert source["file_record_id"] == record.id
|
||||
assert source["file_record_name"] == "record.csv"
|
||||
assert source["dataset_id"] is None
|
||||
assert source["job_id"] == job.id
|
||||
finally:
|
||||
verification_session.close()
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
@@ -158,7 +158,7 @@ def test_apply_points_to_line_towers_computes_ground_slopes() -> None:
|
||||
stats = elevation_service._apply_points_to_line_towers(
|
||||
session,
|
||||
line_id=line.id,
|
||||
dataset=SimpleNamespace(id="ds-1", code="DEM-001"),
|
||||
elevation_source=SimpleNamespace(id="ds-1", code="DEM-001"),
|
||||
mode="overwrite_all",
|
||||
points=points,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user