flightanalysis 0.3.16__tar.gz → 0.3.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/PKG-INFO +3 -3
- flightanalysis-0.3.18/examples/tailslide.py +25 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/pyproject.toml +3 -3
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/analysis/el_analysis.py +15 -0
- flightanalysis-0.3.18/src/flightanalysis/analysis/manoeuvre_analysis/alignment.py +126 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/analysis/manoeuvre_analysis/basic.py +74 -26
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/analysis/manoeuvre_analysis/complete.py +85 -45
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/analysis/manoeuvre_analysis/scored.py +13 -5
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/analysis/sch_analysis.py +41 -16
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/builders/elbuilders.py +11 -1
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/builders/manbuilder.py +17 -12
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/eldef.py +12 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/mandef.py +10 -11
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/manparm.py +3 -3
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/scheddef.py +2 -2
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/elements/__init__.py +2 -1
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/elements/element.py +2 -8
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/elements/line.py +8 -11
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/elements/loop.py +9 -5
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/elements/snap.py +1 -4
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/elements/stall_turn.py +10 -18
- flightanalysis-0.3.18/src/flightanalysis/elements/tail_slide.py +90 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/manoeuvre.py +14 -20
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/box/box.py +8 -8
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/downgrade.py +35 -29
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/measurement.py +6 -10
- flightanalysis-0.3.18/src/flightanalysis/scoring/reffuncs.py +40 -0
- flightanalysis-0.3.18/src/flightanalysis/scoring/results/__init__.py +4 -0
- flightanalysis-0.3.18/src/flightanalysis/scoring/results/dgplot.py +40 -0
- flightanalysis-0.3.18/src/flightanalysis/scoring/results/elements_results.py +67 -0
- flightanalysis-0.3.18/src/flightanalysis/scoring/results/manoeuvre_results.py +84 -0
- flightanalysis-0.3.18/src/flightanalysis/scoring/results/result.py +259 -0
- flightanalysis-0.3.18/src/flightanalysis/scoring/results/results.py +108 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/results.py +96 -60
- flightanalysis-0.3.18/tests/data/00000100.ajson +249252 -0
- flightanalysis-0.3.18/tests/test_box.py +28 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_criteria.py +2 -0
- flightanalysis-0.3.18/tests/test_schedule/test_element/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_schedule/test_element/test_schedule_element_loop.py +16 -26
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_schedule/test_element/test_schedule_element_spin.py +0 -11
- flightanalysis-0.3.18/tests/test_schedule/test_manoeuvre.py +41 -0
- flightanalysis-0.3.16/src/flightanalysis/analysis/manoeuvre_analysis/alignment.py +0 -125
- flightanalysis-0.3.16/src/flightanalysis/scoring/measurements/__init__.py +0 -13
- flightanalysis-0.3.16/src/flightanalysis/scoring/measurements/attitude.py +0 -108
- flightanalysis-0.3.16/src/flightanalysis/scoring/measurements/curvature.py +0 -47
- flightanalysis-0.3.16/src/flightanalysis/scoring/measurements/measurement.py +0 -160
- flightanalysis-0.3.16/src/flightanalysis/scoring/measurements/positioning.py +0 -40
- flightanalysis-0.3.16/src/flightanalysis/scoring/measurements/roll.py +0 -92
- flightanalysis-0.3.16/src/flightanalysis/scoring/measurements/speed.py +0 -25
- flightanalysis-0.3.16/src/flightanalysis/scoring/measurements/stall.py +0 -92
- flightanalysis-0.3.16/src/flightanalysis/scoring/measurements/track.py +0 -150
- flightanalysis-0.3.16/src/flightanalysis/scoring/reffuncs.py +0 -6
- flightanalysis-0.3.16/src/flightanalysis/scoring/selectors.py +0 -118
- flightanalysis-0.3.16/src/flightanalysis/scoring/smoothing.py +0 -102
- flightanalysis-0.3.16/tests/test_box.py +0 -49
- flightanalysis-0.3.16/tests/test_schedule/test_schedule_manoeuvre.py +0 -24
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/.github/workflows/publish_pypi.yml +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/.gitignore +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/LICENSE +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/README.md +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/examples/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/examples/making_elements.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/analysis/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/analysis/manoeuvre_analysis/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/analysis/manoeuvre_analysis/analysis.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/base/ref_funcs.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/base/utils.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/builders/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/builders/example/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/builders/example/schedule_builder.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/collectors.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/dg_applicator.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/manoption.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/operations/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/operations/funopp.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/operations/itemopp.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/operations/mathopp.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/operations/operation.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/definition/operations/sumopp.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/elements/spin.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/py.typed +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/schedule.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/box/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/box/rectangular_box.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/box/triangular_box.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/criteria/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/criteria/criteria.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/criteria/exponential.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/criteria/inter/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/criteria/inter/combination.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/criteria/inter/comparison.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/criteria/intra/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/criteria/intra/bounded.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/criteria/intra/continuous.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/criteria/intra/peak.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/criteria/intra/single.py +0 -0
- /flightanalysis-0.3.16/tests/__init__.py → /flightanalysis-0.3.18/src/flightanalysis/scoring/results/summary.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/scoring/visibility.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/src/flightanalysis/version.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/EmailedBox.f3a +0 -0
- {flightanalysis-0.3.16/tests/test_schedule → flightanalysis-0.3.18/tests}/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/conftest.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/data/manual_F3A_P23.json +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/data/old_json.json +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/data/p23.BIN +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/data/p23_box.f3a +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/data/p23_fc.json +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/data/p23_flight.json +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/data/scored_fcj.json +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/data/unscored_fcj.json +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_operations.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_rfuncs.py +0 -0
- {flightanalysis-0.3.16/tests/test_schedule/test_element → flightanalysis-0.3.18/tests/test_schedule}/__init__.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_schedule/test_element/loop_analysis.json +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_schedule/test_element/p23_th_e0.csv +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_schedule/test_element/p23_th_e0.json +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_schedule/test_element/p23_th_e0_template.csv +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_schedule/test_element/test_schedule_element.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_schedule/test_element/test_schedule_element_line.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_schedule/test_element/test_schedule_element_snap.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/tests/test_schedule/test_element/test_schedule_element_stallturn.py +0 -0
- {flightanalysis-0.3.16 → flightanalysis-0.3.18}/uv.lock +0 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flightanalysis
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.18
|
|
4
4
|
Summary: Python tools for Analysis of Flight Data
|
|
5
5
|
Author-email: Thomas David <thomasdavid0@gmail.com>
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Requires-Python: >=3.12
|
|
8
8
|
Requires-Dist: fastdtw>=0.3.4
|
|
9
|
-
Requires-Dist: flightdata>=0.
|
|
9
|
+
Requires-Dist: flightdata>=0.3.0
|
|
10
10
|
Requires-Dist: joblib>=1.4.2
|
|
11
11
|
Requires-Dist: json-stream>=2.3.2
|
|
12
12
|
Requires-Dist: loguru>=0.7.2
|
|
13
13
|
Requires-Dist: numpy>=2.1.3
|
|
14
14
|
Requires-Dist: pandas>=2.2.3
|
|
15
|
-
Requires-Dist: pfcschemas>=0.1.
|
|
15
|
+
Requires-Dist: pfcschemas>=0.1.10
|
|
16
16
|
Requires-Dist: scipy>=1.14.1
|
|
17
17
|
Requires-Dist: simplejson>=3.19.3
|
|
18
18
|
Provides-Extra: dataflash
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from flightanalysis import Loop, Line, Snap, Elements, Manoeuvre
|
|
2
|
+
import numpy as np
|
|
3
|
+
from flightdata import State
|
|
4
|
+
import geometry as g
|
|
5
|
+
from plotting import plotsec
|
|
6
|
+
|
|
7
|
+
from flightanalysis.elements.tail_slide import TailSlide
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# man = Manoeuvre(Elements([
|
|
11
|
+
# Line('entry_line', 30, 60, 0),
|
|
12
|
+
# Loop('loop', 30, np.pi/2, 50, 0, 0),
|
|
13
|
+
# Line("upline", 30, 100, 0),
|
|
14
|
+
# TailSlide("tslide", -5, np.pi, np.radians(30), np.pi),
|
|
15
|
+
# Line("downline", 30, 100, 0),
|
|
16
|
+
# Loop("loop2", 30, np.pi/2, 50, 0,0 )
|
|
17
|
+
# ]), Line('exit_line', 30, 60, 0))
|
|
18
|
+
#tp = man.create_template(State.from_transform(g.Transformation()))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
tp = TailSlide("tslide", -5, -np.pi, np.radians(30), np.pi).create_template(
|
|
22
|
+
State.from_transform(g.Transformation(g.Euler(0, -np.pi/2, 0)))
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
plotsec(tp, nmodels=10, scale=1).show()
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "flightanalysis"
|
|
3
|
-
version="0.3.
|
|
3
|
+
version="0.3.18"
|
|
4
4
|
description = "Python tools for Analysis of Flight Data"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Thomas David", email = "thomasdavid0@gmail.com" }]
|
|
7
7
|
requires-python = ">=3.12"
|
|
8
8
|
dependencies = [
|
|
9
9
|
"fastdtw>=0.3.4",
|
|
10
|
-
"flightdata>=0.
|
|
10
|
+
"flightdata>=0.3.0",
|
|
11
11
|
"joblib>=1.4.2",
|
|
12
12
|
"json-stream>=2.3.2",
|
|
13
13
|
"loguru>=0.7.2",
|
|
14
14
|
"numpy>=2.1.3",
|
|
15
15
|
"pandas>=2.2.3",
|
|
16
|
-
"pfcschemas>=0.1.
|
|
16
|
+
"pfcschemas>=0.1.10",
|
|
17
17
|
"scipy>=1.14.1",
|
|
18
18
|
"simplejson>=3.19.3",
|
|
19
19
|
]
|
|
@@ -15,6 +15,18 @@ class ElementAnalysis:
|
|
|
15
15
|
tp: State
|
|
16
16
|
ref_frame: g.Transformation
|
|
17
17
|
|
|
18
|
+
def update(self, new_fl: State):
|
|
19
|
+
new_el = self.el.match_intention(self.ref_frame, new_fl)
|
|
20
|
+
new_tp = new_el.create_template(self.tp[0], new_fl.time)
|
|
21
|
+
return ElementAnalysis(
|
|
22
|
+
self.edef,
|
|
23
|
+
self.mps,
|
|
24
|
+
new_el,
|
|
25
|
+
new_fl,
|
|
26
|
+
new_tp,
|
|
27
|
+
self.ref_frame
|
|
28
|
+
)
|
|
29
|
+
|
|
18
30
|
def plot_3d(self, **kwargs):
|
|
19
31
|
from plotting import plotsec
|
|
20
32
|
return plotsec(dict(fl=self.fl, tp=self.tp), **kwargs)
|
|
@@ -34,6 +46,9 @@ class ElementAnalysis:
|
|
|
34
46
|
g.Transformation.from_dict(data['ref_frame'])
|
|
35
47
|
)
|
|
36
48
|
|
|
49
|
+
def score_dg(self, dg: str):
|
|
50
|
+
return self.edef.dgs[dg](self.el, self.fl, self.tp)
|
|
51
|
+
|
|
37
52
|
def intra_score(self):
|
|
38
53
|
return self.edef.dgs.apply(self.el, self.fl, self.tp) #[dg.apply(self.el.uid + (f'_{k}' if len(k) > 0 else ''), self.fl, self.tp) for k, dg in self.edef.dgs.items()]
|
|
39
54
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from flightdata import State, align
|
|
6
|
+
|
|
7
|
+
from flightanalysis.definition import ManDef
|
|
8
|
+
from flightanalysis.elements import Element
|
|
9
|
+
from flightanalysis.manoeuvre import Manoeuvre
|
|
10
|
+
|
|
11
|
+
from ..el_analysis import ElementAnalysis
|
|
12
|
+
from .basic import Basic
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(repr=False)
|
|
16
|
+
class Alignment(Basic):
|
|
17
|
+
manoeuvre: Manoeuvre | None
|
|
18
|
+
templates: dict[str, State] | None
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def template(self):
|
|
22
|
+
return State.stack(self.templates, "element")
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def template_list(self):
|
|
26
|
+
return list(self.templates.values())
|
|
27
|
+
|
|
28
|
+
def get_ea(self, name_or_id: str | int) -> ElementAnalysis:
|
|
29
|
+
el: Element = self.manoeuvre.elements[name_or_id]
|
|
30
|
+
|
|
31
|
+
return ElementAnalysis(
|
|
32
|
+
self.mdef.eds[name_or_id],
|
|
33
|
+
self.mdef.mps,
|
|
34
|
+
el,
|
|
35
|
+
self.flown.element[el.uid],
|
|
36
|
+
self.templates[el.uid],
|
|
37
|
+
self.templates[el.uid][0].transform,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def __getattr__(self, name) -> ElementAnalysis:
|
|
41
|
+
return self.get_ea(name)
|
|
42
|
+
|
|
43
|
+
def __getitem__(self, name_or_id) -> ElementAnalysis:
|
|
44
|
+
return self.get_ea(name_or_id)
|
|
45
|
+
|
|
46
|
+
def run_all(
|
|
47
|
+
self, optimise_aligment=True, force=False
|
|
48
|
+
) -> Alignment | Complete | Scored:
|
|
49
|
+
if self.__class__.__name__ == "Scored" and force:
|
|
50
|
+
self = self.downgrade()
|
|
51
|
+
while self.__class__.__name__ != "Scored":
|
|
52
|
+
self = (
|
|
53
|
+
self.run(optimise_aligment)
|
|
54
|
+
if isinstance(self, Complete)
|
|
55
|
+
else self.run()
|
|
56
|
+
)
|
|
57
|
+
return self
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def from_dict(ajman: dict) -> Alignment | Basic:
|
|
61
|
+
basic = Basic.from_dict(ajman)
|
|
62
|
+
if isinstance(basic, Basic) and "manoeuvre" in ajman and ajman["manoeuvre"]:
|
|
63
|
+
if "template" in ajman:
|
|
64
|
+
if set(ajman["template"].keys()) == set(
|
|
65
|
+
[el["uid"] for el in ajman["manoeuvre"]["elements"]] + ["exit_line"]
|
|
66
|
+
):
|
|
67
|
+
return Alignment(
|
|
68
|
+
**basic.__dict__,
|
|
69
|
+
manoeuvre=Manoeuvre.from_dict(ajman["manoeuvre"]),
|
|
70
|
+
templates={
|
|
71
|
+
k: State.from_dict(v) for k, v in ajman["template"].items()
|
|
72
|
+
}
|
|
73
|
+
if "templates" in ajman
|
|
74
|
+
else None,
|
|
75
|
+
)
|
|
76
|
+
return basic
|
|
77
|
+
|
|
78
|
+
def to_dict(self, basic: bool = False) -> dict:
|
|
79
|
+
_basic = super().to_dict(basic)
|
|
80
|
+
if basic:
|
|
81
|
+
return _basic
|
|
82
|
+
return dict(
|
|
83
|
+
**_basic,
|
|
84
|
+
manoeuvre=self.manoeuvre.to_dict(),
|
|
85
|
+
template={k: tp.to_dict(True) for k, tp in self.templates.items()},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def run(self) -> Alignment | Complete:
|
|
89
|
+
if "element" not in self.flown.labels.lgs:
|
|
90
|
+
return self._run(True)[1]
|
|
91
|
+
return self._run(False)[1].proceed()
|
|
92
|
+
|
|
93
|
+
def _run(self, mirror=False, radius=10) -> Alignment:
|
|
94
|
+
res = align(self.flown, self.template, radius, mirror)
|
|
95
|
+
return res.dist, self.update(res.aligned)
|
|
96
|
+
|
|
97
|
+
def update(self, aligned: State) -> Alignment:
|
|
98
|
+
man, tps = self.manoeuvre.match_intention(self.template_list[0][0], aligned)
|
|
99
|
+
mdef = ManDef(
|
|
100
|
+
self.mdef.info,
|
|
101
|
+
self.mdef.mps.update_defaults(man),
|
|
102
|
+
self.mdef.eds,
|
|
103
|
+
self.mdef.box,
|
|
104
|
+
)
|
|
105
|
+
return Alignment(self.id, self.schedule_direction, aligned, mdef, man, tps)
|
|
106
|
+
|
|
107
|
+
def _proceed(self) -> Complete:
|
|
108
|
+
if "element" in self.flown.labels.keys():
|
|
109
|
+
correction = self.mdef.create()
|
|
110
|
+
return Complete(
|
|
111
|
+
self.id,
|
|
112
|
+
self.schedule_direction,
|
|
113
|
+
self.flown,
|
|
114
|
+
self.mdef,
|
|
115
|
+
self.manoeuvre,
|
|
116
|
+
self.template,
|
|
117
|
+
correction,
|
|
118
|
+
correction.create_template(self.template[0], self.flown),
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
return self
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
from .complete import Complete # noqa: E402
|
|
126
|
+
from .scored import Scored # noqa: E402
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
+
import stat
|
|
4
5
|
from typing import Annotated
|
|
6
|
+
from xmlrpc.client import boolean
|
|
5
7
|
|
|
6
8
|
import numpy as np
|
|
7
9
|
|
|
@@ -9,8 +11,12 @@ import geometry as g
|
|
|
9
11
|
from flightdata import State
|
|
10
12
|
from schemas.positioning import Direction, Heading
|
|
11
13
|
from flightanalysis.definition import ManDef, ManOption
|
|
14
|
+
from schemas.sinfo import ScheduleInfo
|
|
12
15
|
|
|
13
16
|
from .analysis import Analysis
|
|
17
|
+
from schemas import MA, fcj
|
|
18
|
+
from importlib.metadata import version
|
|
19
|
+
|
|
14
20
|
|
|
15
21
|
|
|
16
22
|
@dataclass
|
|
@@ -28,10 +34,13 @@ class Basic(Analysis):
|
|
|
28
34
|
|
|
29
35
|
def __str__(self):
|
|
30
36
|
res = f"{self.__class__.__name__}({self.id}, {self.mdef.info.short_name})"
|
|
31
|
-
if
|
|
32
|
-
res =
|
|
37
|
+
if isinstance(self, Scored):
|
|
38
|
+
res = (
|
|
39
|
+
res[:-1]
|
|
40
|
+
+ f", {', '.join([f'{k}={v:.2f}' for k, v in self.scores.score_summary(3, False).items()])})"
|
|
41
|
+
)
|
|
33
42
|
return res
|
|
34
|
-
|
|
43
|
+
|
|
35
44
|
def __repr__(self):
|
|
36
45
|
return str(self)
|
|
37
46
|
|
|
@@ -39,25 +48,20 @@ class Basic(Analysis):
|
|
|
39
48
|
"""Run the analysis to the final stage"""
|
|
40
49
|
drs = [r._run(True) for r in self.run()]
|
|
41
50
|
|
|
42
|
-
dr = drs[np.argmin([dr[0] for dr in drs])]
|
|
51
|
+
dr: Alignment = drs[np.argmin([dr[0] for dr in drs])][1]
|
|
43
52
|
|
|
44
|
-
return dr
|
|
53
|
+
return dr.run_all(optimise_aligment, force)
|
|
45
54
|
|
|
46
55
|
def proceed(self) -> Complete:
|
|
47
56
|
"""Proceed the analysis to the final stage for the case where the elements have already been labelled"""
|
|
48
|
-
if (
|
|
49
|
-
"element" not in self.flown.data.columns
|
|
50
|
-
or self.flown.data.element.isna().any()
|
|
51
|
-
or not isinstance(self, Basic)
|
|
52
|
-
):
|
|
57
|
+
if "element" not in self.flown.labels.keys() or not isinstance(self, Basic):
|
|
53
58
|
return self
|
|
54
59
|
|
|
55
60
|
mopt = ManOption([self.mdef]) if isinstance(self.mdef, ManDef) else self.mdef
|
|
56
|
-
|
|
61
|
+
|
|
62
|
+
elnames = list(self.flown.labels.element.keys())
|
|
57
63
|
for md in mopt:
|
|
58
|
-
if np.all(
|
|
59
|
-
[np.any(np.char.startswith(elnames, k)) for k in md.eds.data.keys()]
|
|
60
|
-
):
|
|
64
|
+
if len(elnames) != len(md.eds) or np.all([elnames[i] == k for i, k in enumerate(md.eds.data.keys())]):
|
|
61
65
|
mdef = md
|
|
62
66
|
break
|
|
63
67
|
else:
|
|
@@ -66,22 +70,19 @@ class Basic(Analysis):
|
|
|
66
70
|
)
|
|
67
71
|
|
|
68
72
|
itrans = self.create_itrans()
|
|
69
|
-
man,
|
|
73
|
+
man, tps = (
|
|
70
74
|
mdef.create()
|
|
71
75
|
.add_lines()
|
|
72
76
|
.match_intention(State.from_transform(itrans), self.flown)
|
|
73
77
|
)
|
|
74
78
|
mdef = ManDef(mdef.info, mdef.mps.update_defaults(man), mdef.eds, mdef.box)
|
|
75
|
-
corr = mdef.create().add_lines()
|
|
76
79
|
return Complete(
|
|
77
80
|
self.id,
|
|
78
81
|
self.schedule_direction,
|
|
79
82
|
self.flown,
|
|
80
83
|
mdef,
|
|
81
84
|
man,
|
|
82
|
-
|
|
83
|
-
corr,
|
|
84
|
-
corr.create_template(itrans, self.flown),
|
|
85
|
+
tps,
|
|
85
86
|
)
|
|
86
87
|
|
|
87
88
|
@staticmethod
|
|
@@ -89,25 +90,34 @@ class Basic(Analysis):
|
|
|
89
90
|
return Basic(
|
|
90
91
|
id=data["id"],
|
|
91
92
|
schedule_direction=Heading[data["schedule_direction"]]
|
|
92
|
-
if (data["schedule_direction"] and data[
|
|
93
|
+
if (data["schedule_direction"] and data["schedule_direction"] != "Infer")
|
|
93
94
|
else None,
|
|
94
95
|
flown=State.from_dict(data["flown"]),
|
|
95
96
|
mdef=ManDef.from_dict(data["mdef"]),
|
|
96
97
|
)
|
|
97
98
|
|
|
98
|
-
def to_dict(self, basic:bool=False) -> dict:
|
|
99
|
+
def to_dict(self, basic: bool = False) -> dict:
|
|
99
100
|
return dict(
|
|
100
101
|
id=self.id,
|
|
101
|
-
schedule_direction=self.schedule_direction.name
|
|
102
|
-
|
|
102
|
+
schedule_direction=self.schedule_direction.name
|
|
103
|
+
if self.schedule_direction
|
|
104
|
+
else None,
|
|
105
|
+
flown=self.flown.to_dict(True),
|
|
103
106
|
**(dict(mdef=self.mdef.to_dict()) if not basic else {}),
|
|
104
107
|
)
|
|
105
108
|
|
|
106
109
|
def create_itrans(self) -> g.Transformation:
|
|
107
|
-
if
|
|
108
|
-
|
|
110
|
+
if (
|
|
111
|
+
self.schedule_direction
|
|
112
|
+
and self.mdef.info.start.direction is not Direction.CROSS
|
|
113
|
+
):
|
|
114
|
+
entry_direction = self.mdef.info.start.direction.wind_swap_heading(
|
|
115
|
+
self.schedule_direction
|
|
116
|
+
)
|
|
109
117
|
else:
|
|
110
|
-
entry_direction = Heading.infer(
|
|
118
|
+
entry_direction = Heading.infer(
|
|
119
|
+
self.flown[0].att.transform_point(g.PX()).bearing()[0]
|
|
120
|
+
)
|
|
111
121
|
|
|
112
122
|
return g.Transformation(
|
|
113
123
|
self.flown[0].pos,
|
|
@@ -133,6 +143,44 @@ class Basic(Analysis):
|
|
|
133
143
|
)
|
|
134
144
|
return als
|
|
135
145
|
|
|
146
|
+
def export_ma(self, schedule: ScheduleInfo, history: dict = None) -> MA:
|
|
147
|
+
return MA(
|
|
148
|
+
**self.to_dict(),
|
|
149
|
+
name=self.mdef.info.short_name,
|
|
150
|
+
schedule=schedule,
|
|
151
|
+
history={
|
|
152
|
+
**(history if history else {}),
|
|
153
|
+
**(
|
|
154
|
+
{
|
|
155
|
+
version("fatuning"): fcj.ManResult.model_validate(
|
|
156
|
+
self.fcj_results()
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
if self.__class__.__name__ == "Scored"
|
|
160
|
+
else {}
|
|
161
|
+
),
|
|
162
|
+
},
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
@staticmethod
|
|
166
|
+
def parse_analyse_serialise(pad: dict, optimise: boolean, name:str):
|
|
167
|
+
import tuning
|
|
168
|
+
from flightanalysis import enable_logging
|
|
169
|
+
logger = enable_logging("INFO")
|
|
170
|
+
logger.info(f"Running {name}")
|
|
171
|
+
try:
|
|
172
|
+
pad = Scored.from_dict(pad)
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logger.exception(f"Failed to parse {pad['id']}")
|
|
175
|
+
return pad
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
pad = pad.proceed().run_all(optimise)
|
|
179
|
+
logger.info(f"Completed {name}")
|
|
180
|
+
return pad.to_dict()
|
|
181
|
+
except Exception as e:
|
|
182
|
+
logger.exception(f"Failed to process {name}")
|
|
183
|
+
return pad.to_dict()
|
|
136
184
|
|
|
137
185
|
from .alignment import Alignment # noqa: E402
|
|
138
186
|
from .complete import Complete # noqa: E402
|
|
@@ -1,56 +1,40 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
+
from functools import partial
|
|
5
|
+
from numbers import Number
|
|
4
6
|
|
|
5
7
|
import geometry as g
|
|
6
8
|
import numpy as np
|
|
9
|
+
import pandas as pd
|
|
7
10
|
from flightdata import State
|
|
8
11
|
from loguru import logger
|
|
12
|
+
from joblib import Parallel, delayed
|
|
9
13
|
|
|
10
14
|
from flightanalysis.definition import ElDef, ManDef
|
|
11
15
|
from flightanalysis.elements import Element
|
|
12
|
-
from flightanalysis.manoeuvre import Manoeuvre
|
|
13
16
|
from flightanalysis.scoring import (
|
|
14
17
|
ElementsResults,
|
|
15
18
|
ManoeuvreResults,
|
|
16
19
|
Results,
|
|
17
20
|
)
|
|
18
|
-
|
|
21
|
+
import os
|
|
19
22
|
from ..el_analysis import ElementAnalysis
|
|
20
23
|
from .alignment import Alignment
|
|
21
24
|
from .basic import Basic
|
|
22
25
|
|
|
23
26
|
|
|
24
|
-
@dataclass
|
|
27
|
+
@dataclass(repr=False)
|
|
25
28
|
class Complete(Alignment):
|
|
26
|
-
corrected: Manoeuvre
|
|
27
|
-
|
|
29
|
+
# corrected: Manoeuvre
|
|
30
|
+
# corrected_templates: dict[str, State]
|
|
28
31
|
|
|
29
32
|
@staticmethod
|
|
30
33
|
def from_dict(ajman: dict) -> Complete | Alignment | Basic:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
and ajman["corrected_template"]
|
|
36
|
-
):
|
|
37
|
-
return Complete(
|
|
38
|
-
**analysis.__dict__,
|
|
39
|
-
corrected=Manoeuvre.from_dict(ajman["corrected"]),
|
|
40
|
-
corrected_template=State.from_dict(ajman["corrected_template"]),
|
|
41
|
-
)
|
|
42
|
-
else:
|
|
43
|
-
return analysis
|
|
44
|
-
|
|
45
|
-
def to_dict(self, basic: bool=False) -> dict:
|
|
46
|
-
_basic = super().to_dict(basic)
|
|
47
|
-
if basic:
|
|
48
|
-
return _basic
|
|
49
|
-
return dict(
|
|
50
|
-
**_basic,
|
|
51
|
-
corrected=self.corrected.to_dict(),
|
|
52
|
-
corrected_template=self.corrected_template.to_dict(),
|
|
53
|
-
)
|
|
34
|
+
return Alignment.from_dict(ajman).proceed()
|
|
35
|
+
|
|
36
|
+
def to_dict(self, basic: bool = False) -> dict:
|
|
37
|
+
return super().to_dict(basic)
|
|
54
38
|
|
|
55
39
|
def run(self, optimise_aligment=True) -> Scored:
|
|
56
40
|
if optimise_aligment:
|
|
@@ -70,7 +54,10 @@ class Complete(Alignment):
|
|
|
70
54
|
yield self.get_ea(edn)
|
|
71
55
|
|
|
72
56
|
def __getitem__(self, i):
|
|
73
|
-
|
|
57
|
+
if isinstance(i, Number):
|
|
58
|
+
return self.get_ea(self.mdef.eds[i + 1].name)
|
|
59
|
+
else:
|
|
60
|
+
return self.get_ea(i)
|
|
74
61
|
|
|
75
62
|
def __getattr__(self, name):
|
|
76
63
|
if name in self.mdef.eds.data.keys():
|
|
@@ -83,7 +70,7 @@ class Complete(Alignment):
|
|
|
83
70
|
def get_ea(self, name):
|
|
84
71
|
el: Element = getattr(self.manoeuvre.all_elements(), name)
|
|
85
72
|
st = el.get_data(self.flown)
|
|
86
|
-
tp = el.
|
|
73
|
+
tp = self.templates[el.uid].relocate(st.pos[0])
|
|
87
74
|
|
|
88
75
|
return ElementAnalysis(
|
|
89
76
|
self.get_edef(name), self.mdef.mps, el, st, tp, el.ref_frame(tp)
|
|
@@ -100,8 +87,8 @@ class Complete(Alignment):
|
|
|
100
87
|
self.mdef.info,
|
|
101
88
|
self.mdef.mps.update_defaults(self.manoeuvre),
|
|
102
89
|
self.mdef.eds,
|
|
90
|
+
self.mdef.box,
|
|
103
91
|
)
|
|
104
|
-
correction = mdef.create().add_lines()
|
|
105
92
|
|
|
106
93
|
return Complete(
|
|
107
94
|
self.id,
|
|
@@ -110,8 +97,6 @@ class Complete(Alignment):
|
|
|
110
97
|
mdef,
|
|
111
98
|
manoeuvre,
|
|
112
99
|
template,
|
|
113
|
-
correction,
|
|
114
|
-
correction.create_template(template[0], self.flown),
|
|
115
100
|
)
|
|
116
101
|
else:
|
|
117
102
|
return self
|
|
@@ -129,9 +114,9 @@ class Complete(Alignment):
|
|
|
129
114
|
) -> int:
|
|
130
115
|
el1: Element = self.manoeuvre.all_elements()[eln1]
|
|
131
116
|
el2: Element = self.manoeuvre.all_elements()[eln2]
|
|
132
|
-
|
|
117
|
+
min_len = 3
|
|
133
118
|
def score_split(steps: int) -> float:
|
|
134
|
-
new_fl = fl.
|
|
119
|
+
new_fl = fl.step_label("element", eln1, steps, fl.t, min_len)
|
|
135
120
|
res1, new_iatt = self.get_score(eln1, itrans, el1.get_data(new_fl))
|
|
136
121
|
|
|
137
122
|
el2fl = el2.get_data(new_fl)
|
|
@@ -145,9 +130,17 @@ class Complete(Alignment):
|
|
|
145
130
|
return res1.total + res2.total
|
|
146
131
|
|
|
147
132
|
dgs = {0: score_split(0)}
|
|
133
|
+
|
|
134
|
+
def check_steps(stps: int):
|
|
135
|
+
return not ((stps > 0 and len(el2.get_data(fl)) <= stps + min_len) or (
|
|
136
|
+
stps < 0 and len(el1.get_data(fl)) <= -stps + min_len
|
|
137
|
+
))
|
|
148
138
|
|
|
149
139
|
steps = int(len(el1.get_data(fl)) > len(el2.get_data(fl))) * 2 - 1
|
|
150
|
-
|
|
140
|
+
|
|
141
|
+
if not check_steps(steps):
|
|
142
|
+
return 0
|
|
143
|
+
|
|
151
144
|
new_dg = score_split(steps)
|
|
152
145
|
if new_dg > dgs[0]:
|
|
153
146
|
steps = -steps
|
|
@@ -155,12 +148,11 @@ class Complete(Alignment):
|
|
|
155
148
|
steps += np.sign(steps)
|
|
156
149
|
dgs[steps] = new_dg
|
|
157
150
|
|
|
158
|
-
while
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
151
|
+
while check_steps(steps):
|
|
152
|
+
try:
|
|
153
|
+
new_dg = score_split(steps)
|
|
154
|
+
except ValueError:
|
|
162
155
|
break
|
|
163
|
-
new_dg = score_split(steps)
|
|
164
156
|
|
|
165
157
|
if new_dg < list(dgs.values())[-1]:
|
|
166
158
|
dgs[steps] = new_dg
|
|
@@ -194,8 +186,8 @@ class Complete(Alignment):
|
|
|
194
186
|
f"Adjusting split between {eln1} and {eln2} by {steps} steps"
|
|
195
187
|
)
|
|
196
188
|
|
|
197
|
-
fl = fl.shift_label(steps, 2, manoeuvre=self.name, element=eln1)
|
|
198
|
-
|
|
189
|
+
# fl = fl.shift_label(steps, 2, manoeuvre=self.name, element=eln1)
|
|
190
|
+
fl = fl.step_label("element", eln1, steps, fl.t, 3)
|
|
199
191
|
adjusted.update([eln1, eln2])
|
|
200
192
|
|
|
201
193
|
padjusted = adjusted
|
|
@@ -206,6 +198,9 @@ class Complete(Alignment):
|
|
|
206
198
|
|
|
207
199
|
return Basic(self.id, self.schedule_direction, fl, self.mdef).proceed()
|
|
208
200
|
|
|
201
|
+
def optimise_alignment_v2(self):
|
|
202
|
+
pass
|
|
203
|
+
|
|
209
204
|
def intra(self):
|
|
210
205
|
return ElementsResults([ea.intra_score() for ea in self])
|
|
211
206
|
|
|
@@ -216,10 +211,55 @@ class Complete(Alignment):
|
|
|
216
211
|
return self.mdef.box.score(self.mdef.info, self.flown, self.template)
|
|
217
212
|
|
|
218
213
|
def plot_3d(self, **kwargs):
|
|
219
|
-
from plotting import
|
|
214
|
+
from plotting import plotsec
|
|
220
215
|
|
|
221
|
-
fig =
|
|
216
|
+
fig = self.flown.plotlabels("element")
|
|
222
217
|
return plotsec(self.flown, color="blue", nmodels=20, fig=fig, **kwargs)
|
|
223
218
|
|
|
219
|
+
def set_boundaries(self, boundaries: list[float]):
|
|
220
|
+
new_man = Basic(
|
|
221
|
+
self.id,
|
|
222
|
+
self.schedule_direction,
|
|
223
|
+
self.flown.set_boundaries("element", boundaries),#.resample(),
|
|
224
|
+
self.mdef,
|
|
225
|
+
).proceed()
|
|
226
|
+
|
|
227
|
+
return new_man.run_all(False) if isinstance(self, Scored) else new_man
|
|
228
|
+
|
|
229
|
+
def set_boundary(self, el: str | int, boundary: float):
|
|
230
|
+
# TODO check if boundary is within bounds
|
|
231
|
+
boundaries = self.flown.labels.element.boundaries
|
|
232
|
+
elid = el if isinstance(el, int) else self.elnames.index(el)
|
|
233
|
+
boundaries[elid] = boundary
|
|
234
|
+
return self.set_boundaries(boundaries)
|
|
235
|
+
|
|
236
|
+
def boundary_sweep(self, el: str | int, width: float, substeps: int = 5):
|
|
237
|
+
"""Sweep an element boundary through a width and return a set of results
|
|
238
|
+
width is in seconds,
|
|
239
|
+
substeps is the number of steps to take within each timestep
|
|
240
|
+
TODO make sure range doesn't cross adjacent boundary
|
|
241
|
+
"""
|
|
242
|
+
elid = el if isinstance(el, int) else self.elnames.index(el)
|
|
243
|
+
bopt = self.flown.labels.element.boundaries[elid]
|
|
244
|
+
tstart = bopt - width
|
|
245
|
+
tstop = bopt + width
|
|
246
|
+
ts = self.flown.data.t[tstart:tstop].to_numpy()
|
|
247
|
+
splits = np.array(
|
|
248
|
+
[
|
|
249
|
+
t0 + (t1 - t0) * i / substeps
|
|
250
|
+
for t0, t1 in zip(ts[:-1], ts[1:])
|
|
251
|
+
for i in range(substeps)
|
|
252
|
+
]
|
|
253
|
+
)
|
|
254
|
+
logger.info(f"Starting {os.cpu_count() * 2 - 1} processes to run {len(splits)} manoeuvres")
|
|
255
|
+
madicts = Parallel(n_jobs=os.cpu_count() * 2 - 1)(
|
|
256
|
+
delayed(partial(Basic.parse_analyse_serialise, optimise=False, name=i))(self.set_boundary(el, ic).to_dict())
|
|
257
|
+
for i, ic in enumerate(splits)
|
|
258
|
+
)
|
|
259
|
+
outdata = {ic: Scored.from_dict(mad).scores.dg_dict() for ic, mad in zip(splits, madicts) }
|
|
260
|
+
|
|
261
|
+
return pd.DataFrame(outdata).T
|
|
262
|
+
|
|
263
|
+
|
|
224
264
|
|
|
225
265
|
from .scored import Scored # noqa: E402
|
|
@@ -4,7 +4,7 @@ from flightanalysis.scoring import ManoeuvreResults
|
|
|
4
4
|
from .complete import Complete
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
@dataclass
|
|
7
|
+
@dataclass(repr=False)
|
|
8
8
|
class Scored(Complete):
|
|
9
9
|
scores: ManoeuvreResults
|
|
10
10
|
|
|
@@ -16,14 +16,16 @@ class Scored(Complete):
|
|
|
16
16
|
self.mdef,
|
|
17
17
|
self.manoeuvre,
|
|
18
18
|
self.template,
|
|
19
|
-
self.corrected,
|
|
20
|
-
self.corrected_template,
|
|
21
19
|
)
|
|
22
20
|
|
|
23
21
|
@staticmethod
|
|
24
22
|
def from_dict(ajman: dict) -> Scored:
|
|
25
23
|
analysis = Complete.from_dict(ajman)
|
|
26
|
-
if
|
|
24
|
+
if (
|
|
25
|
+
isinstance(analysis, Complete)
|
|
26
|
+
and "scores" in ajman
|
|
27
|
+
and ajman["scores"] is not None
|
|
28
|
+
):
|
|
27
29
|
return Scored(
|
|
28
30
|
**analysis.__dict__, scores=ManoeuvreResults.from_dict(ajman["scores"])
|
|
29
31
|
)
|
|
@@ -39,4 +41,10 @@ class Scored(Complete):
|
|
|
39
41
|
return dict(**_basic, scores=self.scores.to_dict())
|
|
40
42
|
|
|
41
43
|
def fcj_results(self):
|
|
42
|
-
return dict(
|
|
44
|
+
return dict(
|
|
45
|
+
els=[
|
|
46
|
+
dict(name=k, start=v.start, stop=v.stop)
|
|
47
|
+
for k, v in self.flown.labels.element.labels.items()
|
|
48
|
+
],
|
|
49
|
+
results=self.scores.fcj_results(),
|
|
50
|
+
)
|