Focal Point Basics Jobsheets

Megawidget to Product Part

Purpose:

This jobsheet presents one possible workflow for triggering product part inclusion based on a new megawidget in the hazard information display. The specific application will be to remove the impacts basis bullet in the FL.Y product based on a megawidget for specifying whether the river height is falling.

Tasks:

ORIENTATION (optional)

Follow these quick steps to visualize what will be changed in the coming steps.

  1. Launch Hazard Services and open the Hydro Perspective
  2. Click on a river flood (FL-type) hazard in the Hazard Services console to launch its associated Hazard Information Dialogue (HID)
    1. You may need to generate a river flood hazard or switch to a time when one exists before proceeding. To create a river flood hazard:

      1. Open the Hydro Perspective

      2. Right click and hold on any river gage and select "Create Hazard" 

      3. A popup will appear for the Flood Recommender. Click "Run"

      4. The HID will appear, at which point you can change the "type" to any FL-related hazard. 

    2. Do not choose an ending/ed hazard, or the HID will have a different construction
  3. In the open HID, change the Type to FL.Y
  4. Observe the following default layout:
    1. There are two tabs in the "Details" section... one for "Point Details" and  one for "Impact Statement". We will add be adding a third tab for a new megawidget
    2. While there are already some fields about the river height and forecast, we will be adding a simple megawidget to manually indicate the trend in the river height 
  5. Press "Preview" to generate the FL.Y product
    1. Press "Continue" in the Product Staging Dialog
    2. In the Product Editor which loads, scroll down until you see the "Impacts Bullet" field, and notice that it is populated
  6. GOAL: In this jobsheet, we will make the inclusion of this Impacts Bullet conditional based on a new megawidget
    1. This will prevent the Impacts Bullet from being included in FL.Y products if the river height is "falling"
    2. In the product Editor, we can get a good sense for the "pieces" or, as they're more formally known, the Product Parts that build up a final product. Similar to its "Impacts Bullet" name in the editor window here, you'll see soon that the part which we need to manage is referenced behind the scenes as the "pointImpactsBullet"
  7. Press "Dismiss" to exit the Product Editor.
  8. Close the HID and continue on to making the actual changes.

 

PART 1: Create a Megawidget on the FL.Y HID

This step practices some of the megawidget setup which was introduced in other jobsheets, but it also, crucially, gives us a starting point for triggering some change to the product assembly later on. In this part, we create a radio button which is also embedded in a separate tab on the FL.Y HID.

  1. In the Localization Perspective, navigate to the Hazard Meta Data sub-directory under the Hazard Services folder.
  2. Double click on MetaData_FL_Y.py to show the available versions
  3. Create a user override version if none exists (user override is best practice for testing changes):
    1. If there is only a BASE version: right click on BASE and select Create Override File, then select User
    2. If there is another version (e.g. SITE) you wish to use: right click on that file and select Copy To, then select User
  4. If it's not already open, double click on the USER version of MetaData_FL_Y.py to open it for editing
  5. (No action in this step) Notice, if this is a new override file, that the Localization Perspective auto-populated the same class statement as the reference file as an important first step in your override. In this case, your new file should look like example below:
    class MetaData(CommonMetaData.MetaData):
        pass
    1. You can delete the "pass" line if you like, although it's not required as long as you proceed with adding code to the class. The "pass" line is simply a needed placeholder in new, empty class overrides, which does nothing.
  6. Open a reference copy of the existing MetaData_FL_Y.py file, which will be used for comparing to and copying existing code for overrides.
    1. The BASE file will usually be a good reference, but any pre-existing overrides should be used IF they already contain overrides for the "execute" or other methods below, which you'll want to maintain.
  7. In the reference version of MetaData_FL_Y that you opened, scroll slightly down find the "execute" method which begins with the line below
    def execute(self, hazardEvent=None, metaDict=None):
    1. INFO: With ALL metadata files, the "execute" method is the primary method of assembling the megawidgets which appear on the HID. Since we want to add a megawidget, we'll need to override this method to let it know about the new component.
  8. Once found, select the entire execute method by clicking and dragging from (and including) the "def execute(self, hazardEvent=None, metaDict=None):" line which begins the function, all the way to (and including) the last line of the function which has the closing } bracket after the return statement.
  9. Copy the selected contents by right-clicking and selecting "Copy" from the pop-up menu, or by hitting CTRL-C on your keyboard
  10. Switch back to the USER override you were working on
  11. Paste the execute method into your USER override on a blank line after the "class..." line, by right-clicking and selecting "Paste" from the pop-up menu, or by hitting CTRL-V on your keyboard
  12. In the USER copy of the execute method you just pasted in:
    1. Find the lines which contain the code below:
      pointDetails += [
                (... some other lines ...)
                ]
      pointDetails.extend(self.getRiseCrestFall())
      
      impacts = []
      if self.riverForecastPoint:
            impacts = [self.getCrestsOrImpacts("impacts")]
      else:
            impacts = [self.getMissingRFPLabel("impacts")]
      
    2. These two variables (pointDetails, and impacts) are creating list [ ]-bracketed groups of megawidgets, which you can see just a little further down appearing in the "pageFields" properties of the "TabbedComposite" fieldType. Let's take an opportunity to learn about this TabbedComposite megawidget by extending it for our new megawidget...
    3. Just below the lines identified, add a new line with the following code (this should be outside of the if/else block, with indentation at the same level as the "if" and "else"):
      otherInfo=[self.riverStatusCheck()]
      1. As always, make sure this line is properly indented 
      2. You might notice that self.riverStatusCheck() refers to a "riverStatusCheck" method that doesn't actually exist yet... we'll get to that shortly!
    4. A little further down in your USER copy of MetaData_FL_Y, find the lines of code below, and add the highlighted code to contribute another page to the existing "TabbedComposite" megawidget:
      {
      "pageName" : "Point Details",
      "pageFields" : pointDetails
      },
      {
       "pageName" : "Impact Statement",
       "pageFields" : impacts
      },
      {
       "pageName" : "Other Info",
       "pageFields" : otherInfo
      }
      
      1. If you need to fix the indentation so that these new lines line up with the previous ones, try selecting all of your new lines together, then pressing TAB (or SHIFT-TAB) to indent (or un-indent) them as a group
      2. NOTE: Notice that we're referencing our "otherInfo" variable from the previous step in assigning our pageFields here. That means that whatever megawidget(s) the "otherInfo" variable holds will be shown on this new tab.
    5. (No action in this step) Now, time to create the radio button megawidget, and we'll do so via a separate method as we've practiced before!
  13. Create a new method in your USER copy of Metadata_FL_Y by typing or copy-pasting the following code at the end of the file:
    def riverStatusCheck(self):
        return {}
    
    1. Make sure you've maintained proper indentation... the "def riverStatusCheck" line should be indented to the same level as your "def execute" line, AND the return statment should be indented one further than that. If you need to fix your indentation, you can select the whole method, then press TAB to indent everything further, or SHIFT-TAB to remove one level of indentation for the entire selection
  14. Type or copy-paste the highlighted code below in between the { } brackets you just created on the return line for your riverStatusCheck method, in the USER copy of your file
    {
        "fieldType": "RadioButtons",
        "fieldName": "riverStatusButtons",
        "label": "What is the status of the river?",
        "choices": ["Rising", "Static", "Falling"],
    }
    1. Again, clean up any indentation issues if applicable
  15. (No action in this step) It's very useful to pay attention to the "fieldName" for our new megawidget, specifically that it's "riverStatusButtons".
    1. This could be anything we like, but fieldName is the identification used throughout the code (even once we get to the product logic) for retrieving the value of megawidget, i.e. this little piece of MetaData from the event that it'll produce
  16. You're finished with the needed edits! Proofread for Python syntax mistakes in any areas you edited. Your override file should appear as follows: 
  17. When finished, save the file by hitting Ctrl+S, or selecting Save from the File menu
    1. If a "File Version Conflict" is encountered, click OK to accept merge
    2. In the two-panel "Merge" tab that opens, make no changes, once again save the file by hitting Ctrl+s, then close the "Merge" tab
    3. When a "File Changed" message appears,  click Yes.
    4. This behavior will only be encountered each time a new override file that did not exist before is created and saved
  18. Switch back to the Hydro perspective (or whichever perspective you were previously using) to once again view Hazard Services
  19. Click on your previous hazard to launch the HID (if it's not open), then force a refresh of our FL.Y metadata by switching to a BLANK hazard type, then switching back to FL.Y
  20. Observe that we now have a new "Other Info" tab, and if you click on it, that it contains the radio button that we configured!
  21. You can now close out of the open HID.

 

 

PART 2: Implementing Megawidget-based Logic on Section Parts Assembly

Our goal from the start has been to show or hide the product part known as the pointImpactsBullet, based on some logic from a custom megawidget. Now we get to modifying the actual method that contributes this part, which is the sectionPartsList_FFA_FLW_FLS_point method in HydroProductParts.py.

  1. In the Localization Perspective, navigate to the Utilities sub-directory under the Hazard Services folder.
  2. Double click on HydroProductParts.py to show the available versions
  3. Create a USER override version if none exists (user override is best practice for testing changes):
    1. If there is only a BASE version: right click on BASE and select Create Override File, then select User
    2. If there is another version (e.g. SITE) you wish to use: right click on that file and select Copy To, then select User
  4. Double click on the USER version of HydroProductParts.py to open it for editing
  5. (No action in this step) Notice, if this is a new override file, that the Localization Perspective auto-populated the same class statement as the reference file as an important first step in your override. In this case, your new file should look like example below:
    class HydroProductParts(object):
        pass
    1. As usual, feel free to either delete or keep the "pass" line
  6. Open a reference copy of the existing HydroProductParts.py file, which will be used for comparing to and copying existing code for overrides.
    1. The BASE file will usually be a good reference, but any pre-existing overrides should be used IF they already contain overrides for the "execute" or other methods below, which you'll want to maintain.
  7. In the reference version of HydroProductParts.py that you opened, scroll or search to find the definition of the "sectionPartsList_FFA_FLW_FLS_point" method
    1. INFO: This method manages, this time, the more granular "section-level" product parts for the FFA_FLW_FLS point products.
  8. Once found, select the entire sectionPartsList_FFA_FLW_FLS_point method by clicking and dragging from (and including) the "def sectionPartsList_FFA_FLW_FLS_point(self, sectionDict):" line which begins the function, all the way to (and including) the last line of the function where we see "return partsList".
  9. Copy the selected contents by right-clicking and selecting "Copy" from the pop-up menu, or by hitting CTRL-C on your keyboard
  10. Switch back to the USER override you were working on
  11. Paste the sectionPartsList_FFA_FLW_FLS_point method into your USER override on a blank line after the end of the segmentParts method from before, by right-clicking and selecting "Paste" from the pop-up menu, or by hitting CTRL-V on your keyboard.
    1. As always, clean up any indentation issues in this new addition to your USER HydroProductParts
  12. (No action in this step) Take a minute to review the function...
    1. As you might be able to guess, these partsList methods are responsible for assembling placeholders for the content that will populate the final product
    2. It's easy to see how the building blocks known as "product parts" are combined together in "partsList" variables, with various logic dictating the included parts
    3. Each method (like sectionPartsList_FFA_FLW_FLS_point) is not only focused on a specific "level" of the product (like section here), but on specific PILS (like FFA_FLW_FLS), allowing focused control of the necessary product parts
  13. Now find the following code within the method:
    if phensig in ['FL.Y']:
        partsList += [
            'observedStageBullet',
    #       'bankFullStageBullet',
            'actionStageBullet',
            'recentActivityBullet',
            'forecastStageBullet',
            'pointImpactsBullet',
        ]
    else:
    
    1. NOTE: Here we see specific parts being added (+=) to the running partsList, in particular when the phensig is FL.Y. One of these is the pointImpactsBullet, which we want to change to be added only under some conditions.
  14. Modify the same lines as above by making the highlighted changes shown here, so that these lines match the code below:
    if phensig in ['FL.Y']:
        partsList += [
            'observedStageBullet',
    #       'bankFullStageBullet',
            'actionStageBullet',
            'recentActivityBullet',
            'forecastStageBullet',
        ]
        if not self.skipImpactsByRiverStatus(sectionDict):
            partsList += [
                'pointImpactsBullet',
            ]
    else:
    1. NOTICE that we moved "pointImpactsBullet" so that it is NO LONGER in the original list, and now only shows up in the second "if" statement (the highlighted section). 
    2. Pay close attention to correct indentation here or the code may fail. If you're copy- pasting, it might be helpful to select the new code and use TAB and SHIFT-TAB to move things in bulk
  15. (No action for this step) What exactly changed above??
    1. 'pointImpactsBullet' is now separated out from the rest of the parts before, and put in its own "partsList += [ ]" wrapper to independently add it to the partsList
    2. An if-statement was added before this, governing whether or not 'pointImpactsBullet' is added to partsList
    3. The newly added if-statement depends on the value of some function "skipImpactsByRiverStatus" which we have not yet created, but which (if it yields True) is designed to cause pointImpactsBullet to NOT be added to the partsList
  16. Finally, create a new method in your USER copy of HydroProductParts.py by typing or copy-pasting the following code at the end of the file:
    def skipImpactsByRiverStatus(self,sectionDict):
        # Get hazard event dictionary out from sectionDict
        eventDict=sectionDict.get('eventDicts')[0] # technically grabs only first event
    
        # From event dictionary, get value of riverStatusButtons megawidget
        riverStatus = eventDict.get("riverStatusButtons")
    
        # Determine whether to skipImpacts based on status
        skipImpacts=False
        if 'Falling' in riverStatus:
            skipImpacts=True
    
        # Return True or False value whether to skipImpacts
        return skipImpacts
    
    1. Make sure the indentation of the the "def skipImpactsByRiverStatus" line matches up with the other "def" lines in this file, AND that the body of this function is indented one step further, as shown. In other words, this method (SkipImpactsByRiverStatus) needs to be intended one step further than the "class" definition at the beginning of the file. 
  17. (No action for this step) What does this function do??
    1. The main magic in this function is in extracting the "eventDict", which is basically the Python dictionary of metadata corresponding to the current event, from the sectionDict
    2. From this eventDict, it's easy to directly retrieve megawidget values using the fieldName identifiers of each (in this case, the "riverStatusButtons")
    3. This function then checks whether the riverStatusButton value contained "Falling", in which case it sets the final returned "skipImpacts" value to True
    4. When this function is called by the sectionPartsList code that checks whether or not to add the "pointImpactsBullet", the True/False value returned here will affect that conditional logic
  18. You're finished with the needed edits! Proofread for Python syntax mistakes in any areas you edited.
  19. When finished, save the file by hitting Ctrl+S, or selecting Save from the File menu
  20. Switch back to the Hydro perspective (or whichever perspective you were previously using) to once again view Hazard Services
  21. Click on your previous hazard to launch the HID (if it's not open), then force a refresh of our FL.Y metadata by switching to a BLANK hazard type, then switching back to FL.Y
  22. On our own "Other Info" tab, select the radio button for either "Rising" or "Static", then press "Preview" to generate a product
    1. Press "Continue" in the Product Staging Dialogue
    2. In the Product Editor which loads, scroll down until you see the "Impacts Bullet" field, and notice that there's still a field for it, which may also be populated
    3. Press "Dismiss"
  23. In the same "Other Info" tab of the open FL.Y HID, select the radio button for "Falling" , then press "Preview" to generate a product
    1. Press "Continue" in the Product Staging Dialogue
    2. In the Product Editor which loads, scroll down to where "Impacts Bullet" used to be, but notice that this time it is missing! This is the due to our selection on the megawidget.