flightanalysis 0.3.4__tar.gz → 0.3.6__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.
Files changed (193) hide show
  1. {flightanalysis-0.3.4/flightanalysis.egg-info → flightanalysis-0.3.6}/PKG-INFO +6 -5
  2. flightanalysis-0.3.6/create_schedules.py +25 -0
  3. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/make_manoeuvre.py +12 -10
  4. flightanalysis-0.3.6/examples/plot_manoeuvre.py +8 -0
  5. flightanalysis-0.3.6/flightanalysis/analysis/analysis_json.py +28 -0
  6. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/analysis/el_analysis.py +2 -2
  7. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/analysis/manoeuvre_analysis/alignment.py +21 -8
  8. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/analysis/manoeuvre_analysis/analysis.py +0 -2
  9. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/analysis/manoeuvre_analysis/basic.py +45 -10
  10. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/analysis/manoeuvre_analysis/complete.py +27 -122
  11. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/analysis/manoeuvre_analysis/scored.py +7 -0
  12. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/analysis/sch_analysis.py +54 -36
  13. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/base/ref_funcs.py +13 -6
  14. flightanalysis-0.3.6/flightanalysis/builders/BAeAGlid/box.py +19 -0
  15. flightanalysis-0.3.6/flightanalysis/builders/BAeAGlid/criteria.py +84 -0
  16. flightanalysis-0.3.6/flightanalysis/builders/BAeAGlid/downgrades.py +79 -0
  17. flightanalysis-0.3.6/flightanalysis/builders/BAeAGlid/manbuilder.py +110 -0
  18. flightanalysis-0.3.6/flightanalysis/builders/elbuilders.py +396 -0
  19. flightanalysis-0.3.6/flightanalysis/builders/f3a/box.py +22 -0
  20. flightanalysis-0.3.4/flightanalysis/scoring/criteria/f3a_criteria.py → flightanalysis-0.3.6/flightanalysis/builders/f3a/criteria.py +11 -7
  21. flightanalysis-0.3.6/flightanalysis/builders/f3a/downgrades.py +91 -0
  22. flightanalysis-0.3.6/flightanalysis/builders/f3a/manbuilder.py +110 -0
  23. flightanalysis-0.3.6/flightanalysis/builders/imac/manbuilder.py +101 -0
  24. flightanalysis-0.3.6/flightanalysis/builders/manbuilder.py +135 -0
  25. {flightanalysis-0.3.4/flightanalysis/definition → flightanalysis-0.3.6/flightanalysis}/builders/schedules/AMA_Intermediate2024.py +13 -8
  26. flightanalysis-0.3.6/flightanalysis/builders/schedules/baeaglid_intermediate.py +177 -0
  27. {flightanalysis-0.3.4/flightanalysis/definition → flightanalysis-0.3.6/flightanalysis}/builders/schedules/f3a_a25.py +16 -10
  28. {flightanalysis-0.3.4/flightanalysis/definition → flightanalysis-0.3.6/flightanalysis}/builders/schedules/f3a_f25.py +24 -14
  29. {flightanalysis-0.3.4/flightanalysis/definition → flightanalysis-0.3.6/flightanalysis}/builders/schedules/f3a_p23.py +20 -13
  30. {flightanalysis-0.3.4/flightanalysis/definition → flightanalysis-0.3.6/flightanalysis}/builders/schedules/f3a_p25.py +22 -12
  31. {flightanalysis-0.3.4/flightanalysis/definition → flightanalysis-0.3.6/flightanalysis}/builders/schedules/f3auk_clubman.py +18 -10
  32. {flightanalysis-0.3.4/flightanalysis/definition → flightanalysis-0.3.6/flightanalysis}/builders/schedules/f3auk_intermediate.py +14 -6
  33. {flightanalysis-0.3.4/flightanalysis/definition → flightanalysis-0.3.6/flightanalysis}/builders/schedules/imac_sport2024.py +14 -12
  34. {flightanalysis-0.3.4/flightanalysis/definition → flightanalysis-0.3.6/flightanalysis}/builders/schedules/imac_unlim2024.py +12 -9
  35. {flightanalysis-0.3.4/flightanalysis/definition → flightanalysis-0.3.6/flightanalysis}/builders/schedules/nsrca_inter2024.py +14 -7
  36. flightanalysis-0.3.6/flightanalysis/data/BAeAGlid_intermediate.json +4157 -0
  37. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/data/__init__.py +3 -1
  38. flightanalysis-0.3.6/flightanalysis/data/baeaglid_intermediate_schedule.json +5014 -0
  39. flightanalysis-0.3.6/flightanalysis/data/f3a_a25_schedule.json +15009 -0
  40. flightanalysis-0.3.6/flightanalysis/data/f3a_f25_schedule.json +21164 -0
  41. flightanalysis-0.3.6/flightanalysis/data/f3a_p23_schedule.json +19287 -0
  42. flightanalysis-0.3.6/flightanalysis/data/f3a_p25_schedule.json +22013 -0
  43. flightanalysis-0.3.6/flightanalysis/data/f3auk_clubman_schedule.json +10530 -0
  44. flightanalysis-0.3.6/flightanalysis/data/f3auk_inter_schedule.json +10292 -0
  45. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/__init__.py +2 -2
  46. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/eldef.py +3 -5
  47. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/mandef.py +79 -45
  48. flightanalysis-0.3.6/flightanalysis/definition/maninfo/__init__.py +2 -0
  49. flightanalysis-0.3.6/flightanalysis/definition/maninfo/maninfo.py +45 -0
  50. flightanalysis-0.3.6/flightanalysis/definition/maninfo/positioning.py +113 -0
  51. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/manparm.py +1 -1
  52. flightanalysis-0.3.6/flightanalysis/definition/scheddef.py +124 -0
  53. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/scheduleinfo.py +39 -4
  54. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/elements/element.py +3 -5
  55. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/elements/line.py +1 -1
  56. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/elements/loop.py +1 -1
  57. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/elements/stall_turn.py +1 -1
  58. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/__init__.py +1 -1
  59. flightanalysis-0.3.6/flightanalysis/scoring/box/__init__.py +3 -0
  60. flightanalysis-0.3.6/flightanalysis/scoring/box/box.py +153 -0
  61. flightanalysis-0.3.6/flightanalysis/scoring/box/rectangular_box.py +27 -0
  62. flightanalysis-0.3.6/flightanalysis/scoring/box/triangular_box.py +34 -0
  63. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/criteria/criteria.py +2 -0
  64. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/downgrade.py +60 -17
  65. flightanalysis-0.3.6/flightanalysis/scoring/measurements/__init__.py +13 -0
  66. flightanalysis-0.3.6/flightanalysis/scoring/measurements/attitude.py +108 -0
  67. flightanalysis-0.3.6/flightanalysis/scoring/measurements/curvature.py +47 -0
  68. flightanalysis-0.3.6/flightanalysis/scoring/measurements/measurement.py +158 -0
  69. flightanalysis-0.3.6/flightanalysis/scoring/measurements/positioning.py +40 -0
  70. flightanalysis-0.3.6/flightanalysis/scoring/measurements/roll.py +92 -0
  71. flightanalysis-0.3.6/flightanalysis/scoring/measurements/speed.py +25 -0
  72. flightanalysis-0.3.6/flightanalysis/scoring/measurements/stall.py +82 -0
  73. flightanalysis-0.3.6/flightanalysis/scoring/measurements/track.py +73 -0
  74. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/results.py +1 -1
  75. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/selectors.py +11 -1
  76. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/smoothing.py +44 -15
  77. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/visibility.py +5 -0
  78. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scripts/plot_scores.py +14 -9
  79. {flightanalysis-0.3.4 → flightanalysis-0.3.6/flightanalysis.egg-info}/PKG-INFO +6 -5
  80. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis.egg-info/SOURCES.txt +49 -19
  81. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis.egg-info/requires.txt +5 -4
  82. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/requirements-dev.txt +3 -3
  83. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/requirements.txt +4 -3
  84. flightanalysis-0.3.6/tests/__init__.py +0 -0
  85. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/data/manual_F3A_P23.json +2 -2
  86. flightanalysis-0.3.6/tests/test_positioning.py +17 -0
  87. flightanalysis-0.3.6/tests/test_schedule/__init__.py +0 -0
  88. flightanalysis-0.3.6/tests/test_schedule/test_definition/__init__.py +0 -0
  89. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_definition/test_definition_eldef.py +1 -1
  90. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_definition/test_definition_manparm.py +1 -1
  91. flightanalysis-0.3.6/tests/test_schedule/test_element/__init__.py +0 -0
  92. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_measurement.py +1 -1
  93. flightanalysis-0.3.4/create_schedules.py +0 -24
  94. flightanalysis-0.3.4/flightanalysis/data/IMAC_Unlimited2024_schedule.json +0 -2857
  95. flightanalysis-0.3.4/flightanalysis/data/f3a_a25_schedule.json +0 -3823
  96. flightanalysis-0.3.4/flightanalysis/data/f3a_f25_schedule.json +0 -5317
  97. flightanalysis-0.3.4/flightanalysis/data/f3a_p23_schedule.json +0 -4839
  98. flightanalysis-0.3.4/flightanalysis/data/f3a_p25_schedule.json +0 -5491
  99. flightanalysis-0.3.4/flightanalysis/data/f3auk_clubman_schedule.json +0 -2833
  100. flightanalysis-0.3.4/flightanalysis/data/f3auk_inter_schedule.json +0 -2744
  101. flightanalysis-0.3.4/flightanalysis/definition/builders/elbuilders.py +0 -262
  102. flightanalysis-0.3.4/flightanalysis/definition/builders/manbuilder.py +0 -322
  103. flightanalysis-0.3.4/flightanalysis/definition/maninfo.py +0 -178
  104. flightanalysis-0.3.4/flightanalysis/definition/scheddef.py +0 -118
  105. flightanalysis-0.3.4/flightanalysis/scoring/f3a_downgrades.py +0 -79
  106. flightanalysis-0.3.4/flightanalysis/scoring/measurement.py +0 -468
  107. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/.github/workflows/publish_pypi.yml +0 -0
  108. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/.gitignore +0 -0
  109. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/LICENSE +0 -0
  110. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/MANIFEST.in +0 -0
  111. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/README.md +0 -0
  112. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/__init__.py +0 -0
  113. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/data/__init__.py +0 -0
  114. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/data/manual_F3A_F23_22_04_28_00000231.json +0 -0
  115. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/data/manual_F3A_P23_22_05_31_00000350.json +0 -0
  116. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/data/manual_F3A_P23_23_08_11_00000094.json +0 -0
  117. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/scoring/__init__.py +0 -0
  118. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/scoring/judging.py +0 -0
  119. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/scoring/manoeuvres/__init__.py +0 -0
  120. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/scoring/manoeuvres/inter_analysis.py +0 -0
  121. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/scoring/manoeuvres/intra_analysis.py +0 -0
  122. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/scoring/manoeuvres/mans/__init__.py +0 -0
  123. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/scoring/manoeuvres/mans/extract_mans.py +0 -0
  124. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/scoring/manoeuvres/mans/tHat.json +0 -0
  125. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/examples/scoring/manoeuvres/positioning_analysis.py +0 -0
  126. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/__init__.py +0 -0
  127. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/analysis/__init__.py +0 -0
  128. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/analysis/manoeuvre_analysis/__init__.py +0 -0
  129. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/base/utils.py +0 -0
  130. {flightanalysis-0.3.4/flightanalysis/definition/builders → flightanalysis-0.3.6/flightanalysis/builders/BAeAGlid}/__init__.py +0 -0
  131. {flightanalysis-0.3.4/flightanalysis/definition/builders/schedules → flightanalysis-0.3.6/flightanalysis/builders}/__init__.py +0 -0
  132. {flightanalysis-0.3.4/flightanalysis/scoring/criteria/inter → flightanalysis-0.3.6/flightanalysis/builders/f3a}/__init__.py +0 -0
  133. {flightanalysis-0.3.4/flightanalysis/scoring/criteria/intra → flightanalysis-0.3.6/flightanalysis/builders/iacpower}/__init__.py +0 -0
  134. {flightanalysis-0.3.4/tests → flightanalysis-0.3.6/flightanalysis/builders/imac}/__init__.py +0 -0
  135. {flightanalysis-0.3.4/tests/test_schedule → flightanalysis-0.3.6/flightanalysis/builders/schedules}/__init__.py +0 -0
  136. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/collectors.py +0 -0
  137. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/manoption.py +0 -0
  138. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/operations/__init__.py +0 -0
  139. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/operations/funopp.py +0 -0
  140. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/operations/itemopp.py +0 -0
  141. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/operations/mathopp.py +0 -0
  142. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/operations/operation.py +0 -0
  143. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/definition/operations/sumopp.py +0 -0
  144. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/elements/__init__.py +0 -0
  145. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/elements/snap.py +0 -0
  146. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/elements/spin.py +0 -0
  147. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/fcjson.py +0 -0
  148. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/manoeuvre.py +0 -0
  149. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/schedule.py +0 -0
  150. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/criteria/__init__.py +0 -0
  151. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/criteria/exponential.py +0 -0
  152. {flightanalysis-0.3.4/tests/test_schedule/test_definition → flightanalysis-0.3.6/flightanalysis/scoring/criteria/inter}/__init__.py +0 -0
  153. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/criteria/inter/combination.py +0 -0
  154. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/criteria/inter/comparison.py +0 -0
  155. {flightanalysis-0.3.4/tests/test_schedule/test_element → flightanalysis-0.3.6/flightanalysis/scoring/criteria/intra}/__init__.py +0 -0
  156. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/criteria/intra/bounded.py +0 -0
  157. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/criteria/intra/continuous.py +0 -0
  158. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/criteria/intra/peak.py +0 -0
  159. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scoring/criteria/intra/single.py +0 -0
  160. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/scripts/collect_scores.py +0 -0
  161. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis/version.py +0 -0
  162. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis.egg-info/dependency_links.txt +0 -0
  163. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis.egg-info/entry_points.txt +0 -0
  164. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/flightanalysis.egg-info/top_level.txt +0 -0
  165. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/pyproject.toml +0 -0
  166. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/setup.cfg +0 -0
  167. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/EmailedBox.f3a +0 -0
  168. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/conftest.py +0 -0
  169. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/data/old_json.json +0 -0
  170. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/data/p23.BIN +0 -0
  171. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/data/p23_box.f3a +0 -0
  172. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/data/p23_fc.json +0 -0
  173. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/data/p23_flight.json +0 -0
  174. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/data/scored_fcj.json +0 -0
  175. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/data/unscored_fcj.json +0 -0
  176. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_criiteria.py +0 -0
  177. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_data.py +0 -0
  178. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_fcjscore.py +0 -0
  179. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_rfuncs.py +0 -0
  180. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_definition/test_definition_mpopp.py +0 -0
  181. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_definition/test_schedule_definition.py +0 -0
  182. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_definition/test_schedule_definition_maninfo.py +0 -0
  183. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_element/loop_analysis.json +0 -0
  184. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_element/p23_th_e0.csv +0 -0
  185. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_element/p23_th_e0.json +0 -0
  186. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_element/p23_th_e0_template.csv +0 -0
  187. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_element/test_schedule_element.py +0 -0
  188. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_element/test_schedule_element_line.py +0 -0
  189. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_element/test_schedule_element_loop.py +0 -0
  190. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_element/test_schedule_element_snap.py +0 -0
  191. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_element/test_schedule_element_spin.py +0 -0
  192. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_element/test_schedule_element_stallturn.py +0 -0
  193. {flightanalysis-0.3.4 → flightanalysis-0.3.6}/tests/test_schedule/test_schedule_manoeuvre.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flightanalysis
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: A package for analysing flight data
5
5
  Author-email: Thomas David <thomasdavid0@gmail.com>
6
6
  License: GNU GPL v3
@@ -15,10 +15,11 @@ Requires-Dist: fastdtw
15
15
  Requires-Dist: simplejson
16
16
  Requires-Dist: loguru
17
17
  Requires-Dist: joblib
18
- Requires-Dist: pfc-geometry>=0.2.10
19
- Requires-Dist: flightdata>=0.2.18
18
+ Requires-Dist: pfc-geometry>=0.2.11
19
+ Requires-Dist: flightdata>=0.2.19
20
20
  Requires-Dist: GitPython
21
21
  Requires-Dist: pyperclip
22
+ Requires-Dist: packaging
22
23
  Provides-Extra: dev
23
24
  Requires-Dist: numpy; extra == "dev"
24
25
  Requires-Dist: pandas; extra == "dev"
@@ -27,8 +28,8 @@ Requires-Dist: fastdtw; extra == "dev"
27
28
  Requires-Dist: simplejson; extra == "dev"
28
29
  Requires-Dist: loguru; extra == "dev"
29
30
  Requires-Dist: joblib; extra == "dev"
30
- Requires-Dist: pfc-geometry>=0.2.10; extra == "dev"
31
- Requires-Dist: flightdata>=0.2.18; extra == "dev"
31
+ Requires-Dist: pfc-geometry>=0.2.11; extra == "dev"
32
+ Requires-Dist: flightdata>=0.2.19; extra == "dev"
32
33
  Requires-Dist: ardupilot_log_reader>=0.3.3; extra == "dev"
33
34
  Requires-Dist: pymavlink; extra == "dev"
34
35
  Requires-Dist: GitPython; extra == "dev"
@@ -0,0 +1,25 @@
1
+ from flightanalysis.builders.schedules.f3a_p23 import p23_def
2
+ from flightanalysis.builders.schedules.f3a_a25 import a25_def
3
+ from flightanalysis.builders.schedules.f3a_p25 import p25_def
4
+ from flightanalysis.builders.schedules.f3a_f25 import f25_def
5
+ #from imac_unlim2024 import sdef as imac_unl2024_def
6
+ from flightanalysis.builders.schedules.f3auk_clubman import clubman_def as f3auk_club_def
7
+ from flightanalysis.builders.schedules.f3auk_intermediate import intermediate_def as f3auk_int_def
8
+ from flightanalysis.builders.schedules.baeaglid_intermediate import sdef as baeaglid_intermediate_def
9
+ sdefs = {
10
+ 'f3a_p23_schedule': p23_def,
11
+ 'f3a_a25_schedule': a25_def,
12
+ 'f3a_p25_schedule': p25_def,
13
+ 'f3a_f25_schedule': f25_def,
14
+ #'IMAC_Unlimited2024_schedule': imac_unl2024_def,
15
+ 'f3auk_clubman_schedule': f3auk_club_def,
16
+ 'f3auk_inter_schedule': f3auk_int_def,
17
+ "baeaglid_intermediate_schedule": baeaglid_intermediate_def
18
+ }
19
+
20
+ def create_all():
21
+ for k, sdef in sdefs.items():
22
+ sdef.to_json(f"flightanalysis/data/{k}.json")
23
+
24
+ if __name__ == '__main__':
25
+ create_all()
@@ -2,30 +2,32 @@ from flightplotting import plotsec, plot_regions
2
2
  from flightplotting.traces import axis_rate_trace
3
3
  from flightanalysis import (
4
4
  ManDef, BoxLocation, Position, Height, Direction,
5
- Orientation, ManInfo, r, MBTags, c45, centred, ManParm, Combination)
5
+ Orientation, ManInfo, Heading)
6
6
  import numpy as np
7
- from flightanalysis.definition import f3amb
7
+ from flightanalysis.builders.manbuilder import r, MBTags, c45, centred
8
+ from flightanalysis.builders.f3a.manbuilder import f3amb
8
9
  from flightdata import NumpyEncoder
9
10
  import plotly.graph_objects as go
10
11
  from json import dumps
11
12
  import geometry as g
12
13
 
13
14
  mdef: ManDef = f3amb.create(ManInfo(
14
- "Spin", "iSpin", k=3, position=Position.CENTRE,
15
+ "half square", "hSqL", k=2, position=Position.END,
15
16
  start=BoxLocation(Height.TOP, Direction.UPWIND, Orientation.INVERTED),
16
- end=BoxLocation(Height.BTM),
17
+ end=BoxLocation(Height.BTM)
17
18
  ),[
18
- MBTags.CENTRE,
19
- f3amb.spin(r(2)),
20
- f3amb.roll(r(0.5), line_length=165),
21
19
  f3amb.loop(np.pi/2),
20
+ f3amb.roll(r(1)),
21
+ f3amb.loop(np.pi/2),
22
22
  ])
23
23
 
24
24
  data = mdef.to_dict()
25
25
  print(dumps(data, indent=2, cls=NumpyEncoder))
26
26
  mdef = ManDef.from_dict(data)
27
27
 
28
- it = g.Transformation(g.Point(-20,150,50), mdef.info.initial_transform(150, 1).rotation)
28
+ it = mdef.guess_itrans(170, Heading.RIGHT)
29
+
30
+ mdef.fit_box(it)
29
31
 
30
32
  man = mdef.create()
31
33
 
@@ -36,5 +38,5 @@ fig = plotsec(tp, fig=fig, nmodels=10, scale=5)
36
38
  #fig.add_traces(boxtrace())
37
39
  fig.show()
38
40
 
39
- fig = go.Figure(data=axis_rate_trace(tp))
40
- fig.show()
41
+ #fig = go.Figure(data=axis_rate_trace(tp))
42
+ #fig.show()
@@ -0,0 +1,8 @@
1
+ from flightanalysis import ManDef, SchedDef
2
+
3
+ from flightanalysis.builders.schedules.baeaglid_intermediate import sdef
4
+
5
+
6
+ md: ManDef = sdef[3]
7
+
8
+ md.plot().show()
@@ -0,0 +1,28 @@
1
+ from flightanalysis import ma, ScheduleInfo, ScheduleAnalysis
2
+ from flightdata import Origin, State
3
+
4
+
5
+
6
+
7
+
8
+ def create_analysis_json(
9
+ origin: Origin, sbin: str, sfcj: str, st: State, sa: ScheduleAnalysis
10
+ ):
11
+ pass
12
+
13
+
14
+ # readonly box: Origin,
15
+ # readonly isComp: boolean,
16
+ # readonly sourceBin: string,
17
+ # readonly sourceFCJ: string,
18
+ # readonly mans: MAExport[],
19
+ # readonly states: States
20
+
21
+
22
+ # readonly name: string,
23
+ # readonly id: number,
24
+ # readonly sinfo: ScheduleInfo,
25
+ # readonly start: number,
26
+ # readonly stop: number,
27
+ # readonly k: number,
28
+ # readonly history: Record<string, FCJManResult> = {},
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
  from flightdata import State
3
3
  from typing import Self
4
- from flightanalysis import ElDef, Element, ManParms
4
+ from flightanalysis import ElDef, Element, ManParms, DownGrades
5
5
  from dataclasses import dataclass
6
6
  import geometry as g
7
7
 
@@ -35,5 +35,5 @@ class ElementAnalysis:
35
35
  )
36
36
 
37
37
  def intra_score(self):
38
- return self.edef.dgs.apply(self.el.uid, 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()]
38
+ 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
39
 
@@ -7,6 +7,7 @@ from loguru import logger
7
7
  from .basic import Basic
8
8
  from flightanalysis.definition import ManDef, ScheduleInfo
9
9
  from ..el_analysis import ElementAnalysis
10
+ import traceback
10
11
 
11
12
 
12
13
  @dataclass
@@ -32,16 +33,27 @@ class Alignment(Basic):
32
33
  self = self.downgrade()
33
34
  new = self
34
35
  while self.__class__.__name__ != "Scored":
35
- new = (
36
- self.run(optimise_aligment)
37
- if isinstance(self, Complete)
38
- else self.run()
39
- )
36
+ try:
37
+ new = (
38
+ self.run(optimise_aligment)
39
+ if isinstance(self, Complete)
40
+ else self.run()
41
+ )
42
+ except Exception as e:
43
+ logger.error(traceback.format_exc())
40
44
  if new.__class__.__name__ == self.__class__.__name__:
41
45
  break
42
46
  self = new
43
47
  return new
44
48
 
49
+ def to_dict(self):
50
+ return dict(
51
+ **super().to_dict(),
52
+ manoeuvre=self.manoeuvre.to_dict(),
53
+ template=self.template.to_dict(),
54
+ )
55
+
56
+
45
57
  @staticmethod
46
58
  def from_dict(data: dict, fallback=True):
47
59
  ia = Basic.from_dict(data)
@@ -77,8 +89,8 @@ class Alignment(Basic):
77
89
 
78
90
  def update(self, aligned: State) -> Alignment:
79
91
  man, tp = self.manoeuvre.match_intention(self.template[0], aligned)
80
- mdef = ManDef(self.mdef.info, self.mdef.mps.update_defaults(man), self.mdef.eds)
81
- return Alignment(self.id, mdef, aligned, self.direction, man, tp)
92
+ mdef = ManDef(self.mdef.info, self.mdef.mps.update_defaults(man), self.mdef.eds, self.mdef.box)
93
+ return Alignment(self.id, mdef, aligned, self.entry, self.exit, man, tp)
82
94
 
83
95
  def _proceed(self) -> Complete:
84
96
  if "element" in self.flown.data.columns:
@@ -87,7 +99,8 @@ class Alignment(Basic):
87
99
  self.id,
88
100
  self.mdef,
89
101
  self.flown,
90
- self.direction,
102
+ self.entry,
103
+ self.exit,
91
104
  self.manoeuvre,
92
105
  self.template,
93
106
  correction,
@@ -6,8 +6,6 @@ from flightanalysis.definition import ScheduleInfo
6
6
  class Analysis:
7
7
  id: int
8
8
 
9
- def to_dict(self):
10
- return {k: (v.to_dict() if hasattr(v, 'to_dict') else v) for k, v in self.__dict__.items()}
11
9
 
12
10
  def to_mindict(self, sinfo: ScheduleInfo):
13
11
  return {
@@ -2,26 +2,39 @@ from __future__ import annotations
2
2
  from dataclasses import dataclass
3
3
  from flightdata import State, Flight, Origin
4
4
  from flightanalysis.definition import ManDef, SchedDef, ManOption
5
+ from flightanalysis.definition.maninfo import Heading
5
6
  import geometry as g
6
7
  from json import load
7
8
  from .analysis import Analysis
8
9
  from flightanalysis.definition.scheduleinfo import ScheduleInfo
9
10
  import numpy as np
10
11
  import pandas as pd
12
+ from typing import Annotated
11
13
 
12
14
 
13
15
  @dataclass
14
16
  class Basic(Analysis):
15
17
  mdef: ManDef | ManOption
16
18
  flown: State
17
- direction: int
19
+ entry: Annotated[Heading | None, "The direction the manoeuvre should start in, None for inferred"]
20
+ exit: Annotated[Heading | None, "The direction the manoeuvre should end in, None for inferred"]
18
21
 
19
22
  @property
20
23
  def name(self):
21
24
  return self.mdef.uid
22
25
 
26
+
27
+ def to_analysis_dict(self) -> dict:
28
+ return dict(
29
+ id=self.id,
30
+ mdef=self.mdef.to_dict(),
31
+ flown=self.flown.to_dict(),
32
+ entry=self.entry.name if self.entry else None,
33
+ exit=self.exit.name if self.exit else None,
34
+ )
35
+
23
36
  def run_all(self, optimise_aligment=True, force=False) -> Scored:
24
- """Run the analysis to the final stage, for the case where the elements have not been labelled"""
37
+ """Run the analysis to the final stage"""
25
38
  drs = [r._run(True) for r in self.run()]
26
39
 
27
40
  dr = drs[np.argmin([dr[0] for dr in drs])]
@@ -30,7 +43,10 @@ class Basic(Analysis):
30
43
 
31
44
  def proceed(self) -> Complete:
32
45
  """Proceed the analysis to the final stage for the case where the elements have already been labelled"""
33
- if "element" not in self.flown.data.columns:
46
+ if (
47
+ "element" not in self.flown.data.columns
48
+ or self.flown.data.element.isna().any()
49
+ ):
34
50
  return self
35
51
  mopt = ManOption([self.mdef]) if isinstance(self.mdef, ManDef) else self.mdef
36
52
  elnames = self.flown.data.element.unique().astype(str)
@@ -51,31 +67,48 @@ class Basic(Analysis):
51
67
  .add_lines()
52
68
  .match_intention(State.from_transform(itrans), self.flown)
53
69
  )
54
- mdef = ManDef(mdef.info, mdef.mps.update_defaults(man), mdef.eds)
70
+ mdef = ManDef(mdef.info, mdef.mps.update_defaults(man), mdef.eds, mdef.box)
55
71
  corr = mdef.create().add_lines()
56
72
  return Complete(
57
73
  self.id,
58
74
  mdef,
59
75
  self.flown,
60
- self.direction,
76
+ self.entry,
77
+ self.exit,
61
78
  man,
62
79
  tp,
63
80
  corr,
64
81
  corr.create_template(itrans, self.flown),
65
82
  )
66
83
 
84
+ def to_dict(self) -> dict:
85
+ return dict(
86
+ mdef=self.mdef.to_dict(),
87
+ flown=self.flown.to_dict(),
88
+ entry=self.entry.name if self.entry else None,
89
+ exit=self.exit.name if self.exit else None,
90
+ )
91
+
67
92
  @classmethod
68
93
  def from_dict(Cls, data: dict) -> Basic:
69
94
  return Basic(
70
95
  -1,
71
96
  ManDef.from_dict(data["mdef"]),
72
97
  State.from_dict(data["flown"]),
73
- data["direction"],
98
+ Heading[data["entry"]] if data["entry"] else None,
99
+ Heading[data["exit"]] if data["exit"] else None,
74
100
  )
75
101
 
76
102
  def create_itrans(self) -> g.Transformation:
103
+ entry = (
104
+ self.entry
105
+ if self.entry is not None
106
+ else Heading.infer(self.flown[0].att.transform_point(g.PX()).bearing()[0])
107
+ )
108
+
77
109
  return g.Transformation(
78
- self.flown[0].pos, self.mdef.info.start.initial_rotation(self.direction)
110
+ self.flown[0].pos,
111
+ g.Euler(self.mdef.info.start.orientation.value, 0, entry.value),
79
112
  )
80
113
 
81
114
  @staticmethod
@@ -83,7 +116,7 @@ class Basic(Analysis):
83
116
  with open(file, "r") as f:
84
117
  data = load(f)
85
118
  flight = Flight.from_fc_json(data)
86
- box = Origin.from_fcjson_parmameters(data["parameters"])
119
+ box = Origin.from_fcjson_parameters(data["parameters"])
87
120
 
88
121
  sdef = SchedDef.load(data["parameters"]["schedule"][1])
89
122
 
@@ -105,7 +138,8 @@ class Basic(Analysis):
105
138
  self.id,
106
139
  mdef,
107
140
  self.flown,
108
- self.direction,
141
+ self.entry,
142
+ self.exit,
109
143
  man,
110
144
  man.create_template(itrans),
111
145
  )
@@ -118,7 +152,8 @@ class Basic(Analysis):
118
152
  name=self.name,
119
153
  id=self.id,
120
154
  data=self.flown._create_json_data().to_dict("records"),
121
- direction=self.direction,
155
+ entry=self.entry.name,
156
+ exit=self.exit.name,
122
157
  )
123
158
  return data
124
159
 
@@ -1,24 +1,24 @@
1
1
  from __future__ import annotations
2
+
2
3
  from dataclasses import dataclass
3
- from ..el_analysis import ElementAnalysis
4
+
5
+ import geometry as g
6
+ import numpy as np
4
7
  from flightdata import State
5
- from flightanalysis.definition import ManDef, ElDef
8
+ from loguru import logger
9
+
10
+ from flightanalysis.definition import ElDef, ManDef
11
+ from flightanalysis.elements import Element
6
12
  from flightanalysis.manoeuvre import Manoeuvre
7
13
  from flightanalysis.scoring import (
8
- Results,
9
- ManoeuvreResults,
10
- Measurement,
11
14
  ElementsResults,
12
- Result,
15
+ ManoeuvreResults,
16
+ Results,
13
17
  )
14
- from flightanalysis.scoring.criteria.f3a_criteria import F3A
15
- from flightanalysis.definition.maninfo import Position
16
- from flightanalysis.elements import Element
17
- import geometry as g
18
- import numpy as np
19
- from .basic import Basic
18
+
19
+ from ..el_analysis import ElementAnalysis
20
20
  from .alignment import Alignment
21
- from loguru import logger
21
+ from .basic import Basic
22
22
 
23
23
 
24
24
  @dataclass
@@ -26,6 +26,13 @@ class Complete(Alignment):
26
26
  corrected: Manoeuvre
27
27
  corrected_template: State
28
28
 
29
+ def to_dict(self):
30
+ return dict(
31
+ **super().to_dict(),
32
+ corrected=self.corrected.to_dict(),
33
+ corrected_template=self.corrected_template.to_dict(),
34
+ )
35
+
29
36
  @staticmethod
30
37
  def from_dict(data: dict, fallback=True):
31
38
  pa = Alignment.from_dict(data, fallback)
@@ -56,14 +63,14 @@ class Complete(Alignment):
56
63
  return list(self.mdef.eds.data.keys())
57
64
 
58
65
  def __iter__(self):
59
- for edn in ["entry_line"] + list(self.mdef.eds.data.keys()):
66
+ for edn in list(self.mdef.eds.data.keys()):
60
67
  yield self.get_ea(edn)
61
68
 
62
69
  def __getitem__(self, i):
63
- return self.get_ea("entry_line" if i == 0 else self.mdef.eds[i + 1].name)
70
+ return self.get_ea(self.mdef.eds[i + 1].name)
64
71
 
65
72
  def __getattr__(self, name):
66
- if name in self.mdef.eds.data.keys() or name == "entry_line":
73
+ if name in self.mdef.eds.data.keys():
67
74
  return self.get_ea(name)
68
75
  raise AttributeError(f"Attribute {name} not found in {self.__class__.__name__}")
69
76
 
@@ -112,7 +119,7 @@ class Complete(Alignment):
112
119
  ed: ElDef = self.get_edef(eln)
113
120
  el: Element = self.manoeuvre.all_elements()[eln].match_intention(itrans, fl)
114
121
  tp = el.create_template(State.from_transform(itrans), fl)
115
- return ed.dgs.apply(el.uid, fl, tp, False), tp[-1].att
122
+ return ed.dgs.apply(el, fl, tp, False), tp[-1].att
116
123
 
117
124
  def optimise_split(
118
125
  self, itrans: g.Transformation, eln1: str, eln2: str, fl: State
@@ -194,97 +201,7 @@ class Complete(Alignment):
194
201
  f"pass {count}, {len(padjusted)} elements adjusted:\n{padjusted}"
195
202
  )
196
203
 
197
- return Basic(self.id, self.mdef, fl, self.direction).proceed()
198
-
199
- def side_box(self):
200
- meas = Measurement.side_box(self.flown)
201
- errors, dgs, keys = F3A.intra.box(F3A.intra.box.prepare(meas.value))
202
- return Result(
203
- "side box",
204
- meas,
205
- meas.value,
206
- np.arange(len(meas.value)),
207
- errors,
208
- dgs * meas.visibility[keys],
209
- keys,
210
- F3A.intra.box,
211
- )
212
-
213
- def top_box(self):
214
- meas = Measurement.top_box(self.flown)
215
- errors, dgs, keys = F3A.intra.box(F3A.intra.box.prepare(meas.value))
216
- return Result(
217
- "top box",
218
- meas,
219
- meas.value,
220
- np.arange(len(meas.value)),
221
- errors,
222
- dgs * meas.visibility[keys],
223
- keys,
224
- F3A.intra.box,
225
- )
226
- # return F3A.intra.box("top box", Measurement.top_box(self.flown))
227
-
228
- def create_centre_result(self, name: str, st: State) -> Result:
229
- meas = Measurement.centre_box(st)
230
- errors, dgs, keys = F3A.intra.angle(meas.value)
231
- return Result(
232
- name,
233
- meas,
234
- meas.value,
235
- np.arange(len(meas.value)),
236
- errors,
237
- dgs * meas.visibility[keys],
238
- keys,
239
- F3A.intra.angle,
240
- )
241
-
242
- def centre(self):
243
- results = Results("centres")
244
- for cpid in self.mdef.info.centre_points:
245
- if cpid == len(self.manoeuvre.elements):
246
- st = self.manoeuvre.elements[-1].get_data(self.flown)[-1]
247
- else:
248
- st = self.manoeuvre.elements[cpid].get_data(self.flown)[0]
249
- results.add(self.create_centre_result(f"centre point {cpid}", st))
250
-
251
- for ceid, fac in self.mdef.info.centred_els:
252
- ce = self.manoeuvre.elements[ceid].get_data(self.flown)
253
- path_length = (abs(ce.vel) * ce.dt).cumsum()
254
- id = np.abs(path_length - path_length[-1] * fac).argmin()
255
- results.add(
256
- self.create_centre_result(
257
- f"centred element {ceid}", State(ce.data.iloc[[id], :])
258
- )
259
- )
260
-
261
- if len(results) == 0 and self.mdef.info.position == Position.CENTRE:
262
- al = State(
263
- self.flown.data.loc[
264
- (self.flown.data.element != "entry_line")
265
- & (self.flown.data.element != "exit_line")
266
- ]
267
- )
268
- midy = (np.max(al.y) + np.min(al.y)) / 2
269
- midid = np.abs(al.pos.y - midy).argmin()
270
- results.add(self.create_centre_result("centred manoeuvre", al[midid]))
271
-
272
- return results
273
-
274
- def distance(self):
275
- # TODO doesnt quite cover it, stalled manoeuvres could drift to > 170 for no downgrade
276
- meas = Measurement.depth(self.flown)
277
- mistakes, dgs, dgids = F3A.intra.depth(F3A.intra.depth.prepare(meas.value))
278
- return Result(
279
- "distance",
280
- meas,
281
- meas.value,
282
- np.arange(len(meas.value)),
283
- mistakes,
284
- dgs * meas.visibility[dgids], # should be weighted average visibility
285
- dgids,
286
- F3A.intra.depth,
287
- )
204
+ return Basic(self.id, self.mdef, fl, self.entry, self.exit).proceed()
288
205
 
289
206
  def intra(self):
290
207
  return ElementsResults([ea.intra_score() for ea in self])
@@ -293,22 +210,10 @@ class Complete(Alignment):
293
210
  return self.mdef.mps.collect(self.manoeuvre, self.template)
294
211
 
295
212
  def positioning(self):
296
- pres = Results("positioning")
297
- if self.mdef.info.position == Position.CENTRE:
298
- pres.add(self.centre())
299
- tp_width = max(self.corrected_template.y) - min(self.corrected_template.y)
300
- if tp_width < 10:
301
- pres.add(self.distance())
302
- tb = self.top_box()
303
- if tb.total > 0:
304
- pres.add(self.top_box())
305
- sb = self.side_box()
306
- if sb.total > 0:
307
- pres.add(self.side_box())
308
- return pres
213
+ return self.mdef.box.score(self.mdef.info, self.flown, self.template)
309
214
 
310
215
  def plot_3d(self, **kwargs):
311
- from flightplotting import plotsec, plotdtw
216
+ from flightplotting import plotdtw, plotsec
312
217
 
313
218
  fig = plotdtw(self.flown, self.flown.data.element.unique())
314
219
  return plotsec(self.flown, color="blue", nmodels=20, fig=fig, **kwargs)
@@ -16,6 +16,13 @@ class Scored(Complete):
16
16
  self.corrected_template
17
17
  )
18
18
 
19
+ def to_dict(self):
20
+ return dict(
21
+ **super().to_dict(),
22
+ scores=self.scores.to_dict()
23
+ )
24
+
25
+
19
26
  @staticmethod
20
27
  def from_dict(data:dict, fallback=True):
21
28
  ca = Complete.from_dict(data, fallback)