diff --git a/docs/classes/docacon-2020/customizing-appearance.md b/docs/classes/docacon-2020/customizing-appearance.md index db06b17f7..aa5849c5c 100644 --- a/docs/classes/docacon-2020/customizing-appearance.md +++ b/docs/classes/docacon-2020/customizing-appearance.md @@ -66,10 +66,7 @@ docassemble documentation includes some One of the cool features of docassemble is the ability to natively access a massive set of free icons called [Font-Awesome](https://fontawesome.com/icons?d=gallery&m=free). -First, you need to tell docassemble to [turn on this -option](https://docassemble.org/docs/config.html#default%20icons). - -Then, to insert an icon into your interview, just reference it using the shorthand +To insert an icon into your interview, just reference it using the shorthand `:icon-name:` syntax. Try running the interview below in your docassemble playground. diff --git a/docs/classes/docacon-2020/logic.md b/docs/classes/docacon-2020/logic.md index 42e776046..2f9f8f718 100644 --- a/docs/classes/docacon-2020/logic.md +++ b/docs/classes/docacon-2020/logic.md @@ -34,6 +34,13 @@ fields: - Name: user_name ``` +:::info Note +Both [YAML](yaml.md) and Python are picky about indentation. If you run into an +error, check to make sure each line is indented the same way as the example +above. +::: + + In the example above, we introduced the use of the `|` line continuation marker, or vertical pipe. We always use this when the text that follows could go on multiple lines, and to handle special characters (like accented letters). You @@ -64,9 +71,19 @@ Notice that inside a `question` block, the line with the `if` statement starts with `%`. We also need to use an `endif` statement, instead of using indentation to show the beginning and end of the `if` statement. -> **Note**: [YAML](yaml.md) is picky about indentation. If you run into an -> error, check to make sure each line is indented the same way as the example -> above. +:::info About the `%` symbol in Mako +The ``%` symbol has a special meaning in [Mako](../../mako.md). It +lets you use Python syntax at the start and end of a block, usually +to control conditional text. It is very handy when you have a large block +of text that you want to show or hide, especially if the block has formatting +or also contains variables. + +In comparison, the `${ }` we've already been using lets us +put much shorter bits of Python code in the middle of a block of text. + +Inside the text of a question or subquestion, use the `%` symbol when the Python +code or conditional logic you are using is meant to control the next line or lines of content. +::: Python allows us to go a little further than using just if-then. We can introduce multiple branches using `elif`: @@ -124,14 +141,127 @@ fields: - Name: user_name ``` +#### What happened + +The "Dammit Jim!" version of the secret message gets displayed because it is the first +version of the test that matched. One the Python interpreter hits the first matching line, +it stops checking to see if any other test matches. + +### An advanced exploration + +In the logic exercise above, notice that the comparison between the user name +and the name we want to check for is **case sensitive**. That means if we check +for the name "Scotty" and the user types "scotty" we won't get a match. + +We can solve it by adding more checks, like this: + +```yaml +code: | + if user_name == "Scotty" or user_name == "scotty": + secret_message = "Beam Me Up, Scotty" +``` + +But what if we want to match "sCotty" and "SCotty", in case the user accidentally +holds the shift key at the wrong time? The one-by-one matching approach gets out +of hand pretty quickly. + +Note that what we explore below is an **advanced** concept. You can get pretty far with +the building blocks we already explored. The advantage of Docassemble is that **we can** +use these advanced solutions. So let's see what that might look like. + +#### A universal approach + +When faced with this problem, someone with a computer science mindset will +think about how to make all of the different possible capitalizations of +the names equivalent to each other. + +Luckily, there is a built-in way to do this in Python. It involves a few new concepts: + +1. We can transform the value of variables in Python by using `functions` and `methods`. +1. Python has a **lot** of built-in functions and methods. We usually don't have to create + a new one for basic tasks. +1. We can transform a copy of a value without destroying the original. +1. We use `functions` by wrapping them around a variable, like `function_name(variable_name)`. + Methods are similar, but we use them by adding a `.` to the end of the variable name, instead, + like `variable.method_name()`. The reason for the difference isn't critical right now. + +Pause for a minute and reflect on this question: + +> What is the "universal" version of the name "Scotty"? + +You likely came up with one of three answers: + +* Scotty +* scotty +* SCOTTY + +Suppose we change the `user_name` variable's capitalization to one of these three forms, regardless of the +original capitalization of the variable. Then we can compare it to the exact string we know it should +match, "Scotty", "scotty" or "SCOTTY". + +How do we change the capitalization of the `user_name` variable? + +Python has handy built-in `methods` that let us transform text. Here are two likely +candidates: + +* [.lower()](https://www.w3schools.com/python/ref_string_lower.asp), which makes text all lowercase. +* [.upper()](https://www.w3schools.com/python/ref_string_upper.asp), which makes text all uppercase. + +We can eliminate the idea of comparing it to the version with an initial capital letter +("Scotty"). While we could achieve it, notice that it would just add an extra step. We'd need +to make everything lowercase first before changing the capitalization of the first letter. + +Here's how we use the `.lower()` and `.upper()` methods: + +`user_name.lower()` applies the `.lower()` method to the text and `returns` a lowercase +version of the text. It doesn't change the value that is stored in `user_name` permanently. + +We can change our comparison to use this `method`, like this: + +```yaml +code: | + if user_name.lower() == "scotty": + secret_message = "Beam Me Up, Scotty" +``` + +We would follow the same pattern to use the `.upper()` method: + +```yaml +code: | + if user_name.upper() == "SCOTTY": + secret_message = "Beam Me Up, Scotty" +``` + +Most programmers will choose the lowercase method first. But it's just habit. Either +method would work fine. + +Suppose we really wanted to try comparing to "Scotty", despite the extra steps it requires. +We can do that by chaining the `.lower()` method with the `.capitalize()` method: + +```yaml +code: | + if user_name.lower().capitalize() == "Scotty": + secret_message = "Beam Me Up, Scotty" +``` + +#### What to take away from this advanced example + +* Logic in Docassemble can take advantage of the full Python programming language +* Python is full of many helpful building blocks. You can look outside of the + Docassemble documentation to find them. + +We've introduced a lot of new concepts here. But you will be able to do a lot +of the Python code in your Docassemble interview by copying and pasting and making +small changes to other working examples. Don't worry about understanding the full +power of the Python language or memorizing the whole library of available functions +and methods. ## Your assignment 1. Modify the Logic exercise so that a new secret message is displayed when a name of your choice is displayed. -2. Stretch goal: make all of the secret messages work regardless of how the user - capitalizes their name. Hint: using the - [`.lower()`](https://www.w3schools.com/python/ref_string_lower.asp) method of a - Python string should help you out. +2. Make all of the secret messages work regardless of how the user + capitalizes their name. Apply the example that we used to check for upper and lowercase + versions of `scotty` to the other conditions. Quinten Steenhuis, June 2020 \ No newline at end of file diff --git a/docs/classes/docacon-2020/question-types.md b/docs/classes/docacon-2020/question-types.md index 89ada9a33..ddad601c1 100644 --- a/docs/classes/docacon-2020/question-types.md +++ b/docs/classes/docacon-2020/question-types.md @@ -109,8 +109,93 @@ fields: - Purple ``` +### Showing the user more than one screen + +If you would like the user to see more than one screen in your interview, +you need to: + +1. Make sure the screen is itself `mandatory` or required by a `mandatory` block +1. Make sure that the user can **reach** the screen (that is, it's not blocked by another screen) + +#### It's not enough to add another `mandatory` block +In the [Hello, World](./hello-world.md) exercise, we used a single `mandatory: True` +on the final screen of the interview. If we want to display another screen after the +`mandatory: True` screen, our first guess might be to add another block with `mandatory: True`, like this: + +```yaml +mandatory: True +question: | + Hello, World! +--- +mandatory: True +question: | + It's a beautiful day today! +``` + +The problem is that the "Hello, World!" screen doesn't have a `continue` button. +Docassemble only displays a `continue` button if the question sets the value of +a variable. + +#### Adding an invisible field to get Docassemble to display a `continue` button + +If we want to display a `continue` button to a screen that doesn't ask any fields, +we can add the `continue button field` modifier, like this: + +```yaml +mandatory: True +question: | + Hello, World! +continue button field: hello_screen_1 +--- +mandatory: True +question: | + It's a beautiful day today! +``` + +The first "Hello, World!" screen will set the value of `hello_screen_1` to `True` +when the `continue` button is pressed. + +#### Using an "interview order" `code` block instead + +While using more than one `mandatory` block is fine for a short interview, it can get +confusing fast in a longer interview. Instead, we recommend using a single +`mandatory` block in each interview. To do this, we can use a mandatory `code` block +that mentions a variable on each screen we want to display. + +We will use the name of: +* A `field` if the question asks for a field. +* A `continue button field` if the question is only informational and doesn't ask a question +* An `event` for an ending screen that doesn't have a `continue` button + +```yaml +mandatory: True +comment: | + This is an interview order block +code: | + user_name + hello_screen_1 + ending_screen +--- +question: | + What is your name? +fields: + - Your name: user_name +--- +question: | + Hello, ${ user_name }! +continue button field: hello_screen_1 +--- +event: ending_screen +question: | + It's a beautiful day today! +``` + +#### Learn more + +[Read more about controlling interview order](../../practical-guide-docassemble/controlling-interview-order.md). + ## Your assignment -1. Modify your Hello, World exercise so that it uses three different question types. +1. Modify your Hello, World exercise so that it uses three different question types. You can use the `continue button field` modifier if you want to display the "Hello, World" question first. Sam Harden, June 2020 \ No newline at end of file