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

Support updates to Circe v1.11.0 #77

Closed
chrisknoll opened this issue Aug 11, 2023 · 5 comments
Closed

Support updates to Circe v1.11.0 #77

chrisknoll opened this issue Aug 11, 2023 · 5 comments

Comments

@chrisknoll
Copy link

Circe 1.11.0 adds a new feature to date-offset start/end dates of 'query' criteria. This allows you to override an end date of an index event, or re-assign a start date to some other time. Example: A pregnancy confirmation event may indicate 12-weeks, so you may want to offset the start date of this event to indicate a pregnancy start by 7*12=84d.

The main change of the of the API is that all Criteria now have a DateAdjustment field, used here.

The DateAdjustment class is described here.

@ablack3
Copy link
Collaborator

ablack3 commented Aug 14, 2023

thanks for the heads up @chrisknoll

@mdlavallee92
Copy link
Collaborator

@chrisknoll making sure I understand this change correctly...

the DateAdjustment class is a type of attribute correct? So a conditionOccurrence query can have a DateAdjustment attribute. That being said this would not mess with the query object, but I could add this attribute to the query. As long as it is not messing with the Query class then this would be an enhancement and we can implement without causing major changes to the circe coercion.

So is the interface is suppose to look like this?

t1dConceptSet <- cs(descendants(195771), name = "T1D") 
  
offsetT1d <- conditionOccurrence(t1dConceptSet,  
                             dateAdjustment(startWith = "start", startDays = 30L, endsWith = "end", endDays = 30L) 

Another question: which domains can the DateAdjustment attribute be enabled?

@chrisknoll
Copy link
Author

chrisknoll commented Sep 6, 2023

DateAdjustment is a type of attribute, and the name of the attribute is 'DateAdjustment', so there could be a little bit of confusion there, but the attribute 'DateAdjustment' of Criteria is of type 'DateAdjustment'. We have a convention that attribute names are Capitalized, hence the confusion there. But I think you got the idea.

This attribute is done at a 'base class' level of Criteria: These are the subtypes:

@JsonSubTypes({
  @JsonSubTypes.Type(value = ConditionEra.class, name = "ConditionEra"),
  @JsonSubTypes.Type(value = ConditionOccurrence.class, name = "ConditionOccurrence"),
  @JsonSubTypes.Type(value = Death.class, name = "Death"),
  @JsonSubTypes.Type(value = DeviceExposure.class, name = "DeviceExposure"),
  @JsonSubTypes.Type(value = DoseEra.class, name = "DoseEra"),
  @JsonSubTypes.Type(value = DrugEra.class, name = "DrugEra"),
  @JsonSubTypes.Type(value = DrugExposure.class, name = "DrugExposure"),
  @JsonSubTypes.Type(value = LocationRegion.class, name = "LocationRegion"),
  @JsonSubTypes.Type(value = Measurement.class, name = "Measurement"),
  @JsonSubTypes.Type(value = Observation.class, name = "Observation"),
  @JsonSubTypes.Type(value = ObservationPeriod.class, name = "ObservationPeriod"),
  @JsonSubTypes.Type(value = ProcedureOccurrence.class, name = "ProcedureOccurrence"),
  @JsonSubTypes.Type(value = Specimen.class, name = "Specimen"),
  @JsonSubTypes.Type(value = VisitOccurrence.class, name = "VisitOccurrence"),
  @JsonSubTypes.Type(value = VisitDetail.class, name = "VisitDetail"),
  @JsonSubTypes.Type(value = PayerPlanPeriod.class, name = "PayerPlanPeriod")
})

For your example, you're bulding the object graph a little differently than I would expect using an object model ie:

conditionOccurrence <-  ConditionOccurrence$new()
conditionOccurrence$CodesetId<- t1dConceptSet$id
dateAdjustment <- DateAdjustment$new()
dateAdjustment$StartWith <- "START_DATE"
dateAdjustment$StartOffset <- 30L
dateAdjustment$EndWith <- "END_DATE"
dateAdjustment$EndOffset <- 30L
conditionOccurrence$DateAdjustment <- dateAdjustment 

If your conditionOccurrence() function is assigning those function params to the object model, then you'd just translate startsWith to the StartWith JSON field and the value of start to `START_DATE'.

On my branch, I built a criteria ConditionOccurrence with the exported JSON as:

      {
        "ConditionOccurrence": {
          "DateAdjustment": {
            "StartWith": "START_DATE",
            "StartOffset": 30,
            "EndWith": "END_DATE",
            "EndOffset": 30
          }
        }
      }

If you produce that from your example, then you're making the correct JSON.

If you are checking the SQL result, then the SQL that is created to adjust dates is in this part:

-- Begin Condition Occurrence Criteria
SELECT C.person_id, C.condition_occurrence_id as event_id, C.start_date, C.end_date, C.visit_occurrence_id, C.start_date as sort_date
FROM 
(
  SELECT co.person_id,co.condition_occurrence_id,co.condition_concept_id,co.visit_occurrence_id,DATEADD(day,30,co.condition_start_date) as start_date, DATEADD(day,30,COALESCE(co.condition_end_date, DATEADD(day,1,co.condition_start_date))) as end_date 
  FROM @cdm_database_schema.CONDITION_OCCURRENCE co
  
) C

WHERE C.end_date >= C.start_date
-- End Condition Occurrence Criteria

Note: the COALESCE here is handling if you have a NULL condition_end_date, it will use 1 day after the condition_start_date as the event's end date.

If you're wondering why the ugly enum values for 'START_DATE' and 'END_DATE', I was wondering about that myself (why, Chris, didn't you just use 'start' and 'end'?) But, It's already published with this, so it feels ugly, but it's released so I am just going to hold my nose when looking at it.

@mdlavallee92
Copy link
Collaborator

Ok great, I thought I was on the right track just wanted to confirm. I want to discuss your object graph a bit but will make that as a separate issue.

@mdlavallee92
Copy link
Collaborator

@chrisknoll, dateAdjustment it is now in v2.0.6. Added the functionality to Capr and got no errors feeding the json into CirceR version 1.3.1. See below:

library(Capr)

#make concept set for celecoxib
celecoxib <- cs(descendants(1118084), name = "celecoxib")

#make cohort for celecoxib
celecoxibCohort <- cohort(
  entry = entry(
    drugExposure(celecoxib,
                 dateAdjustment(startOffset = 30L, endOffset = 30L))
  ),
  exit = exit(
    observationExit()
  )
)


jj <- compile(celecoxibCohort, pretty = TRUE)

cat(jj)
#> {
#>   "ConceptSets": [
#>     {
#>       "id": 0,
#>       "name": "celecoxib",
#>       "expression": {
#>         "items": [
#>           {
#>             "concept": {
#>               "CONCEPT_ID": 1118084,
#>               "CONCEPT_NAME": "",
#>               "STANDARD_CONCEPT": "",
#>               "STANDARD_CONCEPT_CAPTION": "",
#>               "INVALID_REASON": "",
#>               "INVALID_REASON_CAPTION": "",
#>               "CONCEPT_CODE": "",
#>               "DOMAIN_ID": "",
#>               "VOCABULARY_ID": "",
#>               "CONCEPT_CLASS_ID": ""
#>             },
#>             "isExcluded": false,
#>             "includeDescendants": true,
#>             "includeMapped": false
#>           }
#>         ]
#>       }
#>     }
#>   ],
#>   "PrimaryCriteria": {
#>     "CriteriaList": [
#>       {
#>         "DrugExposure": {
#>           "CodesetId": 0,
#>           "DateAdjustment": {
#>             "StartWith": "START_DATE",
#>             "StartOffset": 30,
#>             "EndWith": "END_DATE",
#>             "EndOffset": 30
#>           }
#>         }
#>       }
#>     ],
#>     "ObservationWindow": {
#>       "PriorDays": 0,
#>       "PostDays": 0
#>     },
#>     "PrimaryCriteriaLimit": {
#>       "Type": "First"
#>     }
#>   },
#>   "QualifiedLimit": {
#>     "Type": "First"
#>   },
#>   "ExpressionLimit": {
#>     "Type": "First"
#>   },
#>   "InclusionRules": [],
#>   "CensoringCriteria": [],
#>   "CollapseSettings": {
#>     "CollapseType": "ERA",
#>     "EraPad": 0
#>   },
#>   "CensorWindow": {},
#>   "cdmVersionRange": ">=5.0.0"
#> }

tst <- CirceR::buildCohortQuery(
  expression = jj,
  options = CirceR::createGenerateOptions(generateStats = TRUE)
)
cat(tst)
#> CREATE TABLE #Codesets (
#>   codeset_id int NOT NULL,
#>   concept_id bigint NOT NULL
#> )
#> ;
#> 
#> INSERT INTO #Codesets (codeset_id, concept_id)
#> SELECT 0 as codeset_id, c.concept_id FROM (select distinct I.concept_id FROM
#> ( 
#>   select concept_id from @vocabulary_database_schema.CONCEPT where concept_id in (1118084)
#> UNION  select c.concept_id
#>   from @vocabulary_database_schema.CONCEPT c
#>   join @vocabulary_database_schema.CONCEPT_ANCESTOR ca on c.concept_id = ca.descendant_concept_id
#>   and ca.ancestor_concept_id in (1118084)
#>   and c.invalid_reason is null
#> 
#> ) I
#> ) C;
#> 
#> UPDATE STATISTICS #Codesets;
#> 
#> 
#> SELECT event_id, person_id, start_date, end_date, op_start_date, op_end_date, visit_occurrence_id
#> INTO #qualified_events
#> FROM 
#> (
#>   select pe.event_id, pe.person_id, pe.start_date, pe.end_date, pe.op_start_date, pe.op_end_date, row_number() over (partition by pe.person_id order by pe.start_date ASC) as ordinal, cast(pe.visit_occurrence_id as bigint) as visit_occurrence_id
#>   FROM (-- Begin Primary Events
#> select P.ordinal as event_id, P.person_id, P.start_date, P.end_date, op_start_date, op_end_date, cast(P.visit_occurrence_id as bigint) as visit_occurrence_id
#> FROM
#> (
#>   select E.person_id, E.start_date, E.end_date,
#>          row_number() OVER (PARTITION BY E.person_id ORDER BY E.sort_date ASC, E.event_id) ordinal,
#>          OP.observation_period_start_date as op_start_date, OP.observation_period_end_date as op_end_date, cast(E.visit_occurrence_id as bigint) as visit_occurrence_id
#>   FROM 
#>   (
#>   -- Begin Drug Exposure Criteria
#> select C.person_id, C.drug_exposure_id as event_id, C.start_date, C.end_date,
#>   C.visit_occurrence_id,C.start_date as sort_date
#> from 
#> (
#>   select de.person_id,de.drug_exposure_id,de.drug_concept_id,de.visit_occurrence_id,days_supply,quantity,refills,DATEADD(day,30,de.drug_exposure_start_date) as start_date, DATEADD(day,30,COALESCE(de.drug_exposure_end_date, DATEADD(day,de.days_supply,de.drug_exposure_start_date), DATEADD(day,1,de.drug_exposure_start_date))) as end_date 
#>   FROM @cdm_database_schema.DRUG_EXPOSURE de
#> JOIN #Codesets cs on (de.drug_concept_id = cs.concept_id and cs.codeset_id = 0)
#> ) C
#> 
#> WHERE C.end_date >= C.start_date
#> -- End Drug Exposure Criteria
#> ...truncate....

Created on 2023-09-07 with reprex v2.0.2

Closing...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants