Scenarios for using historical inventory and topology
By @Philip Blackwood , AT&T
This document outlines how to use historical inventory and topology data to support various use cases. The purpose of the exercise is to ensure that all needed capabilities have been identified, and close any gaps discovered.
All use cases assume the feature has been implemented as described in the white paper “Temporal logic for attributes in a data base.” The historical data discussed below has timestamps for when conditions were true in the network as well as the timestamp of when data was written to the data base.
The use cases refer to data filters and other data processing functions that are listed in the Appendix (functioning code).
General capabilities
In the write-ups below, a query is made to the data base and then time logic is applied. The initial query to the data base is free of time constraints; for example, it could return all historical data for a given VNF. A more complex query could return a portion of the graph related to the VNF.
The client will typically not need the complete history, and the first step is to identify all values that might be needed to answer questions about the history starting at a fixed time T (and following). The result of this trimming is then used to answer questions like:
What was the complete history after time T?
What are the times at which changes occurred?
What are the changes that were made at those times?
What are the times when the in-maintenance indicator was set or cleared?
What changes were made to specific attributes?
At what times did the topology change?
What topology changes occurred?
What did this part of the network look like at a specific time (topology or attributes)?
Was any data missing (not yet known) at a particular time (e.g. when a decision was made)?
Which data source provided a specific value? (and history)
Investigating a problem with a VNF
Use case description:
Operations might be investigating a problem and suspect that it may have started two days ago. The historical inventory feature should let users see the state of the network at the time of the event (attributes and relationships).
A common scenario is that once an issue has been detected in the network, Operations wants to be able to see what changes were made in the network just prior to the time of the problem. For example, if service degradation started occurring at a certain time, was anything different in the network between the time things were operating correctly and the start of the service degradation?
To begin analysis, query the historical data base for data related to the VNF, which may include the related VFCs, VMs, servers, storage, and connections to the VNF, and even other VNFs that may affect the functioning of the main VNF of interest. Note that the processing below does not assume the data base query needs to apply any time logic (if it can, the key logic needed is to be able to find data with a start time at or later than the latest start time before a fixed time T, for each attribute).
Select a time T far enough in the past to include all of the history you might care about. Apply the function getBaseDataSetForT to get the subset of data that will be used to answer questions about the history after time T. Apply the function findConnectedSubgraph to get the connected portion of the results that includes the VNF (this step is needed because the initial filtering can in some cases leave a fragmented graph). [note: core code for this function is available, but it needs a “wrapper” to give its result in the standard data format]
The data set is now ready to be used to answer specific questions about history of the VNF and related network elements. All of the questions below are answered using this base data set.
What is the complete history after time T?
Apply the filter getHistoryAfterTimeT.
What are the times at which changes occurred?
Apply the filter getTimesOfChangesAfterT to get the list of times.
What changes were made at those times?
Use the filter getChangesAfterTimeT or getChangesAtTimeT.
What are the times when the in-maintenance indicator was set or cleared?
Use the filter getValuesForAttribute, with attribute “in-maintenance”.
What changes were made to specific attributes?
Use getValuesForAttributeList, with the list of attributes of interest.
At what times did the topology change?
Use the filter getTimesOfTopologyChangesAfterT.
What topology changes occurred?
Use getTopologyChangesAfterT.
What did this part of the network look like at a specific time?
Use the filter getHistoryAtTimeT or, for just the topology view, getTopologyAtTimeT.
Was any data missing (not yet known) at a particular time (e.g. when a decision was made)?
To look for late-arriving data, use getValuesMissingAtTimeT
To see if data was later declared not valid, use getValuesMarkedNotValidAfterTimeT
All of the questions above can also be answered for any interval of time, or using only data that was available at a specific time (by first applying getValuesWrittenBeforeT).
Analyze network events in the context of inventory
Use case description:
Often the historical inventory data needs to be combined with non-inventory data including events in the network (faults or performance monitoring), or VNF syslogs or application logs. Combining the history from these sources could be used to test a new analytic against a set of known problems that have occurred. When a new type of problem is found in the network, the data can be used to confirm hypotheses about the root cause or look for patterns that can be incorporated into the analytics of the monitoring environment. (or train a Machine Learning model)
Prepare the base data set as in the example above.
Identify the times at which changes occurred, using getTimesOfChangesAfterT.
For each interval between times, the network was static, and network events can be analyzed in the context of this static network (which can be viewed using getHistoryAtTimeT).
The network events may in some cases also be correlated to changes made to the network, which can be seen using getChangesAtTimeT for each time when changes occurred. (or getChangesNearTimeT)
Data Integrity analysis
Historical data includes the source of data for each value, along with timestamps. The timestamps may be used to detect trends in the validity of individual values, or to look at the data before and after a source of data was modified to correct a data integrity issue.
Note that the keyword :timeMarkedNotValid was introduced as part of the historical data feature, and intended to be used when a “bad” value is replaced; this intent has not been coordinated with the data integrity initiative (which might have a separate way to marking records with data integrity issues).
VNF existence and feature usage statistics
Use case description:
Historical data for a VNF could be used to ensure the VNF vendor has been paid the correct amount per the licensing agreement. In this case, the duration of each unique combination of charge-affecting attributes may be required during an interval of time, or other similar data based on the terms of the license agreement. Data about the use of license keys and entitlements may also be required. The historical inventory data for a VNF might also be useful in verifying compliance with a licensing agreement (e.g. if the license only permits use of the VNF in certain geographical regions). Data may be required to be retained for up to one year in some cases.
In this use case we will measure the total time duration that certain features were used during a month, based on values in the inventory data for a VNF (referred to as “usage”). Similar logic could be used for other types of reporting, such as average amount of time spent in-maintenance for each type of VNF.
At the end of every month, wait until you expect all data has been updated and then perform this analysis.
Query the historical data base for VNFs that will all have exactly the same rules applied (and typically for a single vendor).
Apply the function getBaseDataSetForT, where T is the beginning of the month being measured. This creates the data set used in all of the following steps.
apply the filter getValuesBetweenTimes to get only the records for the previous month
apply the filter getValuesForAttributeList to get only the records for the list of attributes of interest
apply the function filterOutValuesMarkedNotValid (if these values have been replaced with correct values)
Calculate an interval for each value. Use sortByValueStartTime to sort the data by entity, attribute and start time . For each attribute, the values are now in sequence. For each value, calculate its start and end times within the month (truncating data at beginning and end of the month):
Replace the value of any :startTime that is prior to the beginning of the month with the beginning of the month
Calculate an end time for each value as the earliest of:
the next :startTime for the attribute (if it exists)
the time at which the VNF ceased to exist (if applicable)
the end of the month
accumulate total elapsed time for each VNF/attribute/value
Validate previously calculated existence and feature usage statistics
In the previous use case, it is possible that some data was missing at the time the analysis was done, and the data was received later. Similarly, it is possible that some data was later found to be incorrect and was replaced with the corrected values.
In this use case, we revisit the data (e.g. a month later) to see if either of these scenarios has occurred.
Perform the following steps exactly as before:
run the data base query
apply the filter getValuesBetweenTimes
apply the filter getValuesForAttributeList
Now, with T equal to the time the original analysis was done:
apply getValuesMissingAtTimeT (to identify late-arriving values)
apply getValuesMarkedNotValidAfterTimeT (to see data used that was not valid)
if either of these two steps returns values, then the data used to originally produce the analysis has changed, and the analysis should be rerun
Gather metrics on inventory timeliness and accuracy for closed loop control
Given data about times at which closed loop control was activated, we could review the inventory data to see if any data was missing or invalid as in scenarios above:
apply the filter getValuesMissingAtTimeT to see if any data was missing
apply the function getValuesMarkedNotValidAfterTimeT to see if any data was invalid.
Bill reconciliation
Use case description:
Historical data for a service instance may be helpful in bill reconciliation, e.g. making sure the correct amount has been billed for the service. The historical data needed would involve the attributes that affect the bill (e.g. service bandwidth) and may require knowing the duration of each unique combination of bill-affecting attributes over a period of time. Times when the service was instantiated and when it was “taken down” are also of interest.
The methods used for analyzing feature usage can be applied to this use case.
Compile statistics about processes
A&AI has data about provisioning state changes, and these could be used to do analysis of the process, e.g. duration of different states in the process. The data can be obtained using getValuesForAttributeList.
Major failure at a location
The historical inventory feature could be used to get a snapshot of inventory in a location at a time in the past, and this data might be useful in recovering from a major failure at a location.
Appendix: code
The functions below are written in the Clojure language to align with the historical data feature in ONAP (work in progress, snapshot taken 6/17/2019, master copy is in file “time filters …”).
; time filters written in Clojure language, using a flat data structure
; logic as documented in white paper
; the different filters can be combined, or invoked interactively
; also see listing of use cases
; -------------------------------------------------------------------------------------
; terminology
;
; a "value" is one instance of a value for an attribute (one row of text in the data definition below)
; a value has a timestamp for its startTime and one for its WriteTime
; a "view" consists of the data that is known at a time T (i.e. written before T)
;
; names of filters start with either "getValues..." or "filterOutValues ..." (whichever style is easier to understand)
; the phrase "NotNeeded" means "not needed for any view back to time T"
;
; ---------------------------------------------------------------------------------------
; List of Main Functions
;
;
; Prepare an initial data set that can then be used to answer questions
;
; getBaseDataSetForT
; getConnectedSubgraph (uses mergeIntersectingSets and intersecting?)
;
;
; Topology and Entity Level Views
;
; getTopology ... AfterTimeT, BeforeTimeT, BetweenTimes, or AtTimeT
;
;
; History of attribute values
;
; getHistory ... AfterTimeT, BeforeTimeT, BetweenTImes, or AtTimeT
;
; getTopology ... AfterTimeT, BeforeTimeT, BetweenTimes, or AtTimeT
;
; getChanges ... AfterTimeT, BeforeTimeT, BetweenTimes, or AtTimeT
;
;
; Check for data missing or not valid in a view (e.g. arrived late)
;
; getValuesMissingAtTimeT
;
; getValuesMarkedNotValidAfterTimeT
;
;
; Identify older values to be removed (or marked in data base to improve query performance)
;
; getEntitiesNotNeeded
;
; getValuesNotNeeded
;
;
; Restrict the view to data that was available at time T
;
; getValuesWrittenBeforeTimeT
;
;
; Other
;
; getValuesForAttribute
; getValuesForAttributeList
; getValuesForAttributeAndValue
;
; getValuesMarkedNotValid
;
; filterOutEntitiesGoneBeforeTimeT
;
; findLagTimesAtT
; ------------------------------------------------------------------------------------------
; to do
;
;
; 1. make sure conditions for valid value are included where needed (e.g. exists = no, and valid)
;
; 2. replace integer times with actual time values, and change all time comparisons and calculations
;
; 3. test the functions that have a note saying not tested
; -------------------------------------------------------------------------------------------
; a flat data structure was chosen for ease of coding (e.g. do query and then format like this)
; functions to view the data do not assume any particular order of the entries
; every row of text below should have the same keywords; empty values should be represented as ""
; terminology note: in the data set an "entity" can be either a node or relationship
(def entities '(
{:entity "1111" :attribute "exists" :value "yes" :startTime 3 :writeTime 4 :timeMarkedNotValid "" }
{:entity "1111" :attribute "id" :value "1111" :startTime 3 :writeTime 4 :timeMarkedNotValid "" }
{:entity "1111" :attribute "entity-type" :value "node" :startTime 3 :writeTime 4 :timeMarkedNotValid "" }
{:entity "1111" :attribute "size" :value "M" :startTime 4 :writeTime 6 :timeMarkedNotValid "" }
{:entity "1111" :attribute "size" :value "XL" :startTime 10 :writeTime 25 :timeMarkedNotValid 35}
{:entity "1111" :attribute "in-maintenance" :value "yes" :startTime 8 :writeTime 8 :timeMarkedNotValid "" }
{:entity "1111" :attribute "in-maintenance" :value "no" :startTime 15 :writeTime 15 :timeMarkedNotValid "" }
{:entity "2222" :attribute "exists" :value "yes" :startTime 103 :writeTime 104 :timeMarkedNotValid "" }
{:entity "2222" :attribute "id" :value "111" :startTime 103 :writeTime 104 :timeMarkedNotValid "" }
{:entity "2222" :attribute "entity-type" :value "node" :startTime 103 :writeTime 103 :timeMarkedNotValid "" }
{:entity "2222" :attribute "size" :value "XL" :startTime 110 :writeTime 125 :timeMarkedNotValid "" }
{:entity "2222" :attribute "size" :value "M" :startTime 103 :writeTime 104 :timeMarkedNotValid "" }
{:entity "2222" :attribute "in-maintenance" :value "yes" :startTime 108 :writeTime 108 :timeMarkedNotValid "" }
{:entity "2222" :attribute "in-maintenance" :value "no" :startTime 115 :writeTime 115 :timeMarkedNotValid "" }
{:entity "2222" :attribute "exists" :value "no" :startTime 140 :writeTime 141 :timeMarkedNotValid "" }
{:entity "3333" :attribute "exists" :value "yes" :startTime 503 :writeTime 504 :timeMarkedNotValid "" }
{:entity "3333" :attribute "id " :value "3333" :startTime 503 :writeTime 504 :timeMarkedNotValid "" }
{:entity "3333" :attribute "entity-type " :value "edge" :startTime 503 :writeTime 504 :timeMarkedNotValid "" }
{:entity "3333" :attribute "from-entity" :value "1111" :startTime 503 :writeTime 504 :timeMarkedNotValid "" }
{:entity "3333" :attribute "to-entity " :value "2222" :startTime 503 :writeTime 504 :timeMarkedNotValid "" }
{:entity "3333" :attribute "exists" :value "no" :startTime 530 :writeTime 531 :timeMarkedNotValid "" }
))
; for testing when looking at values for one attribute
(def values '(
{:entity "1111" :attribute "size" :value "M" :startTime 4 :writeTime 5 :timeMarkedNotValid ""}
{:entity "1111" :attribute "size" :value "S" :startTime 12 :writeTime 13 :timeMarkedNotValid ""}
{:entity "1111" :attribute "size" :value "XL" :startTime 8 :writeTime 20 :timeMarkedNotValid ""}))
; =====================================================================================
;
; functions below are listed in a bottoms-up order
;
; =====================================================================================
; getHistoryAfterTimeT and supporting functions
(defn findAttributeLatestStartTimeBeforeTimeT
[T values]
(let [timesList (sort > (map :startTime values)) ; reverse sort the times
candidate (some #(if (<= % T ) %) timesList)] ; select first that is <= T
(if (nil? candidate) (last timesList) candidate ) )) ; or just return earliest
;
; example
; (findAttributeLatestStartTimeBeforeTimeT 1 values)
; not tested
(defn findAttributeValuesForHistoryAfterTimeT
"for one attribute, find values needed for history after time T, in current view"
[T values]
(let [startTimeCutoff (findAttributeLatestStartTimeBeforeTimeT T values)]
(filter #(>= (:startTime %) startTimeCutoff) values) ))
; example
; (findAttributeValuesForHistoryAfterTimeT 1 values)
; all tests passed
(defn applyTimeFilter
"apply an attribute-level filter to each attribute in a data set"
[timeFilter T dataSet]
(let [attributeList (map (juxt :entity :attribute) dataSet)]
(reduce (fn [result attribute]
(let [values (filter #(and (= (:entity %) (first attribute)) (= (:attribute %) (second attribute))) dataSet)
keepers (timeFilter T values) ]
(into result keepers))) [] attributeList) ))
; (applyTimeFilter findAttributeValuesForHistoryAfterTimeT 18 entities)
(defn findEntitiesGoneBeforeTimeT
"find entities gone before time T in the current view"
[T dataSet]
(->> dataSet
(filter #(= (:attribute %) "exists") ,,,)
(filter #(= (:value %) "no" ) ,,,)
(filter #(= (:timeMarkedNotValid %) "" ) ,,,)
(filter #(<= (:startTime %) T ) ,,,)
(map :entity ,,,) ))
; example
; (findEntitiesGoneBeforeTimeT 5 entities)
; all tests passed
(defn filterOutEntitiesGoneBeforeTimeT
"filter out entities that did exist at time T, in the current view"
[T dataSet]
(let [entityList (findEntitiesGoneBeforeTimeT T dataSet)]
(filter #(nil? (some #{(:entity %)} entityList)) dataSet) )) ;keep values for entities not on the list
; example
; (filterOutEntitiesGoneBeforeTimeT 5 entities)
; all tests passed
(defn getHistoryAfterTimeT
" filter to find entries needed to show history starting at time T"
[T dataSet]
(->> dataSet
(filterOutEntitiesGoneBeforeTimeT T ,,,)
(applyTimeFilter findAttributeValuesForHistoryAfterTimeT T ,,,) ))
; example
; (getHistoryAfterTimeT 6 entities)
; =============================================================================
; getBaseDataSetForT and supporting functions not yet listed
(defn filterOutAttributeValuesNotNeeded
"filter out values not needed for any view back to T, for one attribute"
[T values]
(let [startTimeCutoff (->> values
(filter #(< (:writeTime %) T) ,,,)
(findAttributeLatestStartTimeBeforeTimeT T ,,,) )]
(filter #(>= (:startTime %) startTimeCutoff) values) ))
; example
; (filterOutAttributeValuesNotNeeded 18 entities)
(defn filterOutValuesNotNeeded
"filter out values not needed for any view"
[T dataSet]
(applyTimeFilter filterOutAttributeValuesNotNeeded T dataSet) )
; (filterOutValuesNotNeeded 14 entities)
(defn findEntitiesNotNeeded
"find entities not needed for any view back to start time T"
[T dataSet]
(->> dataSet
(filter #(<= (:writeTime %) T) ,,,)
(findEntitiesGoneBeforeTimeT T ,,,) ))
; example
; (findEntitiesNotNeeded 25 entities)
(defn filterOutEntitiesNotNeeded
"filter out entities not needed for any view back to start time T"
[T dataSet]
(let [entityList (findEntitiesNotNeeded T dataSet)]
(filter #(nil? (some #{(:entity %)} entityList)) dataSet) )) ; keep values for entities not on the list
;
; example
; (filterOutEntitiesNotNeeded 300 entities)
(defn getBaseDataSetForT
"keep only the data needed to answer questions about history after time T (for any view)"
[T dataSet]
(->> dataSet
(filterOutEntitiesNotNeeded T ,,,)
(filterOutValuesNotNeeded T ,,,) ))
; ==========================================================================
; getConnectedSubgraph and supporting functions
(defn intersecting?
"check to see if the intersection of two sets is non-empty"
[set1 set2]
(not (empty? (clojure.set/intersection set1 set2))) )
; example
; (intersecting? #{1 2} #{3 4 5})
(defn mergeIntersectingSets
"from a given starting set and a collection of sets, find union of sets reachable by intersections"
[result-set sets]
(let [new-result-set (reduce #(if (intersecting? %1 %2) (clojure.set/union %1 %2) %1) result-set sets)]
(if (= result-set new-result-set)
result-set
(recur new-result-set sets)) ))
; example
; (def sets '( #{1 2} #{2 3} #{5 7} #{3 5} #{1 8 10} #{12 14 20}) )
; (mergeIntersectingSets #{10} sets)
(defn getConnectedSubgraph
"given a collection of pairs representing edges, find connected graph of startNode"
[startNode edges]
(let [nodes (mergeIntersectingSets #{startNode} edges)]
(filter #(and (contains? nodes (first %)) (contains? nodes (second %))) edges)))
; example
; (def edges '( #{1 3} #{8 9} #{21 22} #{4 5} #{3 4} #{4 8}))
; (getConnectedSubgraph 1 edges)
; ==========================================================================
; other functions in alphabetical order
; --------------------------------------------------------------------------
; filterOutValuesMarkedNotValid
(defn filterOutValuesMarkedNotValid
"filter out entries that have been marked not valid"
[dataSet]
(filter #(= (:timeMarkedNotValid %) "") entities) )
; --------------------------------------------------------------------------
; findAttributeValuesGoneBeforeTimeT
; might also need to account for case when the entity is gone before T
(defn findAttributeValuesGoneBeforeTimeT
"find values gone before T, for one attribute, current view"
[T values]
(let [startTimeCutoff (findAttributeLatestStartTimeBeforeTimeT T values)]
(filter #(< (:startTime %) startTimeCutoff) values) ))
; --------------------------------------------------------------------------
; findAttributeValuesNotNeeded (e.g. to archive and remove)
(defn findAttributeValuesNotNeeded
"find values not needed for any view back to T, for one attribute"
[T values]
(let [startTimeCutoff (->> values
(filter #(< (:writeTime %) T) ,,, )
(findAttributeLatestStartTimeBeforeTimeT T ,,,))]
(filter #(< (:startTime %) startTimeCutoff) values) ))
; --------------------------------------------------------------------------
; findLagTimesAtT
(defn findLagTimesAtT
"delay between a time T at which a value was in effect, and when known
Requires dataSet to consist of values in effect as of time T"
[T dataSet]
(->> dataSet
(sort-by #(:writeTime %) > ,,,)
(map #(- (:writeTime %) T) ,,, )))
; example
; (lagTimesAtT 20 entities)
; ----------------------------------------------------------------------
; getChangesAfterTimeT
(defn getChangesAfterTimeT
"filter to find entries with start time greater than or equal to T"
[T dataSet]
(filter #(>= (:startTime %) T ) dataSet))
; example
; (getChangesAfterTimeT 8 entities)
; ---------------------------------------------------------------------
; getChangesAtTimeT
; should replace this with getChangesNearTimeT and allow client to specify size of window around T
(defn getChangesAtTimeT
"filter to find entries with start time equal to T"
[T dataSet]
(filter #(= (:startTime %) T ) dataSet))
; ---------------------------------------------------------------------
; getChangesBeforeTimeT
(defn getChangesBeforeTimeT
"filter to find entries with start time less than or equal to T"
[T dataSet]
(filter #(<= (:startTime %) T ) dataSet))
; example
; (getChangesBeforeTimeT 8 entities)
; ---------------------------------------------------------------------
; getChangesBetweenTimes
(defn getHistoryBeforeTimeT
"filter to find entries with start time less than or equal to T"
[T dataSet]
(filter #(<= (:startTime %) T) dataSet) )
(defn getChangesBetweenTimes
"filter to find entries with start time between T1 and T2 (inclusive)"
[T1 T2 dataSet]
(->> dataSet
(getChangesAfterTimeT T1 ,,,)
(getHistoryBeforeTimeT T2 ,,,) ))
; -----------------------------------------------------------------------
; getHistoryAtTimeT
(defn getHistoryAtTimeT
"get values at time T."
[T dataSet]
(->> dataSet
(getHistoryAfterTimeT T ,,,)
(getHistoryBeforeTimeT T ,,, ) ))
; ----------------------------------------------------------------------
; getHistoryBeforeTimeT ... see getChangesBetweenTimes
; ----------------------------------------------------------------------
; getHistoryBetweenTimes
(defn getHistoryBetweenTimes
"filter to find entries showing values between two times"
[T1 T2 dataSet]
(->> dataSet
(getHistoryAfterTimeT T1 ,,,)
(getHistoryBeforeTimeT T2 ,,,) ))
; ----------------------------------------------------------------------
; getTopologyAtTimeT
; entities that exist at time T
; ---------------------------------------------------------------------
; getValuesForAttribute
; getValuesForAttributeAndValue
; getValuesForAttributeList
(defn getValuesForAttribute
"filter to find entries for a single attribute"
[attribute dataSet]
(filter #(= (:attribute %) attribute) dataSet) )
; example
; (getValuesForAttribute "size" entities)
(defn getValuesForAttributeAndValue
"filter to find entries for a specific attribute and value"
[attribute value dataSet]
(->> dataSet
(filter #(= (:attribute %) attribute) ,,,)
(filter #(= (:value %) value ) ,,,)))
; example
; (getValuesForAttributeAndValue "size" "XL" entities)
(defn getValuesForAttributeList
"filter to find entries for a list of attributes"
[attributeList dataSet]
(filter #(some #{(:attribute %)} attributeList) dataSet) )
; example
; (getValuesForAttributeList '("size" "in-maintenance") entities)
; ---------------------------------------------------------------------
; getValuesForEntityExistence (rename as a topology filter)
(defn getValuesForEntityExistence
"filter to find entries that indicate an entity was created or removed"
[dataSet]
(getValuesForAttribute "exists" dataSet))
; ---------------------------------------------------------------------------------
; getValuesMissingAtTimeT
(defn getValuesWrittenAfterTimeT
"filter to find entries that were not known as of time T"
[T dataSet]
(filter #(> (:writeTime %) T) dataSet))
(defn getValuesMissingAtTimeT
"find values that were delayed in getting written, with startTime before T but written after T"
[T dataSet]
(->> dataSet
(getHistoryBeforeTimeT T ,,,)
(getValuesWrittenAfterTimeT T ,,,) ))
; ---------------------------------------------------------------------------------
; getValuesMarkedNotValidAfterTimeT (e.g. data used prior to time T that was later marked not valid)
(defn getValuesWrittenBeforeTimeT
"filter to find entries that were known as of time T"
[T dataSet]
(filter #(<= (:writeTime %) T) dataSet))
(defn getValuesMarkedNotValid
"filter to find entries that have been marked not valid"
[dataSet]
(filter #(not= (:timeMarkedNotValid %) "") entities) )
(defn getValuesMarkedNotValidAfterTimeT
"find values with a startTime before T and a value of timeMarkedNotValid later than T"
[T dataSet]
(->> dataSet
(getValuesWrittenBeforeTimeT T ,,,)
(getValuesMarkedNotValid ,,,)
(filter #(> (:timeMarkedNotValid %) T) ,,,) ))
; ---------------------------------------------------------------------------------
; getValuesNotNeeded (has not been tested)
(defn getValuesNotNeeded
"filter to find values not needed for any view back to time T"
[T dataSet]
(applyTimeFilter findAttributeValuesNotNeeded T dataSet) )
; ---------------------------------------------------------------------------------
; getValuesWrittenAfterTimeT ... see getValuesMissingAtTimeT
; ---------------------------------------------------------------------------------
; getValuesWrittenBeforeTimeT ... see getValuesMarkedNotValidAfterTimeT
; ---------------------------------------------------------------------------------
; sortByValueStartTimes
(defn sortByValueStartTimes
"sort by entity, attribute, and start time of values of the attribute"
[dataSet]
(sort-by (juxt :entity :attribute :startTime) dataSet))
; -------- end of listing ---------------