compass-lib 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl
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.
- compass_lib/__init__.py +5 -3
- compass_lib/commands/__init__.py +0 -1
- compass_lib/commands/convert.py +14 -8
- compass_lib/commands/encrypt.py +89 -0
- compass_lib/commands/main.py +4 -2
- compass_lib/constants.py +36 -0
- compass_lib/encoding.py +27 -0
- compass_lib/enums.py +44 -8
- compass_lib/models.py +129 -0
- compass_lib/parser.py +397 -244
- compass_lib/utils.py +15 -0
- compass_lib-0.0.2.dist-info/METADATA +63 -0
- compass_lib-0.0.2.dist-info/RECORD +16 -0
- {compass_lib-0.0.1.dist-info → compass_lib-0.0.2.dist-info}/WHEEL +1 -3
- compass_lib-0.0.2.dist-info/entry_points.txt +7 -0
- compass_lib/section.py +0 -18
- compass_lib/shot.py +0 -21
- compass_lib-0.0.1.dist-info/METADATA +0 -268
- compass_lib-0.0.1.dist-info/RECORD +0 -14
- compass_lib-0.0.1.dist-info/entry_points.txt +0 -5
- compass_lib-0.0.1.dist-info/top_level.txt +0 -1
- {compass_lib-0.0.1.dist-info → compass_lib-0.0.2.dist-info/licenses}/LICENSE +0 -0
compass_lib/__init__.py
CHANGED
compass_lib/commands/__init__.py
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
compass_lib/commands/convert.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
from pathlib import Path
|
|
@@ -10,22 +10,25 @@ def convert(args: list[str]) -> int:
|
|
|
10
10
|
parser = argparse.ArgumentParser(prog="compass convert")
|
|
11
11
|
|
|
12
12
|
parser.add_argument(
|
|
13
|
+
"-i",
|
|
13
14
|
"--input_file",
|
|
14
15
|
type=str,
|
|
15
16
|
default=None,
|
|
16
17
|
required=True,
|
|
17
|
-
help="Compass Survey Source File."
|
|
18
|
+
help="Compass Survey Source File.",
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
parser.add_argument(
|
|
22
|
+
"-o",
|
|
21
23
|
"--output_file",
|
|
22
24
|
type=str,
|
|
23
25
|
default=None,
|
|
24
26
|
required=True,
|
|
25
|
-
help="Path to save the converted file at."
|
|
27
|
+
help="Path to save the converted file at.",
|
|
26
28
|
)
|
|
27
29
|
|
|
28
30
|
parser.add_argument(
|
|
31
|
+
"-w",
|
|
29
32
|
"--overwrite",
|
|
30
33
|
action="store_true",
|
|
31
34
|
help="Allow overwrite an already existing file.",
|
|
@@ -33,11 +36,12 @@ def convert(args: list[str]) -> int:
|
|
|
33
36
|
)
|
|
34
37
|
|
|
35
38
|
parser.add_argument(
|
|
39
|
+
"-f",
|
|
36
40
|
"--format",
|
|
37
41
|
type=str,
|
|
38
42
|
choices=["json"],
|
|
39
43
|
required=True,
|
|
40
|
-
help="Conversion format used."
|
|
44
|
+
help="Conversion format used.",
|
|
41
45
|
)
|
|
42
46
|
|
|
43
47
|
parsed_args = parser.parse_args(args)
|
|
@@ -48,9 +52,11 @@ def convert(args: list[str]) -> int:
|
|
|
48
52
|
|
|
49
53
|
output_file = Path(parsed_args.output_file)
|
|
50
54
|
if output_file.exists() and not parsed_args.overwrite:
|
|
51
|
-
raise FileExistsError(
|
|
52
|
-
|
|
55
|
+
raise FileExistsError(
|
|
56
|
+
f"The file {output_file} already existing. "
|
|
57
|
+
"Please pass the flag `--overwrite` to ignore."
|
|
58
|
+
)
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
survey = CompassParser.load_dat_file(dmp_file)
|
|
61
|
+
survey.to_json(filepath=output_file)
|
|
56
62
|
return 0
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from cryptography.fernet import Fernet
|
|
9
|
+
from dotenv import load_dotenv
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
logger.setLevel(logging.INFO)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def encrypt(args: list[str]) -> int:
|
|
16
|
+
parser = argparse.ArgumentParser(prog="compass encrypt")
|
|
17
|
+
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"-i",
|
|
20
|
+
"--input_file",
|
|
21
|
+
type=str,
|
|
22
|
+
default=None,
|
|
23
|
+
required=True,
|
|
24
|
+
help="Compass Survey Source File.",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"-o",
|
|
29
|
+
"--output_file",
|
|
30
|
+
type=str,
|
|
31
|
+
default=None,
|
|
32
|
+
required=True,
|
|
33
|
+
help="Path to save the converted file at.",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"-e",
|
|
38
|
+
"--env_file",
|
|
39
|
+
type=str,
|
|
40
|
+
default=None,
|
|
41
|
+
required=True,
|
|
42
|
+
help="Path of the environment file containing the Fernet key.",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
parser.add_argument(
|
|
46
|
+
"-w",
|
|
47
|
+
"--overwrite",
|
|
48
|
+
action="store_true",
|
|
49
|
+
help="Allow overwrite an already existing file.",
|
|
50
|
+
default=False,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
parsed_args = parser.parse_args(args)
|
|
54
|
+
|
|
55
|
+
if not (input_file := Path(parsed_args.input_file)).exists():
|
|
56
|
+
raise FileNotFoundError(f"Impossible to find: `{input_file}`.")
|
|
57
|
+
|
|
58
|
+
if (
|
|
59
|
+
output_file := Path(parsed_args.output_file)
|
|
60
|
+
).exists() and not parsed_args.overwrite:
|
|
61
|
+
raise FileExistsError(
|
|
62
|
+
f"The file {output_file} already existing. "
|
|
63
|
+
"Please pass the flag `--overwrite` to ignore."
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
if not (envfile := Path(parsed_args.env_file)).exists():
|
|
67
|
+
raise FileNotFoundError(f"Impossible to find: `{envfile}`.")
|
|
68
|
+
load_dotenv(envfile, verbose=True, override=True)
|
|
69
|
+
logger.info("Loaded environment variables from: `%s`", envfile)
|
|
70
|
+
|
|
71
|
+
if (fernet_key := os.getenv("ARTIFACT_ENCRYPTION_KEY")) is None:
|
|
72
|
+
raise ValueError(
|
|
73
|
+
"No Fernet key found in the environment file. "
|
|
74
|
+
"Check if `ARTIFACT_ENCRYPTION_KEY` is set."
|
|
75
|
+
)
|
|
76
|
+
fernet_key = Fernet(fernet_key)
|
|
77
|
+
|
|
78
|
+
with input_file.open("rb") as f:
|
|
79
|
+
clear_data = f.read()
|
|
80
|
+
|
|
81
|
+
with output_file.open("wb") as f:
|
|
82
|
+
f.write(fernet_key.encrypt(clear_data))
|
|
83
|
+
|
|
84
|
+
# Round Trip Check:
|
|
85
|
+
with output_file.open("rb") as f:
|
|
86
|
+
roundtrip_data = fernet_key.decrypt(f.read())
|
|
87
|
+
assert clear_data == roundtrip_data
|
|
88
|
+
|
|
89
|
+
return 0
|
compass_lib/commands/main.py
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
from importlib.metadata import entry_points
|
|
5
5
|
|
|
6
6
|
import compass_lib
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
def main():
|
|
9
|
-
registered_commands = entry_points(group=
|
|
10
|
+
registered_commands = entry_points(group="compass.actions")
|
|
10
11
|
|
|
11
12
|
parser = argparse.ArgumentParser(prog="compass_lib")
|
|
12
13
|
parser.add_argument(
|
|
14
|
+
"-v",
|
|
13
15
|
"--version",
|
|
14
16
|
action="version",
|
|
15
17
|
version=f"%(prog)s version: {compass_lib.__version__}",
|
compass_lib/constants.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from compass_lib.enums import ShotFlag
|
|
6
|
+
|
|
7
|
+
# ============================== SPECIAL CHARS ============================== #
|
|
8
|
+
|
|
9
|
+
COMPASS_SECTION_SEPARATOR = "\f" # Form_feed: https://www.ascii-code.com/12
|
|
10
|
+
COMPASS_END_OF_FILE = "\x1a" # Substitute: https://www.ascii-code.com/26
|
|
11
|
+
|
|
12
|
+
# ================================== REGEX ================================== #
|
|
13
|
+
# Priorized regex:
|
|
14
|
+
# 1. Section Split with `\r\n`
|
|
15
|
+
# 2. Section Split with `\n`
|
|
16
|
+
# 3. Section Split with `\f` alone
|
|
17
|
+
COMPASS_SECTION_SPLIT_RE = re.compile(
|
|
18
|
+
rf"{COMPASS_SECTION_SEPARATOR}\r\n|{COMPASS_SECTION_SEPARATOR}\n|{COMPASS_SECTION_SEPARATOR}"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# String format:
|
|
22
|
+
# - `SURVEY NAME: toc+187?`
|
|
23
|
+
COMPASS_SECTION_NAME_RE = re.compile(r"SURVEY NAME:\s*(?P<section_name>\S*)")
|
|
24
|
+
|
|
25
|
+
# String format:
|
|
26
|
+
# - `SURVEY DATE: 1 26 86`
|
|
27
|
+
# - `SURVEY DATE: 4 22 2001`
|
|
28
|
+
# - `SURVEY DATE: 8 28 1988 COMMENT:Surface to shelter`
|
|
29
|
+
COMPASS_DATE_COMMENT_RE = re.compile(
|
|
30
|
+
r"^SURVEY DATE:\s*(?P<date>\d{1,2}\s+\d{1,2}\s+\d{2,4}|None)(?:\s+COMMENT:\s*(?P<comment>.*))?$" # noqa: E501
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
COMPASS_SHOT_FLAGS_RE = re.compile(
|
|
34
|
+
rf"({ShotFlag.__start_token__}"
|
|
35
|
+
rf"([{''.join(ShotFlag._value2member_map_.keys())}]*){ShotFlag.__end_token__})*(.*)"
|
|
36
|
+
)
|
compass_lib/encoding.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
import json
|
|
5
|
+
import uuid
|
|
6
|
+
from dataclasses import asdict
|
|
7
|
+
from dataclasses import is_dataclass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class EnhancedJSONEncoder(json.JSONEncoder):
|
|
11
|
+
def default(self, obj):
|
|
12
|
+
from compass_lib.parser import ShotFlag # noqa: PLC0415
|
|
13
|
+
|
|
14
|
+
match obj:
|
|
15
|
+
case datetime.date():
|
|
16
|
+
return obj.isoformat()
|
|
17
|
+
|
|
18
|
+
case ShotFlag():
|
|
19
|
+
return obj.value
|
|
20
|
+
|
|
21
|
+
case uuid.UUID():
|
|
22
|
+
return str(obj)
|
|
23
|
+
|
|
24
|
+
if is_dataclass(obj):
|
|
25
|
+
return asdict(obj)
|
|
26
|
+
|
|
27
|
+
return super().default(obj)
|
compass_lib/enums.py
CHANGED
|
@@ -1,10 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from enum import Enum
|
|
4
|
+
from enum import IntEnum
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from typing_extensions import Self
|
|
10
|
+
|
|
2
11
|
|
|
3
12
|
class CustomEnum(Enum):
|
|
4
13
|
@classmethod
|
|
5
14
|
def reverse(cls, name):
|
|
6
15
|
return cls._value2member_map_[name]
|
|
7
16
|
|
|
17
|
+
|
|
18
|
+
class CompassFileType(IntEnum):
|
|
19
|
+
DAT = 0
|
|
20
|
+
MAK = 1
|
|
21
|
+
PLT = 2
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def from_str(cls, value: str) -> Self:
|
|
25
|
+
try:
|
|
26
|
+
return cls[value.upper()]
|
|
27
|
+
except KeyError as e:
|
|
28
|
+
raise ValueError(f"Unknown value: {value.upper()}") from e
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def from_path(cls, filepath: str | Path):
|
|
32
|
+
if not isinstance(filepath, Path):
|
|
33
|
+
filepath = Path(filepath)
|
|
34
|
+
|
|
35
|
+
return cls.from_str(filepath.suffix.upper()[1:]) # Remove the leading `.`
|
|
36
|
+
|
|
37
|
+
|
|
8
38
|
# ============================== Azimuth ============================== #
|
|
9
39
|
|
|
10
40
|
# export const azimuthUnits: { [string]: DisplayAzimuthUnit } = {
|
|
@@ -13,11 +43,13 @@ class CustomEnum(Enum):
|
|
|
13
43
|
# G: 'gradians',
|
|
14
44
|
# }
|
|
15
45
|
|
|
46
|
+
|
|
16
47
|
class AzimuthUnits(CustomEnum):
|
|
17
48
|
DEGREES = "D"
|
|
18
49
|
QUADS = "Q"
|
|
19
50
|
GRADIANS = "G"
|
|
20
51
|
|
|
52
|
+
|
|
21
53
|
# ============================== Inclination Unit ============================== #
|
|
22
54
|
|
|
23
55
|
# export const inclinationUnits: { [string]: DisplayInclinationUnit } = {
|
|
@@ -28,6 +60,7 @@ class AzimuthUnits(CustomEnum):
|
|
|
28
60
|
# W: 'depthGauge',
|
|
29
61
|
# }
|
|
30
62
|
|
|
63
|
+
|
|
31
64
|
class InclinationUnits(CustomEnum):
|
|
32
65
|
DEGREES = "D"
|
|
33
66
|
PERCENT_GRADE = "G"
|
|
@@ -35,6 +68,7 @@ class InclinationUnits(CustomEnum):
|
|
|
35
68
|
GRADIANS = "R"
|
|
36
69
|
DEPTH_GAUGE = "W"
|
|
37
70
|
|
|
71
|
+
|
|
38
72
|
# ============================== Length Unit ============================== #
|
|
39
73
|
|
|
40
74
|
|
|
@@ -44,11 +78,13 @@ class InclinationUnits(CustomEnum):
|
|
|
44
78
|
# M: 'meters',
|
|
45
79
|
# }
|
|
46
80
|
|
|
81
|
+
|
|
47
82
|
class LengthUnits(CustomEnum):
|
|
48
83
|
DECIMAL_FEET = "D"
|
|
49
84
|
FEET_AND_INCHES = "I"
|
|
50
85
|
METERS = "M"
|
|
51
86
|
|
|
87
|
+
|
|
52
88
|
# ============================== LRUD ============================== #
|
|
53
89
|
|
|
54
90
|
# export const lrudItems: { [string]: LrudItem } = {
|
|
@@ -58,12 +94,14 @@ class LengthUnits(CustomEnum):
|
|
|
58
94
|
# D: 'down',
|
|
59
95
|
# }
|
|
60
96
|
|
|
97
|
+
|
|
61
98
|
class LRUD(CustomEnum):
|
|
62
99
|
LEFT = "L"
|
|
63
100
|
RIGHT = "R"
|
|
64
101
|
UP = "U"
|
|
65
102
|
DOWN = "D"
|
|
66
103
|
|
|
104
|
+
|
|
67
105
|
# ============================== ShotItem ============================== #
|
|
68
106
|
|
|
69
107
|
# export const shotMeasurementItems: { [string]: ShotMeasurementItem } = {
|
|
@@ -74,6 +112,7 @@ class LRUD(CustomEnum):
|
|
|
74
112
|
# d: 'backsightInclination',
|
|
75
113
|
# }
|
|
76
114
|
|
|
115
|
+
|
|
77
116
|
class ShotItem(CustomEnum):
|
|
78
117
|
LENGTH = "L"
|
|
79
118
|
FRONTSIGHT_AZIMUTH = "A"
|
|
@@ -81,6 +120,7 @@ class ShotItem(CustomEnum):
|
|
|
81
120
|
BACKSIGHT_AZIMUTH = "a"
|
|
82
121
|
BACKSIGHT_INCLINATION = "d"
|
|
83
122
|
|
|
123
|
+
|
|
84
124
|
# ============================== StationSide ============================== #
|
|
85
125
|
|
|
86
126
|
# export const stationSides: { [string]: StationSide } = {
|
|
@@ -88,6 +128,7 @@ class ShotItem(CustomEnum):
|
|
|
88
128
|
# T: 'to',
|
|
89
129
|
# }
|
|
90
130
|
|
|
131
|
+
|
|
91
132
|
class StationSide(CustomEnum):
|
|
92
133
|
FROM = "F"
|
|
93
134
|
TO = "T"
|
|
@@ -95,6 +136,7 @@ class StationSide(CustomEnum):
|
|
|
95
136
|
|
|
96
137
|
# ============================== ShotFlag ============================== #
|
|
97
138
|
|
|
139
|
+
|
|
98
140
|
class ShotFlag(CustomEnum):
|
|
99
141
|
EXCLUDE_PLOTING = "P"
|
|
100
142
|
EXCLUDE_CLOSURE = "C"
|
|
@@ -102,11 +144,5 @@ class ShotFlag(CustomEnum):
|
|
|
102
144
|
TOTAL_EXCLUSION = "X"
|
|
103
145
|
SPLAY = "S"
|
|
104
146
|
|
|
105
|
-
__start_token__ = r"#\|"
|
|
106
|
-
__end_token__ = r"#"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if __name__ == "__main__":
|
|
111
|
-
print(StationSide.FROM.value)
|
|
112
|
-
print(StationSide.reverse("F"))
|
|
147
|
+
__start_token__ = r"#\|" # noqa: S105
|
|
148
|
+
__end_token__ = r"#" # noqa: S105
|
compass_lib/models.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import datetime # noqa: TC003
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
from pydantic import ConfigDict
|
|
11
|
+
from pydantic import Field
|
|
12
|
+
from pydantic import field_validator
|
|
13
|
+
|
|
14
|
+
from compass_lib.encoding import EnhancedJSONEncoder
|
|
15
|
+
|
|
16
|
+
# from compass_lib.errors import DuplicateValueError
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SurveyShot(BaseModel):
|
|
20
|
+
from_id: str
|
|
21
|
+
to_id: str
|
|
22
|
+
|
|
23
|
+
azimuth: Annotated[float, Field(ge=0, lt=360)]
|
|
24
|
+
|
|
25
|
+
inclination: Annotated[float, Field(ge=-90, le=90)]
|
|
26
|
+
length: Annotated[float, Field(ge=0)]
|
|
27
|
+
|
|
28
|
+
# Optional Values
|
|
29
|
+
comment: str | None = None
|
|
30
|
+
flags: Any | None = None
|
|
31
|
+
|
|
32
|
+
azimuth2: Annotated[float, Field(ge=0, lt=360)] | None = None
|
|
33
|
+
inclination2: Annotated[float, Field(ge=-90, le=90)] | None = None
|
|
34
|
+
|
|
35
|
+
# LRUD
|
|
36
|
+
left: Annotated[float, Field(ge=0)] = 0.0
|
|
37
|
+
right: Annotated[float, Field(ge=0)] = 0.0
|
|
38
|
+
up: Annotated[float, Field(ge=0)] = 0.0
|
|
39
|
+
down: Annotated[float, Field(ge=0)] = 0.0
|
|
40
|
+
|
|
41
|
+
model_config = ConfigDict(extra="forbid")
|
|
42
|
+
|
|
43
|
+
@field_validator("left", "right", "up", "down", mode="before")
|
|
44
|
+
@classmethod
|
|
45
|
+
def validate_lrud(cls, value: float) -> float:
|
|
46
|
+
return value if value > 0 else 0.0
|
|
47
|
+
|
|
48
|
+
@field_validator("azimuth", "azimuth2", mode="before")
|
|
49
|
+
@classmethod
|
|
50
|
+
def validate_azimuth(cls, value: float) -> float:
|
|
51
|
+
return value if value > 0 else 0.0
|
|
52
|
+
|
|
53
|
+
@field_validator("inclination2", mode="before")
|
|
54
|
+
@classmethod
|
|
55
|
+
def validate_inclination2(cls, value: float) -> float:
|
|
56
|
+
return value if -90 <= value <= 90 else 0.0
|
|
57
|
+
|
|
58
|
+
# ======================== VALIDATOR UTILS ======================== #
|
|
59
|
+
|
|
60
|
+
# @classmethod
|
|
61
|
+
# def validate_unique(cls, field: str, values: list) -> list:
|
|
62
|
+
# vals2check = [getattr(val, field) for val in values]
|
|
63
|
+
# dupl_vals = list(duplicates(vals2check))
|
|
64
|
+
# if dupl_vals:
|
|
65
|
+
# raise DuplicateValueError(
|
|
66
|
+
# f"[{cls.__name__}] Duplicate value found for `{field}`: "
|
|
67
|
+
# f"{dupl_vals}"
|
|
68
|
+
# )
|
|
69
|
+
# return values
|
|
70
|
+
|
|
71
|
+
# @field_validator("to_id", mode="before")
|
|
72
|
+
# @classmethod
|
|
73
|
+
# def validate_unique_to_id(cls, value: str | None) -> str:
|
|
74
|
+
# """Note: Validators are only ran with custom fed values.
|
|
75
|
+
# Not autogenerated ones. Hence we need to register the name."""
|
|
76
|
+
|
|
77
|
+
# if value is None or value == "":
|
|
78
|
+
# return cls.to_id.default_factory()
|
|
79
|
+
|
|
80
|
+
# # 1. Verify the name is only composed of valid chars.
|
|
81
|
+
# for char in value:
|
|
82
|
+
# if char.upper() not in [
|
|
83
|
+
# *UniqueNameGenerator.VOCAB,
|
|
84
|
+
# *list("_-~:!?.'()[]{}@*&#%|$")
|
|
85
|
+
# ]:
|
|
86
|
+
# raise ValueError(f"The character `{char}` is not allowed as `name`.")
|
|
87
|
+
|
|
88
|
+
# if len(value) > COMPASS_MAX_NAME_LENGTH:
|
|
89
|
+
# raise ValueError(f"Name {value} is too long, maximum allowed: "
|
|
90
|
+
# f"{COMPASS_MAX_NAME_LENGTH}")
|
|
91
|
+
|
|
92
|
+
# UniqueNameGenerator.register(value=value)
|
|
93
|
+
# return value
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class SurveySection(BaseModel):
|
|
97
|
+
name: str
|
|
98
|
+
comment: str
|
|
99
|
+
correction: list[float]
|
|
100
|
+
correction2: list[float]
|
|
101
|
+
survey_date: datetime.date | None = None
|
|
102
|
+
discovery_date: datetime.date | None = None
|
|
103
|
+
declination: float
|
|
104
|
+
format: str = "DDDDUDLRLADN"
|
|
105
|
+
shots: list[SurveyShot]
|
|
106
|
+
surveyors: str | None = None
|
|
107
|
+
|
|
108
|
+
model_config = ConfigDict(extra="forbid")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class Survey(BaseModel):
|
|
112
|
+
cave_name: str
|
|
113
|
+
description: str = ""
|
|
114
|
+
|
|
115
|
+
sections: list[SurveySection] = []
|
|
116
|
+
|
|
117
|
+
model_config = ConfigDict(extra="forbid")
|
|
118
|
+
|
|
119
|
+
def to_json(self, filepath: str | Path | None = None) -> str:
|
|
120
|
+
filepath = Path(filepath) if filepath else None
|
|
121
|
+
data = self.model_dump()
|
|
122
|
+
|
|
123
|
+
json_str = json.dumps(data, indent=4, sort_keys=True, cls=EnhancedJSONEncoder)
|
|
124
|
+
|
|
125
|
+
if filepath is not None:
|
|
126
|
+
with filepath.open(mode="w") as file:
|
|
127
|
+
file.write(json_str)
|
|
128
|
+
|
|
129
|
+
return json_str
|