<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Seth Meldon</title>
    <description>The latest articles on DEV Community by Seth Meldon (@notedhelms).</description>
    <link>https://dev.to/notedhelms</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F624786%2Fcc3220a8-ebde-48be-ab47-65af31e6c475.jpg</url>
      <title>DEV Community: Seth Meldon</title>
      <link>https://dev.to/notedhelms</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/notedhelms"/>
    <language>en</language>
    <item>
      <title>Browse Healthcare.gov Marketplace Plans with a Corticon.js Dynamic Form</title>
      <dc:creator>Seth Meldon</dc:creator>
      <pubDate>Mon, 04 Nov 2024 14:48:38 +0000</pubDate>
      <link>https://dev.to/notedhelms/browse-healthcaregov-marketplace-plans-with-a-corticonjs-dynamic-form-192i</link>
      <guid>https://dev.to/notedhelms/browse-healthcaregov-marketplace-plans-with-a-corticonjs-dynamic-form-192i</guid>
      <description>&lt;p&gt;Just in time for open enrollment, let’s take a look at how Corticon.js can provide all of the logic involved in building a frontend dynamic form for selecting the optimal healthcare plan using healthcare.gov. As with all Corticon.js dynamic forms, the renderer is entirely independent of the business case at hand—Corticon.js JavaScript decision services tell the renderer what to do and when, the renderer captures the users’ inputs and passes along the inputs back to Corticon.js.&lt;br&gt;
Healthcare.gov is one of the main pathways through which Americans can sign up for a healthcare plan. Like shopping on an ecommerce site and filtering down a search query result, selecting a plan entails narrowing down a list of options from a massive initial set of plan data. The variables considered by this dynamic form are as follows:&lt;br&gt;
• Geography – based upon a household’s address, an applicant may be routed to a different healthcare marketplace—not all US states make use of healthcare.gov, opting instead to manage their own portal or share a portal with other non-healthcare.gov states. Additionally, each state has any number of Rating Areas which impact the rates for each plan. &lt;br&gt;
• Household – who lives in the household? It’s common for plan options to differentiate between characteristics like married/unmarried couples, number of dependents, income sources, whether or not they are tobacco users, if anyone is pregnant, and their residency statuses.&lt;br&gt;&lt;br&gt;
• Cost Savings Programs – Depending on the household’s income and other variables described above, plans may provide cost waivers or tax credits to members. In this form, we’ll determine eligibility for the advance premium tax credit.&lt;br&gt;
• Coverage options – Filtering down through the options, plans have different coverages and benefits like specialty drug coverages, homecare services, or chiropractic care. Copays, coinsurance rates, premiums, and limit for these services likewise vary, sometimes depending on criteria specific to a given household member. &lt;br&gt;
• Quality – Enrollees of plans are annually surveyed about their experiences with their respective plan, providing a 1-5 rating for many different measures like customer service experience and availability of health services in a reasonable amount of time. This measure is indexed, along with patient outcomes data (e.g. readmissions, appropriate frequency and kind of screenings, accurate diagnostic determinations from reported clinical notes). &lt;br&gt;
These factors are evaluated based upon data provided by the end user filling out the form, APIs exposed by the healthcare.gov marketplace API, with rules specifying what data should be obtained from each. Rules concurrently are ruling out plans based upon eligibility criteria and end user preferences for the healthcare plan’s coverages and costs. Finally, all of the matching plans are presented back to the user with links to the plan overview. &lt;br&gt;
Data gathered from the end user filling out the form:&lt;br&gt;
• Household zipcode&lt;br&gt;
• Household members’ ages, marital statuses, tax filing status, income, citizenship status, employment status, and whether they use tobacco or are pregnant,&lt;br&gt;
• Desired ‘metal’ level of plan’s coverage (Bronze, Silver, Gold)&lt;br&gt;
• Desired minimum quality level of plan&lt;br&gt;
Data gathered from REST APIs over the course of the form:&lt;br&gt;
• Retrieve household FIPS code and State Code based upon provided zip code sent to the healthcare.gov /counties/by/zip/{zipcode} endpoint&lt;br&gt;
• Retrieve the rating area for the household based upon where in the state the household is located from the healthcare.gov /rate-areas endpoint&lt;br&gt;
• Retrieve the poverty level for the state where the household is with a household of the size provided by the end user from the healthcare.gov /states/{abbrev}/poverty-guidelines endpoint&lt;br&gt;
• Query the healthcare.gov /api/1/datastore/sql endpoint for the Plan ID of all plans for the household state, rating area, and the age of enrollee(s)&lt;br&gt;
• Retrieve rating, metal-level, and a link to the plan brochure by querying the healthcare.gov /plans/{plan_id} endpoint with the respective Plan ID numbers&lt;br&gt;
Finally, after removing any plans beneath the quality or coverage thresholds specified, the form presents the user with the matching plans:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgazv7qwyn022f78180et.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgazv7qwyn022f78180et.png" alt="final results from form" width="462" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/SethMeldon/embed/wvOGvra?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>corticon</category>
      <category>tutorial</category>
      <category>showdev</category>
      <category>nocode</category>
    </item>
    <item>
      <title>Halloween Candy Trading Rules with Corticon</title>
      <dc:creator>Seth Meldon</dc:creator>
      <pubDate>Thu, 31 Oct 2024 13:34:02 +0000</pubDate>
      <link>https://dev.to/notedhelms/halloween-candy-trading-rules-with-corticon-1345</link>
      <guid>https://dev.to/notedhelms/halloween-candy-trading-rules-with-corticon-1345</guid>
      <description>&lt;p&gt;On October 31, the U.S. celebrates the Halloween holiday, where children dress in costumes and collect candy during an evening ritual called “Trick or Treat.”&lt;/p&gt;

&lt;p&gt;A conscientious parent wants to establish some rules to govern her children’s candy consumption in the aftermath of Halloween. These “screening” rules include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  All unpackaged or unwrapped items must be discarded. This includes fruit, baked goods or candy with damaged wrapping. If fruit, then the parent will replace the item with a new, fresh (and known safe) item of the same kind.&lt;/li&gt;
&lt;li&gt;  Because little Jimmy has a nut allergy, all items containing nuts must be given to his big sister, Sarah.&lt;/li&gt;
&lt;li&gt;  For every item containing nuts given to Sarah by Jimmy, Sarah will repay him with an item not containing nuts.&lt;/li&gt;
&lt;li&gt;  Finally, the parent would like all of this nonsense to be over with as quickly as possible. How many items, on average, must she distribute to each child per day so that all candy is gone within 30 days? Alternatively, if decides the maximum daily allotment per child is 3 pieces, how long will each child’s supply last? The preferred distribution strategy will be the option which gets the whole thing over with as soon as possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like many decisions modeled using Progress &lt;a href="https://www.progress.com/corticon" rel="noopener noreferrer"&gt;Corticon Studio&lt;/a&gt;, this one can be approached as a series of steps. We’ll model these steps as individual rulesheets to help us organize our thinking, making it easier to change the rules later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxl5ww7ea2k43c4iwx0ml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxl5ww7ea2k43c4iwx0ml.png" alt="diagram of a trade: discard and replace - trade - repay - allocate" width="524" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the basics steps we’ll model:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Discard &amp;amp; Replace&lt;/strong&gt;: Applying the first rule, we’ll inspect every item and mark or “tag” those which need to replaced or discarded.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Trade&lt;/strong&gt;: Applying the second rule, Sarah gets all of Jimmy’s items containing nuts.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Repay&lt;/strong&gt;: Applying the third rule, Sarah “pays back” Jimmy with an appropriate item (not containing nuts) for each item he gave her.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Allocate&lt;/strong&gt;: Applying the last rule, we’ll calculate which distribution strategy the parent should choose.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Of course, there are many ways to approach and solve this problem. We prefer those that are clear and organized.&lt;/p&gt;

&lt;p&gt;A sample &lt;a href="https://docs.progress.com/bundle/corticon-basic-guided-journey/page/What-is-a-Vocabulary.html" rel="noopener noreferrer"&gt;Vocabulary&lt;/a&gt; containing the terms needed to model the rules is shown below. Child and Candy entities are related by an association with one-to-many cardinality, enabling any Child to have a collection of Candy, large or small. A piece of Candy has attributes which indicate its characteristics; for example, does it contain nuts? Is it properly packaged? Etc.&lt;/p&gt;

&lt;p&gt;An important assumption is that someone (likely the child’s guardian) will classify each piece of candy using several of these attribute values so that the rules we build have data to work with. Boolean attributes, such as discard and replace, will have values derived by the rules themselves.&lt;/p&gt;

&lt;p&gt;The attribute &lt;code&gt;candyOwed&lt;/code&gt; functions as a “holder” or “counter” value, enabling us to keep track of how many pieces of candy change hands. Because its value is only used internally in the rules, we’ve made it a &lt;strong&gt;transient&lt;/strong&gt; attribute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frg160swsimway4l1pkij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frg160swsimway4l1pkij.png" alt="Vocabulary: Candy - discard, hasNuts, inProperPackage, isFruit, name, replace; Child - candyCount, candyOwed, daysAtThreePieces, name, piecesForThirtyDays, candy" width="228" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first rulesheet, named “discard_and_replace” examines every piece of candy (notice the root-level &lt;strong&gt;Candy&lt;/strong&gt;, which means &lt;em&gt;all&lt;/em&gt; candy) and identifies those which must be discarded or replaced. The rulesheet is complete and unambiguous, meaning every piece of candy will receive one, and only one, value for discard and replace.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://d117h1jjiq768j.cloudfront.net/images/default-source/blogs/2024/10-24/corticon-halloween_discard-and-replace-rulesheet.png?sfvrsn=f3a7562d_2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6fl2bnvxfcdw6kpx444.png" alt="discard and replace rulesheet" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second rulesheet, named “trade,” acts on those pieces of Jimmy’s candy that were identified (by the parent) as having nuts. When candy containing nuts is found in Jimmy’s &lt;a href="https://docs.progress.com/bundle/corticon-js-rule-modeling/page/How-to-visualize-collections.html" rel="noopener noreferrer"&gt;collection&lt;/a&gt;, the &lt;em&gt;Actions&lt;/em&gt; add the item to Sarah’s collection of candy, remove the item from Jimmy’s collection, and increment Jimmy’s &lt;code&gt;candyOwed&lt;/code&gt; tally by 1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://d117h1jjiq768j.cloudfront.net/images/default-source/blogs/2024/10-24/corticon-halloween_trade-rulesheet.png?sfvrsn=c73559a6_2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc9is8s605w7jlwg2umkg.png" alt="trade rulesheet" width="800" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next it’s payback time. For each item Jimmy gave to Sarah because it had nuts, Sarah needs to give one back. But not just any item will do. Sarah can’t repay Jimmy with items that are due to be discarded or replaced—that wouldn’t be fair. Nor can she repay with items containing nuts—that would defeat the whole purpose of the trade.&lt;/p&gt;

&lt;p&gt;In the third rulesheet, named “repay,” one rule takes all of these requirements into account. When an item meeting all Conditions is found in Sarah’s candy collection, the Actions add it to Jimmy’s collection, remove it from Sarah’s and decrement Jimmy’s &lt;code&gt;candyOwed&lt;/code&gt; tally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://d117h1jjiq768j.cloudfront.net/images/default-source/blogs/2024/10-24/corticon-halloween_repay-rulesheet.png?sfvrsn=72da9b7e_2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnthw0s4q4gnbccxus4pj.png" alt="repay rulesheet" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is very similar to the trade rulesheet, with one important exception. Let’s assume that Sarah has 5 items in her candy collection which satisfy Condition rows a, b and c. But let’s also assume that Sarah only owes Jimmy 2 items as repayment. How can we stop the evaluation of Sarah’s candy (and its reassignment) after 2 pieces have been repaid to Jimmy? This is tricky, as the default way Corticon evaluates elements in a collection is to evaluate each element satisfying the rule’s scope independently.&lt;/p&gt;

&lt;p&gt;In other words, the examination of row b (&lt;code&gt;SarahsCandy.discard&lt;/code&gt;) by the Conditions is unaffected by the examination of row c (SarahsCandy.replace), or any other item. That means the mere presence of Condition row d (Jimmy.candyOwed &amp;gt; 0) is no guarantee that rule execution will stop once Jimmy has been repaid in full. We need something else to accomplish this goal.&lt;/p&gt;

&lt;p&gt;A rule that is not action-only (not in column zero) can be set to &lt;a href="https://docs.progress.com/bundle/corticon-rule-modeling-63/page/How-to-use-conditions-as-a-processing-threshold.html" rel="noopener noreferrer"&gt;&lt;strong&gt;use conditions as processing thresholds&lt;/strong&gt;&lt;/a&gt;. This configuration, enabled by selecting it from the rulesheet with the applicable rule column selected, instructs Corticon to reevaluate all Conditions &lt;em&gt;each time&lt;/em&gt; a piece of candy is examined. As a result, the strict independence between rows b and c disappears.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://d117h1jjiq768j.cloudfront.net/images/default-source/blogs/2024/10-24/corticon-halloween_use-conditions-as-processing-thresholds.png?sfvrsn=a245aacd_2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzmhu649p6j3bmkj9ndm.png" alt="use conditions as processing thresholds" width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once selected, the column header number becomes &lt;strong&gt;bold&lt;/strong&gt;, as shown in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84bip43wtx9qa973bmat.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84bip43wtx9qa973bmat.png" alt="column shows 1 in bold" width="102" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the &lt;code&gt;candyOwed&lt;/code&gt; tally is down to zero (meaning Jimmy has been fully repaid), the rule execution stops.&lt;/p&gt;

&lt;p&gt;Once all candy has been checked, discarded, replaced and traded, we need to choose the right strategy so that all this candy disappears as quickly as possible.&lt;/p&gt;

&lt;p&gt;The screenshot below shows two nonconditional rules which calculate two key attribute values needed to select a strategy. Each equation counts the pieces of candy in each child’s collection using the &lt;code&gt;–&amp;gt; size&lt;/code&gt; operator. Then a Condition/Action rule compares the two options and recommends the final allocation strategy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://d117h1jjiq768j.cloudfront.net/images/default-source/blogs/2024/10-24/corticon-halloween_distribution-strategy.png?sfvrsn=45f64c61_2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10h7purrrgov6ny1xuiq.png" alt="two nonconditional rules to calculate fastest distribution strategy" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With all of the steps completed, the parent can now work with candy collections of any size. In the &lt;a href="https://github.com/corticon/corticon-classic-samples/tree/main/Halloween%20Candy%20Trading" rel="noopener noreferrer"&gt;downloadable&lt;/a&gt; rule project, you’ll see three test sheets pre-configured. Here we have the results of the middle-sized candy haul:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://d117h1jjiq768j.cloudfront.net/images/default-source/blogs/2024/10-24/corticon-halloween_rule-message-log.png?sfvrsn=d3a85f64_2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbnd4zndw44pcmkk0tiw9.png" alt="rule message log" width="800" height="730"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running the rule test with the &lt;a href="https://docs.progress.com/bundle/corticon-rule-modeling/page/Trace-rule-execution.html" rel="noopener noreferrer"&gt;trace data view&lt;/a&gt;, we can see how Corticon has incremented the traded candy values. In case Jimmy and Sarah say the trades were unfair, the trace data log provides us with the precise order and nature of the changes made throughout the four rule sheets in the ruleflow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://d117h1jjiq768j.cloudfront.net/images/default-source/blogs/2024/10-24/corticon-halloween_trace-data.png?sfvrsn=2280a300_2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frsvu99fwfktfwkm1hwg4.png" alt="trace data" width="644" height="484"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Want to lean on Corticon for Halloween candy redistribution or another use case? This project is freely available for download &lt;a href="https://github.com/corticon/corticon-classic-samples/tree/main/Halloween%20Candy%20Trading" rel="noopener noreferrer"&gt;from our GitHub&lt;/a&gt;, and can be imported into Corticon Studio, which you can &lt;a href="https://www.progress.com/corticon/get-started" rel="noopener noreferrer"&gt;try free&lt;/a&gt; for 90 days. Better yet, if you already know how you’d like to try to automate complex decisions with Corticon, &lt;a href="https://www.progress.com/corticon/book-a-demo" rel="noopener noreferrer"&gt;give us a shout&lt;/a&gt; and we’ll build a custom demo for you to see how we’d approach it.&lt;/p&gt;

</description>
      <category>nocode</category>
      <category>tutorial</category>
      <category>corticon</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Evaluate a Site for Solar Energy Potential with Corticon</title>
      <dc:creator>Seth Meldon</dc:creator>
      <pubDate>Thu, 03 Oct 2024 19:07:22 +0000</pubDate>
      <link>https://dev.to/notedhelms/evaluate-a-site-for-solar-energy-potential-with-corticon-1bpd</link>
      <guid>https://dev.to/notedhelms/evaluate-a-site-for-solar-energy-potential-with-corticon-1bpd</guid>
      <description>&lt;p&gt;&lt;em&gt;Integrate data from external REST APIs to enrich your decision data&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First Launch of Corticon Studio&lt;/li&gt;
&lt;li&gt;Rule Projects&lt;/li&gt;
&lt;li&gt;
Rule Vocabulary

&lt;ul&gt;
&lt;li&gt;Entities&lt;/li&gt;
&lt;li&gt;Attributes&lt;/li&gt;
&lt;li&gt;Associations&lt;/li&gt;
&lt;li&gt;Complete the Vocabulary&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Create your first rulesheet

&lt;ul&gt;
&lt;li&gt;Logical Integrity Checks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Testing Rules&lt;/li&gt;
&lt;li&gt;
Data Integration

&lt;ul&gt;
&lt;li&gt;Incorporate the REST Datasource&lt;/li&gt;
&lt;li&gt;Update the rules&lt;/li&gt;
&lt;li&gt;Test the Updated Rules&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  First Launch of Corticon Studio
&lt;/h2&gt;

&lt;p&gt;Workspaces, projects, and folders provide a way to organize rule-modeling components. A workspace is a 'root' directory that acts as a container for rule projects created in Corticon Studio. When you first launch Corticon Studio, you'll be prompted for a workspace—this is different than the folders that were assigned during the Corticon installation wizard. A workspace is where all of the things created by you, the end user, are stored. The work directory—the one from the installation wizard—is where all of the things that Corticon itself needs to work as an application will live. If you choose to later uninstall Corticon, make sure to do so using the uninstall wizard in your start menu, not just deleted these folders.&lt;/p&gt;

&lt;p&gt;You can feel free to leave the default workspace folder (it will be created automatically), or point to a different folder if you prefer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Rule Projects
&lt;/h2&gt;

&lt;p&gt;A Corticon rule project is a ‘container’ for rule-modeling files. A Corticon rule project can further contain folders to segregate rule-modeling components. A ‘template’ rule project can be downloaded from here—download the file, but don’t unzip it.&lt;/p&gt;

&lt;p&gt;To import that template:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click ‘File’ on the main toolbar, then click ‘Import…’&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mb4mkfw5e4vzm7v3yif.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mb4mkfw5e4vzm7v3yif.png" alt="import dropdown" width="320" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose the ‘Select archive file:’ option, then browse to the downloaded zip file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Facbdbs4mv28kg56kvbtb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Facbdbs4mv28kg56kvbtb.png" alt="screenshot showing ‘Select archive file' option" width="671" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click Finish&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwetzatjtzlnqx48js1n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwetzatjtzlnqx48js1n.png" alt="finish wizard" width="682" height="752"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Rule Vocabulary
&lt;/h2&gt;

&lt;p&gt;A Corticon Vocabulary is a rule-modeling component that enables you to define all the business terms that you require in your rules. For example, let’s take a look at how we might formulate a rule vocabulary from some common solar site evaluation rules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnc5ne74eybtqoqzfodbp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnc5ne74eybtqoqzfodbp.png" alt="solar site evaluation rules" width="717" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An installation Site will have a size, annual radiation, and aspect. The Site will be where we install some number of Panels, each of which will have various values like length, width, and efficiency.&lt;br&gt;
Let’s define this is as a rule vocabulary, with Site and Panel being entities, each having their respective attributes, and associated to one another as one to many—one site to any number of panels.&lt;/p&gt;
&lt;h3&gt;
  
  
  Entities
&lt;/h3&gt;

&lt;p&gt;Follow these steps to add an entity in the Vocabulary editor:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Double click the vocabulary file in the project explorer—it is in the Vocabulary folder and has the extension ‘.ecore’:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpm35morchzunxntqd5hm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpm35morchzunxntqd5hm.png" alt="opening vocabulary" width="289" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Vocabulary editor, right-click and select Add Entity, and type in &lt;code&gt;Site&lt;/code&gt;. If you don’t enter a name, it will default to ‘Entity_1’, but it can be changed by double clicking on the name of the entity.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fom6k60xm7cm7gao56pqq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fom6k60xm7cm7gao56pqq.png" alt="adding entity in vocabulary" width="324" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add one more entity, and give it the name &lt;code&gt;Panel&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2fj00uyyinivgzvk7yt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2fj00uyyinivgzvk7yt.png" alt="adding second entity in vocabulary" width="168" height="79"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Attributes
&lt;/h3&gt;

&lt;p&gt;After you create an entity, you can add attributes to it. An attribute is like an adjective, whose values are populated at runtime.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right-click the entity name and then select Add Attribute.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdq25ztv4hkf4rsvl5fxc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdq25ztv4hkf4rsvl5fxc.png" alt="adding Attribute in vocabularyn" width="467" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You’re given a choice of the attributes’ data type—Boolean (True or False), Decimal, DateTime, Date, Integer, String, Time. For this example, select decimal. Enter a name, &lt;code&gt;squareMeters&lt;/code&gt;for the attribute, or edit the name in the same way you did with the entity.  After you add an attribute, the property editor panel of the Vocabulary editor displays all the properties for the attribute with default values.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzeoahv7h1xzlsh7934vi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzeoahv7h1xzlsh7934vi.png" alt="renaming attribute" width="683" height="157"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Associations
&lt;/h3&gt;

&lt;p&gt;An association describes the relationship between two entities. In a Corticon Vocabulary, you can define the association under either entity. By default, the association that you define appears under both entities in the Vocabulary tree.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3lmzwydiwjsgf5lxh1v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3lmzwydiwjsgf5lxh1v.png" alt="associations icons" width="193" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You add an association to an entity just as you add an attribute.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right-click Site and select Add Association.&lt;/li&gt;
&lt;li&gt;The default selections should be what we want—one site to many associations. Click OK.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu128gk3emlcsqtgnu1o7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu128gk3emlcsqtgnu1o7.png" alt="association definition window" width="602" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your vocabulary will now look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2as6gmbah5fxc4bkqh5z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2as6gmbah5fxc4bkqh5z.png" alt="vocabulary with new associations" width="211" height="163"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Complete the Vocabulary
&lt;/h3&gt;

&lt;p&gt;Now, add the rest of the vocabulary elements we’ll need.&lt;br&gt;
Add these attributes to ‘Site’:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;length&lt;/code&gt;(decimal)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;width&lt;/code&gt;(decimal)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxPanels&lt;/code&gt;(integer)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;annualRadiation_kWh&lt;/code&gt;(decimal)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aspect&lt;/code&gt;(string)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isSuitable&lt;/code&gt;(boolean)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;slopeDegrees&lt;/code&gt;(decimal)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add these attributes to Panel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;length&lt;/code&gt;(decimal)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;width&lt;/code&gt;(decimal)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;squareMeters&lt;/code&gt;(decimal)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your vocabulary should now look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rnt71cu597yivwz54nn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rnt71cu597yivwz54nn.png" alt="completed vocabulary" width="261" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you likely deduced from the rule, “Aspect should not be north,” the word &lt;code&gt;aspect&lt;/code&gt;is used here to mean compass direction (north, south, east, west). Each of the four compass directions are ‘string’ (alphanumeric) datatypes, but there is a specific list of potential values for those strings—we want to make sure we don’t deviate from those specific values when modeling rules. For this situation, we’re going to define a ‘Custom Data Type’:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click the name of the vocabulary at the top of the vocabulary tree, as shown:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fti7jlk6albehl791qlps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fti7jlk6albehl791qlps.png" alt="accessing cdt pane" width="275" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the righthand side of the vocabulary editor, you’ll see a pane come into focus called ‘Custom Data Types’. In the table, type ‘aspect’ on the first row under Data Type Name, then select ‘String’ from Base Data Type. The default selection under ‘Enumeration’ will be ‘Yes’, don’t change this. Lastly, on the right hand side, under ‘Value’, type the four compass directions on the first four rows. Your custom data types table will look like this once complete:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68j2ef9hj78jgfq6647t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68j2ef9hj78jgfq6647t.png" alt="entering enumerations" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Lastly, click the ‘aspect’ attribute underneath the Site entity that you created previously. On the righthand side, a panel labeled ‘Basic Properties’ should appear. Next to the row called ‘Data Type’, which will currently say ‘String’, select the dropdown button and select the newly created data type ‘aspect:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8ndjkt5d8kk86bd9usy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8ndjkt5d8kk86bd9usy.png" alt="entering enumerations2" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll see why we defined this custom data type in the next step.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create your first rulesheet
&lt;/h2&gt;

&lt;p&gt;Let’s start with a rule that will solve for a site’s area in square meters. First, right click on the ‘Rulesheets’ folder and select New &amp;gt; Rulesheet:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fea5daqwhr2kiohnj3wne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fea5daqwhr2kiohnj3wne.png" alt="new rulesheet dropdown" width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give it the name ‘Site Area’, and then click Finish.&lt;br&gt;
You define your rule logic in a Corticon Rulesheet. A rule is like an ‘if-then’ statement. Each rule consists of one or more conditions (if) that are associated with one or more actions (then). Here is an example of a Rulesheet with five rules:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uht858f7crw5zzqbitb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uht858f7crw5zzqbitb.png" alt="parts of rulesheet" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Rulesheet editor has the following parts:&lt;br&gt;
Conditions rows—for defining the specific value(s) of a given vocabulary attribute which should trigger this rule should fire. For example, &lt;code&gt;car.price&lt;/code&gt;&amp;gt; 45000. The condition value could be a single value (45000), a set of values (45000, 60000), or a range of values (20000 .. 45000).&lt;br&gt;
Actions—where you assign the value(s) to assign to vocabulary attribute(s) when the conditions are met. For example, &lt;code&gt;car.potentialTheftRating&lt;/code&gt;= 'High'.&lt;br&gt;
But what about unconditional rules? For example, let’s say our Solar site is always a rectangle, so square meters can always be solved for by ‘area=length * width’ with no conditions involved. This is called an ‘action-only’ rule—it will always fire. We define these in column 0. Let’s try it out.&lt;br&gt;
Open your newly created ‘Site Area.ers’ rulesheet. When defining rules, it is easiest to arrange the different panels of the screen in the way shown here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpuhot05kr8vo0op5g5xo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpuhot05kr8vo0op5g5xo.png" alt="parts of a rulesheet" width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;From vocabulary panel on the left, select the attribute &lt;code&gt;squareMeters&lt;/code&gt;from underneath the &lt;code&gt;Site&lt;/code&gt;entity, and drag it onto the first action row:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuyaburawxnnxx5rpfckm.png" alt="drag and drog attribute" width="800" height="604"&gt;
&lt;/li&gt;
&lt;li&gt;In the 0 column, we’re going to define the expression which will be used to assign the value for the &lt;code&gt;squareMeters&lt;/code&gt;attribute. Drag and drop (or just type in) the two other attributes that will be multiplied-- Site.length*Site.width:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4xumio1lqe6dbesc1r1n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4xumio1lqe6dbesc1r1n.png" alt="action only rule" width="580" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Now, lets define a rule statement for this rule. Rule statements are ‘explainers’ of a sort for each rule that fires during the execution of the decision service, to help document the rules which contributed to the final output. In the Rule Statement section, copy the text below into the Text column in the first row: Site area is &lt;code&gt;Site.squareMeters&lt;/code&gt; meters&lt;/li&gt;
&lt;li&gt;Add the other values shown in the picture below to your rule statement:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyc3k5l5jipl8m9s1g3zt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyc3k5l5jipl8m9s1g3zt.png" alt="first rule statement" width="735" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s now define the conditions and actions for our initial set of rules. We will set the boolean attribute &lt;code&gt;isSuitable&lt;/code&gt;based upon whether the Site meets these conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The slope must be 45° or less.&lt;/li&gt;
&lt;li&gt;Annual solar radiation should be at least 800 kWh.&lt;/li&gt;
&lt;li&gt;Aspect should not be north.&lt;/li&gt;
&lt;li&gt;Areas with a slope of 10° or less are determined as suitable areas regardless of the aspect.&lt;/li&gt;
&lt;li&gt;20 m² and more area is needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Copy and paste the five rows above into your Rule Statements underneath the one we’ve just defined:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fco7fkmcp4bc4ukhlady0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fco7fkmcp4bc4ukhlady0.png" alt="all rule statements" width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Drag &lt;code&gt;Site.slopeDegrees&lt;/code&gt; onto the first Condition row. In the first row of the ‘1’ column, type '&amp;lt; 45'&lt;/li&gt;
&lt;li&gt;Drag &lt;code&gt;Site.isSuitable&lt;/code&gt; to the second Action row, then ‘T’ from the dropdown.&lt;/li&gt;
&lt;li&gt;Drag &lt;code&gt;Site.annualRadiation_kWh&lt;/code&gt;to the second Condition row. In the second row of the ‘2’ column, type &amp;gt;=800. Set the corresponding action to also be &lt;code&gt;Site.isSuitable = T&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foxzoo7u8qd122x05yejo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foxzoo7u8qd122x05yejo.png" alt="firs two rules" width="759" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Drag &lt;code&gt;Site.aspect&lt;/code&gt; to the third condition row. In column 3, select the third condition row. A dropdown button will be available on the right side of the cell—select North from it:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fikidnwzi35k0px2vvfzs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fikidnwzi35k0px2vvfzs.png" alt="third rule with cdt" width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the corresponding action to set &lt;code&gt;isSuitable&lt;/code&gt;to F.&lt;/li&gt;
&lt;li&gt;Implement the next two rules and then update the Rule Statements as shown below.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xdlo5pcy86a9jytipq7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xdlo5pcy86a9jytipq7.png" alt="five rules" width="800" height="575"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Logical Integrity Checks
&lt;/h3&gt;

&lt;p&gt;Before we test these rules with test data, Corticon can analyze them for logical issues we may have introduced. Look for these three buttons&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjf752hnk9zd78yxypag5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjf752hnk9zd78yxypag5.png" alt="logical integrity check buttons" width="154" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;at the top of the rulesheet editor screen (if they’re greyed out, then click anywhere in the rulesheet to bring it into focus).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click them from left to right, start with the Logical Loop Checker. As the name implies, if we were introducing any circular logic, Corticon would point us to it automatically with this check. We shouldn’t have any loops.&lt;/li&gt;
&lt;li&gt;Next is the Completeness Checker—are there more scenarios implicitly possible based upon the rules we’ve authored so far? Corticon will condense all possibilities into a new column. This can be helpful, but some of the new conditions should be evaluated independently, and we’re not going to worry about null checks. You can select this column and click the delete button.&lt;/li&gt;
&lt;li&gt;Instead, let’s implement the missing rules in a few additional columns such that your rulesheet looks like this:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjbrigy867a830unnu6c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjbrigy867a830unnu6c.png" alt="completeness results" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we have new rules, it is a good idea to add our additional rule statements for the rules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnygai5vz9arp5isrlacy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnygai5vz9arp5isrlacy.png" alt="rule statements for new rules" width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can copy and paste the italicized text below directly into your rule statements pane to save yourself some time.&lt;/p&gt;

&lt;p&gt;|        |               |       |                                                                                               |&lt;br&gt;
   |----    |-----------    |------ |---------------------------------------------------------------------------------------------- |&lt;br&gt;
   | A0     | Info          | Site  | Site area is &lt;code&gt;Site.squareMeters&lt;/code&gt;} meters                                                         |&lt;br&gt;
   | 1      | Info          | Site  | The slope must be 45° or less.                                                                   |&lt;br&gt;
   | 2      | Info          | Site  | Annual solar radiation should be at least 800 kWh.                                            |&lt;br&gt;
   | 3      | Violation     | Site  | Aspect should not be north.                                                                   |&lt;br&gt;
   | 4      | Info          | Site  | Areas with a slope of 10° or less are determined as suitable areas regardless of the aspect.     |&lt;br&gt;
   | 5      | Info          | Site  | 20 m² and more area is needed.                                                                   |&lt;br&gt;
   | 6      | Violation     | Site  | The slope is not 45° or less.                                                                    |&lt;br&gt;
   | 7      | Violation     | Site  | Annual solar radiation is not at least 800 kWh.                                               |&lt;br&gt;
   | 8      | Info          | Site  | Aspect is not north.                                                                          |&lt;br&gt;
   | 9      | Violation     | Site  | Site is not at least 20 m²                                                                       |&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Finally, let’s run the Conflict Checker. Here, we can really see the value of business users that are well-acquainted with the rules being the implementers of the logic versus developers, because they will know best how to address logical mistakes in the rules:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2l94qo1jx4sagt999465.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2l94qo1jx4sagt999465.png" alt="conflcit checker results" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Corticon has identified 15 conflicts—situations where the conditions we’ve defined could presumably overlap. Currently, any of the cells with a ‘ – ‘ in them will evaluate that rule without considering the vocabulary attribute on the left hand side. For example, the first conflict which Corticon shows us is a conflict between 1 and 3, because Corticon has inferred there may be scenarios ‘North’ could be the value in cell c2, or a value &amp;lt; 45 could be in the cell a3—if both conditions are met, which matters more?&lt;/p&gt;

&lt;p&gt;We can resolve this in a few ways—explicitly define every combination of values in every rule, or simply override one with another in cases of conflicts. We’re going to evaluate these rules such that if any aspect of the Site makes is unsuitable for solar, then that it will take precedent over any condition which resolves to &lt;code&gt;isSuitable&lt;/code&gt;=T. However, recall that the condition for rule #4—when the slope is less than 10 degrees—will resolve to &lt;code&gt;isSuitable&lt;/code&gt;=T even when the Site.aspect= ‘North’.&lt;/p&gt;

&lt;p&gt;We’ll define overrides to tell Corticon which action to defer to in cases of conflict. Other than rule #4 overriding number #3 for the reason mentioned above, all other rules that lead to a determination of &lt;code&gt;isSuitable&lt;/code&gt;=F should override those which set &lt;code&gt;isSuitable&lt;/code&gt;=T. At the bottom of each rule column, you’ll see an override row where you can specify which rule(s) that particular rule will override in cases of conflict. You can type them in or select them from the dropdown (hold down control to select multiple). Try this on your own, then check your work based on the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2xow08iw1i5ykib2u6qp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2xow08iw1i5ykib2u6qp.png" alt="highlighted rule with rule statement" width="800" height="581"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing Rules
&lt;/h2&gt;

&lt;p&gt;Next, let’s set up a rule test to run test scenarios against this rulesheet. A Ruletest simulates a business scenario where the rules are applied to input data. If the data satisfies all the conditions in a rule, the rule fires and some output containing the results of the rule execution is produced. You can define different sets of input data to test how the rules behave in different scenarios. You can also use a Ruletest to compare the output of a rule execution with expected results. A Ruletest stores this information in a Ruletest file, enabling you to save use-cases that are of interest, change rules, and run the test again to see how the modified rules behave when applied to the same use-cases.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right click on the Ruletests folder &amp;gt; New &amp;gt; Ruletest.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbmzao9014fqi2q1hx5c5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbmzao9014fqi2q1hx5c5.png" alt="creating new ruletest" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give it a name like ‘Site Check’, and select the RuleTests folder if it isn’t already.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftw0hvt0i9l6b4msoz7b8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftw0hvt0i9l6b4msoz7b8.png" alt="naming rule test" width="673" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hit next, and you should see the ‘Site Area’ rulesheet already selected. This is the ‘test subject’, i.e. the specific rule asset file we’re testing against. Click Finish.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Ruletest editor has four parts:&lt;br&gt;
&lt;strong&gt;Test Subject&lt;/strong&gt;—specifies which Rulesheet or Ruleflow is being tested&lt;br&gt;
&lt;strong&gt;Input&lt;/strong&gt;—where you define input data to be processed by the rules in the Rulesheet.&lt;br&gt;
&lt;strong&gt;Output&lt;/strong&gt;—where Corticon Studio displays the result of a Ruletest execution.&lt;br&gt;
&lt;strong&gt;Expected&lt;/strong&gt;—where you can optionally define the result that you expect.&lt;br&gt;
In the real world, a Corticon Decision Service may receive input in different formats such as XML, JSON, Java, or .NET objects. However, in a Ruletest, you just specify input data using the same Rule Vocabulary we’ve used for the rules.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can specify different entities as well as multiple instances of the same entity. Let’s try it—drag in two instances of the &lt;code&gt;Site&lt;/code&gt;entity, into the input pane. This adds all the attributes in the entity to the Input pane. However, you can delete attributes that you do not need.&lt;/li&gt;
&lt;li&gt;Double click the attributes to specify values. The syntax for specifying values is similar to specifying values for attributes in Rulesheets, with one difference—you must not enter any values in quotes, even if the data type of the attribute is String, DateTime, Date or Time. If you enter quotes, the Ruletest treats it as part of the value. Copy the values as shown below for your input:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Focpjk5ck34hz86d7v3l5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Focpjk5ck34hz86d7v3l5.png" alt="data in input pane of ruletest" width="691" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above the ruletest, click the Run Test button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14nb6mr4wc8z5ncvzrc8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14nb6mr4wc8z5ncvzrc8.png" alt="Run Test button" width="223" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The rules will be compiled into a Decision Service and executed against a test instance of the Corticon Server. In other words, the ruletest enables us to replicate the precise behavior we would expect from the rules if they were deployed as is. You should see the output populated in the Output pane, and Rule Messages sent back for the rules that fired.&lt;/p&gt;
&lt;h2&gt;
  
  
  Data Integration
&lt;/h2&gt;

&lt;p&gt;So far, we’ve built a rulesheet and a test case in which we define the test data. Corticon is not limited to only evaluating data provided on the initial request to the Decision Service (like the data we defined by double clicking the attributes in the ruletest). Let’s incorporate some external data retrieved from a REST API. We’ll use the API provided by the US National Renewable Energy Lab to retrieve data about the solar radiation for a specific address.&lt;br&gt;
We’ll be using an API key that will expire after today’s workshop, but they are free to generate at NREL.gov.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reopen your rule vocabulary&lt;/li&gt;
&lt;li&gt;Add two attributes to &lt;code&gt;Site&lt;/code&gt;—&lt;code&gt;address&lt;/code&gt;(string), and &lt;code&gt;avgDailyRadiation_kWh&lt;/code&gt;(decimal)&lt;/li&gt;
&lt;li&gt;At the top of the screen, click the Vocabulary menu &amp;gt; Add Datasource &amp;gt; REST Datasource&lt;/li&gt;
&lt;li&gt;Click the name of the rule vocabulary to bring the datasource configuration pane into focus:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/creating%2520rest%2520connection" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/creating%2520rest%2520connection" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rename the Datasource Name from the default, “REST Service”, to “NREL”.&lt;/li&gt;
&lt;li&gt;In the REST URL field, paste the URL: &lt;a href="https://developer.nrel.gov/api/solar/solar_resource/v1.json?address=Tremont%20St,%20Boston,%20MA%2002111" rel="noopener noreferrer"&gt;https://developer.nrel.gov/api/solar/solar_resource/v1.json?address=Tremont%20St,%20Boston,%20MA%2002111&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;On the Authentication dropdown, select URL Parameter Token&lt;/li&gt;
&lt;li&gt;Under field name, enter ‘api_key’&lt;/li&gt;
&lt;li&gt;Under token, paste your API key&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi7tc0gxlp34gzzn5ohrp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi7tc0gxlp34gzzn5ohrp.jpg" alt="entering api key" width="701" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click ‘Test Connection’. If everything is in the right place, you should get an alert that the connection was successful.&lt;/li&gt;
&lt;li&gt;Click ‘Discover’. This will import the various fields at that datasource endpoint, which we will then ‘map’ to our entities/attributes.&lt;/li&gt;
&lt;li&gt;Click your &lt;code&gt;Site&lt;/code&gt;entity. On the right hand side, select AUTOREST.REST_DATA from the dropdown next to Table Name, then select ‘address’ from the entity identity dropdown.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frupah8bnswus6kwnc0dw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frupah8bnswus6kwnc0dw.png" alt="setting identity for rest datasource" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Next, click the address attribute. This time on the righty hand side, select ADDRESS from the column name dropdown.&lt;/li&gt;
&lt;li&gt;Finally, do the same for the new &lt;code&gt;avgDailyRadiation_kWh&lt;/code&gt;attribute, selecting &lt;code&gt;OUTPUTS_AVG_DNI_ANNUAL&lt;/code&gt;from the column name dropdown.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxc3okgvpoz8tf2kbstud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxc3okgvpoz8tf2kbstud.png" alt="mapping to rest datasource" width="800" height="337"&gt;&lt;/a&gt;&lt;br&gt;
)&lt;/p&gt;
&lt;h3&gt;
  
  
  Incorporate the REST Datasource
&lt;/h3&gt;

&lt;p&gt;The one file we haven’t worked with yet is the Ruleflow. A Ruleflow enables you to connect two or more Rulesheets in a sequence. When the Ruleflow is processed at runtime, the Rulesheets are executed one by one in that sequence. The output of one Rulesheet becomes the input of the next Rulesheet. Note that a Rulesheet can be used in multiple Ruleflows. This enables you to use Rulesheets as reusable modules of rule logic. Any change in the rule logic only has to be made once in the Rulesheet and it is propagated across all Ruleflows that refer to it.&lt;br&gt;
Besides defining a sequence of rulesheet executions, we can specify behavior like branching to different rulesheets at specific points in the decision execution based upon attribute values. We also specify the REST callout within the ruleflow.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right click the Ruleflows folder &amp;gt; New &amp;gt; Ruleflow&lt;/li&gt;
&lt;li&gt;Name it Solar Flow and click finish&lt;/li&gt;
&lt;li&gt;When we were defining rules in a rulesheets, we dragged and dropped from the Rule Vocabulary. For the ruleflow, we’ll be defining the order to rule execution by dragging and dropping our rulesheets onto the Ruleflow canvas, and then connecting them using the Connection tool on the righthand side of the canvas. Drag the Site Area rulesheet onto the ruleflow&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lo97qdb4299bm3326r7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lo97qdb4299bm3326r7.png" alt="adding rulesheet to ruleflow" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click ‘Service Callout’ on the panel to the right, then click above the Site Area rulesheet to place the callout node. Connect the callout to the rulesheet by click Connection in the righthand pallet and dragging a connection from the callout to Site Area.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feov1y9nrk5t4sqm541ag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feov1y9nrk5t4sqm541ag.png" alt="adding rest service callout to ruleflow" width="557" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the service callout, and bring the ‘Properties’ pane into view. You should see a dropdown for Service Name—select ‘Retrieve Data’.&lt;/li&gt;
&lt;li&gt;Next, click ‘Runtime Properties’ on the tab to the left, underneath the ‘Service Call-out’ tab. Here, you’ll set options for 6 different cells by choosing from each cell’s dropdown. Mirror the content shown below:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fna88t0albg0ojkan3xbh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fna88t0albg0ojkan3xbh.png" alt="properties pane for service callout" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Update the rules
&lt;/h3&gt;

&lt;p&gt;The last step we need to perform is to annualize the solar radiation data because we need the sum total of kWh. We’ll solve for this by multiplying the daily kWh value which will be retrieved from the data source by 365. We can do this by just adding another action only rule to the Site Area rulesheet. Reopen that rulesheet, and define the new rule to annualize the data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpeg12frqryx4sotww94s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpeg12frqryx4sotww94s.png" alt="kWh per year rule" width="758" height="38"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Test the Updated Rules
&lt;/h3&gt;

&lt;p&gt;Let’s give this thing a whirl. Open your ruletest back up. Change the test subject to the ruleflow from the rulesheet by double clicking the folder path above the input pane, highlighted below. Change the inputs to match the input shown in the same photo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frktwl7vrdf5xniy6knt2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frktwl7vrdf5xniy6knt2.png" alt="updated ruletest input" width="524" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you execute this test, Corticon will retrieve the data value for &lt;code&gt;avgDailyRadiation_kWh&lt;/code&gt;from the REST endpoint, incorporate it into the data used for the rule processing, and evaluate the site’s suitability based upon the existing rules and the annualized kWh value.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqd45jnf213393l98b0oa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqd45jnf213393l98b0oa.png" alt="updated ruletest output" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;a href="https://github.com/corticon/corticon-classic-samples/tree/main/Evaluate%20a%20Site%20for%20Solar%20Energy%20Potential" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;➾ Download project from Github&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>corticon</category>
      <category>tutorial</category>
      <category>nocode</category>
      <category>restapi</category>
    </item>
    <item>
      <title>Internal Rate of Return Solver with Corticon</title>
      <dc:creator>Seth Meldon</dc:creator>
      <pubDate>Thu, 03 Oct 2024 18:07:48 +0000</pubDate>
      <link>https://dev.to/notedhelms/internal-rate-of-return-solver-with-corticon-2gm7</link>
      <guid>https://dev.to/notedhelms/internal-rate-of-return-solver-with-corticon-2gm7</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Understanding the Internal Rate of Return Formula&lt;/li&gt;
&lt;li&gt;The Rules&lt;/li&gt;
&lt;/ul&gt;



&lt;h2&gt;
  
  
  Internal Rate of Return
&lt;/h2&gt;

&lt;p&gt;Using Corticon's iterative execution capabilities, we can solve calculations that require solving for the best possible answer. &lt;/p&gt;

&lt;p&gt;Internal Rate of Return (IRR) is a financial measure used, among other things, to evaluate the profitability and opportunity cost of an investment. A typical use case would be to evaluate whether to pursue an investment wherein:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initial expense (investment cost) of $5000&lt;/li&gt;
&lt;li&gt;Year 1 - $0 return&lt;/li&gt;
&lt;li&gt;Year 2 - $2000 return&lt;/li&gt;
&lt;li&gt;Year 3 - $0 return&lt;/li&gt;
&lt;li&gt;Year 4 - $4000 return&lt;/li&gt;
&lt;li&gt;Year 5 - $0 return&lt;/li&gt;
&lt;li&gt;Year 6 - $9000 return&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The internal rate of return is solved for by using the formula:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F849g4egh6e8w4ktbgt47.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F849g4egh6e8w4ktbgt47.jpg" alt="internal rate of return formula" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Plugging in our numbers, we have: &lt;/p&gt;

&lt;p&gt;~0 = (0/(1+IRR)^1 + 2000/(1+IRR)^2 + 0/(1+IRR)^3 + 4000/(1+IRR)^4 + 0/(1+IRR)^5+ 9000/(1+IRR)^6) - 5000&lt;/p&gt;

&lt;p&gt;We are seeking the IRR at which the Net Present Value (NPV) is zero (or as close as we can get within X number of decimal points). We thus need to recurringly try different values for IRR to get as close to zero as possible. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Rules
&lt;/h2&gt;

&lt;p&gt;First, our rule vocabulary. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79j2g0205x9ytbphmvcp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79j2g0205x9ytbphmvcp.png" alt="Rule Vocabulary" width="335" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have 3 entities, Candidate, Cashflow, and the root entity, Investment. The investment has any number of cashflows which we'll be evaluating. It also has any number of candidates that will be created during the decision execution, representing various rates that will be plugged in. &lt;/p&gt;

&lt;p&gt;The inputs will be simply the parent entity, Investment, with all corresponding cashflows and an installment number marking their sequence. The first cashflow is always the cash outflow, so its amount is thus always negative. It will use the value 0 for its installment number. &lt;/p&gt;

&lt;p&gt;Next our rules. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We start by initializing a value for IRR, which will be incremented up or down depending upon the resulting NPV. We likewise will slot the initial cashflow's value into the 'principal' attribute of the Investment entity, and then remove that cashflow to more easily operate upon only the future flows. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6toxwfuugd5cahltkf7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6toxwfuugd5cahltkf7.png" alt="Rulesheet 1" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We'll drag this first rulesheet onto a new ruleflow which will be generated into the runtime decision service later on. A ruleflow can contain any number of rulesheets and any number of 'embedded' ruleflows. We're going to create an embedded ruleflow containing two more rulesheets, and loop through this embedded ruleflow as we try candidate IRR rates by applying the 'Iterative' option to it from the ruleflow pallete. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzf18m4dsr370othrudm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzf18m4dsr370othrudm.png" alt="top loop" width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When an object on a ruleflow is set to iterate, it will repeatedly re-execute until the values derived by the object’s rules cease to change. Once values in the object cease changing, the iteration stops and execution continues to the next object (as determined by the Connectors). &lt;/li&gt;
&lt;li&gt;Within the inner ruleflow, we have two rulesheets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffekqocffykz87z8qbaaq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffekqocffykz87z8qbaaq.png" alt="inner flow" width="616" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; The first will calculate each individual cashflow's portion it contributes to the final calculation-- for example, the 'portion' attribute of cash flow 3 would be the result from 0/(1+IRR)^3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1eljucntmnkowdacj3h1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1eljucntmnkowdacj3h1.png" alt="calculate each flow" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The second rulesheet in the embedded ruleflow will:

&lt;ul&gt;
&lt;li&gt;Set Investment.npv to the sum of each cashflow's portion less the initial investment&lt;/li&gt;
&lt;li&gt;If that npv is greater than zero, increment the irr up by .01, or if less than zero, down by .01. &lt;/li&gt;
&lt;li&gt;However, we need to know if we've already tried a given rate or not so we don't end up in an endless loop. This is where the Candidates come in. We create a new candidate for each rate that we try, until we run into a rate that has already been tried, at which point no action is triggered an we return the calculated value. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklvwsq7cwuc3u7kxmsxa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklvwsq7cwuc3u7kxmsxa.png" alt="evaluate npv" width="800" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Testing against the top level ruleflow, we set the input based upon the use case listed above for the $5000 investment. We see that Corticon has settled on an IRR of .27 (27%).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxeb5wvd7whe5xomwcsq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxeb5wvd7whe5xomwcsq.png" alt="ruletest" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we run the ruletest with ruletrace, we can see how Corticon has looped through the rules with each tweaked rate until it got as close to zero for the investment's NPV as it could. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fky8usm7uza41w4ebxv0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fky8usm7uza41w4ebxv0x.png" alt="ruletest trace view" width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The rule trace data can be exported to a CSV as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8astmnfmdytymgp9bmec.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8astmnfmdytymgp9bmec.png" alt="ruletest trace export to clipboard" width="434" height="153"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/corticon/corticon-classic-samples/tree/main/IRR" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;➾ Download project from Github&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.progress.com/corticon" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;➾ Learn more about Corticon&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>corticon</category>
      <category>tutorial</category>
      <category>nocode</category>
      <category>java</category>
    </item>
    <item>
      <title>Offline Dynamic Forms with Corticon.js - Video Training</title>
      <dc:creator>Seth Meldon</dc:creator>
      <pubDate>Thu, 03 Oct 2024 15:34:25 +0000</pubDate>
      <link>https://dev.to/notedhelms/offline-dynamic-forms-with-corticonjs-video-training-5d7g</link>
      <guid>https://dev.to/notedhelms/offline-dynamic-forms-with-corticonjs-video-training-5d7g</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.youtube.com/playlist?list=PLy058akE_4PufGWh0MKyffsQ0eTsDJSIq&amp;amp;amp%3Bsi=T299SDyE4UkMxKgE" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ytimg.com%2Fvi%2F5XfAWd4nMvA%2Fhqdefault.jpg%3Fsqp%3D-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE%3D%26rs%3DAOn4CLCD1-b7TEqfs2inW9lfpmSkKcR8MQ%26days_since_epoch%3D20019" height="270" class="m-0" width="480"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.youtube.com/playlist?list=PLy058akE_4PufGWh0MKyffsQ0eTsDJSIq&amp;amp;amp%3Bsi=T299SDyE4UkMxKgE" rel="noopener noreferrer" class="c-link"&gt;
          Dynamic Forms with Corticon.js - YouTube
        &lt;/a&gt;
      &lt;/h2&gt;
        
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fs%2Fdesktop%2Fdd166493%2Fimg%2Flogos%2Ffavicon.ico" width="800" height="400"&gt;
        youtube.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>corticon</category>
      <category>javascript</category>
      <category>lowcode</category>
    </item>
    <item>
      <title>File Governance and Versioning in Corticon BRMS</title>
      <dc:creator>Seth Meldon</dc:creator>
      <pubDate>Thu, 03 Oct 2024 15:26:59 +0000</pubDate>
      <link>https://dev.to/notedhelms/file-governance-and-versioning-in-corticon-brms-3f5f</link>
      <guid>https://dev.to/notedhelms/file-governance-and-versioning-in-corticon-brms-3f5f</guid>
      <description>&lt;p&gt;Learn how to integrate Corticon business rule assets into your version control systems.&lt;/p&gt;

&lt;p&gt;Progress Corticon Studio is a robust application, enabling all aspects of business rule development and testing without coding. Because Progress Corticon operates upon data while storing no data, all the components developed within Corticon Studio are straightforward to manage as standard file assets—you have to really go out of your way to introduce external dependencies to Corticon business rules projects!&lt;/p&gt;

&lt;p&gt;Everything authored in Corticon Studio’s respective file types—the vocabulary, rule sheets, rule tests and rule flows—are stored under the covers as XML files, making integration with version control systems like Git a cinch.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Basic Version Control Workflow

&lt;ul&gt;
&lt;li&gt;Repositories&lt;/li&gt;
&lt;li&gt;Branches&lt;/li&gt;
&lt;li&gt;Git Operations&lt;/li&gt;
&lt;li&gt;Integrate a Repository into Corticon Studio’s Project Explorer&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
Basic Version Control Workflow

&lt;ul&gt;
&lt;li&gt;Repositories&lt;/li&gt;
&lt;li&gt;Branches&lt;/li&gt;
&lt;li&gt;Git Operations&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Integrate a Repository into Corticon Studio’s Project Explorer&lt;/li&gt;

&lt;/ul&gt;



&lt;h2&gt;
  
  
  Basic Version Control Workflow
&lt;/h2&gt;

&lt;p&gt;Using version control systems (VCS) is a standard practice for most development teams, but here’s a brief overview of the primary VCS, Git, if it’s newer to you. GitHub is one of the most widely employed Git-based VCS and will be the tool used here.&lt;/p&gt;

&lt;p&gt;Using a version control system provides numerous benefits for organizations, namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Restoring older versions in case you accidentally break something&lt;/li&gt;
&lt;li&gt;  Collaborating with team members without overwriting each other’s changes&lt;/li&gt;
&lt;li&gt;  Documenting who makes a change, when they made it and what they changed&lt;/li&gt;
&lt;li&gt;  Backing up your work in case files are lost&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Repositories
&lt;/h3&gt;

&lt;p&gt;The central hub where everything is stored and tracked is called a Repository. A common practice with Corticon is to create a Repository for each rule project. That way, when you open up Corticon Studio, the repository can be represented as a folder within the Project Explorer.&lt;/p&gt;

&lt;p&gt;Every change made to the rule assets and any new files will be tracked as you go, and when you’ve completed the needed changes, you “stage” the changes to specify which files’ changes you want to save into the repository, and finally commit the rules back to the central repository. A commit is like a save point in a video game but documented with a user-defined commit message which will serve as an explainer of the changes made that can be referred to later on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Branches
&lt;/h3&gt;

&lt;p&gt;Branching is what enables multiple development team members to make changes to a shared repository. When you create a new repository, a “main” branch is created along with it. If one rule developer needs to tweak a few rules, she’ll create a new branch off the main branch to isolate her instance of the rule repository from the master repository, and “merge” her branch’s changes into the main branch once the changes are ready to go live. You can read more about how branching works in GitHub at &lt;a href="https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/making-changes-in-a-branch/managing-branches" rel="noopener noreferrer"&gt;this link&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Operations
&lt;/h3&gt;

&lt;p&gt;Typically, a developer will execute these steps with the command line. For example:&lt;/p&gt;

&lt;p&gt;$ git init     # Initialize a new git database&lt;/p&gt;

&lt;p&gt;$ git clone    # Copy an existing database&lt;/p&gt;

&lt;p&gt;$ git status   # Check the status of the local project&lt;/p&gt;

&lt;p&gt;$ git diff     # Review the changes done to the project&lt;/p&gt;

&lt;p&gt;$ git commit   # Save the current state of the project to database&lt;/p&gt;

&lt;p&gt;Using terminal commands isn’t necessary for basic adoption of Git with Corticon Studio files, though. There are various tools that will allow us to bypass the command line when defining rules, including the built-in Eclipse plugin for Git version control. If you’ll be storing your assets on GitHub, though, an even easier solution is &lt;a href="https://desktop.github.com/" rel="noopener noreferrer"&gt;GitHub Desktop&lt;/a&gt;, a free desktop software that GitHub offers. It can be used in conjunction with Corticon Studio, handling most git commands for you. For full instructions on getting set up with GitHub desktop, follow the guides they provide &lt;a href="https://docs.github.com/en/desktop/installing-and-configuring-github-desktop/installing-and-authenticating-to-github-desktop/authenticating-to-github" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once installed, users can link their account to the installation, then &lt;a href="https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/adding-and-cloning-repositories/cloning-and-forking-repositories-from-github-desktop" rel="noopener noreferrer"&gt;connect to existing repositories&lt;/a&gt; or &lt;a href="https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/adding-and-cloning-repositories/adding-a-repository-from-your-local-computer-to-github-desktop" rel="noopener noreferrer"&gt;create a new one&lt;/a&gt;. The repository will then be stored on GitHub and cloned to your local desktop. You can &lt;a href="https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/making-changes-in-a-branch/managing-branches" rel="noopener noreferrer"&gt;make changes to the files with branches&lt;/a&gt; (recommended), or make changes directly on the main branch. As you make changes to the Corticon rules, the files will be monitored and considered by GitHub as being in one of four states: untracked, modified, staged or committed. Once you're satisfied with your work, you can &lt;a href="https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/working-with-your-remote-repository-on-github-or-github-enterprise/creating-an-issue-or-pull-request" rel="noopener noreferrer"&gt;create a pull request&lt;/a&gt; to merge your changes in the current branch into another branch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrate a Repository into Corticon Studio’s Project Explorer
&lt;/h2&gt;

&lt;p&gt;Whether you’re using Corticon Studio for server-based or JavaScript deployments, the steps for version control integration are the same. Here, we’ll use the Corticon &lt;a href="https://github.com/corticon/Importable-Rule-Projects" rel="noopener noreferrer"&gt;rule project samples repository&lt;/a&gt; for a demo.&lt;/p&gt;

&lt;p&gt;1. Install GitHub Desktop and link to your GitHub account, as described above.&lt;/p&gt;

&lt;p&gt;2. Navigate to the repository’s URL on GitHub (e.g. &lt;a href="https://github.com/corticon/Importable-Rule-Projects" rel="noopener noreferrer"&gt;https://github.com/corticon/Importable-Rule-Projects&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;3. Click the green “Code” button and select “Open with GitHub Desktop.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ura3jehs75pvxn1pfe9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ura3jehs75pvxn1pfe9.png" alt="screenshot of github.com open with github desktop shortcut" width="780" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4. Select either the default or a custom directory which will serve as your local copy of the repository that you’ll work off of in Corticon Studio.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felks0rh8o9wthzrvwg39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felks0rh8o9wthzrvwg39.png" alt="cloning a repo in github desktop" width="780" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5. Open Corticon Studio or Corticon.js Studio.&lt;/p&gt;

&lt;p&gt;6. Right click inside of the Project Explorer pane.&lt;/p&gt;

&lt;p&gt;7. Select “Import.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qwm7xosgkcsh42x1nbp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qwm7xosgkcsh42x1nbp.png" alt="importing in corticon studio" width="761" height="692"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;8. Select “Projects from Folder or Archive” then “Next.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7p3dde5rb2ozixdpek8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7p3dde5rb2ozixdpek8.png" alt="import wizard corticon studio" width="780" height="825"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;9. Select “Directory.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnpxa3hytq44sknpoy8nk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnpxa3hytq44sknpoy8nk.png" alt="import wizard corticon studio - selecting directory" width="780" height="591"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;10. Navigate to the directory path of the GitHub repository on your local machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftrm4pvt43vo7x6a6epg4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftrm4pvt43vo7x6a6epg4.png" alt="import wizard corticon studio - selecting directory 2" width="780" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;11. Choose the parent folder of all rule project files you want to import.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjimnu50fr2fc4web5gzk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjimnu50fr2fc4web5gzk.png" alt="finish import" width="780" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;12. Open up the project, proceed with rule modeling.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkbc8iw99pp2yn2d5om56.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkbc8iw99pp2yn2d5om56.png" alt="rulesheet in studio" width="780" height="629"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;13. Once you wrap up, save all files, then open up GitHub Desktop if not already open, make sure “Current repository” and “Correct branch” are correct, then enter in a commit summary and optionally, a commit description.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frkylpvk1bliebdigsol9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frkylpvk1bliebdigsol9.png" alt="committing in github desktop" width="780" height="880"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;14. Hit “Commit to main” (or the name of the current branch) and then “Push Origin.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2gknxlfqu021n9p86cxj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2gknxlfqu021n9p86cxj.png" alt="pushing to origin in github desktop" width="743" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your rules have been versioned! Whenever you or another rule developer next goes to make edits, you just need to open up GitHub Desktop and “Fetch Origin” so that your local copy of the repository is up to date. Then simply open up Corticon Studio, and the project explorer will automatically contain any of the new/changed files from the repository.&lt;/p&gt;

&lt;p&gt;As mentioned, this is just one, basic way to integrate rules into your VCS workflow. &lt;/p&gt;

</description>
      <category>corticon</category>
      <category>github</category>
      <category>git</category>
    </item>
    <item>
      <title>Wrangling Data across a Logistics Enterprise with Business Rules + SQL</title>
      <dc:creator>Seth Meldon</dc:creator>
      <pubDate>Mon, 09 Sep 2024 00:29:17 +0000</pubDate>
      <link>https://dev.to/notedhelms/taming-sql-logical-complexities-corticon-business-rules-engine-5fdb</link>
      <guid>https://dev.to/notedhelms/taming-sql-logical-complexities-corticon-business-rules-engine-5fdb</guid>
      <description>&lt;h2&gt;
  
  
  Taming SQL Logical Complexities with a Business Rules Engine
&lt;/h2&gt;

&lt;p&gt;For enterprises and government agencies whose operations depend on efficient logistical management of geographically diverse personnel and assets, capturing and acting on relevant data about these moving parts is critical. While the amount of data available and the sources from which this data is logged and exposed are more expansive than ever, the mounting complexity attributable to the continual accumulation of data from new and existing data sources necessitates thoroughly considered the extent to which the data can be acted upon, not accumulated just for the sake of it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zzdy6j8jpwxdq5rq88j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zzdy6j8jpwxdq5rq88j.jpg" alt="stack of files" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Business processes and decision-making for logistical functions like fleet management require data from enormous datasets, often spread across multiple databases and made available through different protocols, and maintained in distinct formats. Individual decisions that seem straightforward, answering simple questions with simple answers-- may rely upon a labyrinth of intermediate business logic and data operations executed along the way.&lt;/p&gt;

&lt;p&gt;With the proliferation of connected technologies and the resulting growth in the scale and variability of data, ensuring that applications are able to access specifically relevant data—no more, no less— for a given business process is a critical performance and security requirement for organizations' enterprise architects.&lt;/p&gt;

&lt;p&gt;Effective use of SQL helps minimize the processing time required to get to desired subsets of data, but this approach can quickly result in organizations unwittingly hoisting an anvil of maintainability issues over their operations. As regulations change, data sources expand, storage and compute costs grow, personnel turn over, and novel security threats appear, the practice of intertwining logic and data within the database using SQL tends to make it increasingly difficult to isolate bottlenecks.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design Approaches for Data Driven Decisions&lt;/li&gt;
&lt;li&gt;Implementation Guide - Corticon Fleet Management&lt;/li&gt;
&lt;li&gt;Create Fleet Manager Database&lt;/li&gt;
&lt;li&gt;
Querying for Driving Compliance Violations with SQL

&lt;ul&gt;
&lt;li&gt;SQL View of Trip Origins and Destinations&lt;/li&gt;
&lt;li&gt;SQL Table Valued Function of Trip Origins, Destinations, and Driver Info&lt;/li&gt;
&lt;li&gt;SQL Stored Procedure of Driving Shifts&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Querying for Driving Compliance Violations with Corticon + SQL

&lt;ul&gt;
&lt;li&gt;How Corticon modularizes queries, data, and rules&lt;/li&gt;
&lt;li&gt;How the rules and queries are organized&lt;/li&gt;
&lt;li&gt;Generate the Rule Vocabulary from the Fleet Management Database Schema&lt;/li&gt;
&lt;li&gt;Connect to the Query Database&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
The Business Rules

&lt;ul&gt;
&lt;li&gt;Initialize Values - Rulesheet&lt;/li&gt;
&lt;li&gt;60 70 Hour Limit - Ruleflow&lt;/li&gt;
&lt;li&gt;Time Driving per Shift Rules - Ruleflow&lt;/li&gt;
&lt;li&gt;remove temporary fields - Rulesheet&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Better Together&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Design Approaches for Data Driven Decisions
&lt;/h2&gt;

&lt;p&gt;In this tutorial, I'll try to solve for some of the fleet management challenges which can present maintainability challenges, first by implementing a SQL only approach to common queries. Then, I'll show how those queries can be made more maintainable, traceable, and understandable by externalizing rules in a rules engine yet operating on the same SQL data source.&lt;/p&gt;

&lt;p&gt;Fleet management encompasses the logistical tasks related to vehicle maintenance, driver dispatch and scheduling, cargo tracking and related services in managing moving people and parts. For example, a cable company's fleet of vehicles driven by field service technicians, or a delivery company providing a network of courier and package delivery services.&lt;/p&gt;

&lt;p&gt;Organizations that have fleets to manage must compete by maximizing efficiencies across numerous different areas—vehicle reliability, driving behavior and safety, route optimization, and trip completion timelines—representing distinct yet interdependent data points to weigh and balance. Moreover, safety regulations define maximum shifts in which a driver can be driving without a break, minimum break periods, and related measures to mitigate fatigue related dangers. For example, in the United States, the Department of Transportation defines 'Hours of Service Rules' which &lt;a href="https://www.fmcsa.dot.gov/regulations/hours-service/summary-hours-service-regulations" rel="noopener noreferrer"&gt;stipulate&lt;/a&gt; the limits such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;14-hour shift limit&lt;/strong&gt; - When a driver comes off any ten hour period in which they were off duty, they can only drive in the time window between when they come on duty and 14 hours later (not that they would be driving for the entire 14 hours). After this 14 hour period, they may not drive again until after another 10 hour period spent off duty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;11-hour driving limit&lt;/strong&gt; - Within this 14 hour window, a driver cannot be be driving for more than 11 total hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;60/70-hour limit&lt;/strong&gt; - If a driver works for a carrier that operates vehicles fewer than 7 days per week, no driver may drive for more than 60 hours within a period of 7 consecutive days.  If the carrier does operate vehicles every day of the week, then their drivers may not drive for more than 70 hours in any 8 consecutive day period&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;30-Minute Driving Break&lt;/strong&gt; - Drivers must take a 30-minute break when they have driven for a period of 8 cumulative hours without at least a 30-minute interruption.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's step through how these data points can be catalogued in a SQL database, and then how to assess these sorts of compliance queries directly within the database versus externalizing the query rule logic with business rules.&lt;/p&gt;

&lt;p&gt;If you'd like to follow along, the instructions, scripts, and project files are all freely available in &lt;a href="https://github.com/corticon/corticon-classic-samples/tree/main/Fleet%20Management%20and%20Dispatch" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementation Guide - Corticon Fleet Management
&lt;/h2&gt;

&lt;p&gt;To follow along you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Progress Corticon Studio&lt;/strong&gt; - can be downloaded for free by following  &lt;a href="https://www.progress.com/corticon/get-started" rel="noopener noreferrer"&gt;the 'download a trial' link here&lt;/a&gt;. The evaluation version of Corticon Studio support in full all functionalities described in this tutorial.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microsoft SQL Server Management Studio (SSMS) 2019 Express&lt;/strong&gt; (other versions of SSMS are &lt;em&gt;likely&lt;/em&gt; to work), which can be downloaded for free &lt;a href="https://learn.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver15" rel="noopener noreferrer"&gt;here&lt;/a&gt;. To ensure compatibility with Corticon's data integration drivers, please follow the installation configuration documented &lt;a href="https://docs.progress.com/bundle/corticon-edc-modeling-tutorial/page/Setting-up-the-tutorial.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Download the template files from &lt;a href="https://github.com/corticon/corticon-classic-samples/tree/main/Fleet%20Management%20and%20Dispatch" rel="noopener noreferrer"&gt;here &lt;/a&gt;to create our database(s) and to import the pre-made rule project files.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Create Fleet Manager Database
&lt;/h2&gt;

&lt;p&gt;Let's start by creating the database and defining its schema.&lt;/p&gt;

&lt;p&gt;Across various interrelated tables, we'll define some sample data about the entities involved with the fleet like Driver, Vehicle, Trip, and Destination. This can be automatically done by copying/downloading &lt;a href="https://github.com/corticon/corticon-classic-samples/blob/main/Fleet%20Management%20and%20Dispatch/Scripts/CreateDataTableAndInsertDate.sql" rel="noopener noreferrer"&gt;this script&lt;/a&gt;, opening it in Microsoft SQL Server Management Studio and clicking execute.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trip table: &lt;code&gt;dateOfArrival&lt;/code&gt;, &lt;code&gt;dateOfDeparture&lt;/code&gt;, &lt;code&gt;driverId&lt;/code&gt;, &lt;code&gt;destinationID&lt;/code&gt;, &lt;code&gt;originDepotID&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Depot table: &lt;code&gt;depotID&lt;/code&gt;, &lt;code&gt;streetAddress&lt;/code&gt;, &lt;code&gt;city&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Destination table: &lt;code&gt;destinationID&lt;/code&gt;, &lt;code&gt;streetAddress&lt;/code&gt;, &lt;code&gt;city&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Driver table: &lt;code&gt;driverId&lt;/code&gt;, &lt;code&gt;first&lt;/code&gt;, &lt;code&gt;last&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;DriverStatusChange: &lt;code&gt;record&lt;/code&gt;, &lt;code&gt;statusStart&lt;/code&gt;, &lt;code&gt;statusEnd&lt;/code&gt;, &lt;code&gt;dutyStatus&lt;/code&gt;, &lt;code&gt;driverId&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should see a message that the query was completed popup in the bottom of the interface. If you don't see the newly created database "CorticonFleetManager" in the Object Explorer on the left hand side of your SQL Studio window, right click the root element in the project explorer (likely has your computer's assigned name followed by "\SQLEXPRESS..."), and click refresh.&lt;/p&gt;

&lt;p&gt;Expand out the database by clicking the '+' icon next to its name, and then expand the Tables folder to show the data tables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0p910z222a666q6cso7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0p910z222a666q6cso7.png" alt="database tables for CorticonFleetManager database" width="567" height="855"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you right click one of the table names and click 'Select Top XYZ Rows', you'll see the initial set of dummy data we're going to use. This data is primarily generated using &lt;a href="https://mockaroo.com/" rel="noopener noreferrer"&gt;Mockaroo&lt;/a&gt;, but was also supplemented by using Corticon itself to generate data!&lt;/p&gt;
&lt;h2&gt;
  
  
  Querying for Driving Compliance Violations with SQL
&lt;/h2&gt;

&lt;p&gt;Let's create some queries which will enable us to answer questions about the data.&lt;/p&gt;
&lt;h3&gt;
  
  
  SQL View of Trip Origins and Destinations
&lt;/h3&gt;

&lt;p&gt;To start, take a look at the data in the &lt;code&gt;Trip&lt;/code&gt; table of the database. You'll see that we have information about the trip's origin depot and the destination, but not the location information in full--only identifiers for &lt;code&gt;destinationID&lt;/code&gt; and &lt;code&gt;originDepotID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmh6js2dm6kje9xoiluwz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmh6js2dm6kje9xoiluwz.png" alt="diagram of Trip, Destination, and Depot tables" width="800" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To view the location information for the trip's start and end points, we could create a &lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/views/views?view=sql-server-ver16" rel="noopener noreferrer"&gt;view&lt;/a&gt; that presents each trip in a row that combines the &lt;code&gt;Origin&lt;/code&gt; and &lt;code&gt;Destination&lt;/code&gt; location data for each &lt;code&gt;Trip&lt;/code&gt;, using the identifiers denoted in the Trip table. To create this view, execute &lt;a href="https://github.com/corticon/corticon-classic-samples/blob/main/Fleet%20Management%20and%20Dispatch/Scripts/CreateTripDetailsView.sql" rel="noopener noreferrer"&gt;this SQL&lt;/a&gt; in a new query window.&lt;/p&gt;

&lt;p&gt;If successful, you should see the text in the 'Messages' pane that "Commands completed successfully."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1q9w9i6gymcc9aw34xi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1q9w9i6gymcc9aw34xi.jpg" alt="Commands completed successfully message" width="392" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we can get to the data we're after by using a query like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tripID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;,[&lt;/span&gt;&lt;span class="n"&gt;originStreetAddress&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;,[&lt;/span&gt;&lt;span class="n"&gt;originCity&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;,[&lt;/span&gt;&lt;span class="n"&gt;originState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;,[&lt;/span&gt;&lt;span class="n"&gt;destinationStreetAddress&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   
    &lt;span class="p"&gt;,[&lt;/span&gt;&lt;span class="n"&gt;destinationCity&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;,[&lt;/span&gt;&lt;span class="n"&gt;destinationState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CorticonFleetManager&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;TripDetails&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tripID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1999323&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which would output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tripID&lt;/th&gt;
&lt;th&gt;originStreetAddress&lt;/th&gt;
&lt;th&gt;originCity&lt;/th&gt;
&lt;th&gt;originState&lt;/th&gt;
&lt;th&gt;destinationStreetAddress&lt;/th&gt;
&lt;th&gt;destinationCity&lt;/th&gt;
&lt;th&gt;destinationState&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1999323&lt;/td&gt;
&lt;td&gt;04 Dexter Terrace&lt;/td&gt;
&lt;td&gt;Worcester&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;85 Hagan Plaza&lt;/td&gt;
&lt;td&gt;Manchester&lt;/td&gt;
&lt;td&gt;New Hampshire&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Not too painful, but this hasn't solved many problems for our fleet company. More realistically, the company compliance officer might request a report that will help them track and document driving shifts to demonstrate compliance with the HOS rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  SQL Table Valued Function of Trip Origins, Destinations, and Driver Info
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A first step could be to pull in data about the driver for each of these trips. This could be created with a &lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/user-defined-functions/create-user-defined-functions-database-engine?view=sql-server-ver16#TVF" rel="noopener noreferrer"&gt;table-valued function&lt;/a&gt;, an alternative approach for selecting and displaying related data from various tables joined by a defined set of key criteria. To create this function, you can run &lt;a href="https://github.com/corticon/corticon-classic-samples/blob/main/Fleet%20Management%20and%20Dispatch/Scripts/GetTripDetailsWithDriverInfo.sql" rel="noopener noreferrer"&gt;this script&lt;/a&gt; in a new query window.&lt;/li&gt;
&lt;li&gt;Once created, we run queries like the following in order to see the driver details for the respective trips:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;GetTripDetailsWithDriverInfo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The top few results we get back should look something like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tripID&lt;/th&gt;
&lt;th&gt;originCity&lt;/th&gt;
&lt;th&gt;originState&lt;/th&gt;
&lt;th&gt;originStreetAddress&lt;/th&gt;
&lt;th&gt;destinationCity&lt;/th&gt;
&lt;th&gt;destinationState&lt;/th&gt;
&lt;th&gt;destinationStreetAddress&lt;/th&gt;
&lt;th&gt;driverFirstName&lt;/th&gt;
&lt;th&gt;driverLastName&lt;/th&gt;
&lt;th&gt;driverEmail&lt;/th&gt;
&lt;th&gt;driverPhone&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1999323&lt;/td&gt;
&lt;td&gt;Worcester&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;04 Dexter Terrace&lt;/td&gt;
&lt;td&gt;Manchester&lt;/td&gt;
&lt;td&gt;New Hampshire&lt;/td&gt;
&lt;td&gt;85 Hagan Plaza&lt;/td&gt;
&lt;td&gt;Vivyanne&lt;/td&gt;
&lt;td&gt;Denizet&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:vdenizet7@csmonitor.com"&gt;vdenizet7@csmonitor.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;598-283-8892&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2105883&lt;/td&gt;
&lt;td&gt;Lynn&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;7556 Fremont Hill&lt;/td&gt;
&lt;td&gt;Boston&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;9562 Roth Circle&lt;/td&gt;
&lt;td&gt;Elvina&lt;/td&gt;
&lt;td&gt;Paddison&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:epaddison8@wordpress.org"&gt;epaddison8@wordpress.org&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;371-456-5406&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2142826&lt;/td&gt;
&lt;td&gt;Springfield&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;914 Heffernan Plaza&lt;/td&gt;
&lt;td&gt;Waltham&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;567 Carberry Center&lt;/td&gt;
&lt;td&gt;Salomi&lt;/td&gt;
&lt;td&gt;Sammonds&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:ssammondst@fastcompany.com"&gt;ssammondst@fastcompany.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;805-578-1666&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2148980&lt;/td&gt;
&lt;td&gt;Lynn&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;7556 Fremont Hill&lt;/td&gt;
&lt;td&gt;Waltham&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;84398 Vermont Circle&lt;/td&gt;
&lt;td&gt;Carol-jean&lt;/td&gt;
&lt;td&gt;Collick&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:ccollickg@ocn.ne.jp"&gt;ccollickg@ocn.ne.jp&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;173-318-5651&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2380224&lt;/td&gt;
&lt;td&gt;Manchester&lt;/td&gt;
&lt;td&gt;New Hampshire&lt;/td&gt;
&lt;td&gt;5657 Nova Park&lt;/td&gt;
&lt;td&gt;Boston&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;1284 Basil Road&lt;/td&gt;
&lt;td&gt;Lucais&lt;/td&gt;
&lt;td&gt;Rickardsson&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:lrickardssonb@etsy.com"&gt;lrickardssonb@etsy.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;435-394-7326&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4214048&lt;/td&gt;
&lt;td&gt;Waltham&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;37767 Bartelt Place&lt;/td&gt;
&lt;td&gt;Boston&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;81 Caliangt Pass&lt;/td&gt;
&lt;td&gt;Reagen&lt;/td&gt;
&lt;td&gt;Roglieri&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:rroglierii@sbwire.com"&gt;rroglierii@sbwire.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;752-814-7885&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4324638&lt;/td&gt;
&lt;td&gt;Manchester&lt;/td&gt;
&lt;td&gt;New Hampshire&lt;/td&gt;
&lt;td&gt;5657 Nova Park&lt;/td&gt;
&lt;td&gt;Worcester&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;56 New Castle Lane&lt;/td&gt;
&lt;td&gt;Elvina&lt;/td&gt;
&lt;td&gt;Paddison&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:epaddison8@wordpress.org"&gt;epaddison8@wordpress.org&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;371-456-5406&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4787172&lt;/td&gt;
&lt;td&gt;Waltham&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;37767 Bartelt Place&lt;/td&gt;
&lt;td&gt;Boston&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;997 Schlimgen Park&lt;/td&gt;
&lt;td&gt;Wendell&lt;/td&gt;
&lt;td&gt;Clatter&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:Wendellc@gmail.com"&gt;Wendellc@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;752-814-8723&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6738436&lt;/td&gt;
&lt;td&gt;Lynn&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;7556 Fremont Hill&lt;/td&gt;
&lt;td&gt;Portland&lt;/td&gt;
&lt;td&gt;Maine&lt;/td&gt;
&lt;td&gt;9 Prairie Rose Trail&lt;/td&gt;
&lt;td&gt;Jae&lt;/td&gt;
&lt;td&gt;Liddon&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:jliddonq@photobucket.com"&gt;jliddonq@photobucket.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;936-348-8823&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7029025&lt;/td&gt;
&lt;td&gt;Manchester&lt;/td&gt;
&lt;td&gt;New Hampshire&lt;/td&gt;
&lt;td&gt;5657 Nova Park&lt;/td&gt;
&lt;td&gt;Montpelier&lt;/td&gt;
&lt;td&gt;Vermont&lt;/td&gt;
&lt;td&gt;017 Waubesa Place&lt;/td&gt;
&lt;td&gt;Desirae&lt;/td&gt;
&lt;td&gt;Bawdon&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:dbawdon9@live.com"&gt;dbawdon9@live.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;430-153-9555&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7240845&lt;/td&gt;
&lt;td&gt;Manchester&lt;/td&gt;
&lt;td&gt;New Hampshire&lt;/td&gt;
&lt;td&gt;21087 Haas Lane&lt;/td&gt;
&lt;td&gt;Brockton&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;0 Emmet Trail&lt;/td&gt;
&lt;td&gt;Stan&lt;/td&gt;
&lt;td&gt;Foran&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:sforanj@ucoz.com"&gt;sforanj@ucoz.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;538-784-6972&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7969721&lt;/td&gt;
&lt;td&gt;Manchester&lt;/td&gt;
&lt;td&gt;New Hampshire&lt;/td&gt;
&lt;td&gt;21087 Haas Lane&lt;/td&gt;
&lt;td&gt;Portsmouth&lt;/td&gt;
&lt;td&gt;New Hampshire&lt;/td&gt;
&lt;td&gt;1 Bayside Center&lt;/td&gt;
&lt;td&gt;Adela&lt;/td&gt;
&lt;td&gt;O'Hederscoll&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:aohederscolll@redcross.org"&gt;aohederscolll@redcross.org&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;738-350-1113&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8648078&lt;/td&gt;
&lt;td&gt;Manchester&lt;/td&gt;
&lt;td&gt;New Hampshire&lt;/td&gt;
&lt;td&gt;5657 Nova Park&lt;/td&gt;
&lt;td&gt;Portsmouth&lt;/td&gt;
&lt;td&gt;New Hampshire&lt;/td&gt;
&lt;td&gt;68506 Moland Center&lt;/td&gt;
&lt;td&gt;Carol-jean&lt;/td&gt;
&lt;td&gt;Collick&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:ccollickg@ocn.ne.jp"&gt;ccollickg@ocn.ne.jp&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;173-318-5651&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8789396&lt;/td&gt;
&lt;td&gt;Waltham&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;37767 Bartelt Place&lt;/td&gt;
&lt;td&gt;Springfield&lt;/td&gt;
&lt;td&gt;Massachusetts&lt;/td&gt;
&lt;td&gt;014 Orin Road&lt;/td&gt;
&lt;td&gt;Cassie&lt;/td&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:cdocker4@blogspot.com"&gt;cdocker4@blogspot.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;189-509-8433&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We're one step closer to documenting the drivers' hours of service regulatory information, but now we'll need to really get creative to get information about the various legs of each trip, in order to verify whether the driving shifts exceeded any of the defined thresholds, such as driving 11 hours in a 14 hour period.&lt;/p&gt;

&lt;h3&gt;
  
  
  SQL Stored Procedure of Driving Shifts
&lt;/h3&gt;

&lt;p&gt;We're presented with a few challenges to accomplish this, as data is spread across a quite distributed database schema. Drivers' vehicles in an organization's fleet generally will have an Electronic Logging Device (ELD) onboard to log and report on the vehicle's statuses. From these ELD devices, fleet managers must maintain drivers' hours of duty records for compliance purposes, capturing data required by regulators in the format required by regulators.&lt;/p&gt;

&lt;p&gt;In the database we're working with, the ELD records are stored in the &lt;code&gt;DriverStatusChange&lt;/code&gt; table. Each driver change in shift records the new status as one of: "Drive", "On Duty", "Sleeper Berth", and "Off-duty". For the rules related to driving time between off duty periods, we must:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Segment out the records chronologically into groupings separated by any row where, in the &lt;code&gt;DriverStateChange&lt;/code&gt; table, the row &lt;code&gt;dutyStatus&lt;/code&gt;='Off Duty' and the duration is greater than 10 hours based upon the &lt;code&gt;statusStart&lt;/code&gt;and &lt;code&gt;statusEnd&lt;/code&gt;rows&lt;/li&gt;
&lt;li&gt;From within each of these groupings, sum together the duration of all rows where &lt;code&gt;dutyStatus&lt;/code&gt;='Driving'. If this value is greater than 11, then this represents a violation of &lt;strong&gt;rule 1: drivers may not drive more than 11 hours after 10 consecutive hours off duty.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;From within each of these groupings between 10 hours off duty, identify any case where the driver had a status of 'Driving' beyond the 14th hour after they had come back on duty. These will represent violations of &lt;strong&gt;rule 2: drivers may not drive beyond the 14th consecutive hour after coming on duty, following 10 consecutive hours off duty&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Rule 1: 11 hours after 10 consecutive hours off duty
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;May drive a maximum of 11 hours after 10 consecutive hours off duty.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rule 1 is fairly straightforward to create a stored procedure for. For a given &lt;code&gt;driver&lt;/code&gt; identified by their &lt;code&gt;driverID&lt;/code&gt; in the &lt;code&gt;DriverStatusChange&lt;/code&gt; table, flag any instance of a row where the &lt;code&gt;DriverStatusChange&lt;/code&gt; &lt;code&gt;dutyStatus&lt;/code&gt; field = 'Driving' and the &lt;code&gt;durationMins&lt;/code&gt; &amp;gt; 660 (11 hours).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE PROCEDURE [dbo].[FlagDrivingOver11Hours]
    @DriverId VARCHAR(255)
AS
BEGIN
    SELECT record, driverId, dutyStatus, durationMins, statusStart, statusEnd
    FROM DriverStatusChange
    WHERE driverId = @DriverId
      AND dutyStatus = 'Driving'
      AND durationMins &amp;gt; 660; 
END;
GO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;When we execute the procedure for any of the Drivers' &lt;code&gt;driverId&lt;/code&gt; values such as
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECLARE @return_value int
EXEC    @return_value = [dbo].[FlagDrivingOver11Hours]
DriverId = N'271177229'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;record&lt;/th&gt;
&lt;th&gt;driverId&lt;/th&gt;
&lt;th&gt;dutyStatus&lt;/th&gt;
&lt;th&gt;durationMins&lt;/th&gt;
&lt;th&gt;statusStart&lt;/th&gt;
&lt;th&gt;statusEnd&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1126&lt;/td&gt;
&lt;td&gt;271177229&lt;/td&gt;
&lt;td&gt;Driving&lt;/td&gt;
&lt;td&gt;668&lt;/td&gt;
&lt;td&gt;2024-01-15 17:10:38.000&lt;/td&gt;
&lt;td&gt;2024-01-16 04:18:38.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1366&lt;/td&gt;
&lt;td&gt;271177229&lt;/td&gt;
&lt;td&gt;Driving&lt;/td&gt;
&lt;td&gt;679&lt;/td&gt;
&lt;td&gt;2024-01-19 09:58:46.000&lt;/td&gt;
&lt;td&gt;2024-01-19 21:17:46.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2846&lt;/td&gt;
&lt;td&gt;271177229&lt;/td&gt;
&lt;td&gt;Driving&lt;/td&gt;
&lt;td&gt;698&lt;/td&gt;
&lt;td&gt;2024-02-09 16:45:36.000&lt;/td&gt;
&lt;td&gt;2024-02-10 04:23:36.000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Rule 2: Driving beyond 14th hour
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;May not drive beyond the 14th consecutive hour after coming on duty, following 10 consecutive hours off duty. Off-duty time does not extend the 14-hour period.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now we're getting into more tricky territory. For this procedure, an approach could be to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a temporary table which will hold the statuses flagged as violating the rule.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE PROCEDURE [dbo].[FlagDrivingViolations]
    @driverId VARCHAR(255)
AS
BEGIN
    CREATE TABLE #Violations (
        driverId VARCHAR(255),
        record INT,
        statusStart DATETIME,
        statusEnd DATETIME,
        violationType VARCHAR(255)
    );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Use a common table expression to find all Off Duty statuses with a duration over 10 hours for the &lt;code&gt;Driver&lt;/code&gt;, along with all Driving statuses with their status start/stop times.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH OffDutyPeriods AS (
        SELECT 
            driverId,
            statusStart AS offDutyStart,
            statusEnd AS offDutyEnd,
            ROW_NUMBER() OVER (PARTITION BY driverId ORDER BY statusStart) AS rowNumber
        FROM DriverStatusChange
        WHERE driverId = @driverId  
        AND dutyStatus = 'Off Duty'
        AND DATEDIFF(MINUTE, statusStart, statusEnd) &amp;gt;= 600  
    DrivingRecords AS (
        SELECT 
            dsc.driverId,
            dsc.record,
            dsc.statusStart,
            dsc.statusEnd,
            dsc.dutyStatus,
            ROW_NUMBER() OVER (PARTITION BY dsc.driverId ORDER BY dsc.statusStart) AS rowNumber
        FROM DriverStatusChange dsc
        WHERE dsc.driverId = @driverId 
        AND dsc.dutyStatus = 'Driving'
    )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;From these results, select any instances with a duty status of 'Driving' beyond 14 hours since 10 hours spent off duty.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
        dsc.driverId,
        dsc.record,
        dsc.statusStart,
        dsc.statusEnd,
        CASE
            WHEN DATEDIFF(HOUR, odp.offDutyEnd, dsc.statusStart) &amp;gt;= 14 THEN '14 Hour Driving Violation'
        END AS violationType
    INTO #TempViolations
    FROM DrivingRecords dsc
    CROSS APPLY (
        SELECT TOP 1 *
        FROM OffDutyPeriods odp
        WHERE odp.driverId = dsc.driverId
        AND odp.offDutyEnd &amp;lt; dsc.statusStart
        ORDER BY odp.offDutyEnd DESC
    ) odp
    WHERE DATEDIFF(HOUR, odp.offDutyEnd, dsc.statusStart) &amp;gt;= 14;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Insert these records into the temporary table and clean up the temporary tables.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    INSERT INTO #Violations (driverId, record, statusStart, statusEnd, violationType)
    SELECT
        driverId,
        record,
        statusStart,
        statusEnd,
        violationType
    FROM #TempViolations;  
    SELECT * FROM #Violations;  
    DROP TABLE #TempViolations;
    DROP TABLE #Violations;
END;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;By calling the procedure with a driverID, we can see the non compliant records: &lt;code&gt;EXEC FlagDrivingViolations @driverId = '202251400';&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;driverId&lt;/th&gt;
&lt;th&gt;record&lt;/th&gt;
&lt;th&gt;statusStart&lt;/th&gt;
&lt;th&gt;statusEnd&lt;/th&gt;
&lt;th&gt;violationType&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;202251400&lt;/td&gt;
&lt;td&gt;594&lt;/td&gt;
&lt;td&gt;2024-01-07T06:09:19.000Z&lt;/td&gt;
&lt;td&gt;2024-01-07T11:24:19.000Z&lt;/td&gt;
&lt;td&gt;14 Hour Driving Violation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Rule 3: 60/70-Hour Driving Limit
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;May not drive after 60/70 hours on duty in 7/8 consecutive days. A driver may restart a 7/8 consecutive day period after taking 34 or more consecutive hours off duty.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is complex! There are a considerable number of 'ifs' to evaluate here. We will need to evaluate the 7 day periods and 8 day periods subsequent to every instance that a &lt;code&gt;DriverStatusChange.dutyStatus&lt;/code&gt;, in order to sum together the hours not 'Off Duty' over that period. Over the 8 day stretch, the sum of the status durations not 'Off Duty' cannot exceed 70 hours, and for the 7 day stretch, 60 hours. But if there exists any instance in that window where the &lt;code&gt;Driver&lt;/code&gt; has a &lt;code&gt;DriverStatusChange&lt;/code&gt; record of &lt;code&gt;dutyStatus&lt;/code&gt;='Off Duty' with a &lt;code&gt;durationHours&lt;/code&gt;&amp;gt;=34, then skip over that calculation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Here we might use a procedure that first creates a temporary table to hold all statuses that are not 'Off Duty'.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE PROCEDURE [dbo].[FlagHoursWorkedOverLimits]
    @DriverId VARCHAR(255)
AS
CREATE TABLE #DriverStatus (
        record INT,
        driverId VARCHAR(250),
        dutyStatus VARCHAR(250),
        durationMins INT,
        statusStart DATETIME,
        statusEnd DATETIME
    );
    INSERT INTO #DriverStatus (record, driverId, dutyStatus, durationMins, statusStart, statusEnd)
    SELECT record, driverId, dutyStatus, durationMins, statusStart, statusEnd
    FROM DriverStatusChange
    WHERE driverId = @DriverId
    AND dutyStatus &amp;lt;&amp;gt; 'Off Duty';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;And a temporary table to hold all flagged violations:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    CREATE TABLE #FlaggedPeriods (
        PeriodType VARCHAR(50),
        StartDate DATETIME,
        EndDate DATETIME,
        TotalHoursWorked DECIMAL(5, 2)
    );  
    INSERT INTO #FlaggedPeriods (PeriodType, StartDate, EndDate, TotalHoursWorked)
    SELECT '7 Day Period', MIN(statusStart), MAX(statusEnd),
           SUM(durationMins) / 60.0 AS TotalHoursWorked
    FROM #DriverStatus
    GROUP BY DATEDIFF(DAY, '1900-01-01', statusStart) / 7
    HAVING SUM(durationMins) &amp;gt; 60 * 60;
    INSERT INTO #FlaggedPeriods (PeriodType, StartDate, EndDate, TotalHoursWorked)
    SELECT '8 Day Period', MIN(statusStart), MAX(statusEnd),
           SUM(durationMins) / 60.0 AS TotalHoursWorked
    FROM #DriverStatus
    GROUP BY DATEDIFF(DAY, '1900-01-01', statusStart) / 8
    HAVING SUM(durationMins) &amp;gt; 70 * 60;
    SELECT * FROM #FlaggedPeriods;  
    DROP TABLE #DriverStatus;
    DROP TABLE #FlaggedPeriods;
END;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;And finally call the procedure with a driverID:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECLARE @return_value int
EXEC    @return_value = [dbo].[FlagHoursWorkedOverLimits]
        @DriverId = N'202251400'
GO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PeriodType&lt;/th&gt;
&lt;th&gt;TotalHoursWorked&lt;/th&gt;
&lt;th&gt;StartDate&lt;/th&gt;
&lt;th&gt;EndDate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;7 Day Period&lt;/td&gt;
&lt;td&gt;75.08&lt;/td&gt;
&lt;td&gt;2024-01-01 02:00:00.000&lt;/td&gt;
&lt;td&gt;2024-01-07 22:30:21.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 Day Period&lt;/td&gt;
&lt;td&gt;69.10&lt;/td&gt;
&lt;td&gt;2024-01-08 06:43:23.000&lt;/td&gt;
&lt;td&gt;2024-01-15 00:58:10.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 Day Period&lt;/td&gt;
&lt;td&gt;76.23&lt;/td&gt;
&lt;td&gt;2024-01-15 03:31:12.000&lt;/td&gt;
&lt;td&gt;2024-01-21 19:55:58.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 Day Period&lt;/td&gt;
&lt;td&gt;72.40&lt;/td&gt;
&lt;td&gt;2024-01-22 03:00:00.000&lt;/td&gt;
&lt;td&gt;2024-01-29 04:16:44.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 Day Period&lt;/td&gt;
&lt;td&gt;77.77&lt;/td&gt;
&lt;td&gt;2024-01-07 04:17:18.000&lt;/td&gt;
&lt;td&gt;2024-01-15 00:58:10.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 Day Period&lt;/td&gt;
&lt;td&gt;86.23&lt;/td&gt;
&lt;td&gt;2024-01-15 03:31:12.000&lt;/td&gt;
&lt;td&gt;2024-01-22 23:26:06.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 Day Period&lt;/td&gt;
&lt;td&gt;73.65&lt;/td&gt;
&lt;td&gt;2024-01-23 07:03:08.000&lt;/td&gt;
&lt;td&gt;2024-01-30 20:05:54.000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's a lot of logic to maintain directly as SQL queries! And we didn't even check for the 34 hour exclusion rule or consider overlapping periods that violate rules. &lt;/p&gt;

&lt;p&gt;Let's shift to Corticon to see how we can solve the above and more by maintaining the rules as...rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Querying for Driving Compliance Violations with Corticon + SQL
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How Corticon modularizes queries, data, and rules
&lt;/h3&gt;

&lt;p&gt;In Corticon, the &lt;a href="https://docs.progress.com/bundle/corticon-rule-modeling/page/Build-the-Vocabulary.html" rel="noopener noreferrer"&gt;_rule vocabulary&lt;/a&gt;_ provides a singular dictionary of business terminologies that will serve as building blocks to build decisions from--elements in the vocabulary are abstractions of all data involved in the decision. For example, an application may pass some data about a driver to a Corticon decision service that conforms to the model of the rule vocabulary, and that decision service may retrieve/overwrite/delete elements of that vocabulary over the course of rule evaluation or by accessing external data sets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvgej9iorju1a6io9hd8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvgej9iorju1a6io9hd8.png" alt="fleet management rule vocabulary" width="553" height="986"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Corticon rule vocabularies can be &lt;em&gt;&lt;a href="https://docs.progress.com/bundle/corticon-data-integration/page/How-Corticon-concepts-apply-to-Datasources.html" rel="noopener noreferrer"&gt;mapped&lt;/a&gt;&lt;/em&gt;  to data in any of the leading relational databases as well as to REST APIs. For this Corticon project, we'll make use of Corticon's &lt;a href="https://docs.progress.com/bundle/corticon-data-integration/page/Getting-Started-with-ADC.html" rel="noopener noreferrer"&gt;Advanced Data Connector&lt;/a&gt; (ADC), to trigger queries to be performed during the execution of a decision service.&lt;/p&gt;

&lt;p&gt;This allows rule architects to optimize decision processing efficiency by retrieving data at specific points in the execution of a decision service, with queries populated by either data sent in the initial input to the decision service, or data produced in preceding rulesheets in a ruleflow. Planning how to use data queries as part of a decision service execution is an essential way to minimize the '&lt;a href="https://www.mushroomnetworks.com/blog/called-chatty-application/" rel="noopener noreferrer"&gt;chattiness&lt;/a&gt;' symptomatic in data-intensive decisioning.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the rules and queries are organized
&lt;/h3&gt;

&lt;p&gt;Similar to the way stored procedures and views are kept separate from the data tables in a database, but preconfigured to be run against the actual data set interested in, we separate out the queries and the data when using ADC:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;CorticonFleetManager Database&lt;/strong&gt; - the same database as used in the exercises above, containing tables like &lt;code&gt;Driver&lt;/code&gt;, &lt;code&gt;Trip&lt;/code&gt;, &lt;code&gt;Destination&lt;/code&gt;, and &lt;code&gt;DriverStatusChange&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fleetQueries Database&lt;/strong&gt; - Serves as the library of preconfigured, parameterized queries to be executed during a decision service.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CORTICON_ADC_READ -&lt;/code&gt;Table containing rows of each &lt;em&gt;read&lt;/em&gt; query's name and unique ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CORTICON_ADC_READ_DEFS -&lt;/code&gt;Table containing one or more rows per read query ID, the sequence in which they should be executed, and the Corticon rule vocabulary entity / entities that are mapped to the data being retrieved&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In &lt;a href="https://www.progress.com/corticon/components/studio" rel="noopener noreferrer"&gt;Corticon Studio&lt;/a&gt;, the vocabulary is mapped to the CorticonFleetManager database tables, and the fleetQueries are imported and then usable in a Ruleflow. This ruleflow, made up of rule logic and these queries, can then be generated into a decision service as illustrated below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsmeldon.sirv.com%2FPresentation1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsmeldon.sirv.com%2FPresentation1.gif" alt="" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate the Rule Vocabulary from the Fleet Management Database Schema
&lt;/h3&gt;

&lt;p&gt;For our fleet management scenario that starts from a database containing the fleet company's data, we'll start by &lt;a href="https://docs.progress.com/bundle/corticon-rule-modeling/page/Populate-a-Vocabulary-from-a-Datasource.html" rel="noopener noreferrer"&gt;generating a vocabulary &lt;/a&gt;from the schema of the CorticonFleetManager database. This way, the schema is automatically incorporated into the rule vocabulary's structure with the entities' interrelations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsmeldon.sirv.com%2FS1gqz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsmeldon.sirv.com%2FS1gqz.gif" alt="" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect to the Query Database
&lt;/h3&gt;

&lt;p&gt;In either the same database or an entirely separate database, we'll next &lt;a href="https://docs.progress.com/bundle/corticon-data-integration/page/Define-and-import-queries-for-ADC.html" rel="noopener noreferrer"&gt;define the queries &lt;/a&gt;to evaluate rules based upon this data. These queries are essentially parameterized SQL that substitutes variables specified 'upstream' in the rules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyop28vebis1vubcc04p6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyop28vebis1vubcc04p6.png" alt="queries" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similar to the ADC datasource, we'll connect to the query database, but the data contained within the database is not data involved with the business decision--rather, this database will contain the queries that will be referenced by Corticon during rule processing. These queries then can be &lt;a href="https://docs.progress.com/bundle/corticon-data-integration/page/Use-an-ADC-connection-as-a-Ruleflow-service-callout.html" rel="noopener noreferrer"&gt;executed at specified points&lt;/a&gt; in a single decision service's execution, in order to fulfill workflows like the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Query 1:&lt;/strong&gt; For the provided DriverID, retrieve the applicable record from the &lt;code&gt;Driver&lt;/code&gt; table in the CorticonFleetManager database, and map the &lt;code&gt;Driver&lt;/code&gt; data to the Driver entity in the Corticon vocabulary. Then pull in all &lt;code&gt;DriverStatusChange&lt;/code&gt; records with this same &lt;code&gt;DriverID&lt;/code&gt;, and associate these &lt;code&gt;DriverStatusChange&lt;/code&gt; records in the Corticon vocabulary as child entities to the &lt;code&gt;Driver&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query 2:&lt;/strong&gt;  Based upon the driver type specified in the &lt;code&gt;Driver&lt;/code&gt; table, retrieve regulatory thresholds from the Thresholds table. Here, we'll externalize the values for thresholds like maximum hours a driver can legally drive consecutively. These can then be referenced in subsequent rulesheets to flag non-compliant shifts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business Rules:&lt;/strong&gt; Identify violations for a given driver of the 60/70 hours in 7/8 days rule, driving after the 14th hour on duty rule, and driving more than 11 hours without a break.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2FSfxncnj%2Fruleflow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2FSfxncnj%2Fruleflow.png" title="ruleflow" alt="ruleflow" width="640" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Business Rules
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Initialize Values - Rulesheet
&lt;/h3&gt;

&lt;p&gt;After the two ADC READ operations are executed, the &lt;a href="https://docs.progress.com/bundle/corticon-quick-reference/page/Ruleflows.html" rel="noopener noreferrer"&gt;ruleflow&lt;/a&gt; subsequently executes the 'Initialize Values' &lt;a href="https://docs.progress.com/bundle/corticon-quick-reference/page/Rulesheets.html" rel="noopener noreferrer"&gt;rulesheet&lt;/a&gt;. This rulesheet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solves &lt;code&gt;DriverStatusChange.durationHours&lt;/code&gt; based on &lt;code&gt;DriverStatusChange.durationMinutes&lt;/code&gt;/60&lt;/li&gt;
&lt;li&gt;Sets &lt;code&gt;Driver.offDutyCount&lt;/code&gt; to be the size of the collection of &lt;code&gt;Driver.driverStatusChange&lt;/code&gt; where &lt;code&gt;dutyStatus&lt;/code&gt; = 'Off Duty'&lt;/li&gt;
&lt;li&gt;Sets a placeholder value of '1' for the attribute &lt;code&gt;Driver.temp&lt;/code&gt;. This will be used as a counter for looping during subsequent rulesheets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fij3yc1q2tpraad8mz7ph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fij3yc1q2tpraad8mz7ph.png" alt="initialize rulesheet" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  60 70 Hour Limit - Ruleflow
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhalrniped8l5jc0xzmk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhalrniped8l5jc0xzmk.png" alt="60 70 hour limit ruleflow" width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Create Work Periods - &lt;a href="https://docs.progress.com/bundle/corticon-quick-reference-63/page/Iteration.html" rel="noopener noreferrer"&gt;&lt;em&gt;Iterative&lt;/em&gt; &lt;/a&gt; Rulesheet
&lt;/h4&gt;

&lt;p&gt;This rulesheet iterates until no more changes occur when evaluated. We create a new entity, &lt;code&gt;Period&lt;/code&gt; associated as a child to &lt;code&gt;Driver&lt;/code&gt;, which will contain the attributes &lt;code&gt;day1&lt;/code&gt;, &lt;code&gt;day7&lt;/code&gt;, and &lt;code&gt;day8&lt;/code&gt;. We iterate over this rulesheet to create unique instances of &lt;code&gt;Period&lt;/code&gt; for the 8 days subsequent to every &lt;code&gt;DriverStatusChange&lt;/code&gt;. Each &lt;code&gt;Period&lt;/code&gt; will contain the date of the &lt;code&gt;DriverStatusChange&lt;/code&gt; in the attribute &lt;code&gt;day1&lt;/code&gt;, add 6 to that value for the attribute &lt;code&gt;day7&lt;/code&gt;, and add 7 to that value for the attribute &lt;code&gt;day8&lt;/code&gt; until the number of periods=the number of statuses.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frk7cq9b2bhhwtwen1nzr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frk7cq9b2bhhwtwen1nzr.png" alt="create work periods" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  8 Day Rules - Ruleflow
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp278l2pvueaha7987pt5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp278l2pvueaha7987pt5.png" alt="8 day" width="800" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Associate statuses with 8 day period - Rulesheet
&lt;/h5&gt;

&lt;p&gt;Add any instance of &lt;code&gt;Driver.driverStatusChange&lt;/code&gt; to the &lt;a href="https://docs.progress.com/bundle/corticon-rule-modeling/page/Collections.html" rel="noopener noreferrer"&gt;collection&lt;/a&gt; &lt;code&gt;Driver.period.driverStatusChange&lt;/code&gt; for any &lt;code&gt;driverStatusChange&lt;/code&gt; where :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Driver.driverStatusChange&lt;/code&gt; attribute &lt;code&gt;dutyStatus&lt;/code&gt; is 'Off Duty' as well as has a &lt;code&gt;durationHours&lt;/code&gt; &amp;gt;= 34&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Driver.driverStatusChange&lt;/code&gt; &lt;code&gt;statusStart&lt;/code&gt; and &lt;code&gt;statusEnd&lt;/code&gt; are within a period's &lt;code&gt;day1&lt;/code&gt; and &lt;code&gt;day8&lt;/code&gt;values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoagymy90aivp0209zbk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoagymy90aivp0209zbk.png" alt="associate statuses 8" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Sum statuses 8 days - Rulesheet
&lt;/h5&gt;

&lt;p&gt;Set the value of &lt;code&gt;Driver.period.hoursNotOffDuty8&lt;/code&gt; to be the sum of all &lt;code&gt;DriverStatusChange.durationHours&lt;/code&gt; where &lt;code&gt;DriverStatusChange&lt;/code&gt; is a child entity of &lt;code&gt;Driver.period&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvy82dijgzrleyrns5gjf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvy82dijgzrleyrns5gjf.png" alt="sum statuses 8" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  7 Day Rules - Ruleflow
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomd1gavqh0ub2mjcotxj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomd1gavqh0ub2mjcotxj.png" alt="7 day" width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Remove association - Rulesheet
&lt;/h5&gt;

&lt;p&gt;Removes the previously created association between &lt;code&gt;DriverStatusChange&lt;/code&gt; and &lt;code&gt;Period&lt;/code&gt; created in the 8 day ruleflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8zq86uptarhxnxx3003t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8zq86uptarhxnxx3003t.png" alt="7 remove associations" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Associate statuses with 7 day period - Rulesheet
&lt;/h5&gt;

&lt;p&gt;Add any instance of &lt;code&gt;Driver.driverStatusChange&lt;/code&gt; to the collection &lt;code&gt;Driver.period.driverStatusChange&lt;/code&gt; for any &lt;code&gt;DriverStatusChange&lt;/code&gt; where :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Driver.driverStatusChange&lt;/code&gt; attribute &lt;code&gt;dutyStatus&lt;/code&gt; is 'Off Duty' as well as has a &lt;code&gt;durationHours&lt;/code&gt;&amp;gt;= 34&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Driver.driverStatusChange&lt;/code&gt; &lt;code&gt;statusStart&lt;/code&gt; and &lt;code&gt;statusEnd&lt;/code&gt; are within a period's &lt;code&gt;day1&lt;/code&gt;and &lt;code&gt;day7&lt;/code&gt;values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fte5sfpza08l604wgqimv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fte5sfpza08l604wgqimv.png" alt="associate statuses 7" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Sum statuses 7 days - Rulesheet
&lt;/h5&gt;

&lt;p&gt;Set the value of &lt;code&gt;Driver.period.hoursNotOffDuty7&lt;/code&gt; to be the sum of all &lt;code&gt;DriverStatusChange.durationHours&lt;/code&gt; where &lt;code&gt;DriverStatusChange&lt;/code&gt;is a child entity of &lt;code&gt;Driver.period.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvp4kb8tjlzxdox04ruco.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvp4kb8tjlzxdox04ruco.png" alt="sum statuses 7" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Overtime Flags - Rulesheet
&lt;/h4&gt;

&lt;p&gt;For any instance of &lt;code&gt;Driver.period&lt;/code&gt; where &lt;code&gt;hoursNotOffDuty7&lt;/code&gt; &amp;gt; 60, create a new instance of &lt;code&gt;HOS_Flag&lt;/code&gt; as a child entity to &lt;code&gt;Driver&lt;/code&gt;, with the attribute &lt;code&gt;type&lt;/code&gt; having a value of '&amp;gt;60 hours in 7 days' and the attribute &lt;code&gt;when&lt;/code&gt; having a value of [&lt;code&gt;Period.day1&lt;/code&gt;] to [&lt;code&gt;Period.day7&lt;/code&gt;], time not off duty was [&lt;code&gt;Driver.period.hoursNotOffDuty7&lt;/code&gt;].&lt;/p&gt;

&lt;p&gt;For any instance of &lt;code&gt;Driver.period&lt;/code&gt; where &lt;code&gt;hoursNotOffDuty8&lt;/code&gt; &amp;gt; 70, create a new instance of &lt;code&gt;HOS_Flag&lt;/code&gt; as a child entity to &lt;code&gt;Driver&lt;/code&gt;, with the attribute &lt;code&gt;type&lt;/code&gt; having a value of '&amp;gt;70 hours in 8 days' and the attribute &lt;code&gt;when&lt;/code&gt; having a value of [&lt;code&gt;Period.day1&lt;/code&gt;] to [&lt;code&gt;Period.day8&lt;/code&gt;], time not off duty was [&lt;code&gt;Driver.period.hoursNotOffDuty8&lt;/code&gt;].&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ti8wau7yobx0h2ys20g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ti8wau7yobx0h2ys20g.png" alt="ot flags" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Time Driving per Shift Rules - Ruleflow
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2llget8v1s5555j46383.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2llget8v1s5555j46383.png" alt="time driving per shift rules" width="631" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Iterative Rulesheet: Create Windows Off Duty
&lt;/h4&gt;

&lt;p&gt;This rulesheet iterates until no more changes occur when evaluated. We create a new entity, &lt;code&gt;ShiftWindow&lt;/code&gt; associated as a child to &lt;code&gt;Driver&lt;/code&gt;, which will contain the attribute &lt;code&gt;statusEnd&lt;/code&gt;, &lt;code&gt;statusStart&lt;/code&gt;, &lt;code&gt;isOffDuty&lt;/code&gt;, and &lt;code&gt;windowID&lt;/code&gt;. Corticon will iterate over this rulesheet to create a new &lt;code&gt;ShiftWindow&lt;/code&gt; for every instance of &lt;code&gt;DriverStatusChange&lt;/code&gt; where &lt;code&gt;dutyStatus&lt;/code&gt; = 'Off Duty'.&lt;/p&gt;

&lt;p&gt;The rulesheet will set the &lt;code&gt;windowID&lt;/code&gt;for each &lt;code&gt;ShiftWindow&lt;/code&gt; to be the value of &lt;code&gt;Driver.temp&lt;/code&gt;, while &lt;code&gt;Driver.temp&lt;/code&gt; will be incremented up by 1 each time the rulesheet executes, repeating until the value of the &lt;code&gt;Driver.temp&lt;/code&gt; is equal to the number of off duty statuses.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqt47t51q5caso8rozeiu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqt47t51q5caso8rozeiu.png" alt="create windows off duty" width="800" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Reset - Rulesheet
&lt;/h4&gt;

&lt;p&gt;Set &lt;code&gt;Driver.temp&lt;/code&gt; back to 1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6ei6qxd7jywbxjjpewy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6ei6qxd7jywbxjjpewy.png" alt="reset" width="351" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Create Windows Driving - Iterative Rulesheet
&lt;/h4&gt;

&lt;p&gt;Here we're creating three &lt;a href="https://docs.progress.com/bundle/corticon-rule-modeling/page/How-to-use-aliases-to-represent-collections.html" rel="noopener noreferrer"&gt;aliases &lt;/a&gt;for &lt;code&gt;Driver.drivingWindow&lt;/code&gt;- 'preceding', 'subsequent', and 'timeNotOff':&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;'preceding' - represents any case where &lt;code&gt;Driver.drivingWindow&lt;/code&gt;.&lt;code&gt;windowID&lt;/code&gt;= &lt;code&gt;Driver.temp&lt;/code&gt; and &lt;code&gt;isOffDuty&lt;/code&gt; = &lt;code&gt;T&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;'subsequent' - represents any case where &lt;code&gt;Driver.drivingWindow&lt;/code&gt;.&lt;code&gt;windowID&lt;/code&gt;= &lt;code&gt;Driver.temp+1&lt;/code&gt; and &lt;code&gt;isOffDuty&lt;/code&gt; = &lt;code&gt;T&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;'timeNotOff' - this will represent the period of time in between the times off duty&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This rulesheet will iteratively create new, unique &lt;code&gt;ShiftWindow&lt;/code&gt; entities under the &lt;code&gt;timeNotOff&lt;/code&gt; alias, until there are no further instances of the &lt;code&gt;subsequent&lt;/code&gt; alias to evaluate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7f2vtg4o8mu7mydhdm9j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7f2vtg4o8mu7mydhdm9j.png" alt="create windows driving" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Group Windows - Rulesheet
&lt;/h4&gt;

&lt;p&gt;Similar to the design pattern with the 60/70 hour rules, here we're going to set &lt;code&gt;DriverStatusChange&lt;/code&gt; to be a child entity to any instance of &lt;code&gt;ShiftWindow&lt;/code&gt; within the same &lt;code&gt;statusStart&lt;/code&gt; and &lt;code&gt;statusEnd&lt;/code&gt; times, where the value of &lt;code&gt;DriverStatusChange.dutyStatus&lt;/code&gt;='Driving'.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wqrnebdfv8h0kcza4hz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wqrnebdfv8h0kcza4hz.png" alt="group windows" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Duration Driving Between off Duty Windows - Rulesheet
&lt;/h4&gt;

&lt;p&gt;Here we evaluate all instances of the newly associated &lt;code&gt;DriverStatusChange&lt;/code&gt; entities that are a child of &lt;code&gt;DrivingWindow&lt;/code&gt; for the driver. If the last instance of &lt;code&gt;Driver.drivingWindow.driverStatusChange&lt;/code&gt; any each &lt;code&gt;Driver.drivingWindow&lt;/code&gt; has a  driving  &lt;code&gt;statusEnd&lt;/code&gt; greater than 14 hours after the &lt;code&gt;drivingWindow&lt;/code&gt;, create a new &lt;code&gt;HOS_flag&lt;/code&gt; with the value for the attribute &lt;code&gt;when&lt;/code&gt; set to = 'Last off duty concluded at [&lt;code&gt;Driver.shiftWindow.statusStart&lt;/code&gt;], driving status ended at [&lt;code&gt;Driver.drivingWindow.driverStatusChange.statusEnd&lt;/code&gt;] and for the attribute &lt;code&gt;type&lt;/code&gt; set to='Driving status ended more than 14 hours since off duty'.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxhrkah2irav7vfpawfi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxhrkah2irav7vfpawfi.png" alt="duration between windows" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  11 in 14 flags - Rulesheet
&lt;/h4&gt;

&lt;p&gt;Evaluate all instances of the newly associated &lt;code&gt;DriverStatusChange&lt;/code&gt; entities that are a child of &lt;code&gt;DrivingWindow&lt;/code&gt; for the driver. If any instance of &lt;code&gt;Driver.drivingWindow&lt;/code&gt; has a &lt;code&gt;durationHours&lt;/code&gt; spent with a duty status of 'Driving' greater than the 11 permissible hours they may drive in a 14 hour window, create a new &lt;code&gt;HOS_flag&lt;/code&gt; with with the value of the &lt;code&gt;when&lt;/code&gt;attribute set to ='Driving shift of [&lt;code&gt;DriverStatusChange.durationHours&lt;/code&gt;] beginning [&lt;code&gt;DriverStatusChange.statusStart&lt;/code&gt;]' and the &lt;code&gt;type&lt;/code&gt; attribute set to '&amp;gt;11 hour driving within 14 hour period on duty'.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft23bfs4n7ssvuj378lyk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft23bfs4n7ssvuj378lyk.png" alt="11 in 14" width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  remove temporary fields - Rulesheet
&lt;/h3&gt;

&lt;p&gt;Remove the newly created entities (&lt;code&gt;Period&lt;/code&gt;, &lt;code&gt;ShiftWindow&lt;/code&gt;) as well as the retrieved and &lt;code&gt;DriverStatusChange&lt;/code&gt; records from the final response payload of the decision services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddwa1hw1j0nzihrmka4m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddwa1hw1j0nzihrmka4m.png" alt="remove temp" width="484" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Before we deploy the rules to a Corticon Server, we can validate rule behavior with &lt;a href="https://docs.progress.com/bundle/corticon-rule-modeling/page/Test-rule-scenarios-in-the-Ruletest-Expected-panel.html" rel="noopener noreferrer"&gt;ruletests &lt;/a&gt;to simulate the logic evaluated for a given &lt;code&gt;driverID&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgw1isejd6ulta8qxhsd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgw1isejd6ulta8qxhsd.png" alt="ruletest input and output results for a driverID" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Better Together
&lt;/h2&gt;

&lt;p&gt;Our ruleflow can now be &lt;a href="https://docs.progress.com/bundle/corticon-deployment/page/Introduction-to-Corticon-deployment.html" rel="noopener noreferrer"&gt;generated into a decision service on Corticon Server&lt;/a&gt;, turning our compliance assessment into a ready to use API endpoint for decisioning. These rules could also be built upon, or we could add in a &lt;a href="https://docs.progress.com/bundle/corticon-data-integration/page/How-to-configure-ADC-writes.html" rel="noopener noreferrer"&gt;&lt;em&gt;write&lt;/em&gt; &lt;/a&gt;step, to persist the final determinations back to the database.&lt;/p&gt;

&lt;p&gt;We could also run this decision as a &lt;a href="https://docs.progress.com/bundle/corticon-data-integration/page/Getting-Started-with-Batch.html" rel="noopener noreferrer"&gt;regular batch job&lt;/a&gt;, evaluating many records concurrently across disparate data sources and persisting the outputs directly back into a database.&lt;/p&gt;

&lt;p&gt;And, we can build in further functionalities that would be very difficult to maintain through other approaches, such as &lt;a href="https://docs.progress.com/bundle/corticon-data-integration/page/Overview-of-the-Autonomous-REST-Connector.html" rel="noopener noreferrer"&gt;incorporating data from REST API endpoints&lt;/a&gt; like geocoding and weather condition APIs, in order to ensure all variables beyond just regulatory considerations are readily available and maintainable in a fleet manager's dispatch application.&lt;/p&gt;

&lt;p&gt;With Corticon, existing application logic doesn't need to be eliminated, and new experts in complex languages don't need to be brought in. Instead Corticon enables enterprises to separate the 'know' from the 'flow' of their complex decision making logic, future proofing the systems from which major competitive differentiators can be derived.&lt;/p&gt;

&lt;p&gt;By externalizing the logic which evaluates driving compliance from the underlying data that the logic is evaluating, business analysts at logistics-intensive companies maintain rules without needing to know all of the complexities of the ever-changing fleet data. Because we can incorporate any number of data sources, we can maintain the regulatory thresholds separate from the logic which pulls in the relevant records, keeping distinct areas of the decision in quickly adaptable modules.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/corticon/corticon-classic-samples/tree/main/Fleet%20Management%20and%20Dispatch" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;➾ Download project from Github&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.progress.com/corticon" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;➾ Learn more about Corticon&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>sql</category>
      <category>lowcode</category>
      <category>tutorial</category>
      <category>corticon</category>
    </item>
  </channel>
</rss>
