Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DFP: Exclude unwanted columns #583

Merged
71 commits merged into from
Jan 21, 2023
Merged
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
53b1ffa
Update container version to 23.01
dagardner-nv Dec 20, 2022
afcabd3
DFP examples require dask
dagardner-nv Dec 20, 2022
0a27eec
Slice the output rather than performing a copy, and return sliced mul…
dagardner-nv Dec 21, 2022
81acb8a
Add a serialize-stage, TODO: update docs & diagrams
dagardner-nv Dec 21, 2022
0f9e791
Merge branch 'david-dfp-23.01' into david-dfp-serialize-stage
dagardner-nv Dec 21, 2022
0a90ec9
Add serializestage to notebooks, and comment the stage
dagardner-nv Dec 21, 2022
4115112
First pass at filtering on a dataframe column or a tensor
dagardner-nv Dec 21, 2022
9eff636
First pass at moving the FilterSource enum to C++
dagardner-nv Dec 21, 2022
a1ef1d2
Switch to the C++ impl of the enum
dagardner-nv Dec 21, 2022
fda041a
New constructor args
dagardner-nv Dec 21, 2022
c49cdca
Remove unused imports, add new args for FilterDetectionsStage
dagardner-nv Dec 21, 2022
796c2b0
Use field_name var
dagardner-nv Dec 21, 2022
d12d28b
wip operate on a dataframe
dagardner-nv Dec 22, 2022
4aa7db8
Give Dtype a default value allowing it to be default contrstructable
dagardner-nv Dec 27, 2022
8ec54d4
Move df column & tensor specific code to their own methods
dagardner-nv Dec 27, 2022
560ee9c
Pass new constructor arguments to CPP impl
dagardner-nv Dec 27, 2022
fdf7a15
tests
dagardner-nv Dec 27, 2022
f08c689
Parameterize copy vs slice
dagardner-nv Dec 27, 2022
567b0a1
more tests
dagardner-nv Dec 27, 2022
7ffa253
Fix cli help string, and always declare probs as a 2d array when empt…
dagardner-nv Dec 27, 2022
1903b8d
wip
dagardner-nv Dec 27, 2022
81ba67e
wip
dagardner-nv Dec 27, 2022
e8bbf29
override get_slice method
dagardner-nv Dec 27, 2022
3a10d18
Accepted type changes to MultiMessage when source is a df column, use…
dagardner-nv Dec 27, 2022
a411642
Filter stage can accept a MultiMessage when the source is a df column
dagardner-nv Dec 27, 2022
ccb646f
Use np.zeros when the dataframe is pandas
dagardner-nv Dec 27, 2022
d3b1e09
Update type hints
dagardner-nv Dec 27, 2022
319bf40
Rename test, and also test for pandas when in python mode
dagardner-nv Dec 27, 2022
cf36b0b
Ensure we are testing against both MultiMessages and MultiResponseProbs
dagardner-nv Dec 27, 2022
212316d
Use the user_id attr from the message itself
dagardner-nv Dec 28, 2022
9c63e61
check for None values
dagardner-nv Dec 28, 2022
7254e2d
Override copy_ranges
dagardner-nv Dec 28, 2022
a090196
Revert "Use the user_id attr from the message itself"
dagardner-nv Dec 28, 2022
4037394
Override copy_ranges to ensure we receive a MultiAEMessage instance w…
dagardner-nv Dec 28, 2022
9718ee0
Add filter stage to notebooks
dagardner-nv Dec 28, 2022
4aef317
Update embedded docs in notebooks
dagardner-nv Dec 28, 2022
dbfd037
Merge branch 'branch-23.01' into david-dfp-serialize-stage
dagardner-nv Dec 28, 2022
802c2a6
Update docs to include info about filter stage
dagardner-nv Dec 28, 2022
601de4f
Fix type-o
dagardner-nv Dec 28, 2022
30938c2
Document serialize stage
dagardner-nv Dec 28, 2022
a67e951
Update DFP diagrams to include filter and serialize stages
dagardner-nv Dec 28, 2022
318023b
Crop the diagram
dagardner-nv Dec 28, 2022
0797d86
Remove unused import
dagardner-nv Dec 28, 2022
cd3688d
Formatting fix
dagardner-nv Dec 28, 2022
2be23ea
Merge branch 'branch-23.01' into david-dfp-serialize-stage
dagardner-nv Dec 28, 2022
846b66c
Merge branch 'branch-23.01' into david-dfp-serialize-stage
dagardner-nv Jan 4, 2023
7a97931
Update CR year
dagardner-nv Jan 4, 2023
98e30a5
Merge branch 'branch-23.01' into david-dfp-serialize-stage
dagardner-nv Jan 6, 2023
8aeda20
Merge branch 'branch-23.01' into david-dfp-serialize-stage
dagardner-nv Jan 17, 2023
054abe3
Rename operate_on to data_source
dagardner-nv Jan 17, 2023
d2c6e01
Change casing of FilterSource::AUTO to match that of the FileTypes enum
dagardner-nv Jan 17, 2023
97391a8
Rename data_source to filter_source matching the enum type
dagardner-nv Jan 17, 2023
5037640
Move bindings for FilterSource into the common module
dagardner-nv Jan 17, 2023
0202519
Merge the file_types python module into common
dagardner-nv Jan 17, 2023
180abca
Cast to MultiTensorMessage
dagardner-nv Jan 17, 2023
f7cac94
Set accepted types to MultiResponseMessage since MultiResponseProbsMe…
dagardner-nv Jan 17, 2023
18c685a
Auto determines the filter source based on the message type
dagardner-nv Jan 18, 2023
edf7aef
fix
dagardner-nv Jan 18, 2023
5dcef3b
Move the check of m_filter_source outside of the hot path
dagardner-nv Jan 18, 2023
1a60846
Fix type-o
dagardner-nv Jan 18, 2023
c74339a
First pass at a refactored DevMemInfo
dagardner-nv Jan 18, 2023
4727191
replace the memory_resource and stream methods with a make_new_buffer…
dagardner-nv Jan 18, 2023
065c69e
wip
dagardner-nv Jan 18, 2023
f80ab01
wip
dagardner-nv Jan 18, 2023
0dfbe40
Replace usage of BufferInfo with DevMemInfo
dagardner-nv Jan 18, 2023
c830c21
Move the get methods into private class methods
dagardner-nv Jan 18, 2023
27a8f6d
Make private attrs const
dagardner-nv Jan 18, 2023
ce587d5
Remove unused variable
dagardner-nv Jan 18, 2023
2af0bd4
Remove default values for filter_source & copy
dagardner-nv Jan 18, 2023
c3732e0
Misc cleanups
dagardner-nv Jan 18, 2023
532c6d8
Fix missed arguments after data_source->filter_source rename
dagardner-nv Jan 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ pipeline.add_stage(WriteToFileStage(config, filename=results_file, overwrite=Tru

Note that we didn't specify the output format. In our example, the result file contains the extension `.jsonlines`. Morpheus will infer the output format based on the extension. At time of writing the extensions that Morpheus will infer are: `.csv`, `.json` & `.jsonlines`

To explicitly set the output format we could specify the `file_type` argument to the `WriteToFileStage` which is an enumeration defined in `morpheus._lib.file_types.FileTypes`. Current values defined are:
To explicitly set the output format we could specify the `file_type` argument to the `WriteToFileStage` which is an enumeration defined in `morpheus._lib.common.FileTypes`. Current values defined are:
* `FileTypes.Auto`
* `FileTypes.JSON`
* `FileTypes.CSV`
Expand Down
17 changes: 10 additions & 7 deletions docs/source/developer_guide/guides/5_digital_fingerprinting.md
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ The `DFPFileToDataFrameStage` ([examples/digital_fingerprinting/production/morph
| `c` | `morpheus.config.Config` | Morpheus config object |
| `schema` | `DataFrameInputSchema` | Schema specifying columns to load, along with any necessary renames and data type conversions |
| `filter_null` | `bool` | Optional: Whether to filter null rows after loading, by default True. |
| `file_type` | `morpheus._lib.file_types.FileTypes` (enum) | Optional: Indicates file type to be loaded. Currently supported values at time of writing are: `FileTypes.Auto`, `FileTypes.CSV`, and `FileTypes.JSON`. Default value is `FileTypes.Auto` which will infer the type based on the file extension, set this value if using a custom extension |
| `file_type` | `morpheus._lib.common.FileTypes` (enum) | Optional: Indicates file type to be loaded. Currently supported values at time of writing are: `FileTypes.Auto`, `FileTypes.CSV`, and `FileTypes.JSON`. Default value is `FileTypes.Auto` which will infer the type based on the file extension, set this value if using a custom extension |
| `parser_kwargs` | `dict` or `None` | Optional: additional keyword arguments to be passed into the `DataFrame` parser, currently this is going to be either [`pandas.read_csv`](https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html) or [`pandas.read_json`](https://pandas.pydata.org/docs/reference/api/pandas.read_json.html) |
| `cache_dir` | `str` | Optional: path to cache location, defaults to `./.cache/dfp` |

Expand Down Expand Up @@ -634,12 +634,15 @@ For any user without an associated model in MLflow, the model for the generic us
| `c` | `morpheus.config.Config` | Morpheus config object |
| `model_name_formatter` | `str` | Format string to control the name of models fetched from MLflow. Currently available field names are: `user_id` and `user_md5` which is an md5 hexadecimal digest as returned by [`hash.hexdigest`](https://docs.python.org/3.8/library/hashlib.html?highlight=hexdigest#hashlib.hash.hexdigest). |

#### Filter Detection Stage (`FilterDetectionsStage`)
This stage filters the output from the inference stage for any anomalous messages. Logs which exceed the specified Z-Score will be passed onto the next stage. All remaining logs which are below the threshold will be dropped. For the purposes of the DFP pipeline, this stage is configured to use the `mean_abs_z` column of the DataFrame as the filter criteria.

| Name | Type | Default | Description |
| --- | --- | --- | :-- |
| `threshold` | `float` | `0.5` | The threshold value above which logs are considered to be anomalous. The default is `0.5`, however the DFP pipeline uses a value of `2.0`. All normal logs will be filtered out and anomalous logs will be passed on. |
| `copy` | `bool` | `True` | When the `copy` argument is `True` (default), rows that meet the filter criteria are copied into a new dataframe. When `False` sliced views are used instead. This is a performance optimization, and has no functional impact. |
| `data_source` | `FilterSource` | `FilterSource.Auto` | Indicates if the filter criteria exists in an output tensor (`FilterSource.TENSOR`) or a column in a DataFrame (`FilterSource.DATAFRAME`). |
| `field_name` | `str` | `probs` | Name of the tensor (`data_source=FilterSource.TENSOR`) or DataFrame column (`data_source=FilterSource.DATAFRAME`) to use as the filter criteria. |

#### Post Processing Stage (`DFPPostprocessingStage`)
The `DFPPostprocessingStage` ([examples/digital_fingerprinting/production/morpheus/dfp/stages/dfp_postprocessing_stage.py](/examples/digital_fingerprinting/production/morpheus/dfp/stages/dfp_postprocessing_stage.py)) stage filters the output from the inference stage for any anomalous messages. Logs which exceed the specified Z-Score will be passed onto the next stage. All remaining logs which are below the threshold will be dropped.

| Argument | Type | Description |
| -------- | ---- | ----------- |
| `c` | `morpheus.config.Config` | Morpheus config object |
| `z_score_threshold` | `float` | Optional, sets the threshold value above which values of `mean_abs_z` must be above in order to be considered an anomaly, default is 2.0 |
This stage adds a new `event_time` column to the DataFrame indicating the time which Morpheus detected the anomalous messages, and replaces any `NAN` values with the a string value of `'NaN'`.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion examples/developer_guide/2_2_rabbitmq/read_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from rabbitmq_source_stage import RabbitMQSourceStage

from morpheus._lib.file_types import FileTypes
from morpheus._lib.common import FileTypes
from morpheus.config import Config
from morpheus.pipeline import LinearPipeline
from morpheus.stages.general.monitor_stage import MonitorStage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import click
from rabbitmq_source_stage import RabbitMQSourceStage

from morpheus._lib.file_types import FileTypes
from morpheus._lib.common import FileTypes
from morpheus.config import Config
from morpheus.config import CppConfig
from morpheus.pipeline import LinearPipeline
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

import cudf

from morpheus._lib.file_types import FileTypes
from morpheus._lib.common import FileTypes
from morpheus.config import Config
from morpheus.io.deserializers import read_file_to_df
from morpheus.pipeline.single_port_stage import SinglePortStage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,14 @@
from morpheus.pipeline.single_port_stage import SinglePortStage
from morpheus.pipeline.stream_pair import StreamPair

from ..messages.multi_dfp_message import DFPMessageMeta

logger = logging.getLogger("morpheus.{}".format(__name__))


class DFPPostprocessingStage(SinglePortStage):

def __init__(self, c: Config, z_score_threshold=2.0):
def __init__(self, c: Config):
super().__init__(c)

self._z_score_threshold = z_score_threshold

@property
def name(self) -> str:
return "dfp-postproc"
Expand All @@ -48,42 +44,32 @@ def supports_cpp_node(self):
def accepted_types(self) -> typing.Tuple:
return (MultiAEMessage, )

def _extract_events(self, message: MultiAEMessage):

z_scores = message.get_meta("mean_abs_z")

above_threshold_df = message.get_meta()[z_scores > self._z_score_threshold]

if (not above_threshold_df.empty):
above_threshold_df['event_time'] = datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ')
above_threshold_df = above_threshold_df.replace(np.nan, 'NaN', regex=True)

return above_threshold_df

return None
def _process_events(self, message: MultiAEMessage):
# Assume that a filter stage preceedes this stage
df = message.get_meta()
df['event_time'] = datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ')
df.replace(np.nan, 'NaN', regex=True, inplace=True)
message.set_meta(None, df)

def on_data(self, message: MultiAEMessage):
if (not message):
if (not message or message.mess_count == 0):
return None

start_time = time.time()

extracted_events = self._extract_events(message)
self._process_events(message)

duration = (time.time() - start_time) * 1000.0

if logger.isEnabledFor(logging.DEBUG):
logger.debug("Completed postprocessing for user %s in %s ms. Event count: %s. Start: %s, End: %s",
message.meta.user_id,
duration,
0 if extracted_events is None else len(extracted_events),
message.mess_count,
message.get_meta(self._config.ae.timestamp_column_name).min(),
message.get_meta(self._config.ae.timestamp_column_name).max())

if (extracted_events is None):
return None

return DFPMessageMeta(extracted_events, user_id=message.meta.user_id)
return message

def _build_single(self, builder: mrc.Builder, input_stream: StreamPair) -> StreamPair:

Expand All @@ -93,4 +79,4 @@ def node_fn(obs: mrc.Observable, sub: mrc.Subscriber):
stream = builder.make_node_full(self.unique_name, node_fn)
builder.make_edge(input_stream[0], stream)

return stream, DFPMessageMeta
return stream, input_stream[1]
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
"from dfp.utils.file_utils import date_extractor\n",
"from dfp.utils.file_utils import iso_date_regex\n",
"\n",
"from morpheus._lib.file_types import FileTypes\n",
"from morpheus._lib.common import FileTypes\n",
"from morpheus._lib.common import FilterSource\n",
"from morpheus.cli.utils import get_package_relative_file\n",
"from morpheus.cli.utils import load_labels_file\n",
"from morpheus.config import Config\n",
Expand All @@ -78,6 +79,8 @@
"from morpheus.pipeline import LinearPipeline\n",
"from morpheus.stages.general.monitor_stage import MonitorStage\n",
"from morpheus.stages.output.write_to_file_stage import WriteToFileStage\n",
"from morpheus.stages.postprocess.filter_detections_stage import FilterDetectionsStage\n",
"from morpheus.stages.postprocess.serialize_stage import SerializeStage\n",
"from morpheus.utils.logger import configure_logging\n",
"from morpheus.utils.logger import get_log_levels\n",
"from morpheus.utils.logger import parse_log_level\n",
Expand Down Expand Up @@ -255,6 +258,7 @@
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "bdfc59de-dea8-4e5f-98e3-98eba1e4621d",
"metadata": {},
Expand Down Expand Up @@ -341,16 +345,29 @@
"| --- | --- | --- | :-- |\n",
"| `model_name_formatter` | `str` | `\"\"` | A format string to use when building the model name. The model name is constructed by calling `model_name_formatter.format(user_id=user_id)`. For example, with `model_name_formatter=\"my_model-{user_id}\"` and a user ID of `\"first:last\"` would result in the model name of `\"my_model-first:last\"`. This should match the value used in `DFPMLFlowModelWriterStage` |\n",
"\n",
"### Filter Detection Stage (`FilterDetectionsStage`)\n",
"This stage filters the output from the inference stage for any anomalous messages. Logs which exceed the specified Z-Score will be passed onto the next stage. All remaining logs which are below the threshold will be dropped. For the purposes of the DFP pipeline, this stage is configured to use the `mean_abs_z` column of the DataFrame as the filter criteria.\n",
"\n",
"| Name | Type | Default | Description |\n",
"| --- | --- | --- | :-- |\n",
"| `threshold` | `float` | `0.5` | The threshold value above which logs are considered to be anomalous. The default is `0.5`, however the DFP pipeline uses a value of `2.0`. All normal logs will be filtered out and anomalous logs will be passed on. |\n",
"| `copy` | `bool` | `True` | When the `copy` argument is `True` (default), rows that meet the filter criteria are copied into a new dataframe. When `False` sliced views are used instead. This is a performance optimization, and has no functional impact. |\n",
"| `data_source` | `FilterSource` | `FilterSource.Auto` | Indicates if the filter criteria exists in an output tensor (`FilterSource.TENSOR`) or a column in a DataFrame (`FilterSource.DATAFRAME`). |\n",
"| `field_name` | `str` | `probs` | Name of the tensor (`data_source=FilterSource.TENSOR`) or DataFrame column (`data_source=FilterSource.DATAFRAME`) to use as the filter criteria. |\n",
"\n",
"### Post Processing Stage (`DFPPostprocessingStage`)\n",
"This stage adds a new `event_time` column to the DataFrame indicating the time which Morpheus detected the anomalous messages, and replaces any `NAN` values with the a string value of `'NaN'`.\n",
"\n",
"This stage filters the output from the inference stage for any anomalous messages. Logs which exceed the specified Z-Score will be passed onto the next stage. All remaining logs which are below the threshold will be dropped.\n",
"### Serialize Stage (`SerializeStage`)\n",
"This stage controls which columns in the DataFrame will be included in the output. For the purposes of the DFP pipeline, we will exclude columns that are used internally by the pipeline which are not of interest to the end-user.\n",
"\n",
"| Name | Type | Default | Description |\n",
"| --- | --- | --- | :-- |\n",
"| `z_score_threshold` | `float` | `2.0` | The Z-Score used to separate anomalous logs from normal logs. All normal logs will be filterd out and anomalous logs will be passed on. |\n",
"| `include` | List of `str` | `[]` | List of regular expression patterns matching columns to include in the output. Specifying an empty list causes all columns to be included not explicitly excluded. |\n",
"| `exclude` | List of `str` | `[r'^ID$', r'^_ts_']` | List of regular expression patterns matching columns to exclude from the output. |\n",
"| `fixed_columns` | `bool` | `True` | When `True` it is assumed that the Dataframe in all messages contain the same columns as the first message received. |\n",
"\n",
"### Write to File Stage (`WriteToFileStage`)\n",
"\n",
"This final stage will write all received messages to a single output file in either CSV or JSON format.\n",
"\n",
"| Name | Type | Default | Description |\n",
Expand Down Expand Up @@ -413,7 +430,12 @@
"pipeline.add_stage(DFPInferenceStage(config, model_name_formatter=model_name_formatter))\n",
"\n",
"# Filter for only the anomalous logs\n",
"pipeline.add_stage(DFPPostprocessingStage(config, z_score_threshold=2.0))\n",
"pipeline.add_stage(\n",
" FilterDetectionsStage(config, threshold=2.0, data_source=FilterSource.DATAFRAME, field_name='mean_abs_z'))\n",
"pipeline.add_stage(DFPPostprocessingStage(config))\n",
"\n",
"# Exclude the columns we don't want in our output\n",
"pipeline.add_stage(SerializeStage(config, exclude=['batch_count', 'origin_hash', '_row_hash', '_batch_id']))\n",
"\n",
"# Write all anomalies to a CSV file\n",
"pipeline.add_stage(WriteToFileStage(config, filename=\"dfp_detections_azure.csv\", overwrite=True))\n",
Expand All @@ -433,7 +455,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.9.2 64-bit",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
Expand All @@ -447,7 +469,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.2"
"version": "3.9.2 (default, Feb 28 2021, 17:03:44) \n[GCC 10.2.1 20210110]"
},
"vscode": {
"interpreter": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
from dfp.utils.file_utils import date_extractor
from dfp.utils.file_utils import iso_date_regex

from morpheus._lib.file_types import FileTypes
from morpheus._lib.common import FileTypes
from morpheus._lib.common import FilterSource
from morpheus.cli.utils import get_package_relative_file
from morpheus.cli.utils import load_labels_file
from morpheus.config import Config
Expand All @@ -54,6 +55,8 @@
from morpheus.pipeline import LinearPipeline
from morpheus.stages.general.monitor_stage import MonitorStage
from morpheus.stages.output.write_to_file_stage import WriteToFileStage
from morpheus.stages.postprocess.filter_detections_stage import FilterDetectionsStage
from morpheus.stages.postprocess.serialize_stage import SerializeStage
from morpheus.utils.logger import configure_logging
from morpheus.utils.logger import get_log_levels
from morpheus.utils.logger import parse_log_level
Expand Down Expand Up @@ -305,7 +308,12 @@ def run_pipeline(train_users,
pipeline.add_stage(MonitorStage(config, description="Inference rate", smoothing=0.001))

# Filter for only the anomalous logs
pipeline.add_stage(DFPPostprocessingStage(config, z_score_threshold=2.0))
pipeline.add_stage(
FilterDetectionsStage(config, threshold=2.0, data_source=FilterSource.DATAFRAME, field_name='mean_abs_z'))
pipeline.add_stage(DFPPostprocessingStage(config))

# Exclude the columns we don't want in our output
pipeline.add_stage(SerializeStage(config, exclude=['batch_count', 'origin_hash', '_row_hash', '_batch_id']))

# Write all anomalies to a CSV file
pipeline.add_stage(WriteToFileStage(config, filename="dfp_detections_azure.csv", overwrite=True))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"from dfp.utils.file_utils import date_extractor\n",
"from dfp.utils.file_utils import iso_date_regex\n",
"\n",
"from morpheus._lib.file_types import FileTypes\n",
"from morpheus._lib.common import FileTypes\n",
"from morpheus.cli.utils import get_package_relative_file\n",
"from morpheus.cli.utils import load_labels_file\n",
"from morpheus.config import Config\n",
Expand Down
Loading