tts-data-utils 0.7.3__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.
- tts_data_utils/__init__.py +0 -0
- tts_data_utils/core/__init__.py +0 -0
- tts_data_utils/core/container_history.py +79 -0
- tts_data_utils/core/data_container.py +2102 -0
- tts_data_utils/core/data_item.py +487 -0
- tts_data_utils/core/diff.py +155 -0
- tts_data_utils/core/generic.py +78 -0
- tts_data_utils/core/lorem_utils.py +206 -0
- tts_data_utils/core/visual_diff.py +148 -0
- tts_data_utils/invulnerable_data_manager/__init__.py +0 -0
- tts_data_utils/invulnerable_data_manager/batch.py +176 -0
- tts_data_utils/invulnerable_data_manager/invulnerable_data_manager.py +171 -0
- tts_data_utils/invulnerable_data_manager/utilities.py +151 -0
- tts_data_utils/multimission/__init__.py +0 -0
- tts_data_utils/multimission/alarms.py +109 -0
- tts_data_utils/multimission/eha.py +253 -0
- tts_data_utils/multimission/evr.py +340 -0
- tts_data_utils/multimission/evr_gaps.py +148 -0
- tts_data_utils/multimission/expected_lad.py +161 -0
- tts_data_utils/multimission/planning_rule.py +106 -0
- tts_data_utils/multimission/taco.py +53 -0
- tts_data_utils/test/__init__.py +0 -0
- tts_data_utils/test/core/__init__.py +0 -0
- tts_data_utils/test/core/test_data_container.py +403 -0
- tts_data_utils/test/core/test_data_item.py +92 -0
- tts_data_utils/test/core/test_diff.py +173 -0
- tts_data_utils/test/core/test_files/diff/diff_dict/actual_diff.csv +5135 -0
- tts_data_utils/test/core/test_files/diff/diff_dict/expected_diff.csv +5135 -0
- tts_data_utils/test/core/test_files/diff/diff_dict/left.csv +637 -0
- tts_data_utils/test/core/test_files/diff/diff_dict/right.csv +637 -0
- tts_data_utils/test/core/test_files/diff/diff_list/actual_diff.csv +5135 -0
- tts_data_utils/test/core/test_files/diff/diff_list/expected_diff.csv +5135 -0
- tts_data_utils/test/core/test_files/diff/diff_list/left.csv +637 -0
- tts_data_utils/test/core/test_files/diff/diff_list/right.csv +637 -0
- tts_data_utils/test/core/test_files/diff/diff_list_size/actual_diff.csv +33 -0
- tts_data_utils/test/core/test_files/diff/diff_list_size/expected_diff.csv +33 -0
- tts_data_utils/test/core/test_files/diff/diff_list_size/left.csv +637 -0
- tts_data_utils/test/core/test_files/diff/diff_list_size/right.csv +637 -0
- tts_data_utils/test/core/test_files/diff/diff_simple_types/actual_diff.csv +17894 -0
- tts_data_utils/test/core/test_files/diff/diff_simple_types/expected_diff.csv +17894 -0
- tts_data_utils/test/core/test_files/diff/diff_simple_types/left.csv +637 -0
- tts_data_utils/test/core/test_files/diff/diff_simple_types/right.csv +637 -0
- tts_data_utils/test/core/test_files/diff/diff_type/actual_diff.csv +5135 -0
- tts_data_utils/test/core/test_files/diff/diff_type/expected_diff.csv +5135 -0
- tts_data_utils/test/core/test_files/diff/diff_type/left.csv +637 -0
- tts_data_utils/test/core/test_files/diff/diff_type/right.csv +637 -0
- tts_data_utils/test/core/test_files/diff/same_not_same/actual_diff.csv +5135 -0
- tts_data_utils/test/core/test_files/visdiff/adjacent_insert_delete.html +1581 -0
- tts_data_utils/test/core/test_files/visdiff/insert_and_delete.html +1581 -0
- tts_data_utils/test/core/test_files/visdiff/no_diff.html +1581 -0
- tts_data_utils/test/core/test_files/visdiff/overlapping_insert_delete.html +1541 -0
- tts_data_utils/test/core/test_files/visdiff/replace.html +1581 -0
- tts_data_utils/test/core/test_files/visdiff/replace_with_delete.html +1581 -0
- tts_data_utils/test/core/test_files/visdiff/simple_delete.html +1581 -0
- tts_data_utils/test/core/test_files/visdiff/simple_insert.html +1581 -0
- tts_data_utils/test/core/test_files/visdiff/stressing_case.html +2061 -0
- tts_data_utils/test/core/test_filters.py +319 -0
- tts_data_utils/test/core/test_generic_container.py +22 -0
- tts_data_utils/test/core/test_history.py +44 -0
- tts_data_utils/test/core/test_visdiff.py +193 -0
- tts_data_utils/test/invulnerable_data_manager/test_invulnerable_data_manager.py +150 -0
- tts_data_utils/test/multimission/__init__.py +0 -0
- tts_data_utils/test/multimission/evr/__init__.py +0 -0
- tts_data_utils/test/multimission/evr/test_evr.py +29 -0
- tts_data_utils/test/multimission/evr/test_files/actual_evr_gaps.csv +12 -0
- tts_data_utils/test/multimission/evr/test_files/evrs_with_gaps.csv +30 -0
- tts_data_utils/test/multimission/evr/test_files/expected_evr_gaps.csv +12 -0
- tts_data_utils/util.py +104 -0
- tts_data_utils-0.7.3.dist-info/METADATA +81 -0
- tts_data_utils-0.7.3.dist-info/RECORD +72 -0
- tts_data_utils-0.7.3.dist-info/WHEEL +5 -0
- tts_data_utils-0.7.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import pdb
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
|
|
4
|
+
from tts_data_utils.core.data_container import DataContainer
|
|
5
|
+
from tts_data_utils.core.data_item import DataItem
|
|
6
|
+
|
|
7
|
+
from jpl_time import Time
|
|
8
|
+
|
|
9
|
+
class PlanningRuleItem(DataItem):
|
|
10
|
+
"""
|
|
11
|
+
A specialized record representing a mission constraint or operational rule.
|
|
12
|
+
|
|
13
|
+
**The Concept:**
|
|
14
|
+
Planning rules are the "laws" of a spacecraft's schedule. They define what the
|
|
15
|
+
spacecraft can and cannot do (e.g., "Do not point the instrument at the sun
|
|
16
|
+
while the cover is open").
|
|
17
|
+
|
|
18
|
+
A `PlanningRuleItem` captures the metadata of these constraints, allowing
|
|
19
|
+
automated schedulers or human planners to understand the risk and impact
|
|
20
|
+
associated with violating a specific mission requirement.
|
|
21
|
+
|
|
22
|
+
**Database Integration:**
|
|
23
|
+
This class includes a `DJANGO_KEY_MAP` to handle the translation between
|
|
24
|
+
database-friendly snake_case fields (used in the Planning Service) and
|
|
25
|
+
report-friendly Title Case fields used in this library.
|
|
26
|
+
|
|
27
|
+
:param Rule ID: Unique identifier for the planning constraint.
|
|
28
|
+
:type Rule ID: str
|
|
29
|
+
:param Functional Category: The subsystem the rule applies to (e.g., 'ADCS', 'Power').
|
|
30
|
+
:type Functional Category: str
|
|
31
|
+
:param Maturity: The current status of the rule (e.g., 'Draft', 'Flight Validated').
|
|
32
|
+
:type Maturity: str
|
|
33
|
+
:param Title: A short, descriptive name for the rule.
|
|
34
|
+
:type Title: str
|
|
35
|
+
:param Description: The full text of the constraint.
|
|
36
|
+
:type Description: str
|
|
37
|
+
:param Violation Impact: Explanation of what happens if this rule is broken.
|
|
38
|
+
:type Violation Impact: str
|
|
39
|
+
:param Criticality: Severity of the rule (e.g., 'High', 'Mission Ending').
|
|
40
|
+
:type Criticality: str
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
DICT_VALID_KEYS = [
|
|
44
|
+
('Rule ID', str),
|
|
45
|
+
('Functional Category', str),
|
|
46
|
+
('Maturity', str),
|
|
47
|
+
('Title', str),
|
|
48
|
+
('Description', str),
|
|
49
|
+
('Violation Impact', str),
|
|
50
|
+
('Criticality', str),
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
TIME_FORMATS = {}
|
|
54
|
+
|
|
55
|
+
DJANGO_KEY_MAP = {
|
|
56
|
+
'uuid': 'uuid',
|
|
57
|
+
'rule_id': 'Rule ID',
|
|
58
|
+
'functional_category': 'Functional Category',
|
|
59
|
+
'maturity': 'Maturity',
|
|
60
|
+
'title': 'Title',
|
|
61
|
+
'description': 'Description',
|
|
62
|
+
'impact_of_violation': 'Violation Impact',
|
|
63
|
+
'criticality': 'Criticality',
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
def __sub_init(self):
|
|
67
|
+
"""Internal initialization hook."""
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
def time(self):
|
|
71
|
+
"""
|
|
72
|
+
[Disabled]
|
|
73
|
+
|
|
74
|
+
Planning rules are static constraints and do not have a single
|
|
75
|
+
point-in-time association.
|
|
76
|
+
"""
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class PlanningRuleContainer(DataContainer):
|
|
81
|
+
"""
|
|
82
|
+
**The Concept:**
|
|
83
|
+
This container acts as a digital "Rulebook." When generating a mission
|
|
84
|
+
plan or analyzing a sequence of commands, this container is used to
|
|
85
|
+
look up the definitions and impacts of the rules being checked.
|
|
86
|
+
|
|
87
|
+
**Mission Specifics:**
|
|
88
|
+
While named for OCO-2, the structure follows the standard patterns
|
|
89
|
+
required for any mission using the Teamtools Studio planning validation
|
|
90
|
+
workflow.
|
|
91
|
+
"""
|
|
92
|
+
NAME = 'Planning Rule'
|
|
93
|
+
DATA_ITEM_CLS = PlanningRuleItem
|
|
94
|
+
DO_NOT_DIFF = []
|
|
95
|
+
|
|
96
|
+
def __init__(self, **kwargs):
|
|
97
|
+
super().__init__(**kwargs)
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def repr_cols(self):
|
|
101
|
+
"""
|
|
102
|
+
The list of columns intended for display in rule reports.
|
|
103
|
+
"""
|
|
104
|
+
#this is here because it's an abstract method, which may
|
|
105
|
+
#not be useful. Consider removal
|
|
106
|
+
return self._repr_cols
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from jpl_time import Time
|
|
2
|
+
|
|
3
|
+
from dexter.core.data import DataItem, DataContainer
|
|
4
|
+
from dexter.core.dispo import Dispositioner, dispo_method
|
|
5
|
+
from dexter.util.utilities import cached_property, filter_on_attr_regex
|
|
6
|
+
|
|
7
|
+
class TacoItem(DataItem):
|
|
8
|
+
@property
|
|
9
|
+
def valid(self):
|
|
10
|
+
# Just double-check we have the right object type
|
|
11
|
+
# TODO: Add additional validation checks here.
|
|
12
|
+
# This isn't good enough.
|
|
13
|
+
return isinstance(self.source, dict)
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def time(self):
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
# @cached_property
|
|
20
|
+
# def start_time(self):
|
|
21
|
+
# return Time(self.source.get_start_lmst())
|
|
22
|
+
|
|
23
|
+
# @cached_property
|
|
24
|
+
# def end_time(self):
|
|
25
|
+
# return Time(self.source.get_end_lmst())
|
|
26
|
+
|
|
27
|
+
# @property
|
|
28
|
+
# def time(self):
|
|
29
|
+
# return self.start_time
|
|
30
|
+
|
|
31
|
+
def stamp(self, dispo_value):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
# @property
|
|
35
|
+
# def seq_id(self):
|
|
36
|
+
# return self.source.seq_id
|
|
37
|
+
|
|
38
|
+
class TacoContainer(DataContainer):
|
|
39
|
+
NAME = 'taco'
|
|
40
|
+
DATA_ITEM_CLS = TacoItem
|
|
41
|
+
|
|
42
|
+
# @classmethod
|
|
43
|
+
# def _format_data(cls, raw_data, copy_data, default_dispo):
|
|
44
|
+
# # Default is just to assume a list of data payloads that can be passed directly to the DataItem class
|
|
45
|
+
# return [cls.DATA_ITEM_CLS(_, copy_data=copy_data, default_dispo=default_dispo) for _ in raw_data.get_executions()]
|
|
46
|
+
|
|
47
|
+
# def get_exec_by_seq_id(self, seq_id):
|
|
48
|
+
# """Gets all executions of a particular Seq ID as a list, allowing regex"""
|
|
49
|
+
# return filter_on_attr_regex(self.data, seq_id=seq_id)
|
|
50
|
+
|
|
51
|
+
# def get_exec_by_seq_id_exact(self, seq_id):
|
|
52
|
+
# """Gets all executions of a particular SeqID as a list, exact match only (e.x. 'aut_00000' must match, version ignored)"""
|
|
53
|
+
# return [_ for _ in self.data if _.seq_id == seq_id]
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
#Standard Library Imports
|
|
2
|
+
import json
|
|
3
|
+
import pdb
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
#Installed Library Importsimport pytest
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
#Teamtool Studio Imports
|
|
12
|
+
from tts_utilities.logger import create_logger
|
|
13
|
+
from tts_utilities.test_utilities import safe_test_paths
|
|
14
|
+
|
|
15
|
+
#This Library Imports
|
|
16
|
+
from tts_data_utils.core.generic import GenericContainer
|
|
17
|
+
|
|
18
|
+
logger = create_logger(f'tts_data_utils.core.data_container')
|
|
19
|
+
TEST_INPUT_DIR, TEST_OUTPUT_DIR = safe_test_paths(Path(__file__).parent, 'test_files/data_container')
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture
|
|
23
|
+
def csv_file(tmp_path):
|
|
24
|
+
path = tmp_path / "test_data.csv"
|
|
25
|
+
df = pd.DataFrame({"col1": [1, 2], "col2": ["a", "b"]})
|
|
26
|
+
df.to_csv(path, index=False)
|
|
27
|
+
return path
|
|
28
|
+
|
|
29
|
+
@pytest.fixture
|
|
30
|
+
def xlsx_file(tmp_path):
|
|
31
|
+
path = tmp_path / "test_data.xlsx"
|
|
32
|
+
df = pd.DataFrame({"col1": [3, 4], "col2": ["c", "d"]})
|
|
33
|
+
df.to_excel(path, index=False)
|
|
34
|
+
return path
|
|
35
|
+
|
|
36
|
+
@pytest.fixture
|
|
37
|
+
def nested_container():
|
|
38
|
+
inner = GenericContainer(raw_data=[{'a': 1}])
|
|
39
|
+
outer = GenericContainer(
|
|
40
|
+
raw_data=[{'id': 'parent'}],
|
|
41
|
+
subcontainers=[{'child_tab': inner}]
|
|
42
|
+
)
|
|
43
|
+
return outer
|
|
44
|
+
@pytest.fixture(scope="module")
|
|
45
|
+
def filter_container():
|
|
46
|
+
time = [datetime.fromtimestamp(ii) for ii in range(100)]
|
|
47
|
+
col1 = [ii for ii in range(50)]*2
|
|
48
|
+
col2 = [ii for ii in range(100, 200)]
|
|
49
|
+
col3 = [str(ii) for ii in range(200, 300)]
|
|
50
|
+
col4 = [ii for ii in range(300, 400)]
|
|
51
|
+
col5 = [ii for ii in range(400, 500)]
|
|
52
|
+
raw_data = [{
|
|
53
|
+
'Time': t,
|
|
54
|
+
'Column 1': c1,
|
|
55
|
+
'Column 2': c2,
|
|
56
|
+
'Column 3': c3,
|
|
57
|
+
'Column 4': c4,
|
|
58
|
+
'Column 5': c5,
|
|
59
|
+
} for t, c1, c2, c3, c4, c5 in zip(time, col1, col2, col3, col4, col5)]
|
|
60
|
+
filter_container = GenericContainer(raw_data=raw_data)
|
|
61
|
+
return filter_container
|
|
62
|
+
|
|
63
|
+
@pytest.fixture(scope="module")
|
|
64
|
+
def filter_container_case_sensitive():
|
|
65
|
+
#I totally could have merged this alongside the other container fixture
|
|
66
|
+
#but I didn't recognize the need for it until the tests that don't need
|
|
67
|
+
#case sensitivity were mature enough that I didn't want to merge them
|
|
68
|
+
#for essentially no value add.
|
|
69
|
+
time = [datetime.fromtimestamp(ii) for ii in range(100)]
|
|
70
|
+
col1 = [ii for ii in range(50)]*2
|
|
71
|
+
col2 = [ii for ii in range(100, 200)]
|
|
72
|
+
col3 = [str(ii) for ii in range(200, 300)]
|
|
73
|
+
col4 = [ii for ii in range(300, 400)]
|
|
74
|
+
col5 = [ii for ii in range(400, 500)]
|
|
75
|
+
col3[30] = 'This is a string that is sentence case'
|
|
76
|
+
col3[40] = 'This Is A String That Is Title Case'
|
|
77
|
+
col3[50] = 'This is A string That has Mixed case'
|
|
78
|
+
raw_data = [{
|
|
79
|
+
'Time': t,
|
|
80
|
+
'Column 1': c1,
|
|
81
|
+
'Column 2': c2,
|
|
82
|
+
'Column 3': c3,
|
|
83
|
+
'Column 4': c4,
|
|
84
|
+
'Column 5': c5,
|
|
85
|
+
} for t, c1, c2, c3, c4, c5 in zip(time, col1, col2, col3, col4, col5)]
|
|
86
|
+
filter_container = GenericContainer(raw_data=raw_data)
|
|
87
|
+
return filter_container
|
|
88
|
+
|
|
89
|
+
@pytest.fixture(scope="module")
|
|
90
|
+
def onchange_container():
|
|
91
|
+
time = [datetime.fromtimestamp(ii) for ii in range(100)]
|
|
92
|
+
col1 = [ii//10 for ii in range(100)]
|
|
93
|
+
col2 = [ii for ii in range(100, 200)]
|
|
94
|
+
col3 = [ii for ii in range(200, 300)]
|
|
95
|
+
col4 = [ii for ii in range(300, 400)]
|
|
96
|
+
col5 = [ii for ii in range(400, 500)]
|
|
97
|
+
raw_data = [{
|
|
98
|
+
'Time': t,
|
|
99
|
+
'Column 1': c1,
|
|
100
|
+
'Column 2': c2,
|
|
101
|
+
'Column 3': c3,
|
|
102
|
+
'Column 4': c4,
|
|
103
|
+
'Column 5': c5,
|
|
104
|
+
} for t, c1, c2, c3, c4, c5 in zip(time, col1, col2, col3, col4, col5)]
|
|
105
|
+
filter_container = GenericContainer(raw_data=raw_data)
|
|
106
|
+
return filter_container
|
|
107
|
+
|
|
108
|
+
class TestFiltering:
|
|
109
|
+
def test_gt(self, filter_container):
|
|
110
|
+
gt_container = filter_container.gt('Column 2', 125)
|
|
111
|
+
assert len(gt_container) == 74
|
|
112
|
+
assert [r['Column 1'] for r in gt_container] == ([ii for ii in range(50)]*2)[26:]
|
|
113
|
+
assert [r['Column 2'] for r in gt_container] == [ii for ii in range(126, 200)]
|
|
114
|
+
assert [r['Column 3'] for r in gt_container] == [str(ii) for ii in range(226, 300)]
|
|
115
|
+
assert [r['Column 4'] for r in gt_container] == [ii for ii in range(326, 400)]
|
|
116
|
+
assert [r['Column 5'] for r in gt_container] == [ii for ii in range(426, 500)]
|
|
117
|
+
|
|
118
|
+
def test_lt(self, filter_container):
|
|
119
|
+
lt_container = filter_container.lt('Column 2', 125)
|
|
120
|
+
assert len(lt_container) == 25
|
|
121
|
+
assert [r['Column 1'] for r in lt_container] == ([ii for ii in range(50)]*2)[:25]
|
|
122
|
+
assert [r['Column 2'] for r in lt_container] == [ii for ii in range(100, 125)]
|
|
123
|
+
assert [r['Column 3'] for r in lt_container] == [str(ii) for ii in range(200, 225)]
|
|
124
|
+
assert [r['Column 4'] for r in lt_container] == [ii for ii in range(300, 325)]
|
|
125
|
+
assert [r['Column 5'] for r in lt_container] == [ii for ii in range(400, 425)]
|
|
126
|
+
|
|
127
|
+
def test_gte(self, filter_container):
|
|
128
|
+
gte_container = filter_container.gte('Column 2', 125)
|
|
129
|
+
assert len(gte_container) == 75
|
|
130
|
+
assert [r['Column 1'] for r in gte_container] == ([ii for ii in range(50)]*2)[25:]
|
|
131
|
+
assert [r['Column 2'] for r in gte_container] == [ii for ii in range(125, 200)]
|
|
132
|
+
assert [r['Column 3'] for r in gte_container] == [str(ii) for ii in range(225, 300)]
|
|
133
|
+
assert [r['Column 4'] for r in gte_container] == [ii for ii in range(325, 400)]
|
|
134
|
+
assert [r['Column 5'] for r in gte_container] == [ii for ii in range(425, 500)]
|
|
135
|
+
|
|
136
|
+
def test_lte(self, filter_container):
|
|
137
|
+
lte_container = filter_container.lte('Column 2', 125)
|
|
138
|
+
assert len(lte_container) == 26
|
|
139
|
+
assert [r['Column 1'] for r in lte_container] == ([ii for ii in range(50)]*2)[:26]
|
|
140
|
+
assert [r['Column 2'] for r in lte_container] == [ii for ii in range(100, 126)]
|
|
141
|
+
assert [r['Column 3'] for r in lte_container] == [str(ii) for ii in range(200, 226)]
|
|
142
|
+
assert [r['Column 4'] for r in lte_container] == [ii for ii in range(300, 326)]
|
|
143
|
+
assert [r['Column 5'] for r in lte_container] == [ii for ii in range(400, 426)]
|
|
144
|
+
|
|
145
|
+
def test_eq(self, filter_container):
|
|
146
|
+
eq_container = filter_container.eq('Column 1', 32)
|
|
147
|
+
assert len(eq_container) == 2
|
|
148
|
+
assert [r['Column 1'] for r in eq_container] == [32, 32]
|
|
149
|
+
assert [r['Column 2'] for r in eq_container] == [132, 182]
|
|
150
|
+
assert [r['Column 3'] for r in eq_container] == ['232', '282']
|
|
151
|
+
assert [r['Column 4'] for r in eq_container] == [332, 382]
|
|
152
|
+
assert [r['Column 5'] for r in eq_container] == [432, 482]
|
|
153
|
+
|
|
154
|
+
def test_ne(self, filter_container):
|
|
155
|
+
ne_container = filter_container.ne('Column 1', 32)
|
|
156
|
+
assert len(ne_container) == 98
|
|
157
|
+
assert [r['Column 1'] for r in ne_container] == [ii for ii in range(50) if ii != 32]*2
|
|
158
|
+
assert [r['Column 2'] for r in ne_container] == [ii for ii in range(100, 200) if ii not in [132, 182]]
|
|
159
|
+
assert [r['Column 3'] for r in ne_container] == [str(ii) for ii in range(200, 300) if ii not in [232, 282]]
|
|
160
|
+
assert [r['Column 4'] for r in ne_container] == [ii for ii in range(300, 400) if ii not in [332, 382]]
|
|
161
|
+
assert [r['Column 5'] for r in ne_container] == [ii for ii in range(400, 500) if ii not in [432, 482]]
|
|
162
|
+
|
|
163
|
+
def test_isin(self, filter_container):
|
|
164
|
+
isin_container_int = filter_container.isin('Column 2', [10, 125, 150, 175, 200, 225, 'A', {}, []])
|
|
165
|
+
isin_container_str = filter_container.isin('Column 3', ['10', '225', '250', '275', '300', '325', 900, {}, []])
|
|
166
|
+
|
|
167
|
+
assert len(isin_container_int) == 3
|
|
168
|
+
assert [r['Column 1'] for r in isin_container_int] == [25, 0, 25]
|
|
169
|
+
assert [r['Column 2'] for r in isin_container_int] == [125, 150, 175]
|
|
170
|
+
assert [r['Column 3'] for r in isin_container_int] == ['225', '250', '275']
|
|
171
|
+
assert [r['Column 4'] for r in isin_container_int] == [325, 350, 375]
|
|
172
|
+
assert [r['Column 5'] for r in isin_container_int] == [425, 450, 475]
|
|
173
|
+
|
|
174
|
+
assert len(isin_container_str) == 3
|
|
175
|
+
assert [r['Column 1'] for r in isin_container_str] == [25, 0, 25]
|
|
176
|
+
assert [r['Column 2'] for r in isin_container_str] == [125, 150, 175]
|
|
177
|
+
assert [r['Column 3'] for r in isin_container_str] == ['225', '250', '275']
|
|
178
|
+
assert [r['Column 4'] for r in isin_container_str] == [325, 350, 375]
|
|
179
|
+
assert [r['Column 5'] for r in isin_container_str] == [425, 450, 475]
|
|
180
|
+
|
|
181
|
+
def test_notin(self, filter_container):
|
|
182
|
+
notin_container_int = filter_container.notin('Column 2', [10, 125, 150, 175, 200, 225, 'A', {}, []])
|
|
183
|
+
notin_container_str = filter_container.notin('Column 3', ['10', '225', '250', '275', '300', '325', 900, {}, []])
|
|
184
|
+
|
|
185
|
+
assert len(notin_container_int) == 97
|
|
186
|
+
assert [r['Column 1'] for r in notin_container_int] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
|
|
187
|
+
assert [r['Column 2'] for r in notin_container_int] == [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199]
|
|
188
|
+
assert [r['Column 3'] for r in notin_container_int] == ['200', '201', '202', '203', '204', '205', '206', '207', '208', '209', '210', '211', '212', '213', '214', '215', '216', '217', '218', '219', '220', '221', '222', '223', '224', '226', '227', '228', '229', '230', '231', '232', '233', '234', '235', '236', '237', '238', '239', '240', '241', '242', '243', '244', '245', '246', '247', '248', '249', '251', '252', '253', '254', '255', '256', '257', '258', '259', '260', '261', '262', '263', '264', '265', '266', '267', '268', '269', '270', '271', '272', '273', '274', '276', '277', '278', '279', '280', '281', '282', '283', '284', '285', '286', '287', '288', '289', '290', '291', '292', '293', '294', '295', '296', '297', '298', '299']
|
|
189
|
+
assert [r['Column 4'] for r in notin_container_int] == [300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399]
|
|
190
|
+
assert [r['Column 5'] for r in notin_container_int] == [400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499]
|
|
191
|
+
|
|
192
|
+
assert len(notin_container_str) == 97
|
|
193
|
+
assert [r['Column 1'] for r in notin_container_str] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
|
|
194
|
+
assert [r['Column 2'] for r in notin_container_str] == [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199]
|
|
195
|
+
assert [r['Column 3'] for r in notin_container_str] == ['200', '201', '202', '203', '204', '205', '206', '207', '208', '209', '210', '211', '212', '213', '214', '215', '216', '217', '218', '219', '220', '221', '222', '223', '224', '226', '227', '228', '229', '230', '231', '232', '233', '234', '235', '236', '237', '238', '239', '240', '241', '242', '243', '244', '245', '246', '247', '248', '249', '251', '252', '253', '254', '255', '256', '257', '258', '259', '260', '261', '262', '263', '264', '265', '266', '267', '268', '269', '270', '271', '272', '273', '274', '276', '277', '278', '279', '280', '281', '282', '283', '284', '285', '286', '287', '288', '289', '290', '291', '292', '293', '294', '295', '296', '297', '298', '299']
|
|
196
|
+
assert [r['Column 4'] for r in notin_container_str] == [300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399]
|
|
197
|
+
assert [r['Column 5'] for r in notin_container_str] == [400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499]
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def test_contains_case_sensitive(self, filter_container_case_sensitive):
|
|
201
|
+
#There's no technical reason to separate this from test_contains.
|
|
202
|
+
#It's really just because I didn't recognize I needed this when I built
|
|
203
|
+
#the fixtures, and then when I decided to add these couple tests that
|
|
204
|
+
#care about sensitiviy/insensitivity, I didn't want to have to rewrite
|
|
205
|
+
#all the rest
|
|
206
|
+
contains_container_case_sensitive = filter_container_case_sensitive.contains('Column 3', 'This is a string')
|
|
207
|
+
|
|
208
|
+
assert len(contains_container_case_sensitive) == 1
|
|
209
|
+
assert [r['Column 1'] for r in contains_container_case_sensitive] == [30]
|
|
210
|
+
assert [r['Column 2'] for r in contains_container_case_sensitive] == [130]
|
|
211
|
+
assert [r['Column 3'] for r in contains_container_case_sensitive] == ['This is a string that is sentence case']
|
|
212
|
+
assert [r['Column 4'] for r in contains_container_case_sensitive] == [330]
|
|
213
|
+
assert [r['Column 5'] for r in contains_container_case_sensitive] == [430]
|
|
214
|
+
|
|
215
|
+
def test_doesnotcontain_case_sensitive(self, filter_container_case_sensitive):
|
|
216
|
+
#There's no technical reason to separate this from test_contains.
|
|
217
|
+
#It's really just because I didn't recognize I needed this when I built
|
|
218
|
+
#the fixtures, and then when I decided to add these couple tests that
|
|
219
|
+
#care about sensitiviy/insensitivity, I didn't want to have to rewrite
|
|
220
|
+
#all the rest
|
|
221
|
+
contains_container_case_insensitive = filter_container_case_sensitive.contains('Column 3', 'This is a string', case_sensitive=False)
|
|
222
|
+
|
|
223
|
+
assert len(contains_container_case_insensitive) == 3
|
|
224
|
+
assert [r['Column 1'] for r in contains_container_case_insensitive] == [30, 40, 0]
|
|
225
|
+
assert [r['Column 2'] for r in contains_container_case_insensitive] == [130, 140, 150]
|
|
226
|
+
assert [r['Column 3'] for r in contains_container_case_insensitive] == ['This is a string that is sentence case', 'This Is A String That Is Title Case', 'This is A string That has Mixed case']
|
|
227
|
+
assert [r['Column 4'] for r in contains_container_case_insensitive] == [330, 340, 350]
|
|
228
|
+
assert [r['Column 5'] for r in contains_container_case_insensitive] == [430, 440, 450]
|
|
229
|
+
|
|
230
|
+
def test_contains(self, filter_container):
|
|
231
|
+
contains_container = filter_container.contains('Column 3', '3')
|
|
232
|
+
|
|
233
|
+
assert len(contains_container) == 19
|
|
234
|
+
assert [r['Column 1'] for r in contains_container] == [3, 13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 43, 3, 13, 23, 33, 43]
|
|
235
|
+
assert [r['Column 2'] for r in contains_container] == [103, 113, 123, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 143, 153, 163, 173, 183, 193]
|
|
236
|
+
assert [r['Column 3'] for r in contains_container] == ['203', '213', '223', '230', '231', '232', '233', '234', '235', '236', '237', '238', '239', '243', '253', '263', '273', '283', '293']
|
|
237
|
+
assert [r['Column 4'] for r in contains_container] == [303, 313, 323, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 343, 353, 363, 373, 383, 393]
|
|
238
|
+
assert [r['Column 5'] for r in contains_container] == [403, 413, 423, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 443, 453, 463, 473, 483, 493]
|
|
239
|
+
|
|
240
|
+
def test_doesnotcontain(self, filter_container):
|
|
241
|
+
doesnotcontain_container = filter_container.doesnotcontain('Column 3', '3')
|
|
242
|
+
|
|
243
|
+
assert len(doesnotcontain_container) == 81
|
|
244
|
+
assert [r['Column 1'] for r in doesnotcontain_container] == [0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 40, 41, 42, 44, 45, 46, 47, 48, 49, 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 35, 36, 37, 38, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49]
|
|
245
|
+
assert [r['Column 2'] for r in doesnotcontain_container] == [100, 101, 102, 104, 105, 106, 107, 108, 109, 110, 111, 112, 114, 115, 116, 117, 118, 119, 120, 121, 122, 124, 125, 126, 127, 128, 129, 140, 141, 142, 144, 145, 146, 147, 148, 149, 150, 151, 152, 154, 155, 156, 157, 158, 159, 160, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 174, 175, 176, 177, 178, 179, 180, 181, 182, 184, 185, 186, 187, 188, 189, 190, 191, 192, 194, 195, 196, 197, 198, 199]
|
|
246
|
+
assert [r['Column 3'] for r in doesnotcontain_container] == ['200', '201', '202', '204', '205', '206', '207', '208', '209', '210', '211', '212', '214', '215', '216', '217', '218', '219', '220', '221', '222', '224', '225', '226', '227', '228', '229', '240', '241', '242', '244', '245', '246', '247', '248', '249', '250', '251', '252', '254', '255', '256', '257', '258', '259', '260', '261', '262', '264', '265', '266', '267', '268', '269', '270', '271', '272', '274', '275', '276', '277', '278', '279', '280', '281', '282', '284', '285', '286', '287', '288', '289', '290', '291', '292', '294', '295', '296', '297', '298', '299']
|
|
247
|
+
assert [r['Column 4'] for r in doesnotcontain_container] == [300, 301, 302, 304, 305, 306, 307, 308, 309, 310, 311, 312, 314, 315, 316, 317, 318, 319, 320, 321, 322, 324, 325, 326, 327, 328, 329, 340, 341, 342, 344, 345, 346, 347, 348, 349, 350, 351, 352, 354, 355, 356, 357, 358, 359, 360, 361, 362, 364, 365, 366, 367, 368, 369, 370, 371, 372, 374, 375, 376, 377, 378, 379, 380, 381, 382, 384, 385, 386, 387, 388, 389, 390, 391, 392, 394, 395, 396, 397, 398, 399]
|
|
248
|
+
assert [r['Column 5'] for r in doesnotcontain_container] == [400, 401, 402, 404, 405, 406, 407, 408, 409, 410, 411, 412, 414, 415, 416, 417, 418, 419, 420, 421, 422, 424, 425, 426, 427, 428, 429, 440, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456, 457, 458, 459, 460, 461, 462, 464, 465, 466, 467, 468, 469, 470, 471, 472, 474, 475, 476, 477, 478, 479, 480, 481, 482, 484, 485, 486, 487, 488, 489, 490, 491, 492, 494, 495, 496, 497, 498, 499]
|
|
249
|
+
|
|
250
|
+
def test_before(self, filter_container):
|
|
251
|
+
before_container = filter_container.before(datetime.fromtimestamp(3), 'Time')
|
|
252
|
+
before_container_inclusive = filter_container.before(datetime.fromtimestamp(3), 'Time', inclusive=True)
|
|
253
|
+
|
|
254
|
+
assert len(before_container) == 3
|
|
255
|
+
assert [r['Column 1'] for r in before_container] == [0, 1, 2]
|
|
256
|
+
assert [r['Column 2'] for r in before_container] == [100, 101, 102]
|
|
257
|
+
assert [r['Column 3'] for r in before_container] == ['200', '201', '202']
|
|
258
|
+
assert [r['Column 4'] for r in before_container] == [300, 301, 302]
|
|
259
|
+
assert [r['Column 5'] for r in before_container] == [400, 401, 402]
|
|
260
|
+
|
|
261
|
+
assert len(before_container_inclusive) == 4
|
|
262
|
+
assert [r['Column 1'] for r in before_container_inclusive] == [0, 1, 2, 3]
|
|
263
|
+
assert [r['Column 2'] for r in before_container_inclusive] == [100, 101, 102, 103]
|
|
264
|
+
assert [r['Column 3'] for r in before_container_inclusive] == ['200', '201', '202', '203']
|
|
265
|
+
assert [r['Column 4'] for r in before_container_inclusive] == [300, 301, 302, 303]
|
|
266
|
+
assert [r['Column 5'] for r in before_container_inclusive] == [400, 401, 402, 403]
|
|
267
|
+
|
|
268
|
+
def test_after(self, filter_container):
|
|
269
|
+
after_container = filter_container.after(datetime.fromtimestamp(97), 'Time')
|
|
270
|
+
after_container_inclusive = filter_container.after(datetime.fromtimestamp(97), 'Time', inclusive=True)
|
|
271
|
+
|
|
272
|
+
assert len(after_container) == 2
|
|
273
|
+
assert [r['Column 1'] for r in after_container] == [48, 49]
|
|
274
|
+
assert [r['Column 2'] for r in after_container] == [198, 199]
|
|
275
|
+
assert [r['Column 3'] for r in after_container] == ['298', '299']
|
|
276
|
+
assert [r['Column 4'] for r in after_container] == [398, 399]
|
|
277
|
+
assert [r['Column 5'] for r in after_container] == [498, 499]
|
|
278
|
+
|
|
279
|
+
assert len(after_container_inclusive) == 3
|
|
280
|
+
assert [r['Column 1'] for r in after_container_inclusive] == [47, 48, 49]
|
|
281
|
+
assert [r['Column 2'] for r in after_container_inclusive] == [197, 198, 199]
|
|
282
|
+
assert [r['Column 3'] for r in after_container_inclusive] == ['297', '298', '299']
|
|
283
|
+
assert [r['Column 4'] for r in after_container_inclusive] == [397, 398, 399]
|
|
284
|
+
assert [r['Column 5'] for r in after_container_inclusive] == [497, 498, 499]
|
|
285
|
+
|
|
286
|
+
def test_between(self, filter_container):
|
|
287
|
+
between_container_both_inclusive = filter_container.between('Time', datetime.fromtimestamp(30), datetime.fromtimestamp(35))
|
|
288
|
+
between_container_lower_inclusive = filter_container.between('Time', datetime.fromtimestamp(30), datetime.fromtimestamp(35), inclusive='lower')
|
|
289
|
+
between_container_upper_inclusive = filter_container.between('Time', datetime.fromtimestamp(30), datetime.fromtimestamp(35), inclusive='upper')
|
|
290
|
+
between_container_neither_inclusive = filter_container.between('Time', datetime.fromtimestamp(30), datetime.fromtimestamp(35), inclusive='neither')
|
|
291
|
+
|
|
292
|
+
assert len(between_container_both_inclusive) == 6
|
|
293
|
+
assert [r['Column 1'] for r in between_container_both_inclusive] == [30, 31, 32, 33, 34, 35]
|
|
294
|
+
assert [r['Column 2'] for r in between_container_both_inclusive] == [130, 131, 132, 133, 134, 135]
|
|
295
|
+
assert [r['Column 3'] for r in between_container_both_inclusive] == ['230', '231', '232', '233', '234', '235']
|
|
296
|
+
assert [r['Column 4'] for r in between_container_both_inclusive] == [330, 331, 332, 333, 334, 335]
|
|
297
|
+
assert [r['Column 5'] for r in between_container_both_inclusive] == [430, 431, 432, 433, 434, 435]
|
|
298
|
+
|
|
299
|
+
assert len(between_container_lower_inclusive) == 5
|
|
300
|
+
assert [r['Column 1'] for r in between_container_lower_inclusive] == [30, 31, 32, 33, 34]
|
|
301
|
+
assert [r['Column 2'] for r in between_container_lower_inclusive] == [130, 131, 132, 133, 134]
|
|
302
|
+
assert [r['Column 3'] for r in between_container_lower_inclusive] == ['230', '231', '232', '233', '234']
|
|
303
|
+
assert [r['Column 4'] for r in between_container_lower_inclusive] == [330, 331, 332, 333, 334]
|
|
304
|
+
assert [r['Column 5'] for r in between_container_lower_inclusive] == [430, 431, 432, 433, 434]
|
|
305
|
+
|
|
306
|
+
assert len(between_container_upper_inclusive) == 5
|
|
307
|
+
assert [r['Column 1'] for r in between_container_upper_inclusive] == [31, 32, 33, 34, 35]
|
|
308
|
+
assert [r['Column 2'] for r in between_container_upper_inclusive] == [131, 132, 133, 134, 135]
|
|
309
|
+
assert [r['Column 3'] for r in between_container_upper_inclusive] == ['231', '232', '233', '234', '235']
|
|
310
|
+
assert [r['Column 4'] for r in between_container_upper_inclusive] == [331, 332, 333, 334, 335]
|
|
311
|
+
assert [r['Column 5'] for r in between_container_upper_inclusive] == [431, 432, 433, 434, 435]
|
|
312
|
+
|
|
313
|
+
assert len(between_container_neither_inclusive) == 4
|
|
314
|
+
assert [r['Column 1'] for r in between_container_neither_inclusive] == [31, 32, 33, 34]
|
|
315
|
+
assert [r['Column 2'] for r in between_container_neither_inclusive] == [131, 132, 133, 134]
|
|
316
|
+
assert [r['Column 3'] for r in between_container_neither_inclusive] == ['231', '232', '233', '234']
|
|
317
|
+
assert [r['Column 4'] for r in between_container_neither_inclusive] == [331, 332, 333, 334]
|
|
318
|
+
assert [r['Column 5'] for r in between_container_neither_inclusive] == [431, 432, 433, 434]
|
|
319
|
+
|
|
320
|
+
def test_matches(self, filter_container):
|
|
321
|
+
matches_container = filter_container.matches('Column 3', r'\d0\d')
|
|
322
|
+
assert [r['Column 1'] for r in matches_container] == [ii for ii in range(10)]
|
|
323
|
+
assert [r['Column 2'] for r in matches_container] == [ii for ii in range(100, 110)]
|
|
324
|
+
assert [r['Column 3'] for r in matches_container] == [str(ii) for ii in range(200, 210)]
|
|
325
|
+
assert [r['Column 4'] for r in matches_container] == [ii for ii in range(300, 310)]
|
|
326
|
+
assert [r['Column 5'] for r in matches_container] == [ii for ii in range(400, 410)]
|
|
327
|
+
|
|
328
|
+
def test_doesnotmatch(self, filter_container):
|
|
329
|
+
does_not_match_container = filter_container.doesnotmatch('Column 3', r'\d0\d')
|
|
330
|
+
assert [r['Column 1'] for r in does_not_match_container] == ([ii for ii in range(50)]*2)[10:]
|
|
331
|
+
assert [r['Column 2'] for r in does_not_match_container] == [ii for ii in range(100, 200)][10:]
|
|
332
|
+
assert [r['Column 3'] for r in does_not_match_container] == [str(ii) for ii in range(200, 300)][10:]
|
|
333
|
+
assert [r['Column 4'] for r in does_not_match_container] == [ii for ii in range(300, 400)][10:]
|
|
334
|
+
assert [r['Column 5'] for r in does_not_match_container] == [ii for ii in range(400, 500)][10:]
|
|
335
|
+
|
|
336
|
+
def test_onchange(self, onchange_container):
|
|
337
|
+
onchange_only = onchange_container.on_change('Column 1')
|
|
338
|
+
assert [r['Column 1'] for r in onchange_only] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
339
|
+
assert [r['Column 2'] for r in onchange_only] == [100, 110, 120, 130, 140, 150, 160, 170, 180, 190]
|
|
340
|
+
assert [r['Column 3'] for r in onchange_only] == [200, 210, 220, 230, 240, 250, 260, 270, 280, 290]
|
|
341
|
+
assert [r['Column 4'] for r in onchange_only] == [300, 310, 320, 330, 340, 350, 360, 370, 380, 390]
|
|
342
|
+
assert [r['Column 5'] for r in onchange_only] == [400, 410, 420, 430, 440, 450, 460, 470, 480, 490]
|
|
343
|
+
|
|
344
|
+
def test_min(self, filter_container):
|
|
345
|
+
try:
|
|
346
|
+
filter_container.eq('Column 1', 1000, minimum=1)
|
|
347
|
+
except Exception as e:
|
|
348
|
+
assert str(e) == 'Filtered length is not at least 1 as specified'
|
|
349
|
+
assert len(filter_container.eq('Column 1', 10, minimum=2)) == 2
|
|
350
|
+
|
|
351
|
+
def test_max(self, filter_container):
|
|
352
|
+
try:
|
|
353
|
+
filter_container.eq('Column 1', 10, maximum=1)
|
|
354
|
+
except Exception as e:
|
|
355
|
+
assert str(e) == 'Filtered length is not less than or equal to 1 as specified'
|
|
356
|
+
assert len(filter_container.eq('Column 1', 10, maximum=2)) == 2
|
|
357
|
+
|
|
358
|
+
def test_exactly(self, filter_container):
|
|
359
|
+
try:
|
|
360
|
+
filter_container.eq('Column 1', 10, exactly=1)
|
|
361
|
+
except Exception as e:
|
|
362
|
+
assert str(e) == 'Filtered length is not exactly 1 as specified'
|
|
363
|
+
assert len(filter_container.eq('Column 1', 10, exactly=2)) == 2
|
|
364
|
+
|
|
365
|
+
class TestDataIO:
|
|
366
|
+
def test_init_from_csv(self, csv_file):
|
|
367
|
+
container = GenericContainer(csv_path=csv_file)
|
|
368
|
+
assert len(container) == 2
|
|
369
|
+
assert container[0]['col2'] == 'a'
|
|
370
|
+
|
|
371
|
+
def test_to_csv_with_mkdirs(self, filter_container, tmp_path):
|
|
372
|
+
deep_path = tmp_path / "new_dir" / "output.csv"
|
|
373
|
+
filter_container.to_csv(deep_path, mkdirs=True)
|
|
374
|
+
assert deep_path.exists()
|
|
375
|
+
|
|
376
|
+
def test_init_with_metadata_cleaning(self):
|
|
377
|
+
# Covers lines 126-127
|
|
378
|
+
meta = {'_internal': 'hide', 'public': 'show', 'dictionary': 'ignore'}
|
|
379
|
+
container = GenericContainer(raw_data=[{'a': 1}], metadata=meta)
|
|
380
|
+
assert 'public' in container.metadata
|
|
381
|
+
assert '_internal' not in container.metadata
|
|
382
|
+
assert 'dictionary' not in container.metadata
|
|
383
|
+
|
|
384
|
+
class TestReporting:
|
|
385
|
+
def test_summarize_feature(self, onchange_container):
|
|
386
|
+
# Fixes the xfailed test and covers lines 1116-1173
|
|
387
|
+
summary = onchange_container.summarize('Column 1')
|
|
388
|
+
assert len(summary) == 10 # 0 through 9
|
|
389
|
+
assert int(summary[0]['Occurances']) == 10
|
|
390
|
+
assert 'First Occurence' in summary[0].values
|
|
391
|
+
|
|
392
|
+
def test_power_table_nested(self, nested_container):
|
|
393
|
+
# Covers the recursive PowerTable calls (line 264)
|
|
394
|
+
table = nested_container.power_table()
|
|
395
|
+
html = table.render()
|
|
396
|
+
assert 'parent' in html
|
|
397
|
+
assert 'child_tab' in html
|
|
398
|
+
|
|
399
|
+
def test_repr_logic(self, filter_container):
|
|
400
|
+
# Covers lines 391-413
|
|
401
|
+
ascii_table = repr(filter_container)
|
|
402
|
+
assert '+' in ascii_table
|
|
403
|
+
assert 'Column 1' in ascii_table
|