If anyone ever finds themselves with a painful workflow, talk to me and I
can suggest (or code) alternatives. In the situation described (running
unit tests on a Python module on a machine separate from the docassemble
server), I can think of many possible workflows that do not involve copying
files or working with zip files. There are features in the Playground and
elsewhere that facilitate a variety of workflows, such as pushing
to/pulling from GitHub, and Google Drive integration. In the documentation
I have a page that describes a variety of workflows
https://docassemble.org/docs/development.html, and there are many
possible workflows that I have not yet written up.
As for Quinten's question about whether to use custom classes, I think
declaring custom object types is an excellent idea, even if the class is
nothing more than:
class InterrogatoryList(DAList):
pass
Because when your interview code says, e.g.,
objects:
- eviction_rogs: InterrogatoryList
then this *means something*. You can write documentation about what the
attributes of an InterrogatoryList are and what they mean and how they
should be used. And the module that defines InterrogatoryList can be used
in different interviews in different packages. At a national level, the
community can collaborate around best practices for managing lists of
interrogatories. So InterrogatoryList could be an incredibly important
object even if its only content is pass. And maybe its content wouldn't
stay empty for long. A developer in a different jurisdiction could do a
pull request and add some methods to it. A useful method might be __str__(),
so that when you refer to ${ eviction_rogs } in a Mako template, a list of
interrogatories appears in a standardized, attractive format. Or you could
have methods that do things differently depending on the jurisdiction and
the type of case. All kinds of complicated things could be done with lists
of interrogatories, and the interview authors who use the InterrogatoryList
object would never need to look at the source code of these complex
functions; they would just read the documentation and invoke the methods.
This is how computer programmers can make "coding the law" accessible to a
wider audience of lawyers -- by hiding complexity behind simple APIs. But
if we always work with low-level objects like DAList, we aren't doing
anything to hide complexity behind convenient interfaces, and we aren't
working together to make "coding the law" more accessible.
If you want an object to have default attributes, you can do something like
this in your interview:
---
generic object: Interrogatory
code:
x.priority = 0
---
But what if you develop six other interviews that use the Interrogatory
object; should you have to copy over this code block into each interview?
If it is universally useful to have that default attribute, why not put the
code in the module, and make all your interview files shorter?
class Interrogatory(Discovery):
def init(self, *pargs, **kwargs):
self.priority = 0
return super(Interrogatory, self).init(*pargs, **kwargs)
For me, the main logistical downside of putting code in modules is that you
have to wait for the web server to restart every time you change a module
file. This is because the code of each loaded module becomes embedded in
each of the threads of the web server. While that is efficient for saving
CPU cycles, it does mean that the web server needs to restart if the
modules change.
As for the question of how to bring the content of a Google Sheet into an
interview, I have an objects_from_file() command that could be used, but I
think the most convenient thing might be for me to write a convenience
function that works as a front end to csv.DictReader()
https://docs.python.org/2/library/csv.html#csv.DictReader so that you
could put CSV files in the Sources folder and then write code to read the
files as a list of dicts, and construct the objects with code. In many
cases, data are is easier to maintain as tables in a spreadsheet than as
"structured data." (On a development server, you could write code to talk
to Google Sheets directly, but I don't think that is a good idea in
production...)
I would recommend giving each claim and each discovery request a short code
name, and then these code names could be the keys of dicts that map key
names to actual objects.
As for list comprehensions
http://www.secnetix.de/olli/Python/list_comprehensions.hawk, I like
writing list comprehensions too because I find they make it easier for me
to read my own code, but often when I try to read other people's list
comprehensions, they just make my head hurt. If the audience for
readability is other people, a more readable alternative may be to
substitute a variable in place of the list comprehension and then write a
short code block that defines that variable (as a list() or whatever) and
uses a for loop to build it. The variable name itself is helpful because
it conveys to the reader what the variable means, whereas if all you can
see is the inline list comprehension, you have to figure out the meaning
from the code. Also, one of my goals when I write docassemble interviews
is to try to make sure that it is readable by people who may only have a
rudimentary knowledge of Python. I might be wrong, but I get the
impression that list comprehensions are a relatively advanced technique.
On Fri, Sep 22, 2017 at 1:09 PM, Steenhuis, Quinten
Thanks Nate, that’s exactly the kind of experience I was looking for.
Do you have any examples of the list comprehensions that you use? A pointer to the file on GitHub would work. That sounds like an elegant functional way to do it.
*From:* Nate Vogel [mailto:nathan.c.vogel@gmail.com] *Sent:* Friday, September 22, 2017 11:56 AM *To:* Steenhuis, Quinten
; docassemble@python.org *Subject:* Re: [Docassemble] When to define a custom class Hi,
I don't have a very technical answer, but my experience with this question might be helpful. I did make a couple custom classes for an interview I worked on for a while, and ultimately I realized it was more trouble than it was worth.
The custom classes became impossible to maintain. I couldn't unit test them in the Playground. When I was developing the interview locally and re-installing it after every change, it didn't make sense to clutter up my interview package with unit tests that couldn't run in docassemble. So I had my classes in a separate directory, and then I needed to copy them (yeesh) from where I was testing them into my docassemble package. And then I needed compress and upload the package, which is time consuming too. It was a very painful workflow.
Eventually I gave up and figured out how to just use DAObjects and DAListObjects to define my objects with whatever properties I wanted them to have, and all the logic for manipulating them stayed in the interview. I use a lot of list comprehensions now. And I actually think the code is a lot easier to read.
So the lesson I drew was that I'll try very hard not to attempt custom classes for future docassemble projects.
On Fri, Sep 22, 2017 at 11:22 AM Steenhuis, Quinten
wrote: I’m thinking about defining a few classes for the interview I’m working on.
1) DiscoveryRequest
2) Interrogatory – inherits from Discovery
3) InterrogatoryList?
4) DocumentRequest – inherits from Discovery
5) DocumentRequestList?
The objects should have a few properties: “Checked” to indicate if it should be shown in the template, “Name”, and “Description” to be used in the interview when showing a summary of the discovery that the system recommends the user to select. For Interrogatorys, they should each have a Priority. There should be a method for the InterrogatoryList class that displays only the 30 highest priority interrogatories to comply with discovery limits.
In the interview, I’d like to pre-define the 85 discovery requests with the name and description (I think as a source file?). I’ll also have a list that links each discovery request to a true/false interview variable. There can be a many-to-many relationship between the claims and the discovery request that’s relevant to the claim. I’m building the table with two columns right now in Google Sheets, but my plan was to convert it to a format usable in DocAssemble.
The example on the website for a custom object doesn’t define any properties, only the methods. I looked at the classes you’ve already defined in /docassemble_base/docassemble/base/util.py and it looks like again, mostly it’s just the methods you’ve defined. I only have a thought for one method so far. Is there any real benefit for me to define a new class for this example use? Would it make more sense to just use the built-in DAObject and specify the properties as I go along?
Quinten Steenhuis
Senior Attorney
Network Administrator
Greater Boston Legal Services
197 Friend St
Boston, MA 02114
(617) 603-1553
F: (617) 371-1222
Qsteenhuis@GBLS.org
_______________________________________________ Docassemble mailing list docassemble@python.org https://mail.python.org/mm3/mailman3/lists/docassemble.python.org/
_______________________________________________ Docassemble mailing list docassemble@python.org https://mail.python.org/mm3/mailman3/lists/docassemble.python.org/