My new branching validation seems to be working with the test cases I’ve given it. Everything seems to be working properly, so this week I started working on refactoring the hierarchy of the Python folder and organizing my files. After fiddling around with __init__.py files, I created a new SurveyMan package in the python folder with subpackages survey, examples, and test. All of the files previously contained in the survey directory in the python folder are now in one of the subpackages. The survey package contains the main components for building the survey (the survey_representation and survey_exception modules).
Emma finished writing the JSON validator and a new schema, so I will be testing my current JSON against that this week. She suggested I alter the SurveyMan __init__.py file to include code to copy the contents of the resources folder for testing against the schema. I’m working on writing a script to call the jsonize() method of some of my (correct) survey programs and validate the output against the new schema. Once we figure out what to do about the JSON, assuming no other alterations are needed, I assume I’ll start to think about how to make the SurveyMan package installable.
I added a few updates and refactorings to my design document, as well as a better description of what motivated the design and why the package is necessary. I’ll try to put up a copy later.
This week, I got a lot of miscellaneous things done. I was stuck on some other stuff for a while, so I added docstrings to each object and function. Emma and Emery decided that for a final paper, I should be turning in some sort of 3 page design document, so I began working on that as well. Right now, I just have a description of each of the objects, as well as a class diagram for each with some arrows that are supposed to show which classes contain instances of the others. I’m not sure whether I need to include my SurveyExceptions class or my idGenerator class; I haven’t added anything for them yet. In addition, I’m not sure how high level or low level to make my descriptions yet; I can’t really add much more detail without making it a lot longer than 3 pages.
Diagram from my draft design document
Regarding the confusion with the branch-one, branch-all, branch-none checks last week, Emma directed me to her blog post regarding the branching paradigms for blocks and subblocks. I considered moving the branch paradigm checks out of the Block object, but in the end, I kept it there and rewrote the check so that it recursively iterates down to the lowest level of blocks contained in a Block, determines how many branch questions they have, and returns “branch-one”, “branch-all”, “branch-none”, or “bad-branch”. The branch paradigms of each blocks’ subblocks are then checked against how many branch questions the block contains at each level, to ensure that the proper combinations of “branch-one”, “branch-all”, etc. are maintained. This seems to work based on the few tests I’ve given it, but I need to test it a bit further just to be sure. It’s probably not the most elegant solution, but given the design I have right now I can’t think of a less complicated one.
Assuming my branch validation checks are correct, the only issue left to address is the JSON. Emma is working on the parser, so hopefully we can figure out what the problems are soon, and then my modules should (hopefully) be completely functional.
During the past few days, I accomplished a lot in terms of testing/debugging my branching validation checks. I created a Constraint test suite which I am using to test that surveys with broken branching throw the appropriate exceptions when their jsonize() functions are called. I created some additional example surveys which branch backward and do not follow the branch-one, branch-all, or branch-none policy. Testing with these surveys led me to discover a ton of bugs/typos within my validation check functions, as well as in other functions. I haven’t completely finished testing the validation methods, but what I’ve tested so far (backwards branching and invalid branch numbers) seems to work, at least for top level blocks. However, I just realized that my check for number of branch questions per block doesn’t check subblocks. I’m not sure how this should be handled for questions contained in subblocks (for example, if block 1 contains a branch question and block 1.1 also contains a branch question, does this count as 2 branch questions in block 1?) Depending on how to interpret this, I may need to rethink where/how I check the number of branch questions in each block. Right now, I have a Block method which iterates over the questions contained within that block and checks if they are branch questions, which is called for each block in the survey from the Survey object. I’m not sure if this is the best way to set things up.
Additionally, I removed all of the functions deemed unnecessary at the last meeting, which makes the module a lot shorter and cleaner looking. Once Emma finishes the JSON parser, I can hopefully determine whether my JSON is valid or how I need to change it; it still isn’t working with the HTML tests, even though it looks valid compared to the schema.
I’ve run into some design flaws regarding how components are identified and removed from the survey. Emma and I agreed that the current component ids should remain hidden from the user and be used only for internal representation, but the issue of how to identify components when trying to remove or find them in the survey remains. Over the weekend, I changed all of the removal methods for each component so that they took an object pointer as an argument, rather than the id of the component being removed, under the assumption that this would be more intuitive than keeping track of arbitrary component ids. However, this poses an issue if the user only has a pointer to the top level survey object (for example, in my test suites, the example surveys are created externally and returned as a single survey object; the test module has no pointers to their inner components). This method also poses an issue if, for example, components are created and added to the survey using list comprehensions, and the user does not retain a pointer to each individual component.
After discussing the above issues with Emery and Emma, we have determined that the functionality for removing questions is unnecessary, and can be removed. My initial assumption of the library’s usage was that a user may want to create several versions of a survey, in which certain questions, blocks, or other components may be added, removed, or changed. We decided that this is no longer the case, and that the library does not need to allow for the alteration of surveys. It exists for the user to program a single survey and obtain the corresponding JSON representation of that survey; any different surveys can be programmed separately, and there is no need to be able to load and alter an existing survey. Given these assumptions, I will focus my testing on everything else and ignore the removal methods (I’ll probably delete them eventually, as long as I’m sure they’re no longer necessary).
In the last couple of days, I started working on testing my blocking implementation more thoroughly by writing a testing module called BlockTests. I decided to just use the unittest module rather than py.test (which Presley suggested), since it seemed easy enough to use and is already built in. My test module creates an instance of each of my current sample surveys (I only have 3 right now, one of which is just a dummy survey with no meaningful content), then runs a unit test called TestBlockNumber which recursively counts the number of blocks and subblocks in the surveys, asserting that there are as many as there should be. To aid with this, I created a new function in the Block class to return a list of the block’s subblocks, if it contains any, and corrected some small errors I noticed in the class.
My next objective was to test that the addition and removal of blocks using each of the available functions worked properly, but I am currently rethinking some of these functions and the structure of the survey. Currently, the user can remove a block either by its index in the survey (if it is a top level block) or by its block id. Since the block ids generated by my representation are hard to keep track of and don’t really convey the survey structure at all, I thought it would be easier to just pass the reference to the block object as an argument, rather than the block’s id. Alternatively, Emma suggested that a block could have a reference to its parent (in the case of a top-level block, the survey, or for a subblock, its parent block), and that a remove function could be defined in the block itself to remove itself from its parent’s block or content list. This seems like the best course of action in terms of simplicity for the user, but I’m going to need to rewrite /rethink the way I currently have things set up.
Once I refactor and verify that my blocking is working correctly, (as well as the other components of my survey) I will probably start addressing branching validation more thoroughly. In addition, in previous meetings, we discussed implementing exchangeable blocks in such a way that the underlying JSON representation is not altered (e.g. no new columns are needed). However, this is not a priority right now, at least not until I verify that everything else is working. Another task that would be useful to address at some point is to create doc strings for each of the objects/methods, but obviously having the survey representation implemented and tested is more pressing right now.