corva-worker-python 2.0.0__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.
Files changed (63) hide show
  1. corva_worker_python-2.0.0.dist-info/METADATA +30 -0
  2. corva_worker_python-2.0.0.dist-info/RECORD +63 -0
  3. corva_worker_python-2.0.0.dist-info/WHEEL +5 -0
  4. corva_worker_python-2.0.0.dist-info/top_level.txt +1 -0
  5. worker/__init__.py +5 -0
  6. worker/app/__init__.py +291 -0
  7. worker/app/modules/__init__.py +265 -0
  8. worker/app/modules/activity_module.py +141 -0
  9. worker/app/modules/connection_module.py +21 -0
  10. worker/app/modules/depth_activity_module.py +21 -0
  11. worker/app/modules/scheduler.py +44 -0
  12. worker/app/modules/time_activity_module.py +21 -0
  13. worker/app/modules/trigger.py +43 -0
  14. worker/constants.py +51 -0
  15. worker/data/__init__.py +0 -0
  16. worker/data/activity/__init__.py +132 -0
  17. worker/data/activity/activity_grouping.py +242 -0
  18. worker/data/alert.py +89 -0
  19. worker/data/api.py +155 -0
  20. worker/data/enums.py +141 -0
  21. worker/data/json_encoder.py +18 -0
  22. worker/data/math.py +104 -0
  23. worker/data/operations.py +477 -0
  24. worker/data/serialization.py +110 -0
  25. worker/data/task_handler.py +82 -0
  26. worker/data/two_way_dict.py +17 -0
  27. worker/data/unit_conversions.py +5 -0
  28. worker/data/wits.py +323 -0
  29. worker/event/__init__.py +53 -0
  30. worker/event/event_handler.py +90 -0
  31. worker/event/scheduled.py +64 -0
  32. worker/event/stream.py +48 -0
  33. worker/exceptions.py +26 -0
  34. worker/mixins/__init__.py +0 -0
  35. worker/mixins/logging.py +119 -0
  36. worker/mixins/rollbar.py +87 -0
  37. worker/partial_rerun_merge/__init__.py +0 -0
  38. worker/partial_rerun_merge/merge.py +500 -0
  39. worker/partial_rerun_merge/models.py +91 -0
  40. worker/partial_rerun_merge/progress.py +241 -0
  41. worker/state/__init__.py +96 -0
  42. worker/state/mixins.py +111 -0
  43. worker/state/state.py +46 -0
  44. worker/test/__init__.py +3 -0
  45. worker/test/lambda_function_test_run.py +196 -0
  46. worker/test/local_testing/__init__.py +0 -0
  47. worker/test/local_testing/to_local_transfer.py +360 -0
  48. worker/test/utils.py +51 -0
  49. worker/wellbore/__init__.py +0 -0
  50. worker/wellbore/factory.py +496 -0
  51. worker/wellbore/measured_depth_finder.py +12 -0
  52. worker/wellbore/model/__init__.py +0 -0
  53. worker/wellbore/model/ann.py +103 -0
  54. worker/wellbore/model/annulus.py +113 -0
  55. worker/wellbore/model/drillstring.py +196 -0
  56. worker/wellbore/model/drillstring_components.py +439 -0
  57. worker/wellbore/model/element.py +102 -0
  58. worker/wellbore/model/enums.py +92 -0
  59. worker/wellbore/model/hole.py +297 -0
  60. worker/wellbore/model/hole_section.py +51 -0
  61. worker/wellbore/model/riser.py +22 -0
  62. worker/wellbore/sections_mixin.py +64 -0
  63. worker/wellbore/wellbore.py +289 -0
@@ -0,0 +1,141 @@
1
+ from itertools import groupby
2
+
3
+ from worker import constants
4
+ from worker.app.modules.__init__ import Module
5
+ from worker.data import operations
6
+ from worker.data.operations import get_config_by_id
7
+
8
+
9
+ class ActivityModule(Module):
10
+ """
11
+ This is an abstract base module that needs to be extended by an actual module.
12
+ """
13
+
14
+ # module_key is used for redis access and state of this module
15
+ module_key = "activity_module"
16
+ collection = "activity_module_collection"
17
+
18
+ # override
19
+ def run(self, wits_stream: list):
20
+ """
21
+ :param wits_stream: a wits stream event
22
+ :return:
23
+ """
24
+ super().run(wits_stream)
25
+
26
+ dataset = self.load_dataset(wits_stream)
27
+
28
+ # this guarantees every group has the same config
29
+ config_grouped_dataset = self.group_data_stream_based_on_config(dataset)
30
+
31
+ for group in config_grouped_dataset:
32
+ results = self.check_for_string_change(group)
33
+
34
+ if self.should_run_processor(group):
35
+ results = self.run_module(group, results)
36
+
37
+ self.save_state()
38
+ self.store_output(self.global_state["asset_id"], results)
39
+
40
+ @staticmethod
41
+ def group_data_stream_based_on_config(dataset):
42
+ """Group our datasets by the metadata such as drillstring, and then reflatten to be a list of lists
43
+ for processing"""
44
+
45
+ groups = groupby(dataset, lambda x: x["metadata"])
46
+ grouped_dataset = [list(dataset) for group, dataset in groups]
47
+
48
+ return grouped_dataset
49
+
50
+ # override
51
+ def should_run_processor(self, event):
52
+ running_string = constants.get("{}.{}.running-string".format(self.app_key, self.module_key))
53
+ if running_string and self.state.get("active_string_type", "") in running_string:
54
+ return True
55
+
56
+ return False
57
+
58
+ def check_for_string_change(self, data):
59
+ """
60
+ To check if the active string in the wellbore has been changed and
61
+ export proper text to the output.
62
+ Note that the last_exported_timestamp is not set here.
63
+ Note that all the configs of this data have the same config properties.
64
+ :param data:
65
+ :return:
66
+ """
67
+
68
+ previous_string = self.state.get("active_string_id", "")
69
+ running_string = constants.get("{}.{}.running-string".format(self.app_key, self.module_key))
70
+ reset_config = constants.get("{}.{}.reset-config".format(self.app_key, self.module_key))
71
+
72
+ if not running_string:
73
+ return []
74
+
75
+ if not data:
76
+ return []
77
+
78
+ first_wits = data[0]
79
+ drillstring_id = first_wits.get("metadata", {}).get("drillstring", None)
80
+ casing_id = first_wits.get("metadata", {}).get("casing", None)
81
+
82
+ if drillstring_id:
83
+ active_string_type = "drillstring"
84
+ active_string_id = drillstring_id
85
+ elif casing_id:
86
+ active_string_type = "casing"
87
+ active_string_id = casing_id
88
+ else:
89
+ active_string_type = None
90
+ active_string_id = None
91
+
92
+ if previous_string == active_string_id:
93
+ return []
94
+
95
+ main_structure = self.build_empty_output(first_wits)
96
+
97
+ warning = None
98
+
99
+ if active_string_type in running_string:
100
+ if active_string_type not in reset_config:
101
+ return []
102
+ else:
103
+ message = "This module does not run while {} is the active string in the well.".format(active_string_type)
104
+ warning = {"message": message}
105
+
106
+ res = self.configure_output_at_config_change(main_structure, active_string_type, active_string_id, warning)
107
+
108
+ return [res]
109
+
110
+ def configure_output_at_config_change(self, main_structure, active_string_type, active_string_id, warning=None):
111
+ extra_elements = self.create_output_for_new_config(active_string_type, active_string_id)
112
+ main_structure["data"] = operations.merge_dicts(main_structure["data"], extra_elements)
113
+
114
+ if warning:
115
+ main_structure["data"] = operations.merge_dicts(main_structure["data"], {"warning": warning})
116
+
117
+ return main_structure
118
+
119
+ def create_output_for_new_config(self, active_string_type, active_string_id):
120
+ self.set_state("active_string_type", active_string_type)
121
+ self.set_state("active_string_id", active_string_id)
122
+
123
+ drillstring_number = None
124
+ if active_string_type == "drillstring":
125
+ drillstring = get_config_by_id(active_string_id, collection="data.drillstring")
126
+
127
+ if drillstring:
128
+ drillstring_number = drillstring.get("data", {}).get("id", None)
129
+
130
+ self.set_state("drillstring_number", drillstring_number)
131
+
132
+ return self.get_config_properties()
133
+
134
+ def get_config_properties(self):
135
+ properties = {
136
+ "active_string_type": self.state.get("active_string_type", ""),
137
+ "active_string_id": self.state.get("active_string_id", ""),
138
+ "drillstring_number": self.state.get("drillstring_number", None),
139
+ }
140
+
141
+ return properties
@@ -0,0 +1,21 @@
1
+ from worker.app.modules.activity_module import ActivityModule
2
+
3
+
4
+ class ConnectionModule(ActivityModule):
5
+ """
6
+ This is an abstract module that needs to be extended by actual modules.
7
+ An example of this module would be running an app after a connection occur.
8
+ """
9
+
10
+ module_key = "connection_module"
11
+ collection = "connection_collection"
12
+
13
+ # override
14
+ def process_module_state(self, state):
15
+ state = super().process_module_state(state)
16
+
17
+ if self.global_state.get("reset", False):
18
+ # todo
19
+ pass
20
+
21
+ return state
@@ -0,0 +1,21 @@
1
+ from worker.app.modules.activity_module import ActivityModule
2
+
3
+
4
+ class DepthActivityModule(ActivityModule):
5
+ """
6
+ This is an abstract module that needs to be extended by actual modules.
7
+ An example of this module would be running an app every 1 ft.
8
+ """
9
+
10
+ module_key = "depth_activity_module"
11
+ collection = "depth_activity_collection"
12
+
13
+ # override
14
+ def process_module_state(self, state):
15
+ state = super().process_module_state(state)
16
+
17
+ if self.global_state.get("reset", False):
18
+ # todo
19
+ pass
20
+
21
+ return state
@@ -0,0 +1,44 @@
1
+ from worker import constants
2
+ from worker.app.modules.__init__ import Module
3
+
4
+
5
+ class Scheduler(Module):
6
+ """
7
+ This is an abstract module that needs to be extended by actual modules.
8
+ An example of this module would be running an app at the frequency of
9
+ once every X seconds regardless of the activity.
10
+ """
11
+
12
+ module_key = "scheduler_module"
13
+ collection = "scheduler_collection"
14
+ module_state_fields = {"last_processed_timestamp": int}
15
+
16
+ # override
17
+ def run(self, wits_stream: list):
18
+ super().run(wits_stream)
19
+
20
+ dataset = self.load_dataset(wits_stream)
21
+
22
+ results = []
23
+ for el in dataset:
24
+ if self.should_run_processor(el):
25
+ results = self.run_module(el, results)
26
+
27
+ self.save_state()
28
+ self.store_output(self.global_state["asset_id"], results)
29
+
30
+ # override
31
+ def should_run_processor(self, event: dict):
32
+ """
33
+ check one wits event and test against the last processed timestamp
34
+ :param event:
35
+ :return:
36
+ """
37
+ timestamp = event.get("timestamp", 0)
38
+ running_frequency = constants.get("{}.{}.running_frequency".format(self.app_key, self.module_key))
39
+ if self.state.get("last_processed_timestamp", 0) + running_frequency <= timestamp:
40
+ # update the last_processed_timestamp
41
+ self.set_state("last_processed_timestamp", timestamp)
42
+ return True
43
+
44
+ return False
@@ -0,0 +1,21 @@
1
+ from worker.app.modules.activity_module import ActivityModule
2
+
3
+
4
+ class TimeActivityModule(ActivityModule):
5
+ """
6
+ This is an abstract time-base module that needs to be extended by actual modules.
7
+ An example of this module would be running an app every 1 minute.
8
+ """
9
+
10
+ module_key = "time_activity_module"
11
+ collection = "time_activity_collection"
12
+
13
+ # override
14
+ def process_module_state(self, state):
15
+ state = super().process_module_state(state)
16
+
17
+ if self.global_state.get("reset", False):
18
+ # todo
19
+ pass
20
+
21
+ return state
@@ -0,0 +1,43 @@
1
+ from worker import constants
2
+ from worker.app.modules.__init__ import Module
3
+
4
+
5
+ class Trigger(Module):
6
+ """
7
+ This is an abstract module that needs to be extended by actual modules.
8
+ The idea behind this module is that it runs once for a bunch of data
9
+ only if the the time step is passed.
10
+ """
11
+
12
+ module_key = "trigger_module"
13
+ collection = "trigger_collection"
14
+ module_state_fields = {"last_processed_timestamp": int}
15
+
16
+ # override
17
+ def run(self, wits_stream: list):
18
+ super().run(wits_stream)
19
+
20
+ dataset = self.load_dataset(wits_stream)
21
+ last_wits = dataset[-1]
22
+ results = []
23
+ if self.should_run_processor(last_wits):
24
+ results = self.run_module(last_wits, results)
25
+
26
+ self.save_state()
27
+ self.store_output(self.global_state["asset_id"], results)
28
+
29
+ # override
30
+ def should_run_processor(self, event: dict):
31
+ """
32
+ check one wits event and test against the last processed timestamp
33
+ :param event:
34
+ :return:
35
+ """
36
+ timestamp = event.get("timestamp", 0)
37
+ running_frequency = constants.get("{}.{}.running_frequency".format(self.app_key, self.module_key))
38
+ if self.state.get("last_processed_timestamp", 0) + running_frequency < timestamp:
39
+ # update the last_processed_timestamp
40
+ self.set_state("last_processed_timestamp", timestamp)
41
+ return True
42
+
43
+ return False
worker/constants.py ADDED
@@ -0,0 +1,51 @@
1
+ """
2
+ this constants module can be overridden by your new constants module by importing
3
+ the this module and updating it like this:
4
+ from worker import constants
5
+ constants.update({
6
+ ...
7
+ })
8
+ """
9
+
10
+ from worker.data.operations import get_data_by_path
11
+
12
+ parameters = {
13
+ "global": {
14
+ "app-name": "my-app",
15
+ "app-key": "my-app-key",
16
+ "event-type": "scheduler",
17
+ "query-limit": 1000,
18
+ },
19
+ }
20
+
21
+
22
+ def get(path, default=None):
23
+ """
24
+ Get the value at the specified path in the parameters dictionary.
25
+
26
+ Args:
27
+ path (str): The path to the value to retrieve.
28
+ default (Any, optional): The default value to return if the path
29
+ is not found. Defaults to None.
30
+
31
+ Returns:
32
+ Any: The value at the specified path, or the default value
33
+ if the path is not found.
34
+ """
35
+ return get_data_by_path(data=parameters, path=path, default=default)
36
+
37
+
38
+ def update(additional_parameters):
39
+ """
40
+ The purpose of this method is to update the existing parameters data
41
+ with the provided additional parameters. Note that the globals will
42
+ be replaced if the additional parameters contains global node.
43
+ :param additional_parameters:
44
+ :return:
45
+ """
46
+ _globals = parameters.pop("global")
47
+ additional_globals = additional_parameters.pop("global", {})
48
+ _globals.update(additional_globals)
49
+ parameters.update(additional_parameters)
50
+ if _globals:
51
+ parameters["global"] = _globals
File without changes
@@ -0,0 +1,132 @@
1
+ from enum import Enum
2
+
3
+ from worker.data.serialization import serialization
4
+
5
+
6
+ class PipeMovementStatus(Enum):
7
+ UNCLASSIFIED = -1
8
+ IN_SLIPS = 0
9
+ STATIC = 1
10
+ ON_BOTTOM = 2
11
+ PULL_OUT_OF_HOLE = 3
12
+ RUN_IN_HOLE = 4
13
+ DRILLING = 5
14
+
15
+ def is_moving_out(self) -> bool:
16
+ move_actions = [
17
+ PipeMovementStatus.PULL_OUT_OF_HOLE,
18
+ ]
19
+ return self in move_actions
20
+
21
+ def is_moving_in(self) -> bool:
22
+ move_actions = [PipeMovementStatus.RUN_IN_HOLE, PipeMovementStatus.DRILLING]
23
+ return self in move_actions
24
+
25
+ def is_moving(self) -> bool:
26
+ return self.is_moving_in() or self.is_moving_out()
27
+
28
+
29
+ class PumpStatus(Enum):
30
+ OFF = 0
31
+ ON = 1
32
+
33
+
34
+ class RotationStatus(Enum):
35
+ OFF = 0
36
+ ON = 1
37
+
38
+
39
+ @serialization
40
+ class Activity(bytes, Enum):
41
+ ROTARY_DRILLING = ("Rotary Drilling", PipeMovementStatus.DRILLING, PumpStatus.ON, RotationStatus.ON)
42
+ SLIDE_DRILLING = ("Slide Drilling", PipeMovementStatus.DRILLING, PumpStatus.ON, RotationStatus.OFF)
43
+
44
+ RUN_IN_HOLE = ("Run in Hole", PipeMovementStatus.RUN_IN_HOLE, PumpStatus.OFF, RotationStatus.OFF)
45
+ WASHING_DOWN = ("Washing Down", PipeMovementStatus.RUN_IN_HOLE, PumpStatus.ON, RotationStatus.OFF)
46
+ DRY_REAMING_DOWN = ("Dry Reaming Down", PipeMovementStatus.RUN_IN_HOLE, PumpStatus.OFF, RotationStatus.ON)
47
+ REAMING_DOWN = ("Reaming Down", PipeMovementStatus.RUN_IN_HOLE, PumpStatus.ON, RotationStatus.ON)
48
+
49
+ PULL_OUT_OF_HOLE = ("Pull out of Hole", PipeMovementStatus.PULL_OUT_OF_HOLE, PumpStatus.OFF, RotationStatus.OFF)
50
+ WASHING_UP = ("Washing Up", PipeMovementStatus.PULL_OUT_OF_HOLE, PumpStatus.ON, RotationStatus.OFF)
51
+ DRY_REAMING_UP = ("Dry Reaming Up", PipeMovementStatus.PULL_OUT_OF_HOLE, PumpStatus.OFF, RotationStatus.ON)
52
+ REAMING_UP = ("Reaming Up", PipeMovementStatus.PULL_OUT_OF_HOLE, PumpStatus.ON, RotationStatus.ON)
53
+
54
+ STATIC_OFF_BOTTOM = ("Static Off Bottom", PipeMovementStatus.STATIC, PumpStatus.OFF, RotationStatus.OFF)
55
+ CIRCULATING = ("Circulating", PipeMovementStatus.STATIC, PumpStatus.ON, RotationStatus.OFF)
56
+ DRY_ROTARY_OFF_BOTTOM = ("Dry Rotary Off Bottom", PipeMovementStatus.STATIC, PumpStatus.OFF, RotationStatus.ON)
57
+ ROTARY_OFF_BOTTOM = ("Rotary Off Bottom", PipeMovementStatus.STATIC, PumpStatus.ON, RotationStatus.ON)
58
+
59
+ STATIC_ON_BOTTOM = ("Static On Bottom", PipeMovementStatus.ON_BOTTOM, PumpStatus.OFF, RotationStatus.OFF)
60
+ CIRCULATING_ON_BOTTOM = ("Circulating On Bottom", PipeMovementStatus.ON_BOTTOM, PumpStatus.ON, RotationStatus.OFF)
61
+ DRY_ROTARY_ON_BOTTOM = ("Dry Rotary On Bottom", PipeMovementStatus.ON_BOTTOM, PumpStatus.OFF, RotationStatus.ON)
62
+ CIRCULATING_AND_ROTARY_ON_BOTTOM = (
63
+ "Circ. & Rot. On Bottom",
64
+ PipeMovementStatus.ON_BOTTOM,
65
+ PumpStatus.ON,
66
+ RotationStatus.ON,
67
+ )
68
+
69
+ IN_SLIPS = ("In Slips", PipeMovementStatus.IN_SLIPS, PumpStatus.OFF, RotationStatus.OFF)
70
+
71
+ UNCLASSIFIED = ("Unclassified", PipeMovementStatus.DRILLING, PumpStatus.OFF, RotationStatus.OFF)
72
+
73
+ def __new__(cls, activity_name: str, pipe_movement_status, pump_status, rotation_status):
74
+ obj = bytes.__new__(cls)
75
+
76
+ obj._value_ = activity_name # using the activity name as the main value
77
+ obj.pipe_movement_status = pipe_movement_status
78
+ obj.pump_status = pump_status
79
+ obj.rotation_status = rotation_status
80
+
81
+ return obj
82
+
83
+ def is_pipe_moving(self) -> bool:
84
+ return self.pipe_movement_status.is_moving()
85
+
86
+ def is_pipe_moving_out(self) -> bool:
87
+ return self.pipe_movement_status.is_moving_out()
88
+
89
+ def is_pipe_moving_in(self) -> bool:
90
+ return self.pipe_movement_status.is_moving_in()
91
+
92
+ def is_pumping(self) -> bool:
93
+ return self.pump_status == PumpStatus.ON
94
+
95
+ def is_rotating(self) -> bool:
96
+ return self.rotation_status == RotationStatus.ON
97
+
98
+ def is_drilling(self) -> bool:
99
+ return self.pipe_movement_status == PipeMovementStatus.DRILLING
100
+
101
+ def is_reaming(self) -> bool:
102
+ """
103
+ Pipe moves up and down with rotation
104
+ :return:
105
+ """
106
+ movement_status = self.pipe_movement_status in [
107
+ PipeMovementStatus.PULL_OUT_OF_HOLE,
108
+ PipeMovementStatus.RUN_IN_HOLE,
109
+ ]
110
+ rotation_status = self.rotation_status == RotationStatus.ON
111
+ return movement_status and rotation_status
112
+
113
+ def is_tripping(self) -> bool:
114
+ """
115
+ Pipe moves up and down
116
+ :return:
117
+ """
118
+ if self == self.RUN_IN_HOLE or self == self.PULL_OUT_OF_HOLE:
119
+ return True
120
+ return False
121
+
122
+ def __eq__(self, other):
123
+ return self is other
124
+
125
+ def __ne__(self, other):
126
+ return not self.__eq__(other)
127
+
128
+ def __hash__(self):
129
+ return hash(self.name)
130
+
131
+ def __str__(self):
132
+ return self.value