diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index ac1e31e7..00000000 --- a/.coveragerc +++ /dev/null @@ -1,8 +0,0 @@ -[run] -omit = - */site-packages/* - */migrations/* - */survey/tests/* - */survey/features/* - */survey/fixtures/* - */ussd* diff --git a/.gitignore b/.gitignore index 9b5f184f..739ca77f 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,4 @@ tags submissions/* answerFiles/* survey/static/admin/* + diff --git a/.travis.yml b/.travis.yml index 2d29de94..564ed30d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,8 @@ +branches: + only: + - master + - uSurvey + language: python python: diff --git a/config/database.yml b/config/database.yml deleted file mode 100644 index 28e2dfe5..00000000 --- a/config/database.yml +++ /dev/null @@ -1,7 +0,0 @@ -test: - adapter: postgresql - encoding: utf8 - database: app_test - username: go - password: go - host: localhost \ No newline at end of file diff --git a/docs/Add_Groups.png b/docs/Add_Groups.png new file mode 100644 index 00000000..1f8f2577 Binary files /dev/null and b/docs/Add_Groups.png differ diff --git a/docs/Add_Variable.png b/docs/Add_Variable.png new file mode 100644 index 00000000..e0eb8f3b Binary files /dev/null and b/docs/Add_Variable.png differ diff --git a/docs/Batch1.png b/docs/Batch1.png new file mode 100644 index 00000000..7ca86967 Binary files /dev/null and b/docs/Batch1.png differ diff --git a/docs/Batch10.png b/docs/Batch10.png new file mode 100644 index 00000000..befef0cf Binary files /dev/null and b/docs/Batch10.png differ diff --git a/docs/Batch11.png b/docs/Batch11.png new file mode 100644 index 00000000..8896ee68 Binary files /dev/null and b/docs/Batch11.png differ diff --git a/docs/Batch12.png b/docs/Batch12.png new file mode 100644 index 00000000..e744d74a Binary files /dev/null and b/docs/Batch12.png differ diff --git a/docs/Batch13.png b/docs/Batch13.png new file mode 100644 index 00000000..001d0acf Binary files /dev/null and b/docs/Batch13.png differ diff --git a/docs/Batch14.png b/docs/Batch14.png new file mode 100644 index 00000000..dcf296fe Binary files /dev/null and b/docs/Batch14.png differ diff --git a/docs/Batch15.png b/docs/Batch15.png new file mode 100644 index 00000000..483b154d Binary files /dev/null and b/docs/Batch15.png differ diff --git a/docs/Batch16.png b/docs/Batch16.png new file mode 100644 index 00000000..5061ce7a Binary files /dev/null and b/docs/Batch16.png differ diff --git a/docs/Batch17.png b/docs/Batch17.png new file mode 100644 index 00000000..f2642729 Binary files /dev/null and b/docs/Batch17.png differ diff --git a/docs/Batch18.png b/docs/Batch18.png new file mode 100644 index 00000000..d241d35e Binary files /dev/null and b/docs/Batch18.png differ diff --git a/docs/Batch2.png b/docs/Batch2.png new file mode 100644 index 00000000..619e9dd3 Binary files /dev/null and b/docs/Batch2.png differ diff --git a/docs/Batch3.png b/docs/Batch3.png new file mode 100644 index 00000000..7bc73bf3 Binary files /dev/null and b/docs/Batch3.png differ diff --git a/docs/Batch4.png b/docs/Batch4.png new file mode 100644 index 00000000..1238504f Binary files /dev/null and b/docs/Batch4.png differ diff --git a/docs/Batch5.png b/docs/Batch5.png new file mode 100644 index 00000000..b40b8557 Binary files /dev/null and b/docs/Batch5.png differ diff --git a/docs/Batch6.png b/docs/Batch6.png new file mode 100644 index 00000000..3b059d37 Binary files /dev/null and b/docs/Batch6.png differ diff --git a/docs/Batch7.png b/docs/Batch7.png new file mode 100644 index 00000000..648224d2 Binary files /dev/null and b/docs/Batch7.png differ diff --git a/docs/Batch8.png b/docs/Batch8.png new file mode 100644 index 00000000..22e39fa2 Binary files /dev/null and b/docs/Batch8.png differ diff --git a/docs/Batch9.png b/docs/Batch9.png new file mode 100644 index 00000000..040cbf85 Binary files /dev/null and b/docs/Batch9.png differ diff --git a/docs/Edit_group.png b/docs/Edit_group.png new file mode 100644 index 00000000..59d04d74 Binary files /dev/null and b/docs/Edit_group.png differ diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md new file mode 100644 index 00000000..efe24890 --- /dev/null +++ b/docs/GettingStarted.md @@ -0,0 +1,69 @@ + +###Getting Started + +**The General flow for the uSurvey is as follows**: + +* Admin creates uSurvey Users and assigns them Roles +* User ‘Data Researcher’ defines Modules, Groups, Listing, Surveys, Batches and creates questionnaires for Listing & Batches in application +* Data Researcher also defines ‘Interviewers’ and assigns them to an Enumeration area to conduct a Listing or Survey +* In the field, Interviewer conducts the actual survey in the designated Enumeration area +* Data collection is done using a hand-held mobile device like Android Smart Phone or Featured Phone via ODK and USSD channels respectively. +* Captured data is sent to uSurvey portal +* Data collected by Interviewers is then viewed on the uSurvey portal for reporting and analysis + +**A Quick step by step uSurvey process flow** + +**Step -1** + +Create [New Module](./Modules.md) or skip this step, if you want to use existing Modules + +**Step -2** + +Create [New Group](./Groups.md) or skip this step, if you want to use existing Groups + +**Step -3** + +Create [New Listing](./Listing.md) or skip this step, if you want to use existing Listing or you can Clone existing Listing and rename it. Here we have to observe two things; + +1. Use existing Listing: This option benefits by providing data collected earlier
+2. Clone existing Listing: Duplicates Listing questions only
+ + 3.0. Once Listing is created, next
+ 3.1. Create Questions in Listing
+ 3.2. If necessary,
+ 3.3. Create Looping in questions and
+ 3.4. Add Logic to questions.
+ +**Step -4** + +Create [New Survey](./Survey.md) or you can Clone existing Survey and rename it,
+**Clone existing Survey**: This option duplicates the survey along with the Listing data, and ready to use. + +4.0. Once a new Survey is created, next
+4.1. Create Batch or Batches
+4.3. then Create Questions in Batch
+ If necessary,
+4.4. Create Looping in questions and
+4.5. Add Logic to questions.
+4.6. Finally, don’t forget to change the status to Open, via Action Item ‘Open/Close’ for Batch + +**Step -5** + +Enroll [an Interviewer](./Interviewer.md)
+To conduct a Survey in the field, define an Interviewer as follows:
+5.0. Provide basic Interviewer details,
+5.1. Assign a Survey,
+5.2. Allocate Enumeration Areas,
+5.3. Create ODK Access ID,
+5.4. Provide Mobile Number to access via USSD channel
+5.5. Finally, finish enrollment
+ +**Step -6** + +**Conduct Survey**: +Data collection and submission is done in two ways: + +1. Android channel - [Using uSurvey App](./ODK_App.md) +2. USSD channel - [Featured Phone](./ussd-integration.md) + +At any point on the uSurvey portal, use breadcrumbs on top of each page for easy navigation. diff --git a/docs/Group1.png b/docs/Group1.png new file mode 100644 index 00000000..631478d8 Binary files /dev/null and b/docs/Group1.png differ diff --git a/docs/Group10.png b/docs/Group10.png new file mode 100644 index 00000000..485e9349 Binary files /dev/null and b/docs/Group10.png differ diff --git a/docs/Group2.png b/docs/Group2.png new file mode 100644 index 00000000..f754cce5 Binary files /dev/null and b/docs/Group2.png differ diff --git a/docs/Group2_1.png b/docs/Group2_1.png new file mode 100644 index 00000000..aa022bdb Binary files /dev/null and b/docs/Group2_1.png differ diff --git a/docs/Group2_2.png b/docs/Group2_2.png new file mode 100644 index 00000000..183f2676 Binary files /dev/null and b/docs/Group2_2.png differ diff --git a/docs/Group3.png b/docs/Group3.png new file mode 100644 index 00000000..3dbae42f Binary files /dev/null and b/docs/Group3.png differ diff --git a/docs/Group4.png b/docs/Group4.png new file mode 100644 index 00000000..39aa11e6 Binary files /dev/null and b/docs/Group4.png differ diff --git a/docs/Group5.png b/docs/Group5.png new file mode 100644 index 00000000..b1830c57 Binary files /dev/null and b/docs/Group5.png differ diff --git a/docs/Group6.png b/docs/Group6.png new file mode 100644 index 00000000..4c10f21f Binary files /dev/null and b/docs/Group6.png differ diff --git a/docs/Group7.png b/docs/Group7.png new file mode 100644 index 00000000..ffde7930 Binary files /dev/null and b/docs/Group7.png differ diff --git a/docs/Group8.png b/docs/Group8.png new file mode 100644 index 00000000..7032fddb Binary files /dev/null and b/docs/Group8.png differ diff --git a/docs/Group9.png b/docs/Group9.png new file mode 100644 index 00000000..034a0834 Binary files /dev/null and b/docs/Group9.png differ diff --git a/docs/Groups.md b/docs/Groups.md new file mode 100644 index 00000000..adee52b1 --- /dev/null +++ b/docs/Groups.md @@ -0,0 +1,84 @@ + +###Groups + +Groups are Survey dependent classification of people/respondents based on their Age, Gender and combination of both into one or more survey- respondents categories called Groups. +Population has to be segment into Groups / multiple groups and our system allows us to create as many as needed. + +From this above Groups page, Groups are managed, i.e. to Create a New Group, Edit existing Group, Delete Groups and Manage Parameters + +###Create a New Group +Groups are available from main menu under, Design >> Groups + +![Group](./Group1.png) + +To create a new Group, click on ‘Add Group’ button at top right of the Groups page, that opens a form to create a new Group. + +![Group](./Group2_1.png) + +###Elements of Group + +Name: Is Group Name, which is a unique identity to a Group and is a mandatory field. + +Description: A short description about Group + +Parameter: Need to select a parameter, based on which Group is created. Like Age, Gender + +Operator: Also need to select an Operator based on the Parameter value, like value starts with, equals to and between, etc. + +![Group](./Group3.png) + +A Group is created by providing above all field, such that a Group can be created using a single or combination of Parameters, finally click on ‘Save’ button to Create Group. + +Existing Criteria +This table shows list of parameters used in creating a Group and can be altered by Deleting the existing one and assigning a different parameter to the same Group Name, by changing the ‘Parameter’ and ‘Operator’ values, then click ‘Save’ button. + +Edit and Delete Groups +From the Groups Table each of the Group has Action items to ‘Edit’ and ‘Delete’ Group. + +![Group](./Group4.png) + +![Group](./Group5.png) + +To Edit a Group – In the table under column name ‘Actions’ select ‘Edit’ to modify existing the criteria, name, description, then click ‘Save’ button. + +To Delete a Group – In the table under column name ‘Actions’ select ‘Delete’ to remove from list, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently. + +###Available Parameters + +Parameters plays a major portion in creating a Group, to check what are the Parameters available, Add, Edit and Delete Parameters are managed. + +![Group](./Group6.png) + +Add Parameters + +To Add Parameter, click on ‘Add Parameter’ button at top right of the Parameter page, that opens a form to create a new Parameter, which has following elements; + +![Group](./Group7.png) + +Elements of Parameter + +Variables Name: Is an identifier for Parameter, Enter the name of the Parameter + +Text: A short description about Parameter + +Answer Type: Select an Answer Type for Parameter from dropdown list, such that Parameter is of type Text, Numeric and Multi choice. + +![Group](./Group8.png) + +A Parameter is created by providing above all field, finally click on ‘Save’ button to Create Parameter or click on ‘Save and Add More’ button to continue adding more Parameters to the list. + +Edit and Delete Parameter +From the Parameter Table each of the Parameter has Action items to ‘Edit’ and ‘Delete’ Parameter. + +![Group](./Group9.png) + +![Group](./Group10.png) + +To Edit a Parameter – In the table under column name ‘Actions’ select ‘Edit’ to modify existing the Variable name, Text and Answer Type, then click ‘Save’ button. + +To Delete a Parameter – In the table under column name ‘Actions’ select ‘Delete’ to remove from list, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently. + +View Options: This Action item is visible only for the Questions that has Answer Type “Multi choice”, to view Answer Choices. + +Export Parameter +Click on ‘Export Parameter’ to download available list of Parameter in ‘.CSV’ file format. diff --git a/docs/Home.png b/docs/Home.png new file mode 100644 index 00000000..7699c59f Binary files /dev/null and b/docs/Home.png differ diff --git a/docs/Ind1.png b/docs/Ind1.png new file mode 100644 index 00000000..8f6ac145 Binary files /dev/null and b/docs/Ind1.png differ diff --git a/docs/Ind10.png b/docs/Ind10.png new file mode 100644 index 00000000..fbac590c Binary files /dev/null and b/docs/Ind10.png differ diff --git a/docs/Ind11.png b/docs/Ind11.png new file mode 100644 index 00000000..1d96bd4c Binary files /dev/null and b/docs/Ind11.png differ diff --git a/docs/Ind12.png b/docs/Ind12.png new file mode 100644 index 00000000..405aee83 Binary files /dev/null and b/docs/Ind12.png differ diff --git a/docs/Ind13.png b/docs/Ind13.png new file mode 100644 index 00000000..7a7a73c7 Binary files /dev/null and b/docs/Ind13.png differ diff --git a/docs/Ind2.png b/docs/Ind2.png new file mode 100644 index 00000000..9f753972 Binary files /dev/null and b/docs/Ind2.png differ diff --git a/docs/Ind3.png b/docs/Ind3.png new file mode 100644 index 00000000..d589e02f Binary files /dev/null and b/docs/Ind3.png differ diff --git a/docs/Ind4.png b/docs/Ind4.png new file mode 100644 index 00000000..4f0abdbf Binary files /dev/null and b/docs/Ind4.png differ diff --git a/docs/Ind5.png b/docs/Ind5.png new file mode 100644 index 00000000..e2659184 Binary files /dev/null and b/docs/Ind5.png differ diff --git a/docs/Ind6.png b/docs/Ind6.png new file mode 100644 index 00000000..047dc06f Binary files /dev/null and b/docs/Ind6.png differ diff --git a/docs/Ind7.png b/docs/Ind7.png new file mode 100644 index 00000000..b0deea21 Binary files /dev/null and b/docs/Ind7.png differ diff --git a/docs/Ind8.png b/docs/Ind8.png new file mode 100644 index 00000000..15a77ae7 Binary files /dev/null and b/docs/Ind8.png differ diff --git a/docs/Ind9.png b/docs/Ind9.png new file mode 100644 index 00000000..c0537689 Binary files /dev/null and b/docs/Ind9.png differ diff --git a/docs/Indicators.md b/docs/Indicators.md new file mode 100644 index 00000000..2cf4563f --- /dev/null +++ b/docs/Indicators.md @@ -0,0 +1,75 @@ +###Indicators + +A Data Researcher can choose one or more fields from their survey and create an arithmetic expression to generate metrics as per their requirement. Each such expression is called an Indicator. Any number of indicators can be created for a Survey. A limited number of these indicators can be selected to be shown on the dashboard. + +Our system facilitates creating customized Indicators for every survey and show an analysis report in tabular and bar chart form. + +**How to create an Indicator?** + +On the ‘Indicator’ page one can view list of all survey wise Indicators in the application and can view analysis report. + +To create an Indicator, click on ‘Add Indicator’ button at top right of the Indicator page, this will open a blank form with following elements; + +**Elements in the Indicator form** + +**Survey**: Select Survey from list, for which the Indicator need to be defined. + +**Listing**: Select respective Listing, for which the Indicator need to be defined. + +**Indicator**: Define a name to the Indicator + +**Description**: A short description about Indicator + +**Variables**: To create an Indicator, certain metrics are needed for calculation, which are derived from survey/ listing questions + +**Formulae**: compose a formula based up on available Variables,
+Auto suggestion feature is available, which will prompt with the ‘Variables’ already defined above for composing.
+i.e. just type, double curly brackets to automatically prompt with the ‘Variables’ already defined,
+Ex: type {{ , system prompts with ‘Variables’ then select any one to insert Variable,
+Like: {{hh_age}}
+ +**Display on dashboard**: This option allows the indicator to be displayed on the dashboard. A user can select a maximum of 5 indicators to be shown on the dashboard. + +**Add Variable**: + +To Add a Variable, click on respective ‘+’ icon adjacent to ‘Variables’ Text box, that opens form to define a Variable, following parameters are involved and expressed. + +**Name**: Define a name to the Variable + +**Description**: A short description about Variable + +**Test question**: Select respective Listing questions, for which the Variable need to be defined. + +**Operator**: Select respective operator based up on the value of the above ‘Test question’ + +Once all the above fields are filled, then click on ‘Add’ button to add to below table ‘Settings for this Variable’, since you can add more Variables for combination, once all Variables are defined then, finally click on ‘Save’ button to add to Variables list. + +One can also ‘Edit’ and ‘Delete’ Variables using the respective icons adjacent to ‘Variables’ Text box. + +Once Variables are defined, now you can compose the formula in ‘Formulae’ Text box, the composed formula is validated automatically and a message is shown here as ‘Valid’ or ‘in valid’. + +Once Variables are defined, formula is composed and validated, finally click on ‘Save’ button to create an Indicator. + +On the ‘Indicator’ page one can view all the Indicator created in the application, in a tabular form with following column names: + +**Sts**: Represents Indicator display status on dashboard, that means status with color code indicates as: + + – Not Displayed
+ – Displayed
+ +**Indicator**: Is the title of Indicator, which is a unique identity to represent Indicator of a Survey. + +**Description**: A short description about Indicator + +**Survey**: Name of the Survey to which this Indicator belongs + +**Batch**: Name of the Survey Batch to which this Indicator belongs + +**Actions**: + +* **Edit**: click on, Action Item ‘Edit’, User can Edit all fields in Indicator. +* **Delete**: click on, Action Item ‘Delete’, the Indicator is removed, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently +* **Formula**: click on, Action Item ‘Formula’, to edit the Indicator Formula directly +* **Analysis**: click on, Action Item ‘Analysis’, navigates to ‘Indicator Analysis’ page, here one can view two forms of district wise reports based up on the Indicator Formula, + * A Bar chart report is generated and displayed + * A tabular report diff --git a/docs/Interviewer.md b/docs/Interviewer.md new file mode 100644 index 00000000..daf34252 --- /dev/null +++ b/docs/Interviewer.md @@ -0,0 +1,71 @@ +###Interviewer + +Interviewer is a role in the system, one who conducts a Survey in the field. +These Interviewers conducts the survey in the designated Enumeration areas and data collection is done using the hand-held mobile device like Android Smart Phone or Featured Phone via ODK and USSD channels respectively. + +Data researcher enrolls Interviewer, assigns Survey and defines the Enumeration areas. + +Enrolled Interviewer are available from main menu under Administration >> Interviewers + +![Interviewer](./Interviewer1.png) + +###Interviewer Registration +On the ‘Interviewers’ page one can view list of enrolled Interviewers in the application, Add, Manage and Edit Interviewer can be done. + +To register a new Interviewer, click on ‘Add Interviewer’ button at top right of the Interviewers page, this will open a registration form with following elements; + +![Interviewer](./Interviewer2.png) + +Elements in the registration form + +![Interviewer](./Interviewer4_1.png) + +Select Locations: Initially get list of Enumeration Areas (EA) by selecting respective, District, County, Sub-county and Parish from locations filters at top of this page, to allocate Enumeration areas to the Interviewer where survey has to be conducted. + +Name: Name of the Interviewer + +Date of Birth: Date of birth of the Interviewer + +Gender: Gender of the Interviewer + +Education: Education qualification of the Interviewer + +Preferred Language: select, Interviewer’s preferred language to write and speak + +Survey: Assign a Survey to the Interviewer, select a Survey from the list + +Enumeration Area: The places or locations where assigned Survey is supposed to be conducted by the Interviewer. Select EAs from ‘All EAs’ list by clicking on the list item, that is on listed EA. + +ODK Access: The process of collecting data using Android channel is known as ODK Access, to provide permissions to access this channel, an Interviewer needs following ODK Access credentials; + +ODK ID: It is the User Id / User Name to access ODK channel, only alphabets are accepted + +ODK Token: It nothing but password to access ODK channel, only numeric values are accepted + +Activated: Allow access or prevent access to ODK channel + +USSD Access: The process of collecting data using Featured Phone is known as USSD Access, to access this channel, an Interviewer needs to provide Mobile Number. + +Options in USSD Access: + +Add Mobile Number: System facilitates, accessing from multiple mobile numbers also. So one can add multiple mobile numbers or remove here using ‘Add’ and ‘Delete’ options. + +Activated: Allow access or prevent access to USSD channel + +Interviewer is enrolled by providing above all field, finally click on ‘Save’ button to complete the process. + +From this above Interviewers page, Interviewers are managed, i.e. to Edit Interviewer Details and block/unblock Interviewer + +Actions: + +![Interviewer](./Interviewer3.png) + +View Details: View Interviewer profile and Edit details, all profile details, Survey Details, ODK Access and USSD Access details are editable by click on ‘Edit’ button at the bottom of the page. + +Un Block/ Block: Option to make Interviewer active or in active. + +Search: One can find Interviewers in system by two ways: +Sort Interviewers location wise by selecting respective, District, County, Sub-county and Parish from locations filters at top of this page. Or +Search using the search bar at top right side of this page, by providing Interviewer’s Name. + +Export Interviewers: On click, ‘Export Interviewers’ button, user can download the Interviewers list in “.csv” file format. diff --git a/docs/Interviewer1.png b/docs/Interviewer1.png new file mode 100644 index 00000000..8ac6957a Binary files /dev/null and b/docs/Interviewer1.png differ diff --git a/docs/Interviewer2.png b/docs/Interviewer2.png new file mode 100644 index 00000000..2639302c Binary files /dev/null and b/docs/Interviewer2.png differ diff --git a/docs/Interviewer3.png b/docs/Interviewer3.png new file mode 100644 index 00000000..37584c18 Binary files /dev/null and b/docs/Interviewer3.png differ diff --git a/docs/Interviewer4.png b/docs/Interviewer4.png new file mode 100644 index 00000000..e1e4bf61 Binary files /dev/null and b/docs/Interviewer4.png differ diff --git a/docs/Interviewer4_1.png b/docs/Interviewer4_1.png new file mode 100644 index 00000000..d839c7ca Binary files /dev/null and b/docs/Interviewer4_1.png differ diff --git a/docs/Library.md b/docs/Library.md new file mode 100644 index 00000000..be37d848 --- /dev/null +++ b/docs/Library.md @@ -0,0 +1,32 @@ +###Library Questions + +List of Questions very common in Surveys are available here which are also categorized into Module wise. This is a feature/ facility available to pick up questions from this library and add to Batch or Listing, by ‘Select Questions’ feature in respective pages. + +Create Library Questions + +To add new questions in Library can be done in 2 ways. + +1. Adding directly question to Library +2. Using feature ‘Update Library’ while creating Batch or Listing Questions + +Adding Directly: +On the ‘Library’ page one can view all the list of Library Questions, +Now click on, ‘Add Question’ button at top right side of this page, this will open a form where one can create a Question, which has following elements; + +Elements in Library Question: + +Module: All Modules in the application are listed here in this dropdown, one has to select, respective Module that is related to survey. + +Variables Name: This is an identifier for Question, type a code for Question + +Text: Is the actual Question, Write a Question. + +Answer Type: Select an Answer Type from dropdown list, such that Question has to be answered in any one of these formats only, that is ‘Answer type’ should be: “Audio, Auto Generate, Date, Geo Point, Image, Multi choice, Multi Select, Numeric, Text and Video” + +A Library Question is created by filling above all fields, finally click on ‘Save’ button to Add Question to Library or click on ‘Save and Add More’ button to continue adding another Question. + +Search: One can find Questions in Library by two ways: +Sort questions using ‘Answer Type’ dropdown at top left side of this page. Or +Search using the search bar at top right side of this page, by providing text or code. + +Export Questions: On click, ‘Export Questions’ button, user can download the Library Questions in “.csv” file format. diff --git a/docs/Listing.md b/docs/Listing.md new file mode 100644 index 00000000..c0c4acf2 --- /dev/null +++ b/docs/Listing.md @@ -0,0 +1,243 @@ +###Listing +
+ +The Listing is generally carried out by field staff other than interviewers, as a separate field operation conducted before the survey starts. This pre-process has the following benefits + +1. It creates set of common questions to be asked before any survey is captured, as a one-time effort. For instance, the demographic details of household residents is required before conducting any household survey. Such data can be captured as part of the Listing. + +2. By creating a separate Listing the system gives the user flexibility to use the same Listing for multiple surveys. + +Listing is available from main menu under **Design** >> **Listing Form** + +Listing is a set of Questions that can be customized/configured for any survey by reusing same Listing. + +![Listing](./Listing1.png) + +###Creating New Listing +
+ +To create a new Listing, click on the ‘Create New Listing Form’ button at top right of the Listing page, that opens a form to create a new Listing, which has following elements; + +![Listing](./Listing2.png) + +**Elements of Listing form**: + +* **Name**: Is Listing Name, which is a unique identity to a Listing and is a mandatory field. + +* **Description**: write about the importance of the Listing in short. + +* **Access channels**: This will identify on which channel this survey has to be conducted and has two channel ODK and USSD + +![Listing](./Listing3.png) + +A Listing Form is created by filling above all fields, finally clicking on ‘Save’ button to create Listing. + +**Search**: One can find Listings in the application, using the search bar at top right side of this Listing Form page, by providing Name or description. + +On the ‘Listing Form’ page one can view all the Listings created in the application, in a tabular form with following column names: + +**Sts**: Represents Status of the Listing by color indicator, that means each of the Color code indicates as: + + – Not Started
+ – Ongoing
+ – Completed
+ +**Name**: Is Name of the Listing, which is a unique identity to represent Listing and is a mandatory field. + +**Description**: A short description about Listing + +**Total Respondents**: once the listing operation is completed, this column will be updated with the count of participants/ respondents. + +**Action**: Each of the Listing has following Actions: ‘Edit’, ‘Delete’, ‘View/Edit Questions’, ‘View Data’ and ‘Clone’ + +**Actions in Listing Form**: + +![Listing](./Listing4.png) + +* **Edit**: click on, Action Item ‘Edit’, User can Edit only the name of the Listing, Description and selection of Access channels (OBK, USSD) + +* **Delete**: click on, Action Item ‘Delete, the entire Listing is removed, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently.
+This option is not available for completed listing + +* **View/Edit Questions**: click on, Action Item ‘View/Edit Questions’, navigates to ‘Listing Questions’ page to View or Edit Respective Questions in Listing + +* **Clone**: click on, Action Item ‘Clone’, an another copy of same Listing Form is created along with Listing Questions, except existing Looping and Logic + +* **View Data**: This Action Item is available only for the completed Listing operation in the field. click on, Action Item ‘View Data’, to view data collected for this particular Listing + +![Listing](./Listing18.png) + +**Creating Questionnaire to the Listing Form**: + +On the ‘Listing Form’ page you can view all the Listings created in the application. + +To add new questions in Listing can be done in 2 ways + +1. Click on the Listing Name +2. Click on Actions Drop down and select the item ‘View/Edit Questions’ + +One can also select the questions from ‘Questions Library’ to add into Listing, using ‘Select Questions’ + +**Add Questions in Listing Form**: + +Click on, ‘Add Question’ button at top right of the particular Listing page, this will open a form where one can create a Question, which has following elements; + +![Listing](./Listing15.png) + +**Elements in Listing Question form**: + +**Variables Name**: This is an identifier for Question, type a code for Question + +**Text**: Is the actual Question, Write a Question. While typing a question, auto suggestion feature is available, which will prompt with the ‘Variable Name’ of preceding questions, that helps to include “answered text of preceding question” in framing this question. +i.e. just type, double curly brackets to automatically prompt with the ‘Variable Name’ from preceding questions,
+Ex: type {{ , system prompts with ‘Variable Name’ then select any one to insert Variable Name,
+Like: {{structure_address}} + +**Answer Type**: Select an Answer Type from dropdown list, such that Question has to be answered in any one of these formats only, that is ‘Answer type’ should be: “Audio, Auto Generate, Date, Geo Point, Image, Multi choice, Multi Select, Numeric, Text and Video” + +**Mandatory**: To mark the Question, that has to answered compulsory. + +A Question in Listing is created by filling above all fields, finally click on ‘Save’ button to Add Question in Listing or click on ‘Save and Add More’ button to continue adding another Question to the same Listing or click on ‘Save and Update Library’ to add same to the ‘Questions Library’. + +**Select Question**: On click, ‘Select Question’ button, User navigates to ‘Select Library Questions’ page where one can Add the Library Questions into Listing. + +![Listing](./Listing13.png) + +To Add Question from Library to Listing, just Click on “Code” or “Text” to move Question between (Library Questions << / >> Listing Questions) tables, then click on ‘Save’ button at bottom of this page, to finish adding Questions to Listing. + +**Search**: One can find Questions in Library by two ways: +**Sort** questions using ‘Answer Type’ dropdown at top left side of this page. Or +**Search** using the search bar at top right side of this page, by providing text or code. + +**Export Questions**: On click, ‘Export Questions’ button, user can download the Listing Questions in “.csv” file format. + +**Update Question Order**: +Questions in the table can be rearranged.
+To change the order of the Question in table, just select the Question then drag (move up or down) and drop at new position/order you want to in the table, then click on ‘Update Question Order’ button at bottom of the Questions table. + +On the ‘Listing Questions’ page one can view all the Questions created in that particular Listing. + +**Search**: One can find Questions in Listing by two ways:
+**Sort** questions using ‘Answer Type’ dropdown at top left side of this page. Or
+**Search** using the search bar at top right side of this page, by providing text or code. + +**Actions for Listing Questions**: + +**Edit Question**: click on, Action Item ‘Edit’, User can Edit respective Question, ‘Variable Name’, ‘Text’ (Question), ‘Answer Type’ and can change ‘Mandatory’ type. + +**Insert Question**: click on, Action Item, ‘Insert Question’ using which User can insert a New Question below the respective Question and rest of the process is similar to ‘Add New Question’. + +**Delete Question**: click on, Action Item ‘Delete’, the Question is removed from list, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently. + +**Caution while deleting a Question**: when a user attempts to delete a question, if that particular question is assigned with Logic or Loop, then along with the question logic or entire loop is removed respectively. + +**View Options**: This Action item is visible only for the Questions that has Answer Type “Multi choice” and “Multi Select”, to view Answer Options. + +**Start/Edit Loop**: + +‘Start/Edit Loop’ is an Action item available only for the Questions that has Answer Type “Auto Generated”. + +![Listing](./Listing6.png) + +Looping means repeatedly asking set of questions based up the need and purpose of the base question. + +To define the Looping concept, first of all one has to understand about terms that are used in creating a Loop are as following: + +![Listing](./Listing8.png) + +**User Defined**: Need to choose what set of Question come into loop, starting from this particular question and has to end loop with any of the consecutive question in the Listing. + +![Listing](./Listing7.png) + +**Repeat Logic**: To apply Looping for a question there should be some base criteria to start loop, that is chosen from ‘Repeat Logic’ as “User Defined”, “Fixed number of repeats” and “Response from previous question” + +**Fixed number of repeats**: Is chosen, need to provide ‘Repeat count’ – any specific no of times loop to be repeated. + +![Listing](./Listing9.png) + +**Response from previous question**: Is chosen, only when a question that exists before this base question with Answer Type “Numerical Answer”. This logic is based up on numeric value provided in the previous question. + +![Listing](./Listing10.png) + +* **Loop Ends At**: When a loop starts that has to be closed, here the choice at which question the loop as to be closed is selected. + +![Listing](./Listing11.png) + +* **Loop Prompt**: This is a message prompt shown on Mobile App during the time of capturing this details. This message will help the Interviewer to proceed further. + +![Listing](./Listing12.png) + +###How to create a Loop? +
+ +One should be very careful while creating a loop, first of all analyze how a loop has to be created, using ‘Repeat logic’ and where to ‘End Loop’, as defined above select accordingly from ‘Repeat logic’ and ‘Loop end at’ to define a loop. + +click on, Action Item ‘Start/Edit Loop’ which will take to ‘Start Loop’ page, now create loop as follows: + +* select “Logic Type” from dropdown ‘Repeat logic’, +* select “Question at which loop should end” from dropdown ‘Loop end at’ and +* in ‘Loop Prompt’ Text Box, write some message about, instructing the loop flow, +* then click on ‘Save’ button to create loop + +Now you will be viewing a looping representation on ‘Listing Questions’ page in the ‘Code’ column of the ‘Questions’ table + +![Listing](./Listing5.png) + +**Loop Representation**: + +* | - icon represents Loop - START, + +* | - icon represents Loop - END, + +* | - icon represents Loop - CONTINUATION + +**Remove Loop**: + +This Action item available only for the Questions that has Answer Type “Auto Generated” and a Loop is created. +click on, Action Item ‘Remove Loop’ which will remove the existing looping logic. + +**Add Logic**: + +‘Add Logic’ is an Action item for every question expect for the Questions that has Answer Type “Auto Generated”. + +![Listing](./Listing16.png) + +Add Logic option will convert a question to conditional one such that question will have choices to “Reconfirm”, “End Interview”, “Ask Sub-Question” and “Skip To” which is based up on value/Answer provided. + +one has to define the Logic here for respective question by satisfying the condition by providing ‘Eligible Criteria’, ‘Attribute’ Value, and ‘Then’ as follows: + +![Listing](./Listing17.png) + +**Eligible Criteria**: This is a condition made based up on this Question value “Starts With”, “Equals”, “Contains” and “Ends With”. + +**Attribute**: Provide the “Value” as per the above ‘Eligible Criteria’ selected + +**Then**: Based up on the ‘Eligible Criteria’ and ‘Attribute’ Value chosen, the condition for Question is applied here with following options: + +* ‘Reconfirm’ – Prompts with conformation Question to validate the Answer + +* ‘End Interview’ – Skips the intermediate Question and moves to end of the questioner. + +* ‘Ask Sub-Question’ – Provides an option to Create a Sub-Question based upon the ‘Attribute’ value.
+ +To do this, select option “Ask Sub-Question” than you will find a button with name ‘Add Sub-Question’ beside this,
+click on it to create a Sub-Question, same like create question, once you click on ‘Save’ button, you will find this Sub-Question in the dropdown ‘Choose Question’ +beside it, now select the “Sub-Question” and click on ‘Save’ button. + +* ‘Skip To’ - Provides the option to jump to any particular/ consecutive question in the list, by skipping / avoiding the intermediate Question. + +**How to Apply Logic to a Question?** + +Click on, Action Item, ‘Add Logic’ this will open a form where one can create a Logic for Question, by providing ‘Eligible Criteria’, ‘Attribute’ Value, and ‘Then’ as per the condition required, then click on ‘Save’ button to create Logic. + +In the same page, logic that is created can be seen in table “Existing Logic”. +Which shows the created logic for this particular question and has option to ‘Delete’ the applied Logic + +**Edit Logic**: +In the ‘Listing Form Questions Template’ page, Questions that has Logic are represented with hyperlink, click on the respective Question, that shows options to ‘View logic’, ‘Edit’ and ‘Delete’ + +* **View Logic**: click to view the Logic that is Applied for this particular question + +* **Edit Logic**: click to Edit the existing Logic that is Applied for this particular question + +* **Delete Logic**: click to Remove the Logic that is Applied for this particular question \ No newline at end of file diff --git a/docs/Listing1.png b/docs/Listing1.png new file mode 100644 index 00000000..99062556 Binary files /dev/null and b/docs/Listing1.png differ diff --git a/docs/Listing10.png b/docs/Listing10.png new file mode 100644 index 00000000..b20c6432 Binary files /dev/null and b/docs/Listing10.png differ diff --git a/docs/Listing11.png b/docs/Listing11.png new file mode 100644 index 00000000..c607f633 Binary files /dev/null and b/docs/Listing11.png differ diff --git a/docs/Listing12.png b/docs/Listing12.png new file mode 100644 index 00000000..71236920 Binary files /dev/null and b/docs/Listing12.png differ diff --git a/docs/Listing13.png b/docs/Listing13.png new file mode 100644 index 00000000..68a1d4a7 Binary files /dev/null and b/docs/Listing13.png differ diff --git a/docs/Listing14.png b/docs/Listing14.png new file mode 100644 index 00000000..06219051 Binary files /dev/null and b/docs/Listing14.png differ diff --git a/docs/Listing15.png b/docs/Listing15.png new file mode 100644 index 00000000..4ca4c6d4 Binary files /dev/null and b/docs/Listing15.png differ diff --git a/docs/Listing16.png b/docs/Listing16.png new file mode 100644 index 00000000..d565a657 Binary files /dev/null and b/docs/Listing16.png differ diff --git a/docs/Listing17.png b/docs/Listing17.png new file mode 100644 index 00000000..b2fd75ff Binary files /dev/null and b/docs/Listing17.png differ diff --git a/docs/Listing18.png b/docs/Listing18.png new file mode 100644 index 00000000..dfaafded Binary files /dev/null and b/docs/Listing18.png differ diff --git a/docs/Listing2.png b/docs/Listing2.png new file mode 100644 index 00000000..9b1088f0 Binary files /dev/null and b/docs/Listing2.png differ diff --git a/docs/Listing3.png b/docs/Listing3.png new file mode 100644 index 00000000..b48bdd19 Binary files /dev/null and b/docs/Listing3.png differ diff --git a/docs/Listing4.png b/docs/Listing4.png new file mode 100644 index 00000000..ece38333 Binary files /dev/null and b/docs/Listing4.png differ diff --git a/docs/Listing5.png b/docs/Listing5.png new file mode 100644 index 00000000..446bd56e Binary files /dev/null and b/docs/Listing5.png differ diff --git a/docs/Listing6.png b/docs/Listing6.png new file mode 100644 index 00000000..ddd78e22 Binary files /dev/null and b/docs/Listing6.png differ diff --git a/docs/Listing7.png b/docs/Listing7.png new file mode 100644 index 00000000..8a1daa5a Binary files /dev/null and b/docs/Listing7.png differ diff --git a/docs/Listing8.png b/docs/Listing8.png new file mode 100644 index 00000000..a608fb48 Binary files /dev/null and b/docs/Listing8.png differ diff --git a/docs/Listing9.png b/docs/Listing9.png new file mode 100644 index 00000000..9433e388 Binary files /dev/null and b/docs/Listing9.png differ diff --git a/docs/Login.png b/docs/Login.png new file mode 100644 index 00000000..6171788e Binary files /dev/null and b/docs/Login.png differ diff --git a/docs/Map1.png b/docs/Map1.png new file mode 100644 index 00000000..342a5338 Binary files /dev/null and b/docs/Map1.png differ diff --git a/docs/Map2.png b/docs/Map2.png new file mode 100644 index 00000000..7d5dc4a8 Binary files /dev/null and b/docs/Map2.png differ diff --git a/docs/Map_groupZ_selected.png b/docs/Map_groupZ_selected.png new file mode 100644 index 00000000..2215d14b Binary files /dev/null and b/docs/Map_groupZ_selected.png differ diff --git a/docs/Module1.png b/docs/Module1.png new file mode 100644 index 00000000..0a3d0712 Binary files /dev/null and b/docs/Module1.png differ diff --git a/docs/Module2.png b/docs/Module2.png new file mode 100644 index 00000000..5bf689d8 Binary files /dev/null and b/docs/Module2.png differ diff --git a/docs/Module3.png b/docs/Module3.png new file mode 100644 index 00000000..4a76eea6 Binary files /dev/null and b/docs/Module3.png differ diff --git a/docs/Module4.png b/docs/Module4.png new file mode 100644 index 00000000..a0c5b607 Binary files /dev/null and b/docs/Module4.png differ diff --git a/docs/Modules.md b/docs/Modules.md new file mode 100644 index 00000000..f9c580fa --- /dev/null +++ b/docs/Modules.md @@ -0,0 +1,28 @@ +###Modules: +Modules are Survey based classification based up on survey type and need. +Modules are available from main menu under, Design >> Modules + +![Module](./Module1.png) + +To create a new Module, click on ‘Add Module’ button at top right of the Modules page, that opens a form to create a new Module. + +![Module](./Module2.png) + +Elements of Module: + +Name: Name of a Module, which is a unique identity to a Module and is a mandatory field. + +Description: A short description about Module + +![Module](./Module3.png) + +A Module is created by providing above field and click on ‘Create’ button to Create a Module. + +On the ‘Modules’ page you can view all the Modules created in the application. each of these Module has Actions to ‘Edit’ and ‘Delete’. + +Actions in Modules: + +![Module](./Module4.png) + +* Edit Module: click on, Action Item ‘Edit’, User can Edit Name and Description of the Module. +* Delete Module: click on, Action Item ‘Delete, the Module is removed, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently. \ No newline at end of file diff --git a/docs/ODK-1.png b/docs/ODK-1.png new file mode 100644 index 00000000..5fd4d599 Binary files /dev/null and b/docs/ODK-1.png differ diff --git a/docs/ODK-10.png b/docs/ODK-10.png new file mode 100644 index 00000000..c8ed8f53 Binary files /dev/null and b/docs/ODK-10.png differ diff --git a/docs/ODK-11.png b/docs/ODK-11.png new file mode 100644 index 00000000..ee51c259 Binary files /dev/null and b/docs/ODK-11.png differ diff --git a/docs/ODK-12.png b/docs/ODK-12.png new file mode 100644 index 00000000..c921a4f5 Binary files /dev/null and b/docs/ODK-12.png differ diff --git a/docs/ODK-13.png b/docs/ODK-13.png new file mode 100644 index 00000000..7dc7f0bf Binary files /dev/null and b/docs/ODK-13.png differ diff --git a/docs/ODK-14.png b/docs/ODK-14.png new file mode 100644 index 00000000..26449fc2 Binary files /dev/null and b/docs/ODK-14.png differ diff --git a/docs/ODK-15.png b/docs/ODK-15.png new file mode 100644 index 00000000..def4001d Binary files /dev/null and b/docs/ODK-15.png differ diff --git a/docs/ODK-16.png b/docs/ODK-16.png new file mode 100644 index 00000000..5aec2c8c Binary files /dev/null and b/docs/ODK-16.png differ diff --git a/docs/ODK-17.png b/docs/ODK-17.png new file mode 100644 index 00000000..59523153 Binary files /dev/null and b/docs/ODK-17.png differ diff --git a/docs/ODK-18.png b/docs/ODK-18.png new file mode 100644 index 00000000..5ec6ad0e Binary files /dev/null and b/docs/ODK-18.png differ diff --git a/docs/ODK-19.png b/docs/ODK-19.png new file mode 100644 index 00000000..ee95768b Binary files /dev/null and b/docs/ODK-19.png differ diff --git a/docs/ODK-2.png b/docs/ODK-2.png new file mode 100644 index 00000000..4fe46cf3 Binary files /dev/null and b/docs/ODK-2.png differ diff --git a/docs/ODK-20.png b/docs/ODK-20.png new file mode 100644 index 00000000..aaf1a671 Binary files /dev/null and b/docs/ODK-20.png differ diff --git a/docs/ODK-21.png b/docs/ODK-21.png new file mode 100644 index 00000000..4ca1526e Binary files /dev/null and b/docs/ODK-21.png differ diff --git a/docs/ODK-22.png b/docs/ODK-22.png new file mode 100644 index 00000000..df660e0b Binary files /dev/null and b/docs/ODK-22.png differ diff --git a/docs/ODK-23.png b/docs/ODK-23.png new file mode 100644 index 00000000..1ee11756 Binary files /dev/null and b/docs/ODK-23.png differ diff --git a/docs/ODK-24.png b/docs/ODK-24.png new file mode 100644 index 00000000..7419530c Binary files /dev/null and b/docs/ODK-24.png differ diff --git a/docs/ODK-25.png b/docs/ODK-25.png new file mode 100644 index 00000000..3260ca16 Binary files /dev/null and b/docs/ODK-25.png differ diff --git a/docs/ODK-26.png b/docs/ODK-26.png new file mode 100644 index 00000000..d7bd41fb Binary files /dev/null and b/docs/ODK-26.png differ diff --git a/docs/ODK-27.png b/docs/ODK-27.png new file mode 100644 index 00000000..7ad0117d Binary files /dev/null and b/docs/ODK-27.png differ diff --git a/docs/ODK-28.png b/docs/ODK-28.png new file mode 100644 index 00000000..3e8aefe3 Binary files /dev/null and b/docs/ODK-28.png differ diff --git a/docs/ODK-29.png b/docs/ODK-29.png new file mode 100644 index 00000000..79f95f4d Binary files /dev/null and b/docs/ODK-29.png differ diff --git a/docs/ODK-2_1.png b/docs/ODK-2_1.png new file mode 100644 index 00000000..b0acf376 Binary files /dev/null and b/docs/ODK-2_1.png differ diff --git a/docs/ODK-2_2.png b/docs/ODK-2_2.png new file mode 100644 index 00000000..9e744bee Binary files /dev/null and b/docs/ODK-2_2.png differ diff --git a/docs/ODK-2_3.png b/docs/ODK-2_3.png new file mode 100644 index 00000000..a64973a5 Binary files /dev/null and b/docs/ODK-2_3.png differ diff --git a/docs/ODK-2_4.png b/docs/ODK-2_4.png new file mode 100644 index 00000000..e7629358 Binary files /dev/null and b/docs/ODK-2_4.png differ diff --git a/docs/ODK-2_5.png b/docs/ODK-2_5.png new file mode 100644 index 00000000..3cb8dfef Binary files /dev/null and b/docs/ODK-2_5.png differ diff --git a/docs/ODK-2_6.png b/docs/ODK-2_6.png new file mode 100644 index 00000000..b33c7414 Binary files /dev/null and b/docs/ODK-2_6.png differ diff --git a/docs/ODK-3.png b/docs/ODK-3.png new file mode 100644 index 00000000..a2583983 Binary files /dev/null and b/docs/ODK-3.png differ diff --git a/docs/ODK-30.png b/docs/ODK-30.png new file mode 100644 index 00000000..836b1bd8 Binary files /dev/null and b/docs/ODK-30.png differ diff --git a/docs/ODK-31.png b/docs/ODK-31.png new file mode 100644 index 00000000..b1971fc2 Binary files /dev/null and b/docs/ODK-31.png differ diff --git a/docs/ODK-32.png b/docs/ODK-32.png new file mode 100644 index 00000000..a6210036 Binary files /dev/null and b/docs/ODK-32.png differ diff --git a/docs/ODK-33.png b/docs/ODK-33.png new file mode 100644 index 00000000..b4b771ff Binary files /dev/null and b/docs/ODK-33.png differ diff --git a/docs/ODK-34.png b/docs/ODK-34.png new file mode 100644 index 00000000..65cfb3a6 Binary files /dev/null and b/docs/ODK-34.png differ diff --git a/docs/ODK-4.png b/docs/ODK-4.png new file mode 100644 index 00000000..d0b8e5c9 Binary files /dev/null and b/docs/ODK-4.png differ diff --git a/docs/ODK-5.png b/docs/ODK-5.png new file mode 100644 index 00000000..b6aa7980 Binary files /dev/null and b/docs/ODK-5.png differ diff --git a/docs/ODK-6.png b/docs/ODK-6.png new file mode 100644 index 00000000..f0c73e40 Binary files /dev/null and b/docs/ODK-6.png differ diff --git a/docs/ODK-7.png b/docs/ODK-7.png new file mode 100644 index 00000000..d5305bbc Binary files /dev/null and b/docs/ODK-7.png differ diff --git a/docs/ODK-8.png b/docs/ODK-8.png new file mode 100644 index 00000000..c49b4da9 Binary files /dev/null and b/docs/ODK-8.png differ diff --git a/docs/ODK-9.png b/docs/ODK-9.png new file mode 100644 index 00000000..30c35970 Binary files /dev/null and b/docs/ODK-9.png differ diff --git a/docs/ODK_App.md b/docs/ODK_App.md new file mode 100644 index 00000000..0ac51bdb --- /dev/null +++ b/docs/ODK_App.md @@ -0,0 +1,301 @@ +###Introduction + +uSurvey portal is intended to design and define the Listings, Surveys and monitor statistical reports. On other end in the field, data collection and submission is done using hand-held mobile devices in two ways. + +1. Offline Data Collection - Using Smart Phone +2. Online Data Collection - Using Featured Phone + +Hence to collect real time data, both offline and online methods are implemented. + In the locations, where there is a mobile phone network connectivity is available, data is transmitted to server instantly via USSD channel, using Featured Phone. + Whereas, in isolated mobile phone network connectivity locations, data collection is done offline via uSurvey App, using Smart Phones. Thus, data collected stores temporally in the hand-held device and transmits to the server later time, once reaches to a location with Internet connectivity. + +Ultimately uSurvey application make paperless work and carry out Survey in a digitalized process with increased ease and accuracy of data. Which results in the release of Survey reports timely. + +###Offline Data Collection +------ +#####About uSurvey App + +**Data collection and submission using Android Smart Phone** + +uSurvey App is exclusively designed for survey data collection and submission using the hand-held mobile devices (Android Smart Phone/ Tablet). Thus using rich text forms, also supports media files, captures GPS location, stores data locally and works offline. + +**Implementing uSurvey App** + +Conducting Survey using uSurvey App is simple. + +Enrolled Interviewers, has to do data collection and submission using Android App, uSurvey App is available on Google play store. + +**uSurvey App Installation** + + Interviewers has to download uSurvey App form Google play store, as follows + +* On Android device, go to Google play store +* Search for “uSurvey” and choose uSurvey App from search results +* Select ‘uSurvey App’ in the result and click the Install button +* Click OK after viewing the security settings +* Once App installation is done, now uSurvey App is available in application list and ready to launch, as shown below in Mobile Screen-1 +* Now launch uSurvey App on mobile + +![ODK](./ODK-1.png) + Mobile Screen-1 + +App Home/landing screen has following options, as shown below in Mobile Screen-2 + +![ODK](./ODK-2.png) + Mobile Screen-2 + +**Configure uSurvey App** + +For first time use and when app reset is done, one has to provide, App user credentials as a part of one-time configuration settings as follows:
+ +* To configure, press the Menu button on the phone while the app is opened, then Select ‘General Settings’, takes you to a new screen to change setting
+ as shown below in Mobile Screen-3 + +![ODK](./ODK-3.png) + Mobile Screen-3 + +* Now under ‘Server Settings’ select ‘Configure platform settings’, as shown below in Mobile Screen-4
+ +![ODK](./ODK-4.png) + Mobile Screen-4 + +* In ‘Configure platform settings’ under ‘ODK Aggregate Settings’, make sure that, URL should be http://usurvey.unicefuganda.org/odk/collect/forms
+ +![ODK](./ODK-5.png) + Mobile Screen-5 + +* Username: enter ‘ODK ID’ of the Interviewer +* Password: enter ‘ODK Token’ of the Interviewer
(For above details, you need an interviewer defined on uSurvey portal and defined with an ODK ID) + +![ODK](./ODK-6.png) + Mobile Screen-6 + +* Now come back to App Home screen using Mobile back key + +**Using uSurvey App** + +Follow these simple steps for survey Data collection:
+Get Blank Form, Fill Blank Form, Edit Save Form, Send Finalized Form, Download Data Forms, Search Forms and Edit Download Form + +######Step -1: Get Blank Form + +Once App is opened, in the Home screen hit on ‘Get Blank Form’ to download blank form, then you will be prompted “Please enter User name and Password for server”, then Interviewer has to conform his/her User name and Password, to proceed by clicking on ‘OK’. As shown in below Mobile Screen-7 + +![ODK](./ODK-2_3.png) + Mobile Screen-7 + +![ODK](./ODK-7.png) + Mobile Screen-8 + +Now you will be navigated to this below Mobile Screen-9, Select ‘Blank Form’ from list, usually blank forms, show with ‘Survey –Listing’ names and hit on ‘Get Selected’ As shown in below Mobile Screen-9, then Hit ‘OK’ + +![ODK](./ODK-8.png) + Mobile Screen-9 + +![ODK](./ODK-9.png) + Mobile Screen-10 + +![ODK](./ODK-10.png) + Mobile Screen-11 + + +######Step -2: Fill Blank Form + +In the Home Screen hit on ‘Fill Blank Form’ to fill questionnaire as part of data collection. Now select any of one the forms listed here. As shown in below Mobile Screen-12 + +![ODK](./ODK-2_1.png) + Mobile Screen-12 + +![ODK](./ODK-11.png) + Mobile Screen-13 + +![ODK](./ODK-12.png) + Mobile Screen-14 + +Now opens the actual questionnaire, to proceed filling the form - Just swipe forward and backward, then finally you will see ‘Save Survey and Exit’ hit to save. + +![ODK](./ODK-13.png) + Mobile Screen-15 + +As part of questionnaire, the very first question is “Select Enumeration Area” where you have to select any one of the Enumeration Area, which is mandatory and proceed to answer next question. +Be very careful while selecting the Enumeration Area, once from is submitted there is no change to edit Enumeration Area. + +![ODK](./ODK-14.png) + Mobile Screen-16 + +Once filling the questionnaire is completed, you will be prompt with message “You are at the end of the ---- form” and select ‘Mark form as Finalized’ then hit on ‘Save Survey and Exit’. + +![ODK](./ODK-15.png) + Mobile Screen-17 + + +######Step -3: Edit Saved form + +This feature provides, option to view or edit completed questionnaire, including Enumeration Area before submission. + +In the Home Screen hit on ‘Edit Saved form’ to edit filled questionnaire. As shown in below Mobile Screen-18 + +![ODK](./ODK-2_2.png) + Mobile Screen-18 + +You can select any form from list to view or edit, usually filled forms, show with ‘Survey –Listing’ names, tap on the listed items to proceed view or edit filled questionnaire, once changes are done, don’t forget to hit on ‘Save Survey and Exit’. + At any instance of the from, one can exit the current form from undo by hitting on Mobile back key, which prompt with message to ‘Save Changes’, ‘Ignore Changes’ and ‘Cancel’ to the Form. + +![ODK](./ODK-16.png) + Mobile Screen-19 + +![ODK](./ODK-28.png) + Mobile Screen-20 + +![ODK](./ODK-29.png) + Mobile Screen-21 + +![ODK](./ODK-30.png) + Mobile Screen-22 + +![ODK](./ODK-31.png) + Mobile Screen-23 + + +######Step -4: Send Finalized Form + +The process of transmitting the completed forms to server, when there is an internet connectivity. + +Now in the Home Screen hit on ‘Send Finalized Form’ to submit the completed questionnaire forms, As shown in below Mobile Screen-24 + +![ODK](./ODK-17.png) + Mobile Screen-24 + +Now you will be navigated to this below shown, Mobile Screen-25, Select forms individually or hit on ‘Toggle All’ to select all forms in the list, then hit on ‘Send Selected’. As shown in below Mobile Screen-26, + if again you will be prompted “Please enter User name and Password for server”, then conform your credentials once again by clicking on ‘OK’. You will get success message if from is submitted or respective error message is shown. + +![ODK](./ODK-18.png) + Mobile Screen-25 + +![ODK](./ODK-19.png) + Mobile Screen-26 + +![ODK](./ODK-20.png) + Mobile Screen-27 + +![ODK](./ODK-21.png) + Mobile Screen-28 + + +######Step -5: Download Data Forms + +The feature to download completed Listing and Survey forms from server to make any changes. + +In the Home Screen hit on ‘Download Data Form’ to get list of recent/current submitted forms. Now screen opens with the complete list of submitted forms, select forms individually or hit on ‘Toggle All’ to select all forms in the list, then hit on ‘Get Selected’. As shown in below Mobile Screen-29, + +![ODK](./ODK-2_6.png) + Mobile Screen-29 + +![ODK](./ODK-22.png) + Mobile Screen-30 + +![ODK](./ODK-23.png) + Mobile Screen-31 + +Then download starts, which takes several minutes, depends up on your Internet speed. As shown below Mobile Screen-32 + +![ODK](./ODK-24.png) + Mobile Screen-32 + +Note: Once Forms are downloaded then these forms are available from ‘Edit Saved Form’ and ‘Search Forms’ + +######Step -6: Search Forms + +The feature to find completed Listing and Survey forms, to view or edit filled questionnaire. + +In the Home Screen hit on ‘Search Forms’ to start searching for forms, which opens a search screen, where an Interviewer can search a beneficiary using data in the filled questionnaire and Hit on Search Icon in Key Pad, as shown in below Mobile Screen-33 + +![ODK](./ODK-2_5.png) + Mobile Screen-33 + +![ODK](./ODK-25.png) + Mobile Screen-34 + +![ODK](./ODK-26.png) + Mobile Screen-35 + +If the search criteria match the input data, then screen will show up with list of forms that matches entered key word, Interviewer has to Identify and select the exact beneficiary to view or edit the details. + +######Edit Download Form + +This feature provides, option to view or edit download questionnaire forms and resubmit. + +In the Home Screen hit on ‘Edit Saved form’ to edit filled questionnaire. As shown in below Mobile Screen-36 + +![ODK](./ODK-2_2.png) + Mobile Screen-36 + +You can select any form from list to view or edit, usually these forms, show with ‘Survey –Listing’ names, tap on the listed items or hit on ‘Go To Start’ to proceed view or edit filled questionnaire, once changes are done, don’t forget to hit on ‘Save Survey and Exit’. If any changes are done, + don’t forget to repeat the steps in Send Finalized Form + + ![ODK](./ODK-27.png) + Mobile Screen-37 + + ![ODK](./ODK-28.png) + Mobile Screen-38 + + ![ODK](./ODK-29.png) + Mobile Screen-39 + + ![ODK](./ODK-30.png) + Mobile Screen-40 + +There is an option to Exit Form by pressing the default ‘BACK’ button of the Mobile at any time while filling or using this App, which prompt with message to ‘Save Changes’, ‘Ignore Changes’ and ‘Cancel’ to the Form. As shown in below Mobile Screen-41 + + ![ODK](./ODK-31.png) + Mobile Screen-41 + +######Delete Saved Form + +This feature allows user to remove the Forms from local device, to clear the local data. + +In the Home Screen hit on ‘Delete Saved form’ to remove Forms. +As shown in below Mobile Screen-42 + + ![ODK](./ODK-2_4.png) + Mobile Screen-42 + +This screen is divided into two tabs,
+**Saved Forms**: Shows list of all completed forms.
+**Blank Forms**: Shows list of all blank forms.
+ + ![ODK](./ODK-32.png) + Mobile Screen-43 + +To delete, select forms individually or hit on ‘Toggle All’ to select all forms in the list, then hit on ‘Delete Selected Survey Forms’ + + ![ODK](./ODK-33.png) + Mobile Screen-44 + + ![ODK](./ODK-34.png) + Mobile Screen-45 + + + +###Online Data Collection +------ +#####USSD Integration +uSurvey fully supports survey participation on USSD using the interactive menu capability of the USSD platform. To begin, from uSurvey portal, interviewers are sent SMS with details of the code to dial in order to commence data collection (e.g. *256#). The interviewer dials the code and then the survey starts. + +![USSD Participation](./ussd-code-example.jpg) + +#####What do I need to conduct survey on USSD? + +1. Any mobile phone would do. +2. You need a USSD Aggregator. +3. You need to choose a mobile network which supports your chosen USSD Aggregator. +4. You need your chosen USSD Aggregator to forward USSD messages to uSurvey as follows: + * Requests can be sent as a HTTP GET or a POST to uSurvey USSD end point. + * If you have hosted uSurvey with host IP `HOST_IP` and port `APP_PORT`, the USSD end point is `HTTP(s)://HOST_IP:APP_PORT/ussd`. + * At a minimum, following parameters need to be sent to uSurvey USSD API from the aggregator: + * `msisdn:` This parameter holds the mobile number of the responding interviewer. + * `ussdRequestString:` This parameter holds the input string sent by the interviewer. + * `transactionId:` This parameter holds the session ID of the USSD Interaction. +5. You need to maintain connectivity to your mobile network (Since USSD participation requires an active USSD session). +6. Now assign the interviewer to the relevant Survey and Enumeration area (for more info on this see the relevant section in the [User manual](./user_manual.md#interviewer-page "Interviewer Page")) + \ No newline at end of file diff --git a/docs/Page01.png b/docs/Page01.png new file mode 100644 index 00000000..b933f3fa Binary files /dev/null and b/docs/Page01.png differ diff --git a/docs/Page02.png b/docs/Page02.png new file mode 100644 index 00000000..e3757f97 Binary files /dev/null and b/docs/Page02.png differ diff --git a/docs/Page03.png b/docs/Page03.png new file mode 100644 index 00000000..40cb256c Binary files /dev/null and b/docs/Page03.png differ diff --git a/docs/Page04.png b/docs/Page04.png new file mode 100644 index 00000000..ece85ed5 Binary files /dev/null and b/docs/Page04.png differ diff --git a/docs/Page1v1.png b/docs/Page1v1.png new file mode 100644 index 00000000..f0824a6d Binary files /dev/null and b/docs/Page1v1.png differ diff --git a/docs/Page2v1.png b/docs/Page2v1.png new file mode 100644 index 00000000..dd898f95 Binary files /dev/null and b/docs/Page2v1.png differ diff --git a/docs/Page3_v1.png b/docs/Page3_v1.png new file mode 100644 index 00000000..5e0a424c Binary files /dev/null and b/docs/Page3_v1.png differ diff --git a/docs/QL1.png b/docs/QL1.png new file mode 100644 index 00000000..885318ea Binary files /dev/null and b/docs/QL1.png differ diff --git a/docs/QL2.png b/docs/QL2.png new file mode 100644 index 00000000..a8b23b6b Binary files /dev/null and b/docs/QL2.png differ diff --git a/docs/QL3.png b/docs/QL3.png new file mode 100644 index 00000000..11cd92e7 Binary files /dev/null and b/docs/QL3.png differ diff --git a/docs/QL4.png b/docs/QL4.png new file mode 100644 index 00000000..6c3c6178 Binary files /dev/null and b/docs/QL4.png differ diff --git a/docs/Site_Map.md b/docs/Site_Map.md new file mode 100644 index 00000000..2cfbfed1 --- /dev/null +++ b/docs/Site_Map.md @@ -0,0 +1,53 @@ + +###Process flow: + +![uSurvey Flow](./uSurvey_flow.png) + + +###Overview: + +![uSurvey Menu](./uSurvey_LOC.jpg) + + +###Sitemap: +----------- +###uSurvey Home + +* **Dashboard** + +* **Design** + * [Survey](./Survey.md) + * [Listing](./Listing.md) + * [Modules](./Modules.md) + * [Groups](./Groups.md) + * [Question Library](./Library.md) + +* **Administration** + * [Interviewers](./Interviewer.md) + * [Notification](#) + * [Data Entry](#) + +* **Monitor** + * [Listing Data](#) + * [Batch Data](#) + * [Completion Summary](#) + * [ODK Submission](./ODK_App.md) + +* **Analyse** + * [Indicators](./Indicators.md) + * [Location Weights](#) + +* **Download** + * [Survey Data](#) + * [Mobile Money Sheet](#) + +* **Settings** + * [Users](#) + * [Locations](#) + * [Setup](#) + +* **User Profile** + * [User](#) + * [Active Power Mode](#) + * [Change Password](#) + * [Logout](#) \ No newline at end of file diff --git a/docs/Survey.md b/docs/Survey.md new file mode 100644 index 00000000..e104423d --- /dev/null +++ b/docs/Survey.md @@ -0,0 +1,273 @@ +###Create Survey + +Surveys are available from main menu under **Design** >> **Surveys** +![Survey0](./Survey1.png) +Screen-1 + +A New Survey can be created and defined here, so before creating a survey first of all one has to know about the terminology used here. + +* **Preferred Listing**: List of existing Listing, which are already in the system.
+Is an option to choose, existing Listing for this New Survey, where already survey was conducted on these Listings, which will include total Listing questions along with the data/results. + +* **New Listing**: List of Newly created Listings, not previously used in any survey.
+This field will enable only if option “None, Create New” is selected, in the dropdown ‘Preferred Listing’. which contains list of Newly created Listings, which are not yet used in any survey. + +* **Randomly selected data label**: Is the identifier for respondents while conducting a Listing survey. + This field will enable only when listing in dropdown ‘New Listing’ is selected. + +**How to create a Survey?** +To create a New Survey, click on ‘Create New Survey’ button at top right of the Survey page, that opens a form to create a new Survey, which has following elements; + +![Survey1](./Survey2.png) +Screen-2 + +**Elements of a Survey**: + +![Survey2](./Survey3.png) + +* **Name**: Is Survey Name, which is a unique identity to a survey and is a mandatory field
+* **Description**: write about the importance of this survey in short
+* **Survey Type**: This identifies how survey is going to take place either using Listing or not
+ If Survey Type is “Sampled”, Survey uses Sample size and Listing
+ If Survey Type is “Census”, Survey doesn’t need Sample size and Listing + +* **Sample size**: Provide survey sample size
+* **Preferred Listing**: select an existing Listing Or
+* **New Listing**: select Newly created Listing
+* **Randomly selected data label**: You need to include one listing response identifier in double curly brackets.
+ i.e. just type, double curly brackets to automatically insert ‘Variable Name’ in above selected Listing
+ Ex: type {{ , system prompts with ‘Variable Name’ then select any one to insert Variable Name
+ Like: {{house_number}}
+* **Email group**: select, User Emails Id to send report + +A New Survey is created by providing above fields and click on ‘Save’ button to Create a Survey. + +**Search**: One can find Surveys in the application, using the search bar at top right side of this survey page, by providing Name or description. + +On the ‘Survey’ page one can view all the Survey created in the application, in a tabular form with following column names: + +![Survey2](./Survey4.png) + +**Sts**: Represents Status of the Survey by color indicator, that means each of the Color code indicates as: + – Not Started
+ – Ongoing
+ – Completed
+ +* **Name**: Is Name of a Survey, which is a unique identity to represent Survey and is a mandatory field.
+* **Description**: A short description about Listing
+* **Type**: Survey type Sampled or Census
+* **Sample size**: Provide survey sample size
+* **Total Respondents**: once the Survey is completed, this column will be updated with the count of participants/ respondents.
+* **Eas Covered**: Count of Enumeration areas covered in this particular survey. + +**Actions in Survey**: + +* **Edit Survey**: click on, Action Item ‘Edit’, User can Edit all fields in the Survey
+* **Delete Survey**: click on, Action Item ‘Delete, the entire Survey is removed, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently.
This option is not available for completed Survey
+* **View Batches in Survey**: click on, Action Item ‘View Batches’, navigates to ‘Survey Batches’ page
+* **Clone a Survey**: click on, Action Item ‘Clone’, an another copy of same Survey is created along with Survey Batches and Batch Questions, except existing Looping and Logic in Batch Questions
+* **Sampling Criteria**: + +###Batches +Batch is a categorization of Survey Questions, that means set of Questions categorized for a Survey convenience. We can create multiple Batches in a survey. + +Once the Survey is created, next step is to create ‘Batch’ and ‘Add Questions’ to Batch. + +**How to create a Batch?** + +Go to Batches Page, which can be done in two ways +On the ‘Survey’ page you can view all the Survey created in the application +Now click on Survey Name, or click on, Action Item ‘View Batches’ to go to ‘Batches’ Page + +![Batch](./Batch1.png) + +To create a New Batch, click on ‘Create New Batch’ button at top right of the Batch page, that opens a form to create a new Batch, which has following elements; + +![Batch](./Batch2.png) + +**Elements of a Batch**: + +* **Name**: Is Batch Name, which is a unique identity to a Batch and is a mandatory field
+* **Description**: write about the importance of this Batch in short
+* **Access channels**: This will identify on which channel this survey has to be conducted and has two channel ODK and USSD + +A New Batch is created by providing above fields and click on ‘Save’ button to Create a New Batch in Survey. + +**Actions in Batch**: + +![Batch](./Batch3.png) + +* **Edit**: click on, Action Item ‘Edit’, User can Edit all fields in a Batch
+* **Delete**: click on, Action Item ‘Delete, the entire Batch along with questions are removed, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently. This option is not available for completed Surveys
+* **View/Edit Questions**: click on, Action Item ‘View /Edit Questions’, takes to the Survey ‘Batch Questions’ page
+* **Open/Close**: click on, Action Item ‘Open/Close’, takes to the page where all Enumeration Areas are listed, here one can change the “status of conducting survey in a particular Enumeration Area” to Open/Close
+i.e. Open- means allow to conduct survey and Close - means it prevents to conduct survey. + +![Batch](./Batch8.png) + +![Batch](./Batch7.png) + +* **View Data**: click on, Action Item ‘View Data’, shows Data collected in this Survey. This option is available only for completed Surveys. + +**How to create Batch Questions?** + +Once Survey and Batches are created, next step is to ‘Add Questions’ to Batch as follows: + +![Batch](./Batch4.png) + +On the ‘Batch’ page you can view all the Batch created in a Survey +Now click on Batch Name, or click on, Action Item ‘View /Edit Question’ to go to ‘Batch Questions’ Page + +![Batch](./Batch5.png) + +Now click on, ‘Add Question’ button at top right side of this page, this will open a form where one can create a Question, which has following elements; + +**Elements in Batch Question form**: + +* **Module**: All Modules in the application are listed here in this dropdown, one has to select, respective Module that is related to survey
+* **Group**: All available Groups are listed in this dropdown, select relevant group name for the question
+* **Variables Name**: This is an identifier for Question, type a code for Question
+* **Text**: Is the actual Question, Write a Question. While typing a question, auto suggestion feature is available, which will prompt with the ‘Variable Name’ of preceding questions, that helps to include “Answered text of preceding question” in framing this question. +i.e. just type, double curly brackets to automatically prompt with the ‘Variable Name’ from preceding questions,
+Ex: type {{ , system prompts with ‘Variable Name’ then select any one to insert Variable Name,
+Like: {{structure_address}}
+* **Answer Type**: Select an Answer Type from dropdown list, such that Question has to be answered in any one of these formats only, that is ‘Answer type’ should be: “Audio, Auto Generate, Date, Geo Point, Image, Multi choice, Multi Select, Numeric, Text and Video”
+* **Mandatory**: To mark the Question, that has to answered compulsory + +A Question in Batch is created by filling above all fields, finally click on ‘Save’ button to Add Question in Batch or click on ‘Save and Add More’ button to continue adding another Question to the same Batch or click on ‘Save and Update Library’ to add same to the ‘Questions Library’. + +**Select Question**: On click, ‘Select Question’ button, User navigates to ‘Select Library Questions’ page where one can Add the Library Questions into Batch. + +![Batch](./Batch6.png) + +To Add Question from Library to Batch, just Click on “Code” or “Text” to move Question between (Library Questions << / >> Batch Questions) tables, then click on ‘Save’ button at bottom of the page, to finish adding Questions to Batch. + +**Search**: One can find Questions in Library by two ways:
+**Sort** questions using ‘Answer Type’ dropdown at top left side of this page Or
+**Search** using the search bar at top right side of this page, by providing text or code + +**Export Questions**: On click, ‘Export Questions’ button, user can download the Batch Questions in “.csv” file format. + +**Update Question Order**:
+Questions in the table can be rearranged.
+To change the order of the Question in table, just select the Question then drag (move up or down) and drop at new position/order you want to in the table, then click on ‘Update Question Order’ button at bottom of the Questions table. + +On the ‘Batch Questions’ page one can view all the Questions created in that particular Batch, + +**Search**: One can find Questions in Batch by two ways:
+**Sort** questions using ‘Answer Type’ dropdown at top left side of this page Or
+**Search** using the search bar at top right side of this page, by providing text or code. + +**Actions for Batch Questions**: + +* **Edit Question**: click on, Action Item ‘Edit’, User can edit respective Question, ‘Variable Name’, ‘Text’ (Question), ‘Answer Type’ and can change ‘Mandatory’ type. This option is not available and cannot be performed for completed survey. + +* **Insert Question**: click on, Action Item, ‘Insert Question’ using which User can insert a New Question below the respective Question and rest of the process is similar to ‘Add New Question’. + +* **Delete Question**: click on, Action Item ‘Delete’, the Question is removed from list, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently. + +Caution, while deleting a Question: when a user attempts to delete a question, if that particular question is assigned with Logic or Loop, then along with the question logic or entire loop is removed respectively. + +**View Options**: This Action item is visible only for the Questions that has Answer Type “Multi choice” and “Multi Select”, to view Answer Options. + +![Batch](./Batch9.png) + +**Start/Edit Loop**: +‘Start/Edit Loop’ is an Action item available only for the Questions that has Answer Type “Auto Generated”. + +Looping means repeatedly asking set of questions based up the need and purpose of the base question. + +To define the Looping concept, first of all one has to understand about terms that are used in creating a Loop are as following: + +![Batch](./Batch10.png) + +**Repeat Logic**: To apply Looping for a question there should be some base criteria to start loop, that is chosen from ‘Repeat Logic’ as “User Defined”, “Fixed number of repeats” and “Response from previous question” + +![Batch](./Batch11.png) + +**User Defined**: Need to choose what set of Question come into loop, starting from this particular question and has to end loop with any of the consecutive question in the Batch. + +**Fixed number of repeats**: Is chosen, need to provide ‘Repeat count’ – any specific no of times loop to be repeated. + +![Batch](./Batch12.png) + +**Response from previous question**: Is chosen, only when a question that exists before this base question with Answer Type “Numerical Answer”. This logic is based up on numeric value provided in the previous question. + +![Batch](./Batch13.png) + +**Loop Ends At**: When a loop starts that has to be closed, here the choice at which question the loop as to be closed is selected. + +![Batch](./Batch14.png) + +**Loop Prompt**: This is a message prompt shown on Mobile App during the time of capturing this details. This message will help the Interviewer to proceed further. +Ex: “Do you what to Add one more”, “Do you what to Add another Household”, etc + +###How to create a Loop? +
+ +One should be very careful while creating a loop, first of all analyze how a loop has to be created, using ‘Repeat logic’ and where to ‘End Loop’, as defined above select accordingly from ‘Repeat logic’ and ‘Loop end at’ to define a loop. + +* click on, Action Item ‘Start/Edit Loop’ which will take to ‘Start Loop’ page, now create loop as follows:
+* select “Logic Type” from dropdown ‘Repeat logic’,
+* select “Question at which loop should end” from dropdown ‘Loop end at’ and
+* in ‘Loop Prompt’ Text Box, write some message about, instructing the loop flow,
+* then click on ‘Save’ button to create loop.
+* Now you will be viewing a looping representation on ‘Batch Questions’ page in the ‘Code’ column of the ‘Questions’ table, + +**Loop Representation**: + +* | - icon represents Loop - START, + +* | - icon represents Loop - END, + +* | - icon represents Loop - CONTINUATION + +![Batch](./Batch18.png) + +**Remove Loop**: + +This Action item available only for the Questions that has Answer Type “Auto Generated” and a Loop is created. +click on, Action Item ‘Remove Loop’ which will remove the existing looping logic. + +**Add Logic**: + +‘Add Logic’ is an Action item for every question expect for the Questions that has Answer Type “Auto Generated”. + +Add Logic option will convert a question to conditional one such that question will have choices to “Reconfirm”, “End Interview”, “Ask Sub-Question” and “Skip To” which is based up on value/Answer provided. + +![Batch](./Batch17.png) + +One has to define the Logic here for respective question by satisfying the condition by providing ‘Eligible Criteria’, ‘Attribute’ Value, and ‘Then’ as follows: + +![Batch](./Batch15.png) + +**Eligible Criteria**: This is a condition made based up on this Question value “Starts With”, “Equals”, “Contains” and “Ends With”. + +**Attribute**: Provide the “Value” as per the above ‘Eligible Criteria’ selected + +**Then**: Based up on the ‘Eligible Criteria’ and ‘Attribute’ Value chosen, the condition for Question is applied here with following options: + +**Reconfirm** – Prompts with conformation Question to validate the Answer + +**End Interview** – Skips the intermediate Question and moves to end of the questioner + +**Ask Sub-Question** – Provides an option to Create a Sub-Question based upon the ‘Attribute’ value.
+To do this, select option “Ask Sub-Question” than you will find a button with name ‘Add Sub-Question’ beside this, +click on it to create a Sub-Question, same like create question, once you click on ‘Save’ button, you will find this Sub-Question in the dropdown ‘Choose Question’ +beside it, now select the “Sub-Question” and click on ‘Save’ button. + +**Skip To** - Provides the option to jump to any particular/ consecutive question in the list, by skipping / avoiding the intermediate Question. + +**How to Apply Logic to a Question?** + +Click on, Action Item, ‘Add Logic’ this will open a form where one can create a Logic for Question, by providing ‘Eligible Criteria’, ‘Attribute’ Value, and ‘Then’ as per the condition required, then click on ‘Save’ button to create Logic. + +In the same page, logic that is created can be seen in table “Existing Logic”. +Which shows the created logic for this particular question and has option to ‘Delete’ the applied Logic + +**Edit Logic**: +In the ‘Batch Questions’ page, Questions that has Logic are represented with hyperlink, click on the respective Question, that shows options like ‘View logic’, ‘Edit’ and ‘Delete’ + +* **View Logic**: click to view the Logic that is Applied for this particular question
+* **Edit Logic**: click to Edit the existing Logic that is Applied for this particular question
+* **Delete Logic**: click to Remove the Logic that is Applied for this particular question \ No newline at end of file diff --git a/docs/Survey1.png b/docs/Survey1.png new file mode 100644 index 00000000..89f97df0 Binary files /dev/null and b/docs/Survey1.png differ diff --git a/docs/Survey2.png b/docs/Survey2.png new file mode 100644 index 00000000..d536b1c0 Binary files /dev/null and b/docs/Survey2.png differ diff --git a/docs/Survey3.png b/docs/Survey3.png new file mode 100644 index 00000000..cdfca41c Binary files /dev/null and b/docs/Survey3.png differ diff --git a/docs/Survey4.png b/docs/Survey4.png new file mode 100644 index 00000000..319803b8 Binary files /dev/null and b/docs/Survey4.png differ diff --git a/docs/Survey4_1.png b/docs/Survey4_1.png new file mode 100644 index 00000000..569fef47 Binary files /dev/null and b/docs/Survey4_1.png differ diff --git a/docs/Survey5.png b/docs/Survey5.png new file mode 100644 index 00000000..a2d34e6c Binary files /dev/null and b/docs/Survey5.png differ diff --git a/docs/Survey6.png b/docs/Survey6.png new file mode 100644 index 00000000..b9f50c64 Binary files /dev/null and b/docs/Survey6.png differ diff --git a/docs/User_Guides.md b/docs/User_Guides.md new file mode 100644 index 00000000..4be2b771 --- /dev/null +++ b/docs/User_Guides.md @@ -0,0 +1,972 @@ +###Login +------ +Welcome to uSurvey portal, enrolled users can access the portal by their Login credentials provided by Administrator. + +To login, click on ‘Login’ at top right of the Home page + +![Home](./Home.png) + +Now Sign in to the portal using your Username and Password, after successful login you will be navigated to uSurvey Dashboard, which shows Uganda Map. + +![Login](./Login.png) + +###Dashboard +------ +The Dashboard, contains Survey Map, which is used to display survey report on a country map as per primary Administrative divisions.
+E.g. In current map of Uganda, every District correspond to primary administrative division for Survey. + +Dashboard is basically divided into two Tabs
+1. **Locations**
+2. **Indicators** + +On Map Dashboard, a drop down at top left contains Surveys, District wise completion rates with respect to its legend and respective Survey Indicators are display adjacent to the map. +Legend at bottom left, shows with color codes used to indicate completion status on map. + +In the ‘Locations’ tab,
+select a Survey and point on relevant District on the map you wish to examine, then the report is loaded into the map and shows District wise Survey report with completion rates and completion status. + +![Map](./Map1.png) + +In the ‘Indicators’ tab,
+Shows maximum five Indicators that defined for each Survey. select a Survey and point on relevant District on the map you wish to examine respective Indicators. + +![Map](./Map2.png) + +###Modules +------ +Modules are Survey based classification based up on survey type and need. + Modules are accessible from main menu under **Design** >> **Modules** + +![Module](./Module1.png) + +To create a new Module, click on ‘Add Module’ button at top right of the Modules page, that opens a form to create a new Module. + +![Module](./Module2.png) + +**Elements of Module**: + +**Name**: Name of a Module, which is a unique identity to a Module and is a mandatory field. + +**Description**: A short description about Module + +![Module](./Module3.png) + +A Module is created by providing above field and click on ‘Create’ button to Create a Module. + +On the ‘Modules’ page you can view all the Modules created in the application. each of these Module has Actions to ‘Edit’ and ‘Delete’. + +**Actions in Modules**: + +![Module](./Module4.png) + +* Edit Module: click on, Action Item ‘Edit’, User can Edit Name and Description of the Module. +* Delete Module: click on, Action Item ‘Delete, the Module is removed, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently. + +###Groups +------ +Groups are Survey dependent classification of people/respondents based on their Age, Gender and combination of both into one or more survey- respondents categories called Groups. +Population has to be segment into Groups / multiple groups and our system allows us to create as many as needed. + +**Create a New Group** + +Groups are accessible from main menu under **Design** >> **Groups** + +![Group](./Group1.png) + +To create a new Group, click on ‘Add Group’ button at top right of the Groups page, that opens a form to create a new Group. + +![Group](./Group2_1.png) + +From this above Groups page, Groups are managed, i.e. to Create a New Group, Edit existing Group, Delete Groups and Manage Parameters. + +**Elements of Group** + +**Name**: Is Group Name, which is a unique identity to a Group. + +**Description**: A short description about Group + +**Parameter**: Need to select a parameter, based on which Group is created. Like: Age, Gender. + +**Operator**: Also need to select an Operator based on the Parameter value. Like: between, equals, grater_than, and less_than. + +![Group](./Group3.png) + +A Group is created by providing above all field, such that a Group can be created using a single or combination of Parameters, finally click on ‘Save’ button to Create Group. + +**Existing Criteria** +This table shows list of parameters used in creating a Group and can be altered by Deleting the existing one and assigning a different parameter to the same Group Name, by changing the ‘Parameter’ and ‘Operator’ values, then click ‘Save’ button. + +**Edit and Delete Groups** +From the Groups Table each of the Group has Action items to ‘Edit’ and ‘Delete’ Group. + +![Group](./Group4.png) + +![Group](./Group5.png) + +To Edit a Group – In the table under column name ‘Actions’ select ‘Edit’ to modify existing the criteria, name, description, then click ‘Save’ button. + +To Delete a Group – In the table under column name ‘Actions’ select ‘Delete’ to remove from list, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently. + +**Available Parameters** + +Parameters plays a major portion in creating a Group, to check what are the Parameters available, Add, Edit and Delete Parameters are managed. + +![Group](./Group6.png) + +**Add Parameters** + +To Add Parameter, click on ‘Add Parameter’ button at top right of the Parameter page, that opens a form to create a new Parameter, which has following elements; + +![Group](./Group7.png) + +**Elements of Parameter** + +**Variables Name**: Is an identifier for Parameter, Enter the name of the Parameter + +**Text**: A short description about Parameter + +**Answer Type**: Select an Answer Type for Parameter from dropdown list, such that Parameter is of type Text, Numeric and Multi choice. + +![Group](./Group8.png) + +A Parameter is created by providing above all field, finally click on ‘Save’ button to Create Parameter or click on ‘Save and Add More’ button to continue adding more Parameters to the list. + +**Edit and Delete Parameter** +From the Parameter Table each of the Parameter has Action items to ‘Edit’ and ‘Delete’ Parameter. + +![Group](./Group9.png) + +![Group](./Group10.png) + +To Edit a Parameter – In the table under column name ‘Actions’ select ‘Edit’ to modify existing the Variable name, Text and Answer Type, then click ‘Save’ button. + +To Delete a Parameter – In the table under column name ‘Actions’ select ‘Delete’ to remove from list, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently. + +**View Options**: This Action item is visible only for the Questions that has Answer Type “Multi choice”, to view Answer Choices. + +**Export Parameter** +Click on ‘Export Parameter’ to download available list of Parameter in ‘.CSV’ file format. + +###Listing +------ +The Listing is generally carried out by field staff other than interviewers, as a separate field operation conducted before the survey starts. This pre-process has the following benefits + +1. It creates set of common questions to be asked before any survey is captured, as a one-time effort. For instance, the demographic details of household residents is required before conducting any household survey. Such data can be captured as part of the Listing. + +2. By creating a separate Listing the system gives the user flexibility to use the same Listing for multiple surveys. + +Listings are accessible from main menu under **Design** >> **Listing Form** + +Listing is a set of Questions that can be customized/configured for any survey by reusing same Listing. + +![Listing](./Listing1.png) + +**Creating New Listing** + +To create a new Listing, click on the ‘Create New Listing Form’ button at top right of the Listing page, that opens a form to create a new Listing, which has following elements; + +![Listing](./Listing2.png) + +**Elements of Listing form**: + +* **Name**: Is Listing Name, which is a unique identity to a Listing and is a mandatory field. + +* **Description**: write about the importance of the Listing in short. + +* **Access channels**: This will identify on which channel this survey has to be conducted and has two channel ODK and USSD + +![Listing](./Listing3.png) + +A Listing Form is created by filling above all fields, finally clicking on ‘Save’ button to create Listing. + +**Search**: One can find Listings in the application, using the search bar at top right side of this Listing Form page, by providing Name or description. + +On the ‘Listing Form’ page one can view all the Listings created in the application, in a tabular form with following column names: + +**Sts**: Represents Status of the Listing by color indicator, that means each of the Color code indicates as: + + – Not Started
+ – Ongoing
+ – Completed
+ +**Name**: Is Name of the Listing, which is a unique identity to represent Listing and is a mandatory field. + +**Description**: A short description about Listing + +**Total Respondents**: once the listing operation is completed, this column will be updated with the count of participants/ respondents. + +**Action**: Each of the Listing has following Actions: ‘Edit’, ‘Delete’, ‘View/Edit Questions’, ‘View Data’ and ‘Clone’ + +**Actions in Listing Form**: + +![Listing](./Listing4.png) + +* **Edit**: click on, Action Item ‘Edit’, User can Edit only the name of the Listing, Description and selection of Access channels (OBK, USSD) + +* **Delete**: click on, Action Item ‘Delete, the entire Listing is removed, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently.
+This option is not available for completed listing + +* **View/Edit Questions**: click on, Action Item ‘View/Edit Questions’, navigates to ‘Listing Questions’ page to View or Edit Respective Questions in Listing + +* **Clone**: click on, Action Item ‘Clone’, an another copy of same Listing Form is created along with Listing Questions, except existing Looping and Logic + +* **View Data**: This Action Item is available only for the completed Listing operation in the field. click on, Action Item ‘View Data’, to view data collected for this particular Listing + +![Listing](./Listing18.png) + +**Creating Questionnaire to the Listing Form**: + +On the ‘Listing Form’ page you can view all the Listings created in the application. + +To add new questions in Listing can be done in 2 ways + +1. Click on the Listing Name +2. Click on Actions Drop down and select the item ‘View/Edit Questions’ + +One can also select the questions from ‘Questions Library’ to add into Listing, using ‘Select Questions’ + +**Add Questions in Listing Form**: + +Click on, ‘Add Question’ button at top right of the particular Listing page, this will open a form where one can create a Question, which has following elements; + +![Listing](./Listing15.png) + +**Elements in Listing Question form**: + +**Variables Name**: This is an identifier for Question, type a code for Question + +**Text**: Is the actual Question, Write a Question. + While writing a question, system prompts with the “Variable Names” of the preceding questions, that helps to include “answer of the preceding question” in composing succeeding questions. + i.e. just type, double curly brackets ( {{ ), to get list of Variable Names of the preceding questions. + Ex: type {{ , to get Variable Names, then select any 'Variable Name' to insert; *{{NAME}}, {{HH_head}}* + **Example Question**: *Gender of the {{HH_head}}* + +**Answer Type**: Select an Answer Type from dropdown list, such that Question has to be answered in any one of these formats only, that is ‘Answer type’ should be: “Audio, Auto Generate, Date, Geo Point, Image, Multi choice, Multi Select, Numeric, Text and Video” + +**Mandatory**: To mark the Question, that has to answered compulsory. + +A Question in Listing is created by filling above all fields, finally click on ‘Save’ button to Add Question in Listing or click on ‘Save and Add More’ button to continue adding another Question to the same Listing or click on ‘Save and Update Library’ to add same to the ‘Questions Library’. + +**Select Question**: On click, ‘Select Question’ button, User navigates to ‘Select Library Questions’ page where one can Add the Library Questions into Listing. + +![Listing](./Listing13.png) + +To Add Question from Library to Listing, just Click on “Code” or “Text” to move Question between (Library Questions << / >> Listing Questions) tables, then click on ‘Save’ button at bottom of this page, to finish adding Questions to Listing. + +**Search**: One can find Questions in Library by two ways: + **Sort** questions using ‘Answer Type’ dropdown at top left side of this page. Or + **Search** using the search bar at top right side of this page, by providing text or code. + +**Export Questions**: On click, ‘Export Questions’ button, user can download the Listing Questions in “.csv” file format. + +**Update Question Order**: +Questions in the table can be rearranged. + To change the order of the Question in table, just select the Question then drag (move up or down) and drop at new position/order you want to in the table, then click on ‘Update Question Order’ button at bottom of the Questions table. + +On the ‘Listing Questions’ page one can view all the Questions created in that particular Listing. + +**Search**: One can find Questions in Listing by two ways: + **Sort** questions using ‘Answer Type’ dropdown at top left side of this page. Or + **Search** using the search bar at top right side of this page, by providing text or code. + +**Actions for Listing Questions**: + +**Edit Question**: click on, Action Item ‘Edit’, User can Edit respective Question, ‘Variable Name’, ‘Text’ (Question), ‘Answer Type’ and can change ‘Mandatory’ type. + +**Insert Question**: click on, Action Item, ‘Insert Question’ using which User can insert a New Question below the respective Question and rest of the process is similar to ‘Add New Question’. + +**Delete Question**: click on, Action Item ‘Delete’, the Question is removed from list, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently. + +**Caution while deleting a Question**: when a user attempts to delete a question, if that particular question is assigned with Logic or Loop, then along with the question logic or entire loop is removed respectively. + +**View Options**: This Action item is visible only for the Questions that has Answer Type “Multi choice” and “Multi Select”, to view Answer Options. + +**Start/Edit Loop**: + +‘Start/Edit Loop’ is an Action item available only for the Questions that has Answer Type “Auto Generated”. + +![Listing](./Listing6.png) + +Looping means repeatedly asking set of questions based up the need and purpose of the base question. + +To define the Looping concept, first of all one has to understand about terms that are used in creating a Loop are as following: + +![Listing](./Listing8.png) + +**User Defined**: Need to choose what set of Question come into loop, starting from this particular question and has to end loop with any of the consecutive question in the Listing. + +![Listing](./Listing7.png) + +**Repeat Logic**: To apply Looping for a question there should be some base criteria to start loop, that is chosen from ‘Repeat Logic’ as “User Defined”, “Fixed number of repeats” and “Response from previous question” + +**Fixed number of repeats**: Is chosen, need to provide ‘Repeat count’ – any specific no of times loop to be repeated. + +![Listing](./Listing9.png) + +**Response from previous question**: Is chosen, only when a question that exists before this base question with Answer Type “Numerical Answer”. This logic is based up on numeric value provided in the previous question. + +![Listing](./Listing10.png) + +* **Loop Ends At**: When a loop starts that has to be closed, here the choice at which question the loop as to be closed is selected. + +![Listing](./Listing11.png) + +* **Loop Prompt**: This is a message prompt shown on Mobile App during the time of capturing this details. This message will help the Interviewer to proceed further. + +![Listing](./Listing12.png) + +**How to create a Loop?** + +One should be very careful while creating a loop, first of all analyze how a loop has to be created, using ‘Repeat logic’ and where to ‘End Loop’, as defined above select accordingly from ‘Repeat logic’ and ‘Loop end at’ to define a loop. + +click on, Action Item ‘Start/Edit Loop’ which will take to ‘Start Loop’ page, now create loop as follows: + +* select “Logic Type” from dropdown ‘Repeat logic’, +* select “Question at which loop should end” from dropdown ‘Loop end at’ and +* in ‘Loop Prompt’ Text Box, write some message about, instructing the loop flow, +* then click on ‘Save’ button to create loop + +Now you will be viewing a looping representation on ‘Listing Questions’ page in the ‘Code’ column of the ‘Questions’ table + +![Listing](./Listing5.png) + +**Loop Representation**: + +* | - icon represents Loop - START, + +* | - icon represents Loop - END, + +* | - icon represents Loop - CONTINUATION + +**Remove Loop**: + +This Action item available only for the Questions that has Answer Type “Auto Generated” and a Loop is created. +click on, Action Item ‘Remove Loop’ which will remove the existing looping logic. + +**Add Logic**: + +‘Add Logic’ is an Action item for every question expect for the Questions that has Answer Type “Auto Generated”. + +![Listing](./Listing16.png) + +Add Logic option will convert a question to conditional one such that question will have choices to “Reconfirm”, “End Interview”, “Ask Sub-Question” and “Skip To” which is based up on value/Answer provided. + +one has to define the Logic here for respective question by satisfying the condition by providing ‘Eligible Criteria’, ‘Attribute’ Value, and ‘Then’ as follows: + +![Listing](./Listing17.png) + +**Eligible Criteria**: This is a condition made based up on this Question value “Starts With”, “Equals”, “Contains” and “Ends With”. + +**Attribute**: Provide the “Value” as per the above ‘Eligible Criteria’ selected + +**Then**: Based up on the ‘Eligible Criteria’ and ‘Attribute’ Value chosen, the condition for Question is applied here with following options: + +* ‘Reconfirm’ – Prompts with conformation Question to validate the Answer + +* ‘End Interview’ – Skips the intermediate Question and moves to end of the questioner + +* ‘Ask Sub-Question’ – Provides an option to Create a Sub-Question based upon the ‘Attribute’ value + +To do this, select option “Ask Sub-Question” than you will find a button with name ‘Add Sub-Question’ beside this, + click on it to create a Sub-Question, same like create question, once you click on ‘Save’ button, you will find this Sub-Question in the dropdown ‘Choose Question’ + beside it, now select the “Sub-Question” and click on ‘Save’ button. + +* ‘Skip To’ - Provides the option to jump to any particular/ consecutive question in the list, by skipping / avoiding the intermediate Question. + +**How to Apply Logic to a Question?** + +Click on, Action Item, ‘Add Logic’ this will open a form where one can create a Logic for Question, by providing ‘Eligible Criteria’, ‘Attribute’ Value, and ‘Then’ as per the condition required, then click on ‘Save’ button to create Logic. + +In the same page, logic that is created can be seen in table “Existing Logic”. +Which shows the created logic for this particular question and has option to ‘Delete’ the applied Logic + +**Edit Logic**: +In the ‘Listing Form Questions Template’ page, Questions that has Logic are represented with hyperlink, click on the respective Question, that shows options to ‘View logic’, ‘Edit’ and ‘Delete’ + +* **View Logic**: click to view the Logic that is Applied for this particular question + +* **Edit Logic**: click to Edit the existing Logic that is Applied for this particular question + +* **Delete Logic**: click to Remove the Logic that is Applied for this particular question + +###Create Survey +------ +Surveys are accessible from main menu under **Design** >> **Surveys** + +![Survey0](./Survey1.png) +Screen-1 + +A New Survey can be created and defined here, so before creating a survey first of all one has to know about the terminology used here. + +* **Preferred Listing**: List of existing Listing, which are already in the system. + Is an option to choose, existing Listing for this New Survey, where already survey was conducted on these Listings, which will include total Listing questions along with the data/results. + +* **New Listing**: List of Newly created Listings, not previously used in any survey. + This field will enable only if option “None, Create New” is selected, in the dropdown ‘Preferred Listing’. which contains list of Newly created Listings, which are not yet used in any survey. + +* **Randomly selected data label**: Is the identifier for respondents while conducting a Listing survey. + This field will enable only when listing in dropdown ‘New Listing’ is selected. + +**How to create a Survey?** + To create a New Survey, click on ‘Create New Survey’ button at top right of the Survey page, that opens a form to create a new Survey, which has following elements; + +![Survey1](./Survey2.png) +Screen-2 + +**Elements of a Survey**: + +![Survey2](./Survey3.png) + +* **Name**: Is Survey Name, which is a unique identity to a survey and is a mandatory field +* **Description**: write about the importance of this survey in short +* **Survey Type**: This identifies how survey is going to take place either using Listing or not + If Survey Type is “Sampled”, Survey uses Sample size and Listing + If Survey Type is “Census”, Survey doesn’t need Sample size and Listing + +* **Sample size**: Provide survey sample size +* **Preferred Listing**: select an existing Listing Or +* **New Listing**: select Newly created Listing +* **Randomly selected data label**: You need to include at least one Listing response identifier (Variable Name) in double curly brackets. + i.e. just type, double curly brackets ( {{ ), to get list of “Variable Names” defined in the above selected Listing. + Ex: type {{ , to get Variable Names, then select any ‘Variable Name’ to insert. + Like: {{Srn_structure}}, {{Srn_HH}} + **Note**: To insert multiple identifiers, do not use any space or special characters between each of the identifier, just continue by using ‘{{’ to insert. + +* **Email group**: select, User Emails Id to send report + +A New Survey is created by providing above fields and click on ‘Save’ button to Create a Survey. + +**Search**: One can find Surveys in the application, using the search bar at top right side of this survey page, by providing Name or description. + +On the ‘Survey’ page one can view all the Survey created in the application, in a tabular form with following column names: + +![Survey2](./Survey4.png) + +**Sts**: Represents Status of the Survey by color indicator, that means each of the Color code indicates as: + – Not Started
+ – Ongoing
+ – Completed
+ +* **Name**: Is Name of a Survey, which is a unique identity to represent Survey and is a mandatory field +* **Description**: A short description about Listing +* **Type**: Survey type Sampled or Census +* **Sample size**: Provide survey sample size +* **Total Respondents**: once the Survey is completed, this column will be updated with the count of participants/ respondents +* **Eas Covered**: Count of Enumeration areas covered in this particular survey + +**Actions in Survey**: + +* **Edit Survey**: click on, Action Item ‘Edit’, User can Edit all fields in the Survey +* **Delete Survey**: click on, Action Item ‘Delete, the entire Survey is removed, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently.
This option is not available for completed Survey
+* **View Batches in Survey**: click on, Action Item ‘View Batches’, navigates to ‘Survey Batches’ page +* **Clone a Survey**: click on, Action Item ‘Clone’, an another copy of same Survey is created along with Survey Batches and Batch Questions, except existing Looping and Logic in Batch Questions +* **Sampling Criteria**: + This feature helps Data researcher to sort collected data for sampling according to listing data, in some cases, the Data researcher might be interested in restricting sampling, so that only listing data which meets specific criteria are sampled + +For example: if interviewer had collected data for houses in a particular Enumeration area, the Data researcher might be interested in sampling only those houses with at least one child. Or +Data researcher might be interested in sampling only residential houses. + +Defining Sampling Criteria provides the admin user to describe how to define the rules for Sampling Criteria + +**Define Sampling Criteria** + +To define Sampling Criteria, click on, Action Item ‘Sampling Criteria’, that opens a form with the following elements; + +![Survey](./Survey4_1.png) + +![Survey](./Survey5.png) + +![Survey](./Survey6.png) + +**Listing Question**: Listing Questions with code, used for this Survey are lorded here, one can select respective question for defining the Sampling Criteria. + +**Validation Test**: Based on the above selected question, choose the eligible criteria to validate the collected data + +**Value / Options**: To sort collected data for sampling, provide valid input for above Validation Test. + +Sampling Criteria is created by satisfying above fields, then click on ‘Save Changes’. + +In the same page, Sampling Criteria that is created can be seen in table the “Existing Criteria”. +Which shows the defined Sampling Criteria and has option to ‘Delete’. + +###Batches +------ +Batch is a categorization of Survey Questions, that means set of Questions categorized for a Survey convenience. We can create multiple Batches in a survey. + +Once the Survey is created, next step is to create ‘Batch’ and ‘Add Questions’ to Batch. + +**How to create a Batch?** + +Go to Batches Page, which can be done in two ways +On the ‘Survey’ page you can view all the Survey created in the application +Now click on Survey Name, or click on, Action Item ‘View Batches’ to go to ‘Batches’ Page + +![Batch](./Batch1.png) + +To create a New Batch, click on ‘Create New Batch’ button at top right of the Batch page, that opens a form to create a new Batch, which has following elements; + +![Batch](./Batch2.png) + +**Elements of a Batch**: + +* **Name**: Is Batch Name, which is a unique identity to a Batch and is a mandatory field +* **Description**: write about the importance of this Batch in short +* **Access channels**: This will identify on which channel this survey has to be conducted and has two channel ODK and USSD + +A New Batch is created by providing above fields and click on ‘Save’ button to Create a New Batch in Survey. + +**Actions in Batch**: + +![Batch](./Batch3.png) + +* **Edit**: click on, Action Item ‘Edit’, User can Edit all fields in a Batch +* **Delete**: click on, Action Item ‘Delete, the entire Batch along with questions are removed, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently. This option is not available for completed Surveys
+* **View/Edit Questions**: click on, Action Item ‘View /Edit Questions’, takes to the Survey ‘Batch Questions’ page +* **View Data**: click on, Action Item ‘View Data’, shows Data collected in this Survey. This option is available only for completed Surveys. + +######Enable Batch +* **Open/Close**: To enable Batch for data collection
+click on, Action Item ‘Open/Close’, takes to the page where all Enumeration Areas are listed, here one can change the “status of conducting survey in a particular Enumeration Area” to Open/Close
+i.e. **Open** - means, enable Batch for data collection and **Close** – means, disable Batch for data collection. + +![Batch](./Batch8.png) + +![Batch](./Batch7.png) + +**How to create Batch Questions?** + +Once Survey and Batches are created, next step is to ‘Add Questions’ to Batch as follows: + +![Batch](./Batch4.png) + +On the ‘Batch’ page you can view all the Batch created in a Survey + Now click on Batch Name, or click on, Action Item ‘View /Edit Question’ to go to ‘Batch Questions’ Page + +![Batch](./Batch5.png) + +Now click on, ‘Add Question’ button at top right side of this page, this will open a form where one can create a Question, which has following elements; + +**Elements in Batch Question form**: + +* **Module**: All Modules in the application are listed here in this dropdown, one has to select, respective Module that is related to survey +* **Group**: All available Groups are listed in this dropdown, select relevant group name for the question +* **Variables Name**: This is an identifier for Question, type a code for Question +* **Text**: Is the actual Question, Write a Question. + While writing a question, system prompts with the “Variable Names” of the preceding questions, that helps to include “answer of the preceding question” in composing succeeding questions. + i.e. just type, double curly brackets ( {{ ), to get list of Variable Names of the preceding questions. + Ex: type {{ , to get Variable Names, then select any ‘Variable Name’ to insert; *{{FIRST_NAME}}, {{name_student}}* + Example Question: *What is {{FIRST_NAME}}'s Ethnicity?* + +* **Answer Type**: Select an Answer Type from dropdown list, such that Question has to be answered in any one of these formats only, that is ‘Answer type’ should be: “Audio, Auto Generate, Date, Geo Point, Image, Multi choice, Multi Select, Numeric, Text and Video” +* **Mandatory**: To mark the Question, that has to answered compulsory + +A Question in Batch is created by filling above all fields, finally click on ‘Save’ button to Add Question in Batch or click on ‘Save and Add More’ button to continue adding another Question to the same Batch or click on ‘Save and Update Library’ to add same to the ‘Questions Library’. + +**Select Question**: On click, ‘Select Question’ button, User navigates to ‘Select Library Questions’ page where one can Add the Library Questions into Batch. + +![Batch](./Batch6.png) + +To Add Question from Library to Batch, just Click on “Code” or “Text” to move Question between (Library Questions << / >> Batch Questions) tables, then click on ‘Save’ button at bottom of the page, to finish adding Questions to Batch. + +**Search**: One can find Questions in Library by two ways:
+**Sort** questions using ‘Answer Type’ dropdown at top left side of this page Or
+**Search** using the search bar at top right side of this page, by providing text or code + +**Export Questions**: On click, ‘Export Questions’ button, user can download the Batch Questions in “.csv” file format. + +**Update Question Order**: + Questions in the table can be rearranged. + To change the order of the Question in table, just select the Question then drag (move up or down) and drop at new position/order you want to in the table, then click on ‘Update Question Order’ button at bottom of the Questions table. + +On the ‘Batch Questions’ page one can view all the Questions created in that particular Batch, + +**Search**: One can find Questions in Batch by two ways:
+**Sort** questions using ‘Answer Type’ dropdown at top left side of this page Or
+**Search** using the search bar at top right side of this page, by providing text or code. + +**Actions for Batch Questions**: + +* **Edit Question**: click on, Action Item ‘Edit’, User can edit respective Question, ‘Variable Name’, ‘Text’ (Question), ‘Answer Type’ and can change ‘Mandatory’ type. This option is not available and cannot be performed for completed survey. + +* **Insert Question**: click on, Action Item, ‘Insert Question’ using which User can insert a New Question below the respective Question and rest of the process is similar to ‘Add New Question’. + +* **Delete Question**: click on, Action Item ‘Delete’, the Question is removed from list, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently. + +Caution, while deleting a Question: when a user attempts to delete a question, if that particular question is assigned with Logic or Loop, then along with the question logic or entire loop is removed respectively. + +**View Options**: This Action item is visible only for the Questions that has Answer Type “Multi choice” and “Multi Select”, to view Answer Options. + +![Batch](./Batch9.png) + +**Start/Edit Loop**: + ‘Start/Edit Loop’ is an Action item available only for the Questions that has Answer Type “Auto Generated”. + +Looping means repeatedly asking set of questions based up the need and purpose of the base question. + +To define the Looping concept, first of all one has to understand about terms that are used in creating a Loop are as following: + +![Batch](./Batch10.png) + +**Repeat Logic**: To apply Looping for a question there should be some base criteria to start loop, that is chosen from ‘Repeat Logic’ as “User Defined”, “Fixed number of repeats” and “Response from previous question” + +![Batch](./Batch11.png) + +**User Defined**: Need to choose what set of Question come into loop, starting from this particular question and has to end loop with any of the consecutive question in the Batch. + +**Fixed number of repeats**: Is chosen, need to provide ‘Repeat count’ – any specific no of times loop to be repeated. + +![Batch](./Batch12.png) + +**Response from previous question**: Is chosen, only when a question that exists before this base question with Answer Type “Numerical Answer”. This logic is based up on numeric value provided in the previous question. + +![Batch](./Batch13.png) + +**Loop Ends At**: When a loop starts that has to be closed, here the choice at which question the loop as to be closed is selected. + +![Batch](./Batch14.png) + +**Loop Prompt**: This is a message prompt shown on Mobile App during the time of capturing this details. This message will help the Interviewer to proceed further. +Ex: “Do you what to Add one more”, “Do you what to Add another Household”, etc + +**How to create a Loop?** + +One should be very careful while creating a loop, first of all analyze how a loop has to be created, using ‘Repeat logic’ and where to ‘End Loop’, as defined above select accordingly from ‘Repeat logic’ and ‘Loop end at’ to define a loop. + +* click on, Action Item ‘Start/Edit Loop’ which will take to ‘Start Loop’ page, now create loop as follows:
+* select “Logic Type” from dropdown ‘Repeat logic’,
+* select “Question at which loop should end” from dropdown ‘Loop end at’ and
+* in ‘Loop Prompt’ Text Box, write some message about, instructing the loop flow,
+* then click on ‘Save’ button to create loop.
+* Now you will be viewing a looping representation on ‘Batch Questions’ page in the ‘Code’ column of the ‘Questions’ table, + +**Loop Representation**: + +* | - icon represents Loop - START, + +* | - icon represents Loop - END, + +* | - icon represents Loop - CONTINUATION + +![Batch](./Batch18.png) + +**Remove Loop**: + +This Action item available only for the Questions that has Answer Type “Auto Generated” and a Loop is created. +click on, Action Item ‘Remove Loop’ which will remove the existing looping logic. + +**Add Logic**: + +‘Add Logic’ is an Action item for every question expect for the Questions that has Answer Type “Auto Generated”. + +Add Logic option will convert a question to conditional one such that question will have choices to “Reconfirm”, “End Interview”, “Ask Sub-Question” and “Skip To” which is based up on value/Answer provided. + +![Batch](./Batch17.png) + +One has to define the Logic here for respective question by satisfying the condition by providing ‘Eligible Criteria’, ‘Attribute’ Value, and ‘Then’ as follows: + +![Batch](./Batch15.png) + +**Eligible Criteria**: This is a condition made based up on this Question value “Starts With”, “Equals”, “Contains” and “Ends With”. + +**Attribute**: Provide the “Value” as per the above ‘Eligible Criteria’ selected + +**Then**: Based up on the ‘Eligible Criteria’ and ‘Attribute’ Value chosen, the condition for Question is applied here with following options: + +**Reconfirm** – Prompts with conformation Question to validate the Answer + +**End Interview** – Skips the intermediate Question and moves to end of the questioner + +**Ask Sub-Question** – Provides an option to Create a Sub-Question based upon the ‘Attribute’ value.
+To do this, select option “Ask Sub-Question” than you will find a button with name ‘Add Sub-Question’ beside this, +click on it to create a Sub-Question, same like create question, once you click on ‘Save’ button, you will find this Sub-Question in the dropdown ‘Choose Question’ +beside it, now select the “Sub-Question” and click on ‘Save’ button. + +**Skip To** - Provides the option to jump to any particular/ consecutive question in the list, by skipping / avoiding the intermediate Question. + +**How to Apply Logic to a Question?** + +Click on, Action Item, ‘Add Logic’ this will open a form where one can create a Logic for Question, by providing ‘Eligible Criteria’, ‘Attribute’ Value, and ‘Then’ as per the condition required, then click on ‘Save’ button to create Logic. + +In the same page, logic that is created can be seen in table “Existing Logic”. +Which shows the created logic for this particular question and has option to ‘Delete’ the applied Logic + +**Edit Logic**: +In the ‘Batch Questions’ page, Questions that has Logic are represented with hyperlink, click on the respective Question, that shows options like ‘View logic’, ‘Edit’ and ‘Delete’ + +* **View Logic**: click to view the Logic that is Applied for this particular question
+* **Edit Logic**: click to Edit the existing Logic that is Applied for this particular question
+* **Delete Logic**: click to Remove the Logic that is Applied for this particular question + +###Library Questions +------ +List of Questions very common in Surveys are available here which are also categorized into Module wise. This is a feature/ facility available to pick up questions from this library and add to Batch or Listing, by ‘Select Questions’ feature in respective pages. + +**Create Library Questions** + +To add new questions in Library can be done in 2 ways. + +1. Adding directly question to Library +2. Using feature ‘Update Library’ while creating Batch or Listing Questions + +Library Questions are accessible from main menu under **Design** >> **Questions Library** + +![Library](./QL1.png) + +![Library](./QL4.png) + +**Adding Directly**: +On the ‘Library’ page one can view all the list of Library Questions, +Now click on, ‘Add Question’ button at top right side of this page, this will open a form where one can create a Question, which has following elements; + +![Library](./QL2.png) + +![Library](./QL3.png) + +**Elements in Library Question**: + +**Module**: All Modules in the application are listed here in this dropdown, one has to select, respective Module that is related to survey. + +**Variables Name**: This is an identifier for Question, type a code for Question + +**Text**: Is the actual Question, Write a Question. + +**Answer Type**: Select an Answer Type from dropdown list, such that Question has to be answered in any one of these formats only, that is ‘Answer type’ should be: “Audio, Auto Generate, Date, Geo Point, Image, Multi choice, Multi Select, Numeric, Text and Video” + +A Library Question is created by filling above all fields, finally click on ‘Save’ button to Add Question to Library or click on ‘Save and Add More’ button to continue adding another Question. + +**Search**: One can find Questions in Library by two ways: +**Sort** questions using ‘Answer Type’ dropdown at top left side of this page. Or +**Search** using the search bar at top right side of this page, by providing text or code. + +**Export Questions**: On click, ‘Export Questions’ button, user can download the Library Questions in “.csv” file format. + +###Interviewer +------ +Interviewer is a role in the system, one who conducts a Survey in the field. +These Interviewers conducts the survey in the designated Enumeration areas and data collection is done using the hand-held mobile device like Android Smart Phone or Featured Phone using uSurvey App and USSD channels respectively. + +Data researcher enrolls Interviewer, assigns Survey and allocates to the Enumeration areas. + +Enrolled Interviewer are accessible from main menu under **Administration** >> **Interviewers** + +![Interviewer](./Interviewer1.png) + +**Interviewer Registration** + +On the ‘Interviewers’ page one can view list of enrolled Interviewers in the application, also can Add and Manage Interviewers. + +To register a new Interviewer, click on ‘Add Interviewer’ button at top right of the Interviewers page, this will open a registration form with following elements; + +![Interviewer](./Interviewer2.png) + +**Elements in the registration form** + +![Interviewer](./Interviewer4_1.png) + +**Select Locations**: Initially get list of Enumeration Areas (EA) by selecting respective, District, County, Sub-county and Parish from locations filters at top of this page, to allocate Enumeration areas to the Interviewer where survey has to be conducted. + +**Name**: Name of the Interviewer + +**Date of Birth**: Date of birth of the Interviewer + +**Gender**: Gender of the Interviewer + +**Education**: Education qualification of the Interviewer + +**Preferred Language**: select, Interviewer’s preferred language to write and speak + +**Survey**: Assign a Survey to the Interviewer, select a Survey from the list + +**Enumeration Area**: The places or locations where assigned Survey is supposed to be conducted by the Interviewer.
+To Search/choose EAs, at least a ‘District’ has to be selected from locations filters at top of this page, then EAs are available. + +**ODK Access**: The process of collecting data using Android channel is known as ODK Access, to provide permissions to access this channel, an Interviewer needs following ODK Access credentials; + +**ODK ID**: It is the User Id / User Name to access ODK channel, only alphabets are accepted + +**ODK Token**: It nothing but password to access ODK channel, only numeric values are accepted + +**Activated**: Allow access or prevent access to ODK channel + +**USSD Access**: The process of collecting data using Featured Phone is known as USSD Access, to access this channel, an Interviewer needs to provide Mobile Number. + +**Add Mobile Number**: System facilitates, accessing from multiple mobile numbers also. So one can add multiple mobile numbers or remove here using ‘Add’ and ‘Delete’ options. + +**Activated**: Allow access or prevent access to USSD channel + +Interviewer is enrolled by providing above all field, finally click on ‘Save’ button to complete the process. + +In this page, Interviewers are also managed, i.e. to Edit Details and block/unblock Interviewer. + +**Actions**: + +![Interviewer](./Interviewer3.png) + +**View Details**: View Interviewer profile and Edit details, all profile details, Survey Details, ODK Access and USSD Access details can be modified, just click on ‘Edit’ button at the bottom of the page. + +**Un Block/ Block**: Option to make Interviewer Active or In-Active to retain in the system. + +**Search**: One can find Interviewers in system by two ways: +**Sort** Interviewers location wise by selecting respective, District, County, Sub-county and Parish using locations filters at top of this page. Or +**Search** using the search bar at top right side of this page, by providing Interviewer’s Name. + +**Export Interviewers**: On click, ‘Export Interviewers’ button, user can download the Interviewers list in “.csv” file format. + +###Indicators +------ +A Data Researcher can choose one or more fields from their survey and create an arithmetic expression to generate metrics as per their requirement. Each such expression is called an Indicator. Any number of indicators can be created for a Survey. A limited number of these indicators can be selected to be shown on the dashboard.
+Our system facilitates creating customized Indicators for every survey and show an analysis report in tabular and bar chart form. + Indicators are accessible from main menu under **Analyse** >> **Indicators** + +![Indicators](./Ind1.png) + +**How to create an Indicator?** + +On the ‘Indicator’ page one can view list of all survey wise Indicators in the application and can view analysis report. + +To create an Indicator, click on ‘Add Indicator’ button at top right of the Indicator page, this will open a blank form with following elements; + +![Indicators](./Ind2.png) + +**Elements in the Indicator form** + +![Indicators](./Ind3.png) + +**Survey**: Select Survey from list, for which the Indicator need to be defined + **Listing**: Select respective Listing, for which the Indicator need to be defined + **Indicator**: Give a name to the Indicator + **Description**: A short description about Indicator + **Variables**: To create an Indicator, certain metrics are needed for calculation, which are derived from survey/ listing questions + **Formulae**: Compose a formula using/based up on available Variables, + While composing formula, auto suggestion feature is available, which will prompt with the "Variables" already defined. + i.e. just type, double curly brackets ( {{ ), to get list of ‘Variables’ defined for an Indicator. + Ex: type {{ , to get “Variables”, then select any Variable to insert. + Like: {{hh_age}}, {{total_men}}, {{men_above_50_years}} + **Sample formula** for *Percentage of Male Age above 50 Years*: {{men_above_50_years}}/{{total_men}}*100 + +![Indicators](./Ind12.png) + +**Display on dashboard**: This option allows the indicator to be displayed on the dashboard. A user can select a maximum of 5 indicators to be shown on the dashboard. + +**Add Variable**: + +To Add a Variable, click on respective ‘+’ icon adjacent to ‘Variables’ Text box, that opens form to define a Variable, following parameters are involved and expressed. + +![Indicators](./Ind5.png) + +![Indicators](./Ind6.png) + +![Indicators](./Ind7.png) + +**Parameters in Variable**: + +**Name**: Define a name to the Variable + +**Description**: A short description about Variable + +**Test question**: Select respective Listing questions, for which the Variable need to be defined. + +**Operator**: Select respective operator based up on the value of the above ‘Test question’ + +Once all the above fields are filled, then click on ‘Add’ button to add to below table ‘Settings for this Variable’, since you can add more Variables for combination, once all Variables are defined then, finally click on ‘Save’ button to add to Variables list. + +![Indicators](./Ind8.png) + +![Indicators](./Ind9.png) + +![Indicators](./Ind10.png) + + One can also ‘Edit’ and ‘Delete’ Variables using the respective icons adjacent to ‘Variables’ Text box. + +![Indicators](./Ind11.png) + +Once Variables are defined, now you can compose the formula in ‘Formulae’ Text box, the composed formula is validated automatically and a message is shown here as ‘Valid’ or ‘in valid’. + +![Indicators](./Ind13.png) + +Once Variables are defined, formula is composed and validated, finally click on ‘Save’ button to create an Indicator. + +On the ‘Indicator’ page one can view all the Indicator created in the application, in a tabular form with following column names: + +**Sts**: Represents Indicator display status on dashboard, that means status with color code indicates as: + + – Not Displayed
+ – Displayed
+ +* **Indicator**: Is the title of Indicator, which is a unique identity to represent Indicator of a Survey. +* **Description**: A short description about Indicator +* **Survey**: Name of the Survey to which this Indicator belongs +* **Batch**: Name of the Survey Batch to which this Indicator belongs + +* **Actions**: + +![Indicators](./Ind4.png) + +* **Edit**: click on, Action Item ‘Edit’, User can Edit all fields in Indicator. +* **Delete**: click on, Action Item ‘Delete’, the Indicator is removed, before deleting you will be prompted with conformation to delete, click ‘Yes’ to Delete permanently +* **Formula**: click on, Action Item ‘Formula’, to edit the Indicator Formula directly +* **Analysis**: click on, Action Item ‘Analysis’, navigates to ‘Indicator Analysis’ page, here one can view two forms of district wise reports based up on the Indicator Formula, + + A Bar chart report is generated and displayed + + A tabular report + +###Manage Users +------ +This section allows to add additional administration users to uSurvey.
+Portal Users’ enrollment is done here by Administrator, this system is developed in the model of ‘Role-based User access’, hence in the system we will find different ‘Roles’, based on application access privileges, these roles are defined. In this context, based on the ‘Roles’ of individual ‘Users’ access to perform a specific task, such as view, create or modify a form are given. + +#####Roles in the System +**Administrator**: Granted full access to View, Add, Edit and Delete in entire application, including creating the users, uploading the location and also clear the data of any Survey/ Listing using ‘Power Mode’, etc.
+**Please note**: This Role is very powerful and one should be very careful while allocation + +**Data collector**: Have permission to View access only, but cannot create new Listing/Survey or any other new records. + +**Data Email Reports**: System will send daily email report of the latest data collected for the last day. who can also monitor the data on the Portal. + +**Researcher**: Have permission to View, Add and Edit Interviewer, Listing, Survey, Group, Module and create questionnaire. + +**Supervisor**: One who is superior to Interviewers, who can view the data of any Interviewer on the phone before submitting to the portal. And have permission to monitor the data on the Portal. + +**Viewer**: Have permission to View Dashboard and Analysis. + +#####Create Users + +Administrator enrolls portal Users and assign Roles. + +Enrolled Users are accessible from main menu under **Settings** >> **Users** + +![Users](./Users1.png) + +On the ‘Users’ page one can view list of enrolled Users in the application, also can Add and Manage Users. + +To enroll a new User, click on ‘Add User’ button at top right of the Users page, this will open a registration form with following elements; + +![Users](./Users2.png) + +![Users](./Users3.png) + +**Elements in the registration form** + +* **First Name**: First Name of the User +* **Last Name**: Last Name of the User +* **Email Address**: email id of the User +* **Mobile Number**: Contact number of the User +* **Roles**: Assign a desired Role to the User +* **User Name**: Enter the desired username for the User, to access the portal +* **Password**: provide password +* **Confirm Password**: conform once again password provided + +A New User is enrolled by providing above all field, finally click on ‘Save Changes’ button to complete the process. + In this page, Users are also managed, i.e. to Edit Details and Activate/Deactivate User + +**Actions**: + +**View Details**: View User profile and Edit details, just click on ‘Edit’ button at the bottom of the page + **Edit**: To quickly edit User profile + **Activate/Deactivate**: Option to make User Active or In-Active to retain in the system. + +* **Search**: One can find Users in system by two ways: +* **Sort** Users by their ‘Status’ by selecting status at top of this page. Or +* **Search** using the search bar at top right side of this page, by providing User’s Name. + +**Export Users**: On click, ‘Download Users’ button, one can export the Users list in “.csv” file format. + diff --git a/docs/Users1.png b/docs/Users1.png new file mode 100644 index 00000000..8fbdf75d Binary files /dev/null and b/docs/Users1.png differ diff --git a/docs/Users2.png b/docs/Users2.png new file mode 100644 index 00000000..920b30dd Binary files /dev/null and b/docs/Users2.png differ diff --git a/docs/Users3.png b/docs/Users3.png new file mode 100644 index 00000000..16c8b2b6 Binary files /dev/null and b/docs/Users3.png differ diff --git a/docs/addvariable.png b/docs/addvariable.png new file mode 100644 index 00000000..07231343 Binary files /dev/null and b/docs/addvariable.png differ diff --git a/administrative_divisions.csv.example b/docs/administrative_divisions.csv.example similarity index 100% rename from administrative_divisions.csv.example rename to docs/administrative_divisions.csv.example diff --git a/docs/administrative_divisions_india.csv.example b/docs/administrative_divisions_india.csv.example new file mode 100644 index 00000000..f19f961a --- /dev/null +++ b/docs/administrative_divisions_india.csv.example @@ -0,0 +1,13 @@ +Country,State,District,Mandal,Village,EANAME +INDIA,ANDHRAPRADESH,PRAKASAM,MARKAPURAM,MARKAPURAM,MARKAPURAM A +INDIA,ANDHRAPRADESH,PRAKASAM,MARKAPURAM,MARKAPURAM,MARKAPURAM B +INDIA,ANDHRAPRADESH,PRAKASAM,YERRRAGONDAPALEM,YERRAGONDAPALEM,YERRAGONDAPALEM +INDIA,ANDHRAPRADESH,GUNTUR,GUNTUR,MACHERLA,MACHERLA PLACES +INDIA,ANDHRAPRADESH,PRAKASAM,MARKAPURAM,DEVARAJUGATTU,DEVARAJUGATTU +INDIA,TELANGANA,HYDERABAD,RANGAREDDY,KUKATPALLY,KUKATPALLY +INDIA,TELANGANA,HYDERABAD,RANGAREDDY,NIJAMPET,NIJAMPET A +INDIA,TELANGANA,HYDERABAD,RANGAREDDY,NIJAMPET,NIJAMPET B +INDIA,TELANGANA,HYDERABAD,RANGAREDDY,NIJAMPET,NIJAMPET C +INDIA,TELANGANA,HYDERABAD,RANGAREDDY,BACHEPALLY,BACHEPALLY +INDIA,TELANGANA,HYDERABAD,RANGAREDDY,LINGAMPALLY,LINGAMPALLY +INDIA,TELANGANA,HYDERABAD,RANGAREDDY,MADHEENAGOODA,MADHEENAGOODA diff --git a/docs/deployment_guide.md b/docs/deployment_guide.md index 39f50fd1..77d8416f 100644 --- a/docs/deployment_guide.md +++ b/docs/deployment_guide.md @@ -32,7 +32,7 @@ For development purpose, please see following considerations: * uSurvey has been tested on Ubuntu and OS X. However it should run on most Linux machines (since there are no distribution specific dependency). -* Minimum of 1GB RAM, 1.6GHz 1 CPU core, 20GB disk space is required for setup and testing. +* 16GB RAM, 2.5GHz 4+ core processors and 200GB disk space should suffice for a typical setup (though actual sizing depends on projected traffic). * Postgres and redis-server needs to be installed and should be running (all can run on same machine, but you'll get better experience with higher RAM and CPU specs in that case) @@ -94,4 +94,4 @@ As an option, three models exist for hosting uSurvey: 1. Local instance deployed and managed on your servers. 2. Dedicated hosting deployed and managed in the cloud. 3. Shared hosting where your instance would coexist with others (although your data would be collected separately). - \ No newline at end of file + diff --git a/docs/docker_installation.md b/docs/docker_installation.md new file mode 100644 index 00000000..c3f6d49e --- /dev/null +++ b/docs/docker_installation.md @@ -0,0 +1,109 @@ +Docker Setup (Linux) +=================== + +* This section covers uSurvey installation from docker image. +* For uSurvey installation from source, [check here](./installation.md) + + +Prerequisites +------------- + +* Linux Machine (Preferably Ubuntu 12+) + +* Minimum of 2GB RAM for test environment + +* 16GB RAM should be enough for production setups with less than 1 million survey submissions per day on a 2GHz clock speed machine. + +* Docker must be installed. You can find details for your Linux [here](https://docs.docker.com/engine/installation/) + + +Quick Start +----------- + +* Clone the uSurvey Application from Github + + git clone https://github.com/unicefuganda/uSurvey.git + + +* Enter the project directory + + cd uSurvey + +* Update the database entries in ``.env`` file in the project directory + +* Run the setup script in the project directory and follow the instructions + + chmod +x docker_setup_linux.sh + + ./docker_setup_linux.sh + + + * This step performs the following activities: + 1. Creates path where database files are stored on host machine + 2. Loads the necessary Permissions categories. + 3. Creates a super user to enable you login to uSurvey (requires you to supply login credentials) + 4. Attempts to setup up the map for your country + +* Once done, enter the following address on your browser to be sure uSurvey is properly setup: + + + http://localhost:8071/ + + + +Customizing the country data +---------------------------- + +###Loading Location Data + +* Before using the setup, you need to load data for administrative divisions of the required country. + + * A sample of the required CSV file is available in the project directory ([administrative_divisions.csv.example](./administrative_divisions.csv.example) for Uganda and [administrative_divisions_india.csv.example](./administrative_divisions_india.csv.example) for India) + +* To load administrative divisions into the system, run the following commands from the project directory and follow the steps: + + + sh ./loaders/load_ea_locations.sh + +####Note +**The first line of the csv file shall be taken as file header.** + +**The file header is expected to contain names as per the administrative division in a comma separated format. In addition, there can be an additional header for enumeration area. This header should be named *EAName*. Sample file for Uganda is included in the project directory as [administrative_divisions.csv.example](./administrative_divisions.csv.example).** + + +###Setting Up Map Reporting + +**If you plan to use uSurvey in Uganda, then this step is not required!** + +**The recommended way to setup the map on linux is using the docker_setup_linux.sh script. However if that fails, the following steps explains how map setup can be done manually** + +To enable uSurvey capture survey data in a specific country's map, you need to update the map settings section within the ``.env`` file. This file is available in the project directory. + +This is required because uSurvey must be made aware of the shape file to use. + +The the map settings section holds sufficient documentation on the purpose of each settings field. + +The expected GeoJson files should be compatible with specification at [http://geojson.org/geojson-spec.html](http://geojson.org/geojson-spec.html) + +The GeoJson file for your country most likely can be downloaded from [https://mapzen.com/data/borders/](https://mapzen.com/data/borders/). + +Several shape files are presented there for each download so be sure to select the file which captures the administrative level map which you are interested in. + + +Starting Up +----------- + +* **By default uSurvey is set to run on 8071, however you can change this in the .env file** +* **You can have uSurvey sit behind a reverse proxy server like nginx also.** +* **With current the docker setup, you can scale up just as easily as with any docker containers.** + +####Note: + * If you have started uSurvey with the docker_setup_linux.sh, then uSurvey must already be up and running. + +* To startup using docker-compose, run the command below: + + docker-compose up -d + +* To stop uSurvey using docker-compose, run the command below: + + docker-compose down diff --git a/docs/index.md b/docs/index.md index dbdf0bcb..bec80f44 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,73 +1,130 @@ Welcome to uSurvey’s documentation! -=================================== +======== uSurvey is an innovative data collection tool designed to provide statistically representative real time estimates of a given indicator. It runs on USSD (Unstructured Supplementary Service Data) interactive secured channel and on ODK (Open Data Kit), for off-line data collection in locations with intermittent mobile network connections. The system has been designed to collect a wide range of data for the structured survey; to generate and produce descriptive statistics and graphical representation of the collected information whenever desired, as well as during the process of data collection. - Source code for this project is available on github [here](https://github.com/unicefuganda/uSurvey/ "github repo"). -Features --------- +###Features +------ * Admin management portal - -* Highly customizable surveys - -* Online data collection via USSD channel - -* Offline data collection via ODK collect - -* Dynamic generation and download of ODK forms from defined survey questions on the admin portal - -* Support for simple conditional question flows - -* Inbuilt ODK aggregator. - - -User Guides ------------ - -* **User Manual** - * [uSurvey User Manual](./user_manual.md#usurvey-user-manual) - * [Login](./user_manual.md#login) - * [Map Page](./user_manual.md#map-page) - * [Interviewer Page](./user_manual.md#interviewer-page) - * [Managing Enumeration Area](./user_manual.md#managing-enumeration-area) - * [Question Module](./user_manual.md#question-module) - * [House Member Group](./user_manual.md#house-member-group) - * [Question Library](./user_manual.md#question-library) - * [Survey Management](./user_manual.md#survey-management) - * [Enabling Batch For Data Collection](./user_manual.md#enabling-batch-for-data-collection) - * [Download Data](./user_manual.md#download-data) - * [Batch Data Collecting Interviewers](./user_manual.md#batch-data-collecting-interviewers) - * [Analysis](./user_manual.md#analysis) - * [Survey Completion Statistics](./user_manual.md#survey-completion-statistics) - * [Managing Users](./user_manual.md#managing-users) - -* **Offline Data Collection** - * [Offline Data Collection](./odk_guide.md#offline-data-collection) - * [What do I need to collect data offline?](./odk_guide.md#what-do-i-need-to-collect-data-offline) - * [How To Use ODK Collect For uSurvey?](./odk_guide.md#how-to-use-odk-collect-for-usurvey) - * [Download ODK collect from Google Play](./odk_guide.md#download-odk-collect-from-google-play) - * [Using the ODK collect](./odk_guide.md#using-the-odk-collect) - -* **USSD Integration** - * [USSD Integration](./ussd-integration.md#ussd-integration) - * [What do I need to conduct survey on USSD?](./ussd-integration.md#what-do-i-need-to-conduct-survey-on-ussd) - -* **Set up Guide** - * [Prerequisites](./installation.md#prerequisites) - * [Installation Instructions](./installation.md#installation-instructions) - * [Before Using The System](./installation.md#before-using-the-system) - * [Starting Up](./installation.md#starting-up) - -* **Deployment Guide** - * [uSurvey Deployment](./deployment_guide.md#usurvey-deployment) - * [Application Architecture](./deployment_guide.md#application-architecture) - * [Components](./deployment_guide.md#components) - * [Deployment Considerations](./deployment_guide.md#deployment-considerations) - * [uSurvey Hosting](./deployment_guide.md#usurvey-hosting) - -* **Tests** - * [Testing](./tests.md) - +* Classification of surveys using Modules +* Classification of population using Groups +* Highly customizable Listings and Surveys +* Preview questionnaire before go-live +* Offline data collection Using Smart Phone +* Online data collection Using Featured Phone +* Download, search and edit submitted forms in uSurvey App +* Country Map is used to demonstrate district wise Completion rates and Indicators +* Customizable Indicators to measure survey results +* Configurable data analysis reports generation + +###Getting Started +------ +**General flow of the uSurvey**: + +* Admin creates uSurvey Users and assigns them Roles +* Data Researcher defines Modules, Groups, Listing, Surveys, Batches and creates questionnaires for Listing & Batch in the application +* Data Researcher also defines ‘Interviewers’ and assigns them to an Enumeration area to conduct Listing or Survey +* In the field, Interviewer does data collection in the designated Enumeration area +* Data collection is done using hand-held mobile devices like Android Smart Phone or Featured Phone via uSurvey App and USSD channel respectively. +* Captured data is sent to uSurvey portal +* Data collected by Interviewers is then viewed on the uSurvey portal for reporting and analysis + +###Work Flow +------ +Following are the simple steps to be executed in the given sequence, to ensure a Survey is completed. + +######Step -1: Create a Module + +Create [New Module](./User_Guides.md#modules) or skip this step, if you want to use existing Modules +######Step -2: Create a Group + +Create [New Group](./User_Guides.md#groups) or skip this step, if you want to use existing Groups +######Step -3: Create a Listing + +In Listing, we have 3 choices as follows: + +
+
3.0. Create New Listing
+                          or
+            Use existing Listing - Proceed to reuse Listing data already collected
+                          or
+            Clone existing Listing – Proceed to reuse Listing questions only
+
+
3.1. Create Questions in Listing
+
3.2. Create Looping in questions (If necessary)
+
3.3. Add Logic to questions (If necessary)
+
+ +######Step -4: Create a Survey + +In Survey, we have 2 choices as follows: + + +
+
4.0. Create New Survey
+                          or
+          Clone existing Survey and rename: Proceed to reuse the survey along with Listing data and ready to use.
+ +
+
4.1. Create Batch
+
4.2. Create Questions in Batch (If necessary)
+
4.3. Create Looping in questions and
+
4.4. Add Logic to questions
+
4.5. Finally, enable Batch for data collection, via Action Item ‘Open/Close’ for Batch
+ Please note that Survey can not be downloaded on Mobile until this step is done +
+ +######Step -5: Enroll Interviewer + +To conduct a Survey in the field, enroll an [Interviewer](./User_Guides.md#interviewer) as follows: + +
+
5.0. Provide basic Interviewer details,
+
5.1. Assign a Survey,
+
5.2. Allocate Enumeration Areas,
+
5.3. Create ODK Access ID,
+
5.4. Provide Mobile Number to access via USSD channel
+
5.5. Finally, finish enrollment
+
+Note: At any point on the uSurvey portal, use breadcrumbs on top of each page for easy navigation. + +######Step -6: Conduct Survey + +Data collection and submission is done in two ways: + + 1. Offline Data Collection - [Using Smart Phone](./ODK_App.md#offline-data-collection) +2. Online Data Collection - [Using Featured Phone](./ODK_App.md#online-data-collection) + +###Complete List of User Guides +------ ++ ######User Manual + - [Modules](./User_Guides.md#modules) + - [Groups](./User_Guides.md#groups) + - [Listing](./User_Guides.md#listing) + - [Survey](./User_Guides.md#create-survey) + - [Question Library](./User_Guides.md#library-questions) + - [Interviewers](./User_Guides.md#interviewer) + - [Indicators](./User_Guides.md#indicators) + - [Manage Users](./User_Guides.md#manage-users) +

++ ######Data Collection
+    - [Introduction](./ODK_App.md#introduction)
+    - [Offline Data Collection](./ODK_App.md#offline-data-collection)
+    - [Online Data Collection](./ODK_App.md#online-data-collection)
+

++ ######Installation Guide
+    - [Prerequisites](./installation.md#prerequisites)
+    - [Installation (from source)](./installation.md#installation-instructions)
+    - [Installation (docker)](./docker_installation.md)
+

++ ######Deployment Guide
+    - [uSurvey Deployment](./deployment_guide.md)
+    - [Application Architecture](./deployment_guide.md#application-architecture)
+    - [Components](./deployment_guide.md#components)
+    - [Hosting](./deployment_guide.md#usurvey-hosting)
+

++ ######Testing & Coverage
+    - [Tests](./tests.md)
diff --git a/docs/installation.md b/docs/installation.md
index cb89ff95..2b6bfe00 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -1,9 +1,21 @@
+uSurvey Installation (From source)
+==================================
+
+* This section covers uSurvey installation from source. 
+
+Installation from docker image
+------------------------------
+* **For uSurvey installation from already prepared docker image, [check here](./docker_installation.md)**
+
+
 Prerequisites
--------------
+------------- 
 
 * Has been tested on Ubuntu and OS X. However it should run on most Linux machines (since there are no distribution specific dependency).
 
-* For development server, minimum of 1GB RAM, 1.6GHz 1 CPU core is required for setup (for production sizing requirements see the relevant section in the [Deployment guide](./deployment_guide.md#deployment-considerations "Deployment Guide")).
+* Minimum of 2GB RAM for test environment 
+
+* 16GB RAM should be enough for production setups with less than 1million survey submissions per day on a 2GHz clock speed machine..
 
 * Postgres, redis-server should be running
 
@@ -17,17 +29,30 @@ Prerequisites
 Installation Instructions
 -------------------------
 
-* Execute the following commands from your installation directory:
+* Clone the uSurvey Application from Github 
 
         git clone https://github.com/unicefuganda/uSurvey.git
 
-        cd uSurvey/mics
+* Switch to dev branch (most recent changes are here. Merge with main branch is in progress)
+
+        cd uSurvey
+        git checkout dev
+
+* Go to mics folder in uSurvey 
+
+        cd mics
+
+* Copy customized settings in localsettings.py 
 
         cp travis-settings.py localsettings.py
         (adjust localsettings.py for db and test_db setup)
+        
+* Go to survey folder in uSurvey
 
         cd ../survey
 
+* Copy config file interviewer_configs.py
+
         cp interviewer_configs.py.example interviewer_configs.py
 
         cd ..
@@ -103,4 +128,4 @@ Starting Up
         supervisord -c supervisord.conf
 
 > In supervisord.conf, the configuration under [program:odk-server] is required to serve ODK requests, while the configuration under [program:django-interface-server] is for serving other requests.
-> Only the ports configured in [program:odk-server] and [program:django-interface-server] are required to handle requests. Other ports configured on supervisord.conf file are for managing supervisor
+> Only the ports configured in [program:odk-server] and [program:web-app-server] are required to handle requests. Other ports configured on supervisord.conf file are for managing supervisor
diff --git a/docs/odk_guide.md b/docs/odk_guide.md
index 17494992..75ad2498 100644
--- a/docs/odk_guide.md
+++ b/docs/odk_guide.md
@@ -1,30 +1,3 @@
-Introduction
-============
-
-Once a survey is designed on the uSurvey portal, the data can be collected online using the USSD channel or offline using ODK Collect.
-
-Offline data collection on uSurvey happens with the use of [ODK Collect](./https://play.google.com/store/apps/details?id=org.odk.collect.android&hl=en). 
-
-ODK Collect provides support for richer datasets compared to USSD (for example, it supports media files and GPS location capture). 
-
-Because data collection on ODK is entirely offline, it enables data collection in regions where network connectivity might be an issue. For this, data can be collected seamlessly for as many participants as possible into corresponding Android device and upon completion, all the data can be uploaded once to the server in a location with connectivity.
-  
-
-###What do I need to collect data offline?
-
-1. An Android device able to run [ODK Collect](./https://play.google.com/store/apps/details?id=org.odk.collect.android&hl=en). Typically Jelly Bean and newer versions should be fine.
-2. The Android device needs to have some space to keep collected data (If your survey does not require the upload of media files, 100MB space would usually be big enough. But having 1GB dedicated for uSurvey is best). 
-2. You need to configure ODK collect to use the in-built ODK aggregator in uSurvey (described below).
-3. Enter your interviewer credentials and download the survey allocated to you
-
-Once you have completed the above steps, you can collect the survey data completely offline! Upon completion, you move to a location with network connectivity to upload to uSurvey.  
-
-
-
-
-How To Use ODK Collect For uSurvey?
-==================================
-
 To use the ODK Collect for uSurvey is easy. Just follow the steps below and you are good to go.
 
 ###Download ODK collect from Google Play
diff --git a/docs/tests.md b/docs/tests.md
index 63c39f1b..d20aa3ec 100644
--- a/docs/tests.md
+++ b/docs/tests.md
@@ -18,5 +18,4 @@ Travis builds are a pass only when the the project builds without errors and all
         coverage report
 
 
-[![Build Status](https://travis-ci.org/unicefuganda/uSurvey.svg)](https://travis-ci.org/unicefuganda/uSurvey)
-[![Coverage Status](https://coveralls.io/repos/unicefuganda/uSurvey/badge.png)](https://coveralls.io/r/unicefuganda/uSurvey)
\ No newline at end of file
+[![Build Status](https://travis-ci.org/unicefuganda/uSurvey.svg)](https://travis-ci.org/unicefuganda/uSurvey)
\ No newline at end of file
diff --git a/docs/uSurvey-Deployment-Architecture.jpg b/docs/uSurvey-Deployment-Architecture.jpg
index d43abb76..9f65c19b 100644
Binary files a/docs/uSurvey-Deployment-Architecture.jpg and b/docs/uSurvey-Deployment-Architecture.jpg differ
diff --git a/docs/uSurvey_LOC.jpg b/docs/uSurvey_LOC.jpg
new file mode 100644
index 00000000..e8c93ea1
Binary files /dev/null and b/docs/uSurvey_LOC.jpg differ
diff --git a/docs/uSurvey_flow.png b/docs/uSurvey_flow.png
new file mode 100644
index 00000000..a2d0fd53
Binary files /dev/null and b/docs/uSurvey_flow.png differ
diff --git a/docs/uSurvey_manual.md b/docs/uSurvey_manual.md
new file mode 100644
index 00000000..f2781e62
--- /dev/null
+++ b/docs/uSurvey_manual.md
@@ -0,0 +1,43 @@
+Welcome to uSurvey’s documentation!
+========
+uSurvey is an innovative data collection tool designed to provide statistically representative real time estimates of a given indicator. It runs on USSD (Unstructured Supplementary Service Data) interactive secured channel and on ODK (Open Data Kit), for off-line data collection in locations with intermittent mobile network connections.
+
+The system has been designed to collect a wide range of data for the structured survey; to generate and produce descriptive statistics and graphical representation of the collected information whenever desired, as well as during the process of data collection.
+Source code for this project is available on github [here]()
+
+User Guides
+-----------
+
+* **Dashboard**
+
+* **Design** 
+   * [Survey](./Survey.md)
+   * [Listing](./Listing.md)
+   * [Modules](./Modules.md)
+   * [Groups](./Groups.md)
+   * [Question Library](./Library.md)
+
+* **Administration** 
+  * [Interviewers](./Interviewer.md)
+  * [Notification](http://usurvey.unicefuganda.org/bulk_sms)
+  * [Data Entry](#)
+
+* **Monitor**
+   * [Listing Data](#)
+   * [Batch Data](#)
+   * [Completion Summary](http://usurvey.unicefuganda.org/surveys/interviewers_completion/)
+   * [ODK Submission](http://usurvey.unicefuganda.org/odk/aggregate/submission_list/)
+
+* **Analyse** 
+  * [Indicators](http://usurvey.unicefuganda.org/indicators/)
+  * [Result](#)
+  * [Location Weights](http://usurvey.unicefuganda.org/locations/weights/)
+
+* **Download**
+   * [Survey Data](http://usurvey.unicefuganda.org/aggregates/download_spreadsheet)
+   * [Mobile Money Sheet](http://usurvey.unicefuganda.org/interviewer_report/)
+
+* **Settings**
+   * [Users](http://usurvey.unicefuganda.org/users/)
+   * [Locations](http://usurvey.unicefuganda.org/enumeration_area/)
+   * [Setup](#)
\ No newline at end of file
diff --git a/manage.py b/manage.py
deleted file mode 100755
index 14ae57cc..00000000
--- a/manage.py
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env python
-import os
-import sys
-
-if __name__ == "__main__":
-    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mics.settings")
-
-    from django.core.management import execute_from_command_line
-
-    execute_from_command_line(sys.argv)
diff --git a/mics/__init__.py b/mics/__init__.py
deleted file mode 100644
index 396f90fd..00000000
--- a/mics/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# # register a signal do update permissions every migration.
-# # This is based on app django_extensions update_permissions command
-# from south.signals import post_migrate
-#
-# def update_permissions_after_migration(app,**kwargs):
-#     """
-#     Update app permission just after every migration.
-#     This is based on app django_extensions update_permissions management command.
-#     """
-#     import settings
-#     from django.db.models import get_app, get_models
-#     from django.contrib.auth.management import create_permissions
-#
-#     create_permissions(get_app(app), get_models(), 2 if settings.DEBUG else 0)
-#
-# post_migrate.connect(update_permissions_after_migration)
diff --git a/mics/asgi.py b/mics/asgi.py
deleted file mode 100644
index f5e1ccfd..00000000
--- a/mics/asgi.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import os
-from channels.asgi import get_channel_layer
-
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mics.settings")
-
-channel_layer = get_channel_layer()
diff --git a/mics/include/python2.7 b/mics/include/python2.7
deleted file mode 120000
index ad4ca80b..00000000
--- a/mics/include/python2.7
+++ /dev/null
@@ -1 +0,0 @@
-/usr/include/python2.7
\ No newline at end of file
diff --git a/mics/local/__init__.py b/mics/local/__init__.py
deleted file mode 120000
index fa871271..00000000
--- a/mics/local/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-/home/manish/projects/uSurvey/mics/mics/__init__.py
\ No newline at end of file
diff --git a/mics/local/include b/mics/local/include
deleted file mode 120000
index 985d4caf..00000000
--- a/mics/local/include
+++ /dev/null
@@ -1 +0,0 @@
-/home/manish/projects/uSurvey/mics/mics/include
\ No newline at end of file
diff --git a/mics/local/settings.py b/mics/local/settings.py
deleted file mode 120000
index b1ce1368..00000000
--- a/mics/local/settings.py
+++ /dev/null
@@ -1 +0,0 @@
-/home/manish/projects/uSurvey/mics/mics/settings.py
\ No newline at end of file
diff --git a/mics/local/snap-ci b/mics/local/snap-ci
deleted file mode 120000
index 74efb735..00000000
--- a/mics/local/snap-ci
+++ /dev/null
@@ -1 +0,0 @@
-/home/manish/projects/uSurvey/mics/mics/snap-ci
\ No newline at end of file
diff --git a/mics/local/testsettings.py b/mics/local/testsettings.py
deleted file mode 120000
index b51273f1..00000000
--- a/mics/local/testsettings.py
+++ /dev/null
@@ -1 +0,0 @@
-/home/manish/projects/uSurvey/mics/mics/testsettings.py
\ No newline at end of file
diff --git a/mics/local/travis-settings.py b/mics/local/travis-settings.py
deleted file mode 120000
index 6d4ed3cf..00000000
--- a/mics/local/travis-settings.py
+++ /dev/null
@@ -1 +0,0 @@
-/home/manish/projects/uSurvey/mics/mics/travis-settings.py
\ No newline at end of file
diff --git a/mics/local/urls.py b/mics/local/urls.py
deleted file mode 120000
index 332bf6d3..00000000
--- a/mics/local/urls.py
+++ /dev/null
@@ -1 +0,0 @@
-/home/manish/projects/uSurvey/mics/mics/urls.py
\ No newline at end of file
diff --git a/mics/local/wsgi.py b/mics/local/wsgi.py
deleted file mode 120000
index 19679f5c..00000000
--- a/mics/local/wsgi.py
+++ /dev/null
@@ -1 +0,0 @@
-/home/manish/projects/uSurvey/mics/mics/wsgi.py
\ No newline at end of file
diff --git a/mics/routing.py b/mics/routing.py
deleted file mode 100644
index f0c71611..00000000
--- a/mics/routing.py
+++ /dev/null
@@ -1,38 +0,0 @@
-__author__ = 'anthony'
-from django.conf import settings
-from channels.routing import route
-from channels import Channel, Group
-from channels.sessions import channel_session
-from channels.auth import http_session_user, channel_session_user, channel_session_user_from_http
-
-
-def get_group_path(user, path):
-    path = path.strip("/")
-    return '%s/%s' % (path, user.pk)
-
-
-@channel_session_user_from_http
-def ws_add(message):
-    print 'incoming ', message.user.pk
-    path = message.content['path']
-    if message.user.is_authenticated():
-        Group(get_group_path(message.user, path)).add(message.reply_channel)
-
-
-@channel_session_user
-def ws_message(message):
-    pass
-
-
-@channel_session_user
-def ws_disconnect(message):
-    path = message.content['path']
-    if message.user.is_authenticated():
-        Group(get_group_path(message.user, path)
-              ).discard(message.reply_channel)
-
-channel_routing = [
-    route("websocket.connect", ws_add, path=r"^%s$" % settings.WEBSOCKET_URL),
-    route("websocket.receive", ws_message),
-    route("websocket.disconnect", ws_disconnect),
-]
diff --git a/mics/settings.py b/mics/settings.py
deleted file mode 100644
index deb3c354..00000000
--- a/mics/settings.py
+++ /dev/null
@@ -1,372 +0,0 @@
-# Django settings for mics project.
-import os
-BASE_DIR = os.path.dirname(os.path.dirname(__file__))
-
-DEBUG = False
-TEMPLATE_DEBUG = DEBUG
-
-ADMINS = (
-    # ('Your Name', 'your_email@example.com'),
-)
-
-PROJECT_TITLE = 'uSurvey'
-COUNTRY = 'UGANDA'
-
-MANAGERS = ADMINS
-
-DATABASES = {
-    'default': {
-        # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
-        'ENGINE': 'django.db.backends.',
-        # Or path to database file if using sqlite3.
-        'NAME': '',
-        # The following settings are not used with sqlite3:
-        'USER': '',
-        'PASSWORD': '',
-        # Empty for localhost through domain sockets or '127.0.0.1' for
-        # localhost through TCP.
-        'HOST': '',
-        'PORT': '',                      # Set to empty string for default.
-    }
-}
-
-CACHES = {
-    'default': {
-        'BACKEND': 'redis_cache.RedisCache',
-        'LOCATION': [
-            '127.0.0.1:6379',
-        ],
-        'OPTIONS': {
-            'DB': 1,
-            'PARSER_CLASS': 'redis.connection.HiredisParser',
-            'CONNECTION_POOL_CLASS': 'redis.BlockingConnectionPool',
-            'CONNECTION_POOL_CLASS_KWARGS': {
-                'max_connections': 50,
-                'timeout': 500,
-            },
-            'MAX_CONNECTIONS': 1000,
-            'PICKLE_VERSION': -1,
-        },
-    },
-}
-
-# Hosts/domain names that are valid for this site; required if DEBUG is False
-# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
-ALLOWED_HOSTS = ['127.0.0.1', 'localhost']
-
-# Local time zone for this installation. Choices can be found here:
-# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
-# although not all choices may be available on all operating systems.
-# In a Windows environment this must be set to your system time zone.
-TIME_ZONE = 'Africa/Kampala'
-
-# Language code for this installation. All choices can be found here:
-# http://www.i18nguy.com/unicode/language-identifiers.html
-LANGUAGE_CODE = 'en-us'
-
-SITE_ID = 1
-
-# If you set this to False, Django will make some optimizations so as not
-# to load the internationalization machinery.
-USE_I18N = True
-
-# If you set this to False, Django will not format dates, numbers and
-# calendars according to the current locale.
-USE_L10N = True
-
-# If you set this to False, Django will not use timezone-aware datetimes.
-USE_TZ = True
-
-# Absolute filesystem path to the directory that will hold user-uploaded files.
-# Example: "/var/www/example.com/media/"
-MEDIA_ROOT = ''
-
-# URL that handles the media served from MEDIA_ROOT. Make sure to use a
-# trailing slash.
-# Examples: "http://example.com/media/", "http://media.example.com/"
-MEDIA_URL = ''
-
-# Absolute path to the directory static files should be collected to.
-# Don't put anything in this directory yourself; store your static files
-# in apps' "static/" subdirectories and in STATICFILES_DIRS.
-# Example: "/var/www/example.com/static/"
-STATIC_ROOT = ''
-
-# URL prefix for static files.
-# Example: "http://example.com/static/", "http://static.example.com/"
-STATIC_URL = '/static/'
-
-# Additional locations of static files
-STATICFILES_DIRS = (
-    # Put strings here, like "/home/html/static" or "C:/www/django/static".
-    # Always use forward slashes, even on Windows.
-    # Don't forget to use absolute paths, not relative paths.
-)
-
-# List of finder classes that know how to find static files in
-# various locations.
-STATICFILES_FINDERS = (
-    'django.contrib.staticfiles.finders.FileSystemFinder',
-    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
-    #    'django.contrib.staticfiles.finders.DefaultStorageFinder',
-)
-
-# Make this unique, and don't share it with anybody.
-SECRET_KEY = '6-bycz-+xpv@9+u8b^)#$-l&3cheum3i4cb_6$u6s%j6uu6s91'
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
-    'django.template.loaders.filesystem.Loader',
-    'django.template.loaders.app_directories.Loader',
-    #     'django.template.loaders.eggs.Loader',
-)
-
-TEMPLATE_CONTEXT_PROCESSORS = (
-    "django.contrib.auth.context_processors.auth",
-    "django.core.context_processors.debug",
-    "django.core.context_processors.i18n",
-    "django.core.context_processors.media",
-    "django.core.context_processors.static",
-    "django.contrib.messages.context_processors.messages",
-    "survey.context_processor.context_extras",
-    "django.core.context_processors.request",
-)
-
-MIDDLEWARE_CLASSES = (
-    'django.middleware.common.CommonMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.middleware.csrf.CsrfViewMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django.contrib.messages.middleware.MessageMiddleware',
-    'pagination_bootstrap.middleware.PaginationMiddleware',
-    'breadcrumbs.middleware.BreadcrumbsMiddleware',
-    # Uncomment the next line for simple clickjacking protection:
-    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
-)
-
-ROOT_URLCONF = 'mics.urls'
-
-# Python dotted path to the WSGI application used by Django's runserver.
-WSGI_APPLICATION = 'mics.wsgi.application'
-
-TEMPLATE_DIRS = (
-    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
-    # Always use forward slashes, even on Windows.
-    # Don't forget to use absolute paths, not relative paths.
-    os.path.join(BASE_DIR, 'survey', 'templates'),
-)
-
-INSTALLED_APPS = (
-    'django.contrib.contenttypes',
-    'django.contrib.auth',
-    'django.contrib.sessions',
-    'django.contrib.sites',
-    'django.contrib.messages',
-    'django.contrib.staticfiles',
-    'django.contrib.admin',
-    'django_nose',
-    'lettuce.django',
-    'django_extensions',
-    'pagination_bootstrap',
-    'cacheops',
-    'survey',
-    'mptt',
-    'django_rq',
-    'django_rq_dashboard',
-    'channels',
-    # Uncomment the next line to enable the admin:
-    # 'django.contrib.admin',
-    # Uncomment the next line to enable admin documentation:
-    # 'django.contrib.admindocs',
-)
-
-CHANNEL_LAYERS = {
-    "default": {
-        "BACKEND": "asgi_redis.RedisChannelLayer",
-        "CONFIG": {
-            "hosts": [("localhost", 6379)],
-        },
-        "ROUTING": "mics.routing.channel_routing",
-    },
-}
-
-
-# A sample logging configuration. The only tangible logging
-# performed by this configuration is to send an email to
-# the site admins on every HTTP 500 error when DEBUG=False.
-# See http://docs.djangoproject.com/en/dev/topics/logging for
-# more details on how to customize your logging configuration.
-LOGGING = {
-    'version': 1,
-    'disable_existing_loggers': False,
-    'filters': {
-        'require_debug_false': {
-            '()': 'django.utils.log.RequireDebugFalse'
-        }
-    },
-    'handlers': {
-        'mail_admins': {
-            'level': 'ERROR',
-            'filters': ['require_debug_false'],
-            'class': 'django.utils.log.AdminEmailHandler'
-        }
-    },
-    'loggers': {
-        'django.request': {
-            'handlers': ['mail_admins'],
-            'level': 'ERROR',
-            'propagate': True,
-        },
-    }
-}
-
-CACHEOPS_REDIS = {
-    'host': 'localhost',  # redis-server is on same machine
-    'port': 6379,        # default redis port
-    'db': 1,             # SELECT non-default redis database
-                         # using separate redis db or redis instance
-                         # is highly recommended
-
-}
-
-CACHE_REFRESH_DURATION = 10800
-CACHEOPS = {
-    # refresh every 3 hrs
-    'survey.point': {'ops': ('all', ), 'timeout': CACHE_REFRESH_DURATION},
-    # refresh every 3 hrs
-    'survey.locationtype': {'ops': ('all', ), 'timeout': CACHE_REFRESH_DURATION},
-    # refresh every 3 hrs
-    'survey.location': {'ops': ('all', ), 'timeout': CACHE_REFRESH_DURATION},
-    # refresh every 3 hrs
-    'survey.enumerationarea': {'ops': ('all', ), 'timeout': CACHE_REFRESH_DURATION},
-    # refresh every 3 hrs,
-    'survey.batch': {'ops': (), 'timeout': CACHE_REFRESH_DURATION},
-    # refresh every 3 hrs,
-    'survey.survey': {'ops': (), 'timeout': CACHE_REFRESH_DURATION},
-}
-
-# DJANGO-WS CONFIG
-WEBSOCKET_URL = '/ws/statusbar'
-WS_HEARTBEAT = 3
-UPDATE_INTERVAL = 3  # INTERVAL BETWEEN UPDATES IN SECS
-# how long downloaded results would cached in secs before discarded
-DOWNLOAD_CACHE_DURATION = 1800
-DOWNLOAD_CACHE_KEY = '/DOWNLOADS/EXPORT/BATCH/%(user_id)s/%(batch_id)s'
-
-SURVEY_REDIS_KEY = "/usurvey/completion_rates/%(survey_id)s"
-
-TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
-
-
-INSTALLED_BACKENDS = {
-    # "HTTP": {
-    #     "ENGINE": "rapidsms.backends.database.DatabaseBackend",
-    # },
-}
-
-COUNTRY_CODE = 'UG'
-COUNTRY_PHONE_CODE = '256'
-
-PRODUCTION = False
-
-# cookies settings
-SESSION_EXPIRE_AT_BROWSER_CLOSE = True
-SESSION_COOKIE_AGE = 1800
-SESSION_SAVE_EVERY_REQUEST = True
-
-# email settings
-EMAIL_USE_TLS = True
-EMAIL_HOST = 'smtp.gmail.com'
-EMAIL_PORT = 587
-EMAIL_HOST_USER = ''
-EMAIL_HOST_PASSWORD = ''
-DEFAULT_EMAIL_SENDER = ''
-
-# odk settings
-TOKEN_DEFAULT_SIZE = 5
-ODK_DEFAULT_TOKEN = '12345'
-SUBMISSION_UPLOAD_BASE = os.path.join(BASE_DIR, 'submissions')
-ANSWER_UPLOADS = os.path.join(BASE_DIR, 'answerFiles')
-TEMP_DIR = os.path.join(BASE_DIR, 'tmp')
-ODK_SUBMISSION_SUCCESS_MSG = "Successful submission. Your submission is been Processed"
-INTERVIEWER_EXPORT_HEADERS = [
-    'ea', 'name', 'age', 'level_of_education', 'language', 'mobile_numbers', 'odk_id']
-from collections import OrderedDict
-HOUSEHOLD_EXPORT_HEADERS = OrderedDict([
-    ('HOUSE NUMBER', 'house_number'),
-    ('PHYSICAL ADDRESS', 'physical_address'),
-    ('HEAD MEMBER',  'head_desc'),
-    ('SEX', 'head_sex'),
-    ('ENUMERATION AREA', 'listing__ea__name'),
-    ('REGISTRAR', 'last_registrar__name'),
-    ('REGISTRATION_CHANNEL',
-     'registration_channel'),
-    ('SURVEY_LISTING',
-     'listing__initial_survey__name')
-])
-AGGREGATORS = [('testAggregator', 'testAggregator'), ]
-DEFAULT_AGGREGATOR = 'testAggregator'
-TWITTER_URL = 'https://twitter.com/unicefuganda'
-TWITTER_TOKEN = '617036281340657664'
-
-###USSD config ##
-USSD_NEXT = '*'
-USSD_PREVIOUS = '#'
-USSD_ITEMS_PER_PAGE = 10
-USSD_RESTART = '##'
-# USSD_STARTER = 'survey.ussd.flows.Start'
-USSD_IGNORED_CHARACTERS = "*!#';&"
-MAX_DISPLAY_PER_PAGE = 50
-DEFAULT_TOTAL_HOUSEHOLDS_IN_EA = 1000
-DATE_FORMAT = "%d-%m-%Y"
-MOBILE_NUM_MIN_LENGTH = 9
-MOBILE_NUM_MAX_LENGTH = 9
-LOOP_QUESTION_REPORT_DEPT = 3  # reports up to 5 question loops
-SHAPE_FILE_URI = '/static/map_resources/uganda_districts_2011_005.json'
-SHAPE_FILE_LOC_FIELD = 'DNAME_2010'
-SHAPE_FILE_LOC_ALT_FIELD = 'DNAME_2006'
-
-RESULT_REFRESH_FREQ = 6
-MEMORIZE_TIMEOUT = 120
-
-RQ_QUEUES = {
-    'default': {
-        'HOST': 'localhost',
-        'PORT': 6379,
-        'DB': 0,
-        'DEFAULT_TIMEOUT': 360,
-    },
-    'results-queue': {
-        'HOST': 'localhost',
-        'PORT': 6379,
-        'DB': 0,
-        'DEFAULT_TIMEOUT': 360,
-    },
-    'email': {
-        'HOST': 'localhost',
-        'PORT': 6379,
-        'DB': 0,
-    },
-    'ws-notice': {
-        'HOST': 'localhost',
-        'PORT': 6379,
-        'DB': 0,
-    },
-    'upload_task': {
-        'HOST': 'localhost',
-        'PORT': 6379,
-        'DB': 0,
-    },
-    'odk': {
-        'HOST': 'localhost',
-        'PORT': 6379,
-        'DB': 0,
-    }
-}
-
-##end USSD config ##
-# Importing server specific settings
-try:
-    from localsettings import *
-except ImportError:
-    pass
diff --git a/mics/snap-ci/ft.sh b/mics/snap-ci/ft.sh
deleted file mode 100644
index cee0ac50..00000000
--- a/mics/snap-ci/ft.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-source ../mics_env/bin/activate
-cp mics/snap-ci/snap-settings.py mics/localsettings.py
-cp survey/investigator_configs.py.example survey/investigator_configs.py
-./manage.py harvest
diff --git a/mics/snap-ci/setup.sh b/mics/snap-ci/setup.sh
deleted file mode 100644
index 219e6a9d..00000000
--- a/mics/snap-ci/setup.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/sh
-
-wget http://phantomjs.googlecode.com/files/phantomjs-1.9.1-linux-x86_64.tar.bz2
-tar xjf phantomjs-1.9.1-linux-x86_64.tar.bz2
-sudo rm -rf /opt/local/phantomjs
-sudo mv  phantomjs-1.9.1-linux-x86_64 /opt/local/phantomjs
-
-sudo yum remove -y libmemcached libmemcached-devel
-sudo wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
-sudo wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
-sudo rpm -Uvh remi-release-6*.rpm epel-release-6*.rpm
-sudo yum --enablerepo=remi install -y libmemcached-last libmemcached-last-devel
-sudo yum install -y memcached
-sudo service memcached restart
-
-cd ..
-virtualenv mics_env
-source mics_env/bin/activate
-cd -
-pip install -r pip-requires.txt
-cp mics/snap-ci/snap-settings.py mics/localsettings.py
-cp survey/investigator_configs.py.example survey/investigator_configs.py
-./manage.py syncdb --noinput
-./manage.py migrate
-
diff --git a/mics/snap-ci/snap-settings.py b/mics/snap-ci/snap-settings.py
deleted file mode 100644
index e7362208..00000000
--- a/mics/snap-ci/snap-settings.py
+++ /dev/null
@@ -1,40 +0,0 @@
-DATABASES = {
-    "default": {
-        "ENGINE": "django.db.backends.postgresql_psycopg2",
-        "NAME": "app_test",
-        "USER": "go",
-        "PASSWORD": "go",
-        "HOST": "localhost",
-    }
-}
-
-CACHES = {
-    'default': {
-        'BACKEND': 'django_pylibmc.memcached.PyLibMCCache',
-        'LOCATION': 'localhost:11211',
-        'TIMEOUT': 3,
-        'BINARY': False,
-        'OPTIONS': {  # Maps to pylibmc "behaviors"
-            'tcp_nodelay': True,
-            'ketama': True
-        }
-    }
-}
-
-INSTALLED_BACKENDS = {
-    "HTTP": {
-        "ENGINE": "rapidsms.backends.database.DatabaseBackend",
-    },
-}
-
-LETTUCE_AVOID_APPS = (
-    'django_nose',
-    'south',
-    'django_extensions',
-    'rapidsms.contrib.locations',
-    'rapidsms.contrib.locations.nested',
-    'bootstrap_pagination',
-    'rapidsms.backends.database',
-    'rapidsms.contrib.httptester',
-    'djcelery',
-)
diff --git a/mics/snap-ci/ut.sh b/mics/snap-ci/ut.sh
deleted file mode 100644
index 1d5e31fa..00000000
--- a/mics/snap-ci/ut.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-source ../mics_env/bin/activate
-cp mics/snap-ci/snap-settings.py mics/localsettings.py
-cp survey/investigator_configs.py.example survey/investigator_configs.py
-./manage.py test
diff --git a/mics/travis-settings.py b/mics/travis-settings.py
deleted file mode 100644
index 8e83de46..00000000
--- a/mics/travis-settings.py
+++ /dev/null
@@ -1,30 +0,0 @@
-DATABASES = {
-    "default": {
-        "ENGINE": "django.db.backends.postgresql_psycopg2",
-        "NAME": "mics_test",
-        "USER": "postgres",
-        "PASSWORD": "",
-        "HOST": "localhost",
-    }
-
-}
-
-CACHES = {
-    'default': {
-        'BACKEND': 'redis_cache.RedisCache',
-        'LOCATION': [
-            '127.0.0.1:6379',
-        ],
-        'OPTIONS': {
-            'DB': 1,
-            'PARSER_CLASS': 'redis.connection.HiredisParser',
-            'CONNECTION_POOL_CLASS': 'redis.BlockingConnectionPool',
-            'CONNECTION_POOL_CLASS_KWARGS': {
-                'max_connections': 50,
-                'timeout': 5000,
-            },
-            'MAX_CONNECTIONS': 1000,
-            'PICKLE_VERSION': -1,
-        },
-    },
-}
diff --git a/mics/urls.py b/mics/urls.py
deleted file mode 100644
index d55dab66..00000000
--- a/mics/urls.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from django.conf.urls import patterns, include, url
-from django.contrib import admin
-from survey.urls import urlpatterns as survey_urls
-from django.conf import settings
-
-admin.autodiscover()
-
-urlpatterns = patterns('',
-                       (r'^admin/', include(admin.site.urls)),
-                       (r'^admin/rq/', include('django_rq_dashboard.urls')),
-                       # Examples:
-                       # url(r'^$', 'mics.views.home', name='home'),
-                       # url(r'^mics/', include('mics.foo.urls')),
-
-                       # Uncomment the admin/doc line below to enable admin documentation:
-                       # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
-
-                       # Uncomment the next line to enable the admin:
-                       # url(r'^admin/', include(admin.site.urls)),
-                       ) + survey_urls
-
-# Static content serving if not handled by web server
-urlpatterns += patterns('',
-                        (r'^static/(?P.*)$', 'django.views.static.serve',
-                         {'document_root': settings.STATIC_ROOT, 'show_indexes': False}),
-                        (r'^media/(?P.*)$', 'django.views.static.serve',
-                         {'document_root': settings.MEDIA_ROOT, 'show_indexes': False}),
-                        )
diff --git a/mics/wsgi.py b/mics/wsgi.py
deleted file mode 100644
index bc6fe919..00000000
--- a/mics/wsgi.py
+++ /dev/null
@@ -1,32 +0,0 @@
-"""
-WSGI config for mics project.
-
-This module contains the WSGI application used by Django's development server
-and any production WSGI deployments. It should expose a module-level variable
-named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
-this application via the ``WSGI_APPLICATION`` setting.
-
-Usually you will have the standard Django WSGI application here, but it also
-might make sense to replace the whole Django WSGI application with a custom one
-that later delegates to the Django one. For example, you could introduce WSGI
-middleware here, or combine a Django application with an application of another
-framework.
-
-"""
-import os
-
-# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
-# if running multiple sites in the same mod_wsgi process. To fix this, use
-# mod_wsgi daemon mode with each site in its own daemon process, or use
-#os.environ["DJANGO_SETTINGS_MODULE"] = "mics.settings"
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mics.settings")
-
-# This application object is used by any WSGI server configured to use this
-# file. This includes Django's development server, if the WSGI_APPLICATION
-# setting points here.
-from django.core.wsgi import get_wsgi_application
-application = get_wsgi_application()
-
-# Apply WSGI middleware here.
-# from helloworld.wsgi import HelloWorldApplication
-# application = HelloWorldApplication(application)
diff --git a/mkdocs.yml b/mkdocs.yml
index 567a9416..a04e7efc 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -1,11 +1,12 @@
 site_name: uSurvey
 pages:
 - Home: index.md
-- User Manual: user_manual.md
-- Offline Data Collection: odk_guide.md
-- USSD Integration: ussd-integration.md
-- Set up Guide: installation.md
+- User Manual: User_Guides.md
+- Data Collection: ODK_App.md
+- Installation Guide: installation.md
+- Installation Guide (Docker): docker_installation.md
 - Deployment Guide: deployment_guide.md
-- Tests: tests.md
+- Testing and Coverage: tests.md
+
 #theme: readthedocs
-theme: cerulean
+#theme: cerulean
diff --git a/nginx.snippet b/nginx.snippet
deleted file mode 100644
index dd0c6ce7..00000000
--- a/nginx.snippet
+++ /dev/null
@@ -1,33 +0,0 @@
-        server {
-           server_name example.org;
-            listen 443;
-
-                ssl on;
-                ssl_certificate /etc/ssl/example.org.crt;
-                ssl_certificate_key /etc/ssl/examples.org.key;
-
-
-           location /ws/ {
-                proxy_set_header Host $host;
-                proxy_pass http://127.0.0.1:{APP_PORT};
-                proxy_http_version 1.1;
-                proxy_set_header Upgrade $http_upgrade;
-                proxy_set_header Connection "upgrade";
-           }
-
-           location /static {
-              root {PATH_TO_STATIC_FILES};
-         }
-
-           location /odk/collect {
-             proxy_set_header Host $host;
-             proxy_pass http://127.0.0.1:{ODK_SERVER_PORT};
-
-         }
-
-           location / {
-              proxy_set_header Host $host;
-             proxy_pass http://127.0.0.1:{APP_PORT};
-        }
-
-       }
diff --git a/restart_supervisor.sh b/restart_supervisor.sh
deleted file mode 100644
index 6d1a274d..00000000
--- a/restart_supervisor.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-echo 'stoping supervisor...'
-{ # your 'try' block
-    kill $(cat supervisord.pid)
-} || { # your 'catch' block
-    echo 'looks like supervisor is not running!'
-}
-echo 'starting supervisor...'
-supervisord
diff --git a/snap-settings.py b/snap-settings.py
deleted file mode 100644
index fe7846fe..00000000
--- a/snap-settings.py
+++ /dev/null
@@ -1,22 +0,0 @@
-DATABASES = {
-    "default": {
-        "ENGINE": "django.db.backends.postgresql_psycopg2",
-        "NAME": "app_test",
-        "USER": "go",
-        "PASSWORD": "go",
-        "HOST": "localhost",
-    }
-}
-
-CACHES = {
-    'default': {
-        'BACKEND': 'django_pylibmc.memcached.PyLibMCCache',
-        'LOCATION': 'localhost:11211',
-        'TIMEOUT': 500,
-        'BINARY': False,
-        'OPTIONS': {  # Maps to pylibmc "behaviors"
-            'tcp_nodelay': True,
-            'ketama': True
-        }
-    }
-}
diff --git a/supervisord.conf b/supervisord.conf
deleted file mode 100644
index fb00e6dc..00000000
--- a/supervisord.conf
+++ /dev/null
@@ -1,44 +0,0 @@
-[inet_http_server]
-port = localhost:9001
-
-[supervisord]
-logfile = supervisord.log
-pidfile = supervisord.pid
-
-[supervisorctl]
-serverurl = http://localhost:9001
-
-[rpcinterface:supervisor]
-supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
-
-[program:odk-server]
-command=gunicorn -b 0.0.0.0:8080 --workers=3 mics.wsgi
-stdout_logfile=./odk-log.log
-stderr_logfile=./odk-log.log
-stderr_events_enabled=true
-
-[program:django-interface-server]
-command=daphne -p 8082 mics.asgi:channel_layer
-stdout_logfile=./django.log
-stderr_logfile=./django.log
-stderr_events_enabled=true
-
-[program:django-channel-worker]
-command=./manage.py runworker
-stdout_logfile=./channel-worker.log
-stderr_logfile=./channel-worker.log
-stderr_events_enabled=true
-
-[program:job-rq]
-command=./manage.py rqworker default email results-queue ws-notice odk
-stdout_logfile=./job-rq.log
-stderr_logfile=./job-rq.log
-stderr_events_enabled=true
-
-
-[program:job-scheduler]
-command=./manage.py rqscheduler ws-notice
-stdout_logfile=./job-scheduler.log
-stderr_logfile=./job-scheduler.log
-stderr_events_enabled=true
-
diff --git a/survey/__init__.py b/survey/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/survey/bootstrap.min.css b/survey/bootstrap.min.css
deleted file mode 100644
index e69de29b..00000000
diff --git a/survey/context_processor.py b/survey/context_processor.py
deleted file mode 100644
index d8424528..00000000
--- a/survey/context_processor.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from django.conf import settings
-from django.core.cache import cache
-# from survey.interviewer_configs import COUNTRY_PHONE_CODE
-
-
-class CachedValue:
-
-    def __getattr__(self, key):
-        key = key.replace('__', '/')
-        return cache.get(key)
-
-
-def context_extras(request):
-    generals = {'PROJECT_TITLE': settings.PROJECT_TITLE, 'country_phone_code': settings.COUNTRY_PHONE_CODE,
-                'WEBSOCKET_URL': settings.WEBSOCKET_URL, 'WS_HEARTBEAT': settings.WS_HEARTBEAT,
-                'cached_value': CachedValue()}
-    if request.GET:
-        generals['q'] = request.GET.get('q', '')
-    return generals
diff --git a/survey/features/Batch-steps.py b/survey/features/Batch-steps.py
deleted file mode 100644
index a3cff019..00000000
--- a/survey/features/Batch-steps.py
+++ /dev/null
@@ -1,396 +0,0 @@
-from time import sleep
-from lettuce import *
-from rapidsms.contrib.locations.models import *
-
-from survey.features.page_objects.batches import BatchListPage, AddBatchPage, EditBatchPage, AssignQuestionToBatchPage, BatchShowPage
-from survey.features.page_objects.question import BatchQuestionsListPage
-from survey.features.page_objects.root import HomePage
-from survey.investigator_configs import *
-from survey.models import HouseholdMemberGroup, Survey
-from survey.models.question import Question
-from survey.models.batch import Batch, BatchLocationStatus
-
-
-@step(u'And I have a batch')
-def and_i_have_prime_locations(step):
-    world.batch = Batch.objects.create(
-        order=1, name="Batch A", description='description', survey=world.survey)
-
-
-@step(u'And I have prime locations')
-def and_i_have_prime_locations(step):
-    district = LocationType.objects.create(
-        name=PRIME_LOCATION_TYPE, slug=PRIME_LOCATION_TYPE)
-    world.districts = (
-        Location.objects.create(name="Kampala", type=district),
-        Location.objects.create(name="Abim", type=district),
-    )
-
-
-@step(u'And I visit batches listing page')
-def and_i_visit_batches_listing_page(step):
-    world.page = BatchListPage(world.browser, world.survey)
-    world.page.visit()
-
-
-@step(u'And I visit the first batch listed')
-def and_i_visit_the_first_batch_listed(step):
-    world.page = world.page.visit_batch(world.batch)
-
-
-@step(u'Then I should see all the prime locations with open close toggles')
-def then_i_should_see_all_the_prime_locations_with_open_close_toggles(step):
-    world.page.batch_closed_for_all_locations()
-
-
-@step(u'And I open batch for a location')
-@step(u'When I open batch for a location')
-def when_i_open_batch_for_a_location(step):
-    world.page.open_batch_for(world.districts[1])
-
-
-@step(u'Then I should see it is open for that location in db')
-def then_i_should_see_it_is_open_for_that_location_in_db(step):
-    assert BatchLocationStatus.objects.filter(
-        location=world.districts[1]).count() == 1
-    assert BatchLocationStatus.objects.filter(
-        location=world.districts[0]).count() == 0
-
-
-@step(u'When I close batch for a location')
-def when_i_close_batch_for_a_location(step):
-    world.page.close_batch_for(world.districts[1])
-
-
-@step(u'Then I should see it is closed for that location in db')
-def then_i_should_see_it_is_closed_for_that_location_in_db(step):
-    assert BatchLocationStatus.objects.count() == 0
-
-
-@step(u'And I click add batch button')
-def and_i_click_add_batch_button(step):
-    world.page.click_add_batch_button()
-
-
-@step(u'Then I should see a add batch page')
-def then_i_should_see_a_add_batch_page(step):
-    world.page = AddBatchPage(world.browser, world.survey)
-    world.page.validate_url()
-    world.page.validate_fields_present(["New Batch", "Name", "Description"])
-
-
-@step(u'When I fill the details for add batch form')
-def when_i_fill_the_details_for_add_batch_form(step):
-    data = {'name': 'hritik  batch',
-            'description': 'roshan'}
-
-    world.page.fill_valid_values(data)
-
-
-@step(u'Then I should go back to batches listing page')
-def then_i_should_go_back_to_batches_listing_page(step):
-    world.page = BatchListPage(world.browser, world.survey)
-    world.page.validate_url()
-
-
-@step(u'And I should see batch successfully added message')
-def and_i_should_see_batch_successfully_added_message(step):
-    world.page.see_success_message('Batch', 'added')
-
-
-@step(u'And I visit add batch page')
-def and_i_visit_add_batch_page(step):
-    world.page = AddBatchPage(world.browser, world.survey)
-    world.page.visit()
-
-
-@step(u'Then I should see validation error messages')
-def then_i_should_see_validation_error_messages(step):
-    world.page.validate_error_message_on_fields()
-
-
-@step(u'And I have 100 batches')
-def and_i_have_100_batches(step):
-    for i in xrange(100):
-        try:
-            Batch.objects.create(order=i, name="Batch %d" % i,
-                                 description='description %d' % i, survey=world.survey)
-        except Exception:
-            pass
-
-
-@step(u'And I should see the batches list paginated')
-def and_i_should_see_the_batches_list_paginated(step):
-    world.page.validate_fields()
-    world.page.validate_pagination()
-    world.page.validate_fields()
-
-
-@step(u'And I click edit batch link')
-def and_i_click_edit_batch_link(step):
-    world.page.click_link_by_text(' Edit')
-
-
-@step(u'Then I should see edit batch page')
-def then_i_should_see_edit_batch_page(step):
-    world.page = EditBatchPage(world.browser, world.batch, world.survey)
-    world.page.validate_url()
-
-
-@step(u'When I fill the details for the batch')
-def when_i_fill_the_details_for_the_batch(step):
-    data = {'name': 'hritik  batch',
-            'description': 'roshan'}
-
-    world.page.fill_valid_values(data)
-
-
-@step(u'And I should see the batch successfully edited')
-def and_i_should_see_the_batch_successfully_edited(step):
-    world.page.see_success_message('Batch', 'edited')
-
-
-@step(u'And I click delete batch link')
-def and_i_click_delete_batch_link(step):
-    world.page.click_link_by_text(' Delete')
-
-
-@step(u'Then I should see confirm delete batch')
-def then_i_should_see_confirm_delete_bacth(step):
-    world.page.see_confirm_modal_message(world.batch.name)
-
-
-@step(u'And if I click yes')
-def and_if_i_click_yes(step):
-    world.page.click_link_by_text('Yes')
-
-
-@step(u'And I should see the batch successfully deleted')
-def and_i_should_see_the_batch_successfully_deleted(step):
-    world.page.see_success_message('Batch', 'deleted')
-
-
-@step(u'And I click on batch name')
-def and_i_click_on_batch_name(step):
-    world.page.click_link_by_text(world.batch.name)
-
-
-@step(u'Then I should be on the list of questions under the batch page')
-def then_i_should_be_on_the_list_of_questions_under_the_batch_page(step):
-    world.page = BatchQuestionsListPage(world.browser, world.batch)
-    world.page.validate_url()
-
-
-@step(u'And I click on assign question link')
-def and_i_click_on_assign_question_link(step):
-    world.page.click_link_by_text("Select Question")
-
-
-@step(u'Then I should see the assign question page of that batch')
-def then_i_should_see_the_assign_question_page_of_that_batch(step):
-    world.page = AssignQuestionToBatchPage(world.browser, world.batch)
-    world.page.validate_url()
-    world.page.is_text_present(
-        "Select Questions for %s - %s" % (world.survey.name.capitalize(), world.batch.name.capitalize()))
-
-
-@step(u'When I select some questions')
-def when_i_select_some_questions(step):
-    world.page.select('questions', [world.question_1.pk, world.question_2.pk])
-
-
-@step(u'Then I should see the questions successfully assigned to that batch')
-def then_i_should_see_the_questions_successfully_assigned_to_that_batch(step):
-    world.page.see_success_message(
-        "Questions", "assigned to batch: %s" % world.batch.name.capitalize())
-
-
-@step(u'And I have 2 questions')
-def and_i_have_2_questions(step):
-    world.question_1 = Question.objects.create(
-        text="question1", answer_type=Question.NUMBER, order=1)
-    world.question_2 = Question.objects.create(
-        text="question2", answer_type=Question.TEXT, order=2)
-
-
-@step(u'And I visit the assign question to page batch')
-def and_i_visit_the_assign_question_to_page_batch(step):
-    world.page = AssignQuestionToBatchPage(world.browser, world.batch)
-    world.page.visit()
-
-
-@step(u'When I select the group')
-def when_i_select_the_group(step):
-    world.page.select('group', [world.household_member_group.id])
-
-
-@step(u'Then I should see the question which belong to that group')
-def then_i_should_see_the_question_which_belong_to_that_group(step):
-    world.page.see_the_question(True, world.question_1.id)
-    world.page.see_the_question(False, world.question_2.id)
-
-
-def create_question_for_group(group):
-    return Question.objects.create(text="question-group%s" % group.name, answer_type=Question.NUMBER, group=group)
-
-
-@step(u'And I have one question belonging to that group')
-def and_i_have_one_question_belonging_to_that_group(step):
-    world.question_1 = create_question_for_group(world.household_member_group)
-
-
-@step(u'And another question which does not')
-def and_another_question_which_does_not(step):
-    world.question_2 = Question.objects.create(
-        text="question2", answer_type=Question.TEXT)
-
-
-@step(u'And I click add batch modal button')
-def and_i_click_add_batch_modal_button(step):
-    world.page.click_link_by_partial_href("#new_batch")
-
-
-@step(u'Then I should see the add batch modal')
-def then_i_should_see_the_add_batch_modal(step):
-    world.page.validate_page_got_survey_id()
-    world.page.validate_fields_present(["New Batch", "Name", "Description"])
-
-
-@step(u'And I have 2 member groups')
-def and_i_have_2_member_groups(step):
-    world.household_member_group = HouseholdMemberGroup.objects.create(
-        name='Age 4-5', order=1)
-    world.member_group_2 = HouseholdMemberGroup.objects.create(
-        name='Age 15-49', order=2)
-
-
-@step(u'And I have questions belonging to those groups')
-def and_i_have_questions_belonging_to_those_groups(step):
-    world.question_1_with_group_1 = create_question_for_group(
-        world.household_member_group)
-    world.question_2_with_group_1 = create_question_for_group(
-        world.household_member_group)
-    world.question_1_with_group_2 = create_question_for_group(
-        world.member_group_2)
-    world.question_2_with_group_2 = create_question_for_group(
-        world.member_group_2)
-
-
-@step(u'And I select a question from the list')
-def and_i_select_a_question_from_the_list(step):
-    world.page.select_multiple("#id_questions", world.question_1_with_group_2)
-
-
-@step(u'Then I should see in selected list the question which belong to that group')
-def then_i_should_see_in_selected_list_the_question_which_belong_to_that_group(step):
-    world.page.see_the_question(True, world.question_1_with_group_1.id)
-    world.page.see_the_question(True, world.question_2_with_group_1.id)
-    world.page.see_the_question(False, world.question_2_with_group_2.id)
-
-
-@step(u'And I should see the previously selected questions on the page')
-def and_i_should_see_the_previously_selected_questions_on_the_page(step):
-    world.page.see_the_selected_question(
-        True, world.question_1_with_group_2.id)
-
-
-@step(u'When I fill the same name of the batch')
-def when_i_fill_the_same_name_of_the_batch(step):
-    world.page.fill('name', world.batch.name)
-    world.page.fill('description', 'some description')
-
-
-@step(u'Then I should see batch name already exists error message')
-def then_i_should_see_batch_name_already_exists_error_message(step):
-    world.page.is_text_present("Batch with the same name already exists.")
-
-
-@step(u'And If I have an open batch in another survey in this location')
-def and_if_i_have_an_open_batch_in_another_survey_in_this_location(step):
-    world.survey1 = Survey.objects.create(name='another survey', description='another survey descrpition', type=False,
-                                          sample_size=10)
-    batch = Batch.objects.create(
-        order=1, name="Batch B", description='description', survey=world.survey1)
-    batch.open_for_location(world.districts[1])
-
-
-@step(u'Then I should see an error that another batch from another survey is already open')
-def then_i_should_see_an_error_that_another_batch_from_another_survey_is_already_open(step):
-    open_batch_error_message = "%s has already open batches from survey %s" % (
-        world.districts[1].name, world.survey1.name)
-    world.page.is_text_present(open_batch_error_message)
-
-
-@step(u'And I should not be able to open this batch')
-def and_i_should_not_be_able_to_open_this_batch(step):
-    world.page.is_disabled("open_close_switch_%s" % world.districts[1].id)
-
-
-@step(u'When I activate non response for batch and location')
-def when_i_activate_non_response_for_batch_and_location(step):
-    world.page.activate_non_response_for_batch_and(world.districts[1])
-
-
-@step(u'Then I should see it is activated for that location in db')
-def then_i_should_see_it_is_activated_for_that_location_in_db(step):
-    assert world.batch.non_response_is_activated_for(
-        world.districts[0]) is True
-    assert world.batch.non_response_is_activated_for(
-        world.districts[1]) is False
-
-
-@step(u'When I deactivate non response for batch and location')
-def when_i_deactivate_non_response_for_batch_and_location(step):
-    world.page.deactivate_non_response_for_batch_and(world.districts[0])
-
-
-@step(u'Then I should see it is deactivated for that location in db')
-def then_i_should_see_it_is_deactivated_for_that_location_in_db(step):
-    assert world.batch.non_response_is_activated_for(
-        world.districts[0]) is False
-    assert world.batch.non_response_is_activated_for(
-        world.districts[1]) is False
-
-
-@step(u'Then I should see message batch is closed that location')
-def then_i_should_see_message_batch_is_closed_that_location(step):
-    world.page.is_text_present("%s is not open for %s" %
-                               (world.batch.name, world.districts[1]))
-
-
-@step(u'And I should not be able to activate this batch')
-def and_i_should_not_be_able_to_activate_this_batch(step):
-    world.page.is_disabled("open_close_switch_%s" % world.districts[1].id)
-
-
-@step(u'When I open batch for a different location')
-def when_i_open_batch_for_a_different_location(step):
-    world.batch.open_for_location(world.districts[0])
-
-
-@step(u'And I activate non response for that location')
-def and_i_activate_non_response_for_that_location(step):
-    world.page.activate_non_response_for_batch_and(world.districts[0])
-
-
-@step(u'When I visit the home page')
-def when_i_visit_the_home_page(step):
-    world.page = HomePage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see that it is still activated')
-def then_i_should_see_that_it_is_still_activated_for_that_location_in_db(step):
-    world.page.is_text_present("On")
-
-
-@step(u'When I close the batch of the other survey')
-def when_i_close_the_batch_of_the_other_survey(step):
-    world.batch.close_for_location(world.districts[1])
-
-
-@step(u'Then the non-response switch for that location is active')
-def then_the_non_response_switch_for_that_location_is_active(step):
-    world.page.is_disabled('activate_non_response_switch_%d' % world.batch.id)
-    world.page.is_text_present("%s is not open for %s" % (
-        world.batch.name, world.districts[1]), False)
diff --git a/survey/features/Batch.feature b/survey/features/Batch.feature
deleted file mode 100644
index 926e1f85..00000000
--- a/survey/features/Batch.feature
+++ /dev/null
@@ -1,142 +0,0 @@
-Feature: Batch related features
-
-  Scenario: Open-Close and activate and deactivate non-response for batch
-    Given I am logged in as researcher
-    And I have prime locations
-    And I have a survey
-    And I have a batch
-    And I visit batches listing page
-    And I visit the first batch listed
-    Then I should see all the prime locations with open close toggles
-    When I open batch for a location
-    Then I should see it is open for that location in db
-    When I close batch for a location
-    Then I should see it is closed for that location in db
-    And If I have an open batch in another survey in this location
-    When I open batch for a location
-    Then I should see an error that another batch from another survey is already open
-    And I should not be able to open this batch
-    When I activate non response for batch and location
-    Then I should see message batch is closed that location
-    When I close the batch of the other survey
-    And I open batch for a location
-    Then the non-response switch for that location is active
-    And I should not be able to activate this batch
-    When I open batch for a different location
-    And I activate non response for that location
-    Then I should see it is activated for that location in db
-    When I visit the home page
-    And I visit batches listing page
-    And I visit the first batch listed
-    Then I should see that it is still activated
-    When I deactivate non response for batch and location
-    Then I should see it is deactivated for that location in db
-
-
-  Scenario: Add new Batch
-    Given I am logged in as researcher
-    And I have a survey
-    And I visit add batch page
-    When I fill the details for add batch form
-    And I submit the form
-    Then I should go back to batches listing page
-    And I should see batch successfully added message
-
-  Scenario: Add new batch- Js validations
-    Given I am logged in as researcher
-    And I have a survey
-    And I visit add batch page
-    And I submit the form
-    Then I should see validation error messages
-
-  Scenario: List Batches
-    Given I am logged in as researcher
-    And I have a survey
-    And I have 100 batches
-    And I visit batches listing page
-    And I should see the batches list paginated
-
-  Scenario: Edit Batch
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I visit batches listing page
-    And I click edit batch link
-    Then I should see edit batch page
-    When I fill the details for the batch
-    And I submit the form
-    Then I should go back to batches listing page
-    And I should see the batch successfully edited
-
-  Scenario: Delete Batch
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I visit batches listing page
-    And I click delete batch link
-    Then I should see confirm delete batch
-    And if I click yes
-    Then I should go back to batches listing page
-    And I should see the batch successfully deleted
-
-  Scenario: Batch Details- Listing questions under batch
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I visit batches listing page
-    And I click on batch name
-    Then I should be on the list of questions under the batch page
-
-  Scenario: Assign questions to batch
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have 2 questions
-    And I visit batches listing page
-    And I click on batch name
-    And I click on assign question link
-    Then I should see the assign question page of that batch
-    When I select some questions
-    And I submit the form
-    Then I should see the questions successfully assigned to that batch
-
-  Scenario: Assign question filter
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have a member group
-    And I have one question belonging to that group
-    And another question which does not
-    And I visit the assign question to page batch
-    When I select the group
-    Then I should see the question which belong to that group
-
-  Scenario: Add new Batch modal
-    Given I am logged in as researcher
-    And I have a survey
-    And I visit batches listing page
-    And I click add batch modal button
-    Then I should see the add batch modal
-    When I fill the details for add batch form
-    And I click the modal save button
-    And I should see batch successfully added message
-
-  Scenario: Assign questions from multiple groups to batch
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have 2 member groups
-    And I have questions belonging to those groups
-    And I visit the assign question to page batch
-    And I select a question from the list
-    When I select the group
-    Then I should see in selected list the question which belong to that group
-    And I should see the previously selected questions on the page
-
-  Scenario: remote validation for unique (name,survey) for batch
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I visit add batch page
-    When I fill the same name of the batch
-    Then I should see batch name already exists error message
diff --git a/survey/features/Layout.feature b/survey/features/Layout.feature
deleted file mode 100644
index bee9ca5e..00000000
--- a/survey/features/Layout.feature
+++ /dev/null
@@ -1,20 +0,0 @@
-Feature: Layout Page feature
-  Scenario: Survey Administration Tab
-    Given I am logged in as researcher
-    And I click Survey Administration tab
-    Then I should see survey administration dropdown list
-
-  Scenario: Downloads Tab
-    Given I am logged in as researcher
-    And I click Downloads tab
-    Then I should see downloads dropdown list
-
-  Scenario: Analysis Tab
-    Given I am logged in as researcher
-    And I click Analysis tab
-    Then I should see analysis dropdown list
-
-  Scenario: Settings Tab
-    Given I am logged in as admin
-    And I click Settings tab
-    Then I should see Settings dropdown list
diff --git a/survey/features/Question-steps.py b/survey/features/Question-steps.py
deleted file mode 100644
index 3ee39316..00000000
--- a/survey/features/Question-steps.py
+++ /dev/null
@@ -1,562 +0,0 @@
-from random import randint
-from time import sleep
-from lettuce import *
-from rapidsms.contrib.locations.models import Location
-from survey.features.page_objects.question import BatchQuestionsListPage, AddQuestionPage, ListAllQuestionsPage, CreateNewQuestionPage, CreateNewSubQuestionPage, EditQuestionPage
-from survey.models import Batch, QuestionModule, BatchQuestionOrder
-from survey.models.question import Question, QuestionOption
-from survey.models.householdgroups import HouseholdMemberGroup
-from survey.models.answer_rule import AnswerRule
-
-
-@step(u'And I have 100 questions under the batch')
-def and_i_have_100_questions_under_the_batch(step):
-    for i in xrange(100):
-        q = Question.objects.create(text="some questions %d" % i,
-                                    answer_type=Question.NUMBER, identifier='ID %d' % i, order=i)
-        q.batches.add(world.batch)
-        BatchQuestionOrder.objects.create(
-            batch=world.batch, question=q, order=i)
-
-
-@step(u'And I visit questions listing page of the batch')
-def and_i_visit_questions_listing_page_of_the_batch(step):
-    world.page = BatchQuestionsListPage(world.browser, world.batch)
-    world.page.visit()
-
-
-@step(u'Then I should see the questions list paginated')
-def then_i_should_see_the_questions_list_paginated(step):
-    world.page.validate_fields()
-    world.page.validate_pagination()
-    world.page.validate_fields()
-
-
-@step(u'When I change to 100 questions per page')
-def when_i_change_to_100_questions_per_page(step):
-    world.page.fill_valid_values({'number_of_questions_per_page': 100})
-    world.page.click_by_css('#a-question-list')
-
-
-@step(u'Then I should not see pagination')
-def then_i_should_not_see_pagination(step):
-    world.page.validate_pagination(False)
-
-
-@step(u'And I have no questions under the batch')
-def and_i_have_no_questions_under_the_batch(step):
-    Question.objects.filter(batches=world.batch).delete()
-
-
-@step(u'Then I should see error message on the page')
-def then_i_should_see_error_message_on_the_page(step):
-    world.page.is_text_present(
-        "There are no questions associated with this batch yet.")
-
-
-@step(u'And I click add question button')
-def and_i_click_add_question_button(step):
-    world.page.click_link_by_text("Select Question")
-
-
-@step(u'Then I should see a add question page')
-def then_i_should_see_a_add_question_page(step):
-    world.page = AddQuestionPage(world.browser, world.batch)
-    world.page.validate_url()
-
-
-@step(u'When I fill the details for add question form')
-def when_i_fill_the_details_for_add_question_form(step):
-    data = {'module': world.module.id,
-            'text': 'hritik  question',
-            'answer_type': Question.NUMBER,
-            'identifier': 'ID 1'}
-
-    world.page.fill_valid_values(data)
-
-
-@step(u'Then I should go back to questions listing page')
-def then_i_should_go_back_to_questions_listing_page(step):
-    world.page = BatchQuestionsListPage(world.browser, world.batch)
-    world.page.validate_url()
-
-
-@step(u'And I should see question successfully added message')
-def and_i_should_see_question_successfully_added_message(step):
-    world.page.is_text_present("Question successfully added.")
-
-
-@step(u'And I have a member group')
-def and_i_have_a_member_group(step):
-    world.household_member_group = HouseholdMemberGroup.objects.create(
-        name='Age 4-5', order=1)
-
-
-@step(u'And I visit add new question page of the batch')
-def and_i_visit_add_new_question_page_of_the_batch(step):
-    world.page = AddQuestionPage(world.browser, world.batch)
-    world.page.visit()
-
-
-@step(u'And I fill the details for question')
-def and_i_fill_the_details_for_question(step):
-    world.page.fill_valid_values(
-        {'identifier': 'ID 1', 'module': world.module.id, 'text': 'hritik  question'})
-    world.page.select('group', [world.household_member_group.pk])
-
-
-@step(u'When I select multichoice for answer type')
-def when_i_select_multichoice_for_answer_type(step):
-    world.page.select('answer_type', [Question.MULTICHOICE])
-
-
-@step(u'Then I should see one option field')
-def then_i_should_see_one_option_field(step):
-    world.page.see_one_option_field("Option 1")
-    world.page.see_option_add_and_remove_buttons(1)
-
-
-@step(u'When I click add-option icon')
-def when_i_click_add_option_icon(step):
-    world.page.click_by_css(".icon-plus")
-
-
-@step(u'Then I should see two options field')
-def then_i_should_see_two_options_field(step):
-    world.page.see_one_option_field("Option 1")
-    world.page.see_one_option_field("Option 2")
-    world.page.see_option_add_and_remove_buttons(2)
-
-
-@step(u'When I click remove-option icon')
-def when_i_click_remove_option_icon(step):
-    world.page.click_by_css(".icon-remove")
-
-
-@step(u'Then I should see only one option field')
-def then_i_should_see_only_one_option_field(step):
-    world.page.see_one_option_field("Option 1")
-    world.page.see_option_add_and_remove_buttons(1)
-    world.page.option_not_present("Option 2")
-
-
-@step(u'And I fill an option question')
-def and_i_fill_an_option_question(step):
-    world.option = {'options': 'some option question text'}
-    world.page.fill_valid_values(world.option)
-
-
-@step(u'And I have more than 50 questions')
-def and_i_have_100_questions(step):
-    for i in xrange(100):
-        Question.objects.create(text="some questions %d" % i, answer_type=Question.NUMBER, identifier='ID %d' % i,
-                                order=i)
-
-
-@step(u'And I visit questions list page')
-def and_i_visit_questions_list_page(step):
-    world.page = ListAllQuestionsPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And If I click create new question link')
-def and_if_i_click_create_new_question_link(step):
-    world.page.click_link_by_text("Create New Question")
-
-
-@step(u'Then I should see create new question page')
-def then_i_should_see_create_new_question_page(step):
-    world.page = CreateNewQuestionPage(world.browser)
-    world.page.validate_url()
-
-
-@step(u'And I visit create new question page')
-def and_i_visit_create_new_question_page(step):
-    world.page = CreateNewQuestionPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I have a multichoice question')
-def and_i_have_a_multichoice_question(step):
-    world.multi_choice_question = Question.objects.create(module=world.module, text="Are these insecticide?",
-                                                          answer_type=Question.MULTICHOICE, order=6,
-                                                          group=world.household_member_group, identifier='ID 1')
-    world.option1 = QuestionOption.objects.create(
-        question=world.multi_choice_question, text="Yes", order=1)
-    world.option2 = QuestionOption.objects.create(
-        question=world.multi_choice_question, text="No", order=2)
-    world.option3 = QuestionOption.objects.create(
-        question=world.multi_choice_question, text="Dont Know", order=3)
-
-
-@step(u'And I click on view options link')
-def and_i_click_on_view_options_link(step):
-    world.page.click_link_by_partial_href(
-        "#view_options_%d" % world.multi_choice_question.id)
-
-
-@step(u'Then I should see the question options in a modal')
-def then_i_should_see_the_question_options_in_a_modal(step):
-    world.page.validate_fields_present(
-        [world.multi_choice_question.text, "Text", "Order"])
-
-
-@step(u'And when I click the close button')
-def and_when_i_click_the_close_button(step):
-    world.page.click_link_by_text("Close")
-
-
-@step(u'Then I should be back to questions list page')
-def then_i_should_see_questions_list_page(step):
-    sleep(2)
-    world.page.validate_fields()
-
-
-@step(u'And I click on view add subquestion link')
-def and_i_click_on_view_add_subquestion_link(step):
-    world.browser.click_link_by_text("Add Subquestion")
-
-
-@step(u'Then I should go to add subquestion page')
-def then_i_should_go_to_add_subquestion_page(step):
-    world.page = CreateNewSubQuestionPage(
-        world.browser, question=world.multi_choice_question)
-    world.page.validate_url()
-
-
-@step(u'When I fill in subquestion details')
-def when_i_fill_in_subquestion_details(step):
-    world.page.fill_valid_values(
-        {'module': world.module.id, 'text': 'hritik  question', 'identifier': 'Q001'})
-    world.page.select('group', [world.household_member_group.pk])
-    world.page.select('answer_type', [Question.NUMBER])
-
-
-@step(u'And I should see subquestion successfully added message')
-def and_i_should_see_subquestion_successfully_added_message(step):
-    world.page.see_success_message('Sub question', 'added')
-
-
-@step(u'And I fill the invalid details details for question')
-def and_i_fill_the_invalid_details_details_for_question(step):
-    a_very_long_text = "Is there something here I'm missing? Is uni_form " \
-                       "overriding the setting somehow? If not, any advice as " \
-                       "to what I might look for in debug to see where/why this is happening?"
-    world.page.fill_valid_values({'text': a_very_long_text})
-
-
-@step(u'And I should see question was not added')
-def and_i_should_see_question_was_not_added(step):
-    world.page.see_message("Question was not added.")
-
-
-@step(u'And I should see that option in the form')
-def and_i_should_see_that_option_in_the_form(step):
-    world.page.see_option_text(world.option['options'], 'options')
-
-
-@step(u'And I visit question listing page')
-def and_i_visit_question_listing_page(step):
-    world.page = ListAllQuestionsPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I click the edit question link')
-def and_i_click_the_edit_question_link(step):
-    world.page.click_link_by_text(" Edit")
-
-
-@step(u'Then I should see the edit question page')
-def then_i_should_see_the_edit_question_page(step):
-    world.page = EditQuestionPage(world.browser, world.multi_choice_question)
-    world.page.validate_url()
-
-
-@step(u'And I see the question form with values')
-def and_i_see_the_question_form_with_values(step):
-    world.form = {'module': 'Module',
-                  'text': 'Text',
-                  'group': 'Group',
-                  'answer_type': 'Answer type'}
-
-    form_values = {'module': world.module.id,
-                   'text': world.multi_choice_question.text,
-                   'group': world.multi_choice_question.group.id,
-                   'answer_type': world.multi_choice_question.answer_type}
-    world.page.validate_form_present(world.form)
-    world.page.validate_form_values(form_values)
-
-
-@step(u'When I fill in edited question details')
-def when_i_fill_in_edited_question_details(step):
-    world.edited_question_details = {'module': world.module.id,
-                                     'text': 'edited question',
-                                     'group': world.multi_choice_question.group.id
-                                     }
-    world.page.see_select_option(['Number'], 'answer_type')
-    world.page.fill_valid_values(world.edited_question_details)
-
-
-@step(u'Then I should see the question successfully edited')
-def then_i_should_see_the_question_successfully_edited(step):
-    world.page.see_success_message("Question", "edited")
-
-
-@step(u'And I click on delete question link')
-def and_i_click_on_delete_question_link(step):
-    world.page.click_link_by_partial_href(
-        "#delete_question_%d" % world.multi_choice_question.id)
-
-
-@step(u'Then I should see a delete question confirmation modal')
-def then_i_should_see_a_delete_question_confirmation_modal(step):
-    world.page.see_confirm_modal_message(world.multi_choice_question.text)
-
-
-@step(u'Then I should see that the question was deleted successfully')
-def then_i_should_see_that_the_question_was_deleted_successfully(step):
-    world.page.see_success_message("Question", "deleted")
-
-
-@step(u'And I have a sub question for that question')
-def and_i_have_a_sub_question_for_that_question(step):
-    world.sub_question = Question.objects.create(module=world.module, parent=world.multi_choice_question,
-                                                 text="Sub Question 2?",
-                                                 answer_type=Question.NUMBER, subquestion=True, identifier='Q101')
-
-
-@step(u'Then I should not see the sub question')
-def then_i_should_not_see_the_sub_question(step):
-    world.page.is_text_present(world.sub_question.text, False)
-
-
-@step(u'And I have a non multichoice question')
-def and_i_have_a_non_multi_choice_question(step):
-    world.multi_choice_question = Question.objects.create(module=world.module, text="Are these insecticide?",
-                                                          answer_type=Question.NUMBER, order=7,
-                                                          group=world.household_member_group, identifier='Q921')
-    world.multi_choice_question.batches.add(world.batch)
-    BatchQuestionOrder.objects.create(
-        batch=world.batch, question=world.multi_choice_question, order=1)
-
-
-@step(u'When I click on the question')
-def and_i_click_on_the_question(step):
-    world.page.click_link_by_text(world.multi_choice_question.text)
-
-
-@step(u'Then I should see the sub question below the question')
-def then_i_should_see_the_sub_question_below_the_question(step):
-    world.page.is_text_present("Subquestion")
-    world.page.is_text_present(world.sub_question.text)
-
-
-@step(u'And I have a rule linking one option with that subquestion')
-def and_i_have_a_rule_linking_one_option_with_that_subquestion(step):
-    world.answer_rule = AnswerRule.objects.create(question=world.multi_choice_question,
-                                                  action=AnswerRule.ACTIONS[
-                                                      'ASK_SUBQUESTION'],
-                                                  condition=AnswerRule.CONDITIONS[
-                                                      'EQUALS_OPTION'],
-                                                  validate_with_option=world.option3, next_question=world.sub_question)
-
-
-@step(u'And I have a subquestion under that question')
-def and_i_have_a_subquestion_under_that_question(step):
-    world.sub_question = Question.objects.create(module=world.module, subquestion=True,
-                                                 parent=world.multi_choice_question,
-                                                 text="this is a subquestion", identifier='Q022')
-
-
-@step(u'When I fill in duplicate subquestion details')
-def when_i_fill_in_duplicate_subquestion_details(step):
-    world.page.fill_valid_values(
-        {'module': world.module.id, 'identifier': 'ID 1', 'text': world.sub_question.text})
-    world.page.select('group', [world.household_member_group.pk])
-    world.page.select('answer_type', [Question.NUMBER])
-
-
-@step(u'And I should see subquestion not added message')
-def and_i_should_see_subquestion_not_added_message(step):
-    world.page.is_text_present("Sub question not saved.")
-
-
-@step(u'And I have a rule on value with that subquestion')
-def and_i_have_a_rule_on_value_with_that_subquestion(step):
-    world.answer_rule = AnswerRule.objects.create(question=world.multi_choice_question, validate_with_value=1,
-                                                  condition=AnswerRule.CONDITIONS[
-                                                      'EQUALS'],
-                                                  action=AnswerRule.ACTIONS[
-                                                      'ASK_SUBQUESTION'],
-                                                  next_question=world.sub_question, batch=world.batch)
-
-
-@step(u'And I click on view logic link')
-def and_i_click_on_view_logic_link(step):
-    world.page.click_link_by_partial_href(
-        "#view_logic_%d" % world.multi_choice_question.id)
-
-
-@step(u'Then I should see the logic in a modal')
-def then_i_should_see_the_logic_in_a_modal(step):
-    world.page.validate_fields_present(
-        [world.multi_choice_question.text, "Eligibility Criteria", "Question/Value/Option", "Action"])
-
-
-@step(u'Then I should see delete logic icon')
-def then_i_should_delete_logic_icon(step):
-    world.browser.find_by_css('.icon-trash')
-
-
-@step(u'When I click delete logic icon')
-def when_i_click_delete_logic_icon(step):
-    world.page.click_by_css('#delete-icon-%s' % world.answer_rule.id)
-
-
-@step(u'And I click confirm delete')
-def and_i_click_confirm_delete(step):
-    world.page.click_by_css('#delete-logic-%s' % world.answer_rule.id)
-
-
-@step(u'Then I should redirected to batch question page')
-def then_i_should_redirected_to_batch_question_page(step):
-    world.page = BatchQuestionsListPage(world.browser, world.batch)
-    world.page.validate_url()
-
-
-@step(u'Then I should see special characters message')
-def and_i_should_see_special_characters_message(step):
-    special_characters = "Please note that the following special characters will be removed ["
-    for character in Question.IGNORED_CHARACTERS:
-        special_characters = special_characters + character + " "
-    special_characters = special_characters.strip() + "]"
-    world.page.is_text_present(special_characters)
-
-
-@step(u'And I click delete sub question link')
-def and_i_click_delete_sub_question_link(step):
-    sleep(3)
-    world.page.click_delete_subquestion()
-
-
-@step(u'Then I should see a confirm delete subqestion modal')
-def then_i_should_see_a_confirm_delete_subqestion_modal(step):
-    world.page.see_confirm_modal_message(world.sub_question.text)
-
-
-@step(u'Then I should see the sub question deleted successfully')
-def then_i_should_see_the_sub_question_deleted_successfully(step):
-    world.page.see_success_message("Sub question", "deleted")
-
-
-@step(u'When I click confirm delete')
-def when_i_click_confirm_delete(step):
-    world.page.click_by_css("#delete-subquestion-%s" % world.sub_question.id)
-
-
-@step(u'And I click edit sub question link')
-def and_i_click_edit_sub_question_link(step):
-    sleep(3)
-    world.page.click_by_css("#edit_subquestion_%s" % world.sub_question.id)
-
-
-@step(u'Then I see the sub question form with values')
-def then_i_see_the_sub_question_form_with_values(step):
-    form_values = {'module': world.module.id, 'text': world.sub_question.text,
-                   'group': world.multi_choice_question.group.id,
-                   'identifier': "Q101",
-                   'answer_type': world.sub_question.answer_type}
-    world.page.validate_form_values(form_values)
-
-
-@step(u'When I fill in edited sub question details')
-def when_i_fill_in_edited_sub_question_details(step):
-    world.edited_sub_question_details = {'identifier': 'Q101', 'module': world.module.id, 'text': 'edited question',
-                                         'group': world.multi_choice_question.group.id
-                                         }
-    world.page.see_select_option(['Number'], 'answer_type')
-    world.page.fill_valid_values(world.edited_sub_question_details)
-
-
-@step(u'Then I should see the sub question successfully edited')
-def then_i_should_see_the_sub_question_successfully_edited(step):
-    world.page.see_success_message("Sub question", "edited")
-
-
-@step(u'And I click delete question rule')
-def and_i_click_delete_question_rule(step):
-    sleep(2)
-    world.page.click_by_css('#delete-icon-%s' % world.answer_rule.id)
-
-
-@step(u'And I should see that the logic was deleted successfully')
-def and_i_should_see_that_the_logic_was_deleted_successfully(step):
-    world.page.see_success_message("Logic", "deleted")
-
-
-@step(u'And I select multichoice question in batch')
-def and_i_select_multichoice_question_in_batch(step):
-    world.batch = Batch.objects.create(
-        order=1, name="Batch A", description='description', survey=world.survey)
-    world.multi_choice_question.batches.add(world.batch)
-    BatchQuestionOrder.objects.create(
-        batch=world.batch, question=world.multi_choice_question, order=1)
-
-
-@step(u'And I have a module')
-def and_i_have_a_module(step):
-    world.module = QuestionModule.objects.create(name="Education")
-
-
-@step(u'And I have a location')
-def and_i_have_a_location(step):
-    world.kampala = Location.objects.create(name="Kampala")
-
-
-@step(u'And I have an open batch in that location')
-def and_i_have_an_open_batch_in_that_location(step):
-    world.batch = Batch.objects.create(
-        order=1, name="Batch A", description='description', survey=world.survey)
-    world.batch.open_for_location(world.kampala)
-
-
-@step(u'Then I should see question list with only view options action')
-def then_i_should_see_question_list_with_only_view_options_action(step):
-    world.page.validate_only_view_options_action_exists()
-
-
-@step(u'And I have a multichoice and numeric questions with logics')
-def and_i_have_a_multichoice_and_numeric_questions(step):
-    world.numeric_question = Question.objects.create(text="some questions", answer_type=Question.NUMBER,
-                                                     identifier='ID', order=1)
-    world.multi_choice_question = Question.objects.create(text="Are these insecticide?",
-                                                          answer_type=Question.MULTICHOICE, order=6, identifier='ID 1')
-    world.option3 = QuestionOption.objects.create(
-        text="haha", order=1, question=world.multi_choice_question)
-
-    world.numeric_question.batches.add(world.batch)
-    world.multi_choice_question.batches.add(world.batch)
-    BatchQuestionOrder.objects.create(
-        batch=world.batch, question=world.numeric_question, order=1)
-    BatchQuestionOrder.objects.create(
-        batch=world.batch, question=world.multi_choice_question, order=2)
-
-    AnswerRule.objects.create(batch=world.batch, question=world.multi_choice_question,
-                              action=AnswerRule.ACTIONS['END_INTERVIEW'],
-                              condition=AnswerRule.CONDITIONS['EQUALS_OPTION'],
-                              validate_with_option=world.option3)
-
-    AnswerRule.objects.create(batch=world.batch, question=world.numeric_question,
-                              action=AnswerRule.ACTIONS['END_INTERVIEW'],
-                              condition=AnswerRule.CONDITIONS['EQUALS_OPTION'],
-                              validate_with_value=2)
-
-
-@step(u'Then I should see field required error message')
-def then_i_should_see_field_required_error_message(step):
-    world.page.is_text_present("This field is required.")
-
-
-@step(u'And I should be able to export questions')
-def and_i_should_be_able_to_export_questions(step):
-    world.page.find_by_css("#export_question", "Export Questions")
diff --git a/survey/features/Questions.feature b/survey/features/Questions.feature
deleted file mode 100644
index 9349f9e7..00000000
--- a/survey/features/Questions.feature
+++ /dev/null
@@ -1,210 +0,0 @@
-
-Feature: Question feature
-  Scenario: List Questions Under a batch
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have 100 questions under the batch
-    And I visit questions listing page of the batch
-    Then I should see the questions list paginated
-    When I change to 100 questions per page
-    Then I should not see pagination
-    And I should be able to export questions
-
-
-  Scenario: List Questions Under an open-batch
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a location
-    And I have an open batch in that location
-    And I have a multichoice and numeric questions with logics
-    And I visit questions listing page of the batch
-    Then I should see question list with only view options action
-    And I should be able to export questions
-
-  Scenario: Error Message on no question
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have no questions under the batch
-    And I visit questions listing page of the batch
-    Then I should see error message on the page
-
-  Scenario: Add new question to batch
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I visit questions listing page of the batch
-    And I have a member group
-    And I click add question button
-    Then I should see the assign question page of that batch
-
-  Scenario: MultiChoice question
-    Given I am logged in as researcher
-    And I have a member group
-    And I have a module
-    And I visit create new question page
-    Then I should see special characters message
-    And I fill the details for question
-    When I select multichoice for answer type
-    Then I should see one option field
-    When I click add-option icon
-    Then I should see two options field
-    When I click remove-option icon
-    Then I should see only one option field
-    And I fill an option question
-    And I submit the form
-    And I should see question successfully added message
-
-  Scenario: List All Questions
-    Given I am logged in as researcher
-    And I have more than 50 questions
-    And I visit questions list page
-    Then I should see the questions list paginated
-    And I should be able to export questions
-    And If I click create new question link
-    Then I should see create new question page
-
-
-  Scenario: View multichoice question options
-    Given I am logged in as researcher
-    And I have a member group
-    And I have a survey
-    And I have a module
-    And I have a multichoice question
-    And I select multichoice question in batch
-    And I have a sub question for that question
-    And I have a rule linking one option with that subquestion
-    And I visit questions list page
-    And I click on view options link
-    Then I should see the question options in a modal
-    And when I click the close button
-    Then I should be back to questions list page
-    And I visit questions listing page of the batch
-    And I click on view options link again
-    And I click delete question rule
-    And I click confirm delete
-    Then I should go back to questions listing page
-    And I should see that the logic was deleted successfully
-
-  Scenario: Add Subquestion
-    Given I am logged in as researcher
-    And I have a member group
-    And I have a module
-    And I have a multichoice question
-    And I visit questions list page
-    And I click on view add subquestion link
-    Then I should go to add subquestion page
-    And I submit the form
-    Then I should see field required error message
-    When I fill in subquestion details
-    And I submit the form
-    And I should see subquestion successfully added message
-
-  Scenario: Edit question
-    Given I am logged in as researcher
-    And I have a member group
-    And I have a module
-    And I have a multichoice question
-    And I visit question listing page
-    And I click the edit question link
-    Then I should see the edit question page
-    And I see the question form with values
-    When I fill in edited question details
-    And I submit the form
-    Then I should see the question successfully edited
-
-  Scenario: Delete question
-    Given I am logged in as researcher
-    And I have a member group
-    And I have a module
-    And I have a multichoice question
-    And I visit questions list page
-    And I click on delete question link
-    Then I should see a delete question confirmation modal 
-    When I click yes 
-    Then I should see that the question was deleted successfully
-
-  Scenario: View sub question
-    Given I am logged in as researcher
-    And I have a member group
-    And I have a module
-    And I have a multichoice question
-    And I have a sub question for that question
-    And I visit questions list page
-    Then I should not see the sub question
-    When I click on the question
-    Then I should see the sub question below the question
-
-  Scenario: Duplicate sub question
-    Given I am logged in as researcher
-    And I have a member group
-    And I have a module
-    And I have a multichoice question
-    And I have a subquestion under that question
-    And I visit questions list page
-    And I click on view add subquestion link
-    Then I should go to add subquestion page
-    When I fill in duplicate subquestion details
-    And I submit the form
-    And I should see subquestion not added message
-
-  Scenario: View question logic
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have a member group
-    And I have a module
-    And I have a non multichoice question
-    And I have a sub question for that question
-    And I have a rule on value with that subquestion
-    And I visit batches question list page
-    And I click on view logic link
-    Then I should see the logic in a modal
-    Then I should see the sub question below the question
-    And when I click the close button
-    Then I should be back to questions list page
-
-  Scenario: Delete question logic
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have a member group
-    And I have a module
-    And I have a non multichoice question
-    And I have a sub question for that question
-    And I have a rule on value with that subquestion
-    And I visit batches question list page
-    And I click on view logic link
-    Then I should see the logic in a modal
-    Then I should see delete logic icon
-    When I click delete logic icon
-    And I click confirm delete
-    Then I should redirected to batch question page
-
-  Scenario: Delete sub question
-    Given I am logged in as researcher
-    And I have a member group
-    And I have a module
-    And I have a multichoice question
-    And I have a sub question for that question
-    And I visit questions list page
-    When I click on the question
-    And I click delete sub question link
-    Then I should see a confirm delete subqestion modal
-    When I click confirm delete
-    Then I should see the sub question deleted successfully
-
-  Scenario: Edit sub question
-    Given I am logged in as researcher
-    And I have a member group
-    And I have a module
-    And I have a multichoice question
-    And I have a sub question for that question
-    And I visit questions list page
-    When I click on the question
-    And I click edit sub question link
-    Then I see the sub question form with values
-    When I fill in edited sub question details
-    And I submit the form
-    Then I should see the sub question successfully edited
\ No newline at end of file
diff --git a/survey/features/__init__.py b/survey/features/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/survey/features/about_page.feature b/survey/features/about_page.feature
deleted file mode 100644
index 8cdcb204..00000000
--- a/survey/features/about_page.feature
+++ /dev/null
@@ -1,19 +0,0 @@
-Feature: About page
-
-  Scenario: Create about us content
-     Given I am logged in as admin
-     And I have about us content
-     And I visit the about us page
-     Then I should see the sample about us information
-     When I click the edit link
-     Then I should see the existing content in a text area
-     When I modify about us content
-     And I submit the form
-     Then I should see the content was updated successfully
-
-  Scenario: About us anonymous user
-     Given I  am an anonymous user
-     And I have about us content
-     And I visit the about us page
-     Then I should see the sample about us information
-     And I should not see the edit about us button
\ No newline at end of file
diff --git a/survey/features/about_us_steps.py b/survey/features/about_us_steps.py
deleted file mode 100644
index a26895ae..00000000
--- a/survey/features/about_us_steps.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from lettuce import *
-from survey.features.page_objects.root import AboutPage, EditAboutUsPage
-from survey.models import AboutUs
-
-
-@step(u'And I visit the about us page')
-def and_i_visit_the_about_us_page(step):
-    world.page = AboutPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I have about us content')
-def and_i_have_about_us_content(step):
-    world.about_us = AboutUs.objects.create(content="blah blah")
-
-
-@step(u'Then I should see the sample about us information')
-def then_i_should_see_the_sample_about_us_information(step):
-    world.page.is_text_present(world.about_us.content)
-
-
-@step(u'When I click the edit link')
-def when_i_click_the_edit_link(step):
-    world.page.click_by_css("#edit-about_us")
-
-
-@step(u'Then I should see the existing content in a text area')
-def then_i_should_see_the_existing_content_in_a_text_area(step):
-    world.page = EditAboutUsPage(world.browser)
-    world.form_data = {'content': world.about_us.content}
-    world.page.validate_form_values(world.form_data)
-
-
-@step(u'When I modify about us content')
-def when_i_modify_about_us_content(step):
-    world.form_data = {'content': "edited more blah blah blah"}
-    world.page.fill_wywget_textarea(world.form_data)
-
-
-@step(u'Then I should see the content was updated successfully')
-def then_i_should_see_the_content_was_updated_successfully(step):
-    world.page.see_success_message("About us content", "updated")
-
-
-@step(u'And I should not see the edit about us button')
-def and_i_should_not_see_the_edit_about_us_button(step):
-    world.page.assert_edit_link_absent()
diff --git a/survey/features/aggregates-steps.py b/survey/features/aggregates-steps.py
deleted file mode 100644
index 66399353..00000000
--- a/survey/features/aggregates-steps.py
+++ /dev/null
@@ -1,353 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from time import sleep
-from lettuce import *
-from django.utils.datastructures import SortedDict
-from rapidsms.contrib.locations.models import *
-
-from survey.features.page_objects.aggregates import AggregateStatusPage, DownloadExcelPage, InvestigatorReportPage
-from survey.features.page_objects.survey_completion_rates import SurveyCompletionRatesPage
-from survey.models import Survey, EnumerationArea, HouseholdMemberGroup
-from survey.models.batch import Batch
-from survey.models.households import Household, HouseholdMember
-from survey.models.investigator import Investigator
-from survey import investigator_configs
-
-
-@step(u'And I have 2 batches with one open')
-def and_i_have_2_batches_with_one_open(step):
-    world.batch_1 = Batch.objects.create(
-        order=1, name="Batch A", survey=world.survey_1)
-    world.batch_2 = Batch.objects.create(
-        order=2, name="Batch B", survey=world.survey_2)
-    world.kampala_county = Location.objects.get(name="Kampala County")
-    world.someother_county = Location.objects.create(
-        name="Some County", tree_parent=world.kampala_county.tree_parent)
-    world.batch_1.open_for_location(world.kampala_county.tree_parent)
-
-
-@step(u'And I have eas in the lowest location')
-def and_i_have_eas_in_the_lowest_location(step):
-    world.ea = EnumerationArea.objects.create(name="EA", survey=world.survey_1)
-    world.ea.locations.add(world.kampala_village)
-
-
-@step(u'And one household has completed that open batch')
-def and_one_household_has_completed_that_open_batch(step):
-    world.household_1.completed_batches.get_or_create(batch=world.batch_1)
-
-
-@step(u'And I visit aggregate status page')
-def and_i_visit_aggregate_status_page(step):
-    world.page = AggregateStatusPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see an option to select location hierarchically')
-def then_i_should_see_an_option_to_select_location_hierarchically(step):
-    world.page.choose_location(
-        {'district': 'Kampala', 'county': 'Kampala County'})
-
-
-@step(u'And I should see an option to select batch')
-def and_i_should_see_an_option_to_select_batch(step):
-    world.page.check_if_batches_present([world.batch_1])
-
-
-@step(u'And I should see a get status button')
-def and_i_should_see_a_get_status_button(step):
-    world.page.check_get_status_button_presence()
-
-
-@step(u'And I have 2 investigators with households')
-def and_i_have_2_investigators_with_households(step):
-    investigator = Investigator.objects.create(
-        name="Rajini", mobile_number="123", location=world.kampala_county)
-    investigator_2 = Investigator.objects.create(
-        name="Batman", mobile_number="1234", location=world.someother_county)
-    uid_counter = 0
-    for index in range(investigator_configs.NUMBER_OF_HOUSEHOLD_PER_INVESTIGATOR):
-        Household.objects.create(
-            investigator=investigator, uid=uid_counter + index)
-        Household.objects.create(
-            investigator=investigator_2, uid=uid_counter + 1 + index)
-        uid_counter = uid_counter + 2
-
-    world.investigator = investigator
-    world.investigator_2 = investigator_2
-
-
-@step(u'And I choose a location and an open batch')
-def and_i_choose_a_location_and_an_open_batch(step):
-    locations = SortedDict()
-    locations['district'] = 'Kampala'
-    locations['county'] = 'Kampala County'
-    world.page.choose_location(locations)
-    world.page.choose_batch(world.batch_1)
-
-
-@step(u'And I change my mind to select all districts')
-def and_i_change_my_mind_to_select_all_districts(step):
-    world.page.select_all_district()
-
-
-@step(u'And I click get status button')
-def and_i_click_get_status_button(step):
-    world.page.submit()
-
-
-@step(u'And I should see all districts as location selected')
-def and_i_should_see_all_districts_location_selected(step):
-    world.page.see_all_districts_location_selected()
-
-
-@step(u'Then I should see number of households and clusters completed and pending')
-def then_i_should_see_number_of_households_and_clusters_completed_and_pending(step):
-    world.page.assert_status_count(
-        pending_households=20, completed_housesholds=0, pending_clusters=2, completed_clusters=0)
-
-
-@step(u'And I should see a list of investigators with corresponding phone numbers and pending households')
-def and_i_should_see_a_list_of_investigators_with_corresponding_phone_numbers_and_pending_households(step):
-    world.page.check_presence_of_investigators(
-        world.investigator, world.investigator_2)
-
-
-@step(u'And I choose a location and a closed batch')
-def and_i_choose_a_location_and_a_closed_batch(step):
-    world.page.choose_location({'district': 'Kampala'})
-    world.page.choose_batch(world.batch_2)
-
-
-@step(u'And I should see a message that says that this batch is currently closed')
-def and_i_should_see_a_message_that_says_that_this_batch_is_currently_closed(step):
-    world.page.assert_presence_of_batch_is_closed_message()
-
-
-@step(u'And I visit download excel page')
-def and_i_visit_download_excel_page(step):
-    world.page = DownloadExcelPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I visit district aggregate page')
-def and_i_visit_district_aggregate_page(step):
-    world.page = SurveyCompletionRatesPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see a table for completion rates')
-def then_i_should_see_a_table_for_completion_rates(step):
-    world.page.see_completion_rates_table()
-
-
-@step(u'And I should see descendants in the table')
-def and_i_should_see_descendants_in_the_table(step):
-    world.page.is_text_present(world.kampala_subcounty.name)
-
-
-@step(u'When I click on descendant name')
-def when_i_click_on_descendant_name(step):
-    world.page.click_link_by_text(world.kampala_subcounty.name)
-
-
-@step(u'Then I should see status page for that location')
-def then_i_should_see_status_page_for_that_location(step):
-    world.page.see_completion_rates_table()
-    world.page.is_text_present(world.kampala_parish.name)
-
-
-@step(u'And I choose ea and an open batch')
-def and_i_choose_ea_and_an_open_batch(step):
-    locations = SortedDict()
-    locations['district'] = world.kampala_district.name
-    locations['county'] = world.kampala_county.name
-    locations['subcounty'] = world.kampala_subcounty.name
-    locations['parish'] = world.kampala_parish.name
-
-    world.page.choose_location(locations)
-    world.page.choose_batch(world.batch_1)
-    world.page.choose_ea(world.ea)
-
-
-@step(u'Then I should see a table for household completion rates')
-def then_i_should_see_a_table_for_household_completion_rates(step):
-    world.page.see_houdehold_completion_table()
-
-
-@step(u'And I should see household details text')
-def and_i_should_see_household_details_text(step):
-    world.page.is_text_present(
-        "Survey Completion by household in %s EA" % world.ea.name)
-    world.page.is_text_present("%s" % world.household_1.uid)
-    world.page.is_text_present(
-        "%s" % world.household_1.household_member.all().count())
-
-
-@step(u'And I should see investigator details text')
-def and_i_should_see_investigator_details_text(step):
-    world.page.is_text_present('Investigator: %s(%s)' % (
-        world.investigator.name, world.investigator.mobile_number))
-
-
-@step(u'And I have an investigator and households')
-def and_i_have_an_investigator_and_households(step):
-    world.batch = Batch.objects.create(survey=world.survey_1, name="Haha")
-    world.investigator = Investigator.objects.create(
-        name="some_investigator", mobile_number="123456784", ea=world.ea)
-    world.household_1 = Household.objects.create(
-        investigator=world.investigator, uid=101, ea=world.ea, survey=world.survey_1)
-    world.household_2 = Household.objects.create(
-        investigator=world.investigator, uid=102, ea=world.ea, survey=world.survey_1)
-    world.member_2 = HouseholdMember.objects.create(household=world.household_2,
-                                                    date_of_birth=datetime.datetime(2000, 02, 02))
-
-
-@step(u'And I should see percent completion')
-def and_i_should_see_percent_completion(step):
-    world.page.is_text_present('Percent Completion: 50')
-
-
-@step(u'And I have 2 surveys with one batch each')
-def and_i_have_2_surveys_with_one_batch_each(step):
-    world.batch_1 = Batch.objects.create(
-        name='batch1', order=1, survey=world.survey_1)
-    world.batch_2 = Batch.objects.create(
-        name='batch2', order=1, survey=world.survey_2)
-
-
-@step(u'When I select survey 2 from survey list')
-def when_i_select_survey_2_from_survey_list(step):
-    world.page.select('survey', [world.survey_2.id])
-
-
-@step(u'Then I should see batch2 in batch list')
-def then_i_should_see_batch2_in_batch_list(step):
-    world.page.see_select_option([world.batch_2.name], 'batch')
-
-
-@step(u'And I should not see batch1 in batch list')
-def and_i_should_not_see_batch1_in_batch_list(step):
-    world.page.option_not_present([world.batch_1.name], 'batch')
-
-
-@step(u'When I select survey 1 from survey list')
-def when_i_select_survey_1_from_survey_list(step):
-    world.page.select('survey', [world.survey_1.id])
-
-
-@step(u'Then I should see batch1 in batch list')
-def then_i_should_see_batch1_in_batch_list(step):
-    world.page.see_select_option([world.batch_1.name], 'batch')
-
-
-@step(u'And I should not see batch2 in batch list')
-def and_i_should_not_see_batch2_in_batch_list(step):
-    world.page.option_not_present([world.batch_2.name], 'batch')
-
-
-@step(u'And I should see title message')
-def and_i_should_see_title_message(step):
-    world.page.is_text_present('Survey Completion by Region/District')
-
-
-@step(u'When I visit investigator report page')
-def when_i_visit_investigator_report_page(step):
-    world.page = InvestigatorReportPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see title-text message')
-def then_i_should_see_title_text_message(step):
-    world.page.is_text_present(
-        'Choose survey to get investigators who completed the survey')
-
-
-@step(u'And I should see dropdown with two surveys')
-def and_i_should_see_dropdown_with_two_surveys(step):
-    world.page.see_select_option(
-        [world.survey_1.name, world.survey_2.name], 'survey')
-
-
-@step(u'And I should see generate report button')
-def and_i_should_see_generate_report_button(step):
-    assert world.browser.find_by_css(
-        "#download-investigator-form")[0].find_by_tag('button')[0].text == "Generate Report"
-
-
-@step(u'And I have 100 locations')
-def and_i_have_100_locations(step):
-    country = LocationType.objects.create(name="Country", slug="country")
-    district = LocationType.objects.create(name="District", slug="district")
-    world.uganda = Location.objects.create(name="uganda", type=country)
-
-    for i in xrange(100):
-        Location.objects.create(name="name" + str(i),
-                                tree_parent=world.uganda, type=district)
-
-
-@step(u'Then I should see district completion table paginated')
-def then_i_should_see_district_completion_table_paginated(step):
-    world.page.validate_pagination()
-
-
-@step(u'And I have one batch open in those locations')
-def and_i_have_one_batch_open_in_those_locations(step):
-    world.batch_12 = Batch.objects.create(
-        order=12, name="Batch A", survey=world.survey_1)
-    world.batch_12.open_for_location(world.uganda)
-
-
-@step(u'When I select one of the survey')
-def when_i_select_one_of_the_survey(step):
-    world.page.see_select_option(
-        [world.survey_1.name, world.survey_2.name], 'survey')
-
-
-@step(u'Then I should batches in that survey')
-def then_i_should_batches_in_that_survey(step):
-    world.page.validate_select_option(world.batch_1)
-
-
-@step(u'And I click generate report button')
-def and_i_click_generate_report_button(step):
-    world.page.find_by_css("#generate_report", "Generate Report")
-
-
-@step(u'And I have three surveys')
-def and_i_have_three_surveys(step):
-    world.survey_1 = Survey.objects.create(name="Haha Survey")
-    world.survey_2 = Survey.objects.create(name="Hoho Survey")
-
-
-@step(u'And I have batches in those surveys')
-def and_i_have_batches_in_those_surveys(step):
-    world.batch_1 = Batch.objects.create(
-        order=1, name="Batch A haha", survey=world.survey_1)
-    world.batch_2 = Batch.objects.create(
-        order=2, name="Batch A hoho", survey=world.survey_2)
-
-
-@step(u'Then I should only see the batches in that survey')
-def then_i_should_only_see_the_batches_in_that_survey(step):
-    world.page.see_select_option(['All', str(world.batch_2.name)], 'batch')
-
-
-@step(u'When I choose a batch in that survey')
-def when_i_choose_a_batch_in_that_survey(step):
-    world.page.select('batch', [world.batch_2.id])
-
-
-@step(u'Then I should be able to export the responses for that batch')
-def then_i_should_be_able_to_export_the_responses_for_that_batch(step):
-    world.page.find_by_css("#export_excel", "Export to spreadsheet")
-
-
-@step(u'When I select one of the two surveys')
-def when_i_select_one_of_the_two_surveys(step):
-    world.page.select('survey', [str(world.survey_2.id)])
-
-
-@step(u'And I have general member group')
-def and_i_have_general_member_group(step):
-    HouseholdMemberGroup.objects.create(order=1, name="GENERAL")
diff --git a/survey/features/aggregates.feature b/survey/features/aggregates.feature
deleted file mode 100644
index 00e48002..00000000
--- a/survey/features/aggregates.feature
+++ /dev/null
@@ -1,86 +0,0 @@
-Feature: Aggregates feature
-
-    Scenario: Download Excel
-      Given I am logged in as researcher
-      And I have three surveys
-      And I have general member group
-      And I have batches in those surveys
-      And I visit download excel page
-      When I select one of the two surveys
-      Then I should only see the batches in that survey
-      When I choose a batch in that survey
-      Then I should be able to export the responses for that batch
-
-    Scenario: Region/district completion rates
-      Given I am logged in as researcher
-      And I have three surveys
-      And I have locations
-      And I have 2 batches with one open
-      And I visit district aggregate page
-      Then I should see an option to select location hierarchically
-      And I should see an option to select batch
-      And I should see a get status button
-
-    Scenario: Region/district completion rates -- pagination
-      Given I am logged in as researcher
-      And I have three surveys
-      And I have 100 locations
-      And I have one batch open in those locations
-      And I visit district aggregate page
-      And I click get status button
-      Then I should see district completion table paginated
-
-    Scenario: Survey completion rates - with filter data
-      Given I am logged in as researcher
-      And I have three surveys
-      And I have locations
-      And I have 2 batches with one open
-      And I visit district aggregate page
-      And I choose a location and an open batch
-      And I click get status button
-      Then I should see a table for completion rates
-      And I should see title message
-      And I should see descendants in the table
-
-    Scenario: Survey completion for Lowest level location type
-      Given I am logged in as researcher
-      And I have three surveys
-      And I have locations
-      And I have eas in the lowest location
-      And I have an investigator and households
-      And I have 2 batches with one open
-      And one household has completed that open batch
-      And I visit district aggregate page
-      And I choose ea and an open batch
-      And I click get status button
-      Then I should see a table for household completion rates
-      And I should see household details text
-      And I should see investigator details text
-      And I should see percent completion
-
-    Scenario: Survey Completion - Javascript
-       Given I am logged in as researcher
-        And I have locations
-        And I have three surveys
-        And I have eas in the lowest location
-        And I have an investigator and households
-        And I have 2 batches with one open
-        And I visit district aggregate page
-        When I select survey 2 from survey list
-        Then I should see batch2 in batch list
-        And I should not see batch1 in batch list
-        When I select survey 1 from survey list
-        Then I should see batch1 in batch list
-        And I should not see batch2 in batch list
-
-    Scenario: Download Investigators Report - GET
-       Given I am logged in as researcher
-       And I have locations
-       And I have three surveys
-       And I have 2 surveys with one batch each
-       When I visit investigator report page
-       Then I should see title-text message
-       When I select one of the survey
-       Then I should batches in that survey
-       When I select a batch
-       And I click generate report button
\ No newline at end of file
diff --git a/survey/features/bulksms-steps.py b/survey/features/bulksms-steps.py
deleted file mode 100644
index 082460f3..00000000
--- a/survey/features/bulksms-steps.py
+++ /dev/null
@@ -1,74 +0,0 @@
-from lettuce import *
-from rapidsms.contrib.locations.models import *
-
-from survey.features.page_objects.root import BulkSMSPage
-from survey.investigator_configs import *
-from survey.models import Backend, EnumerationArea
-from survey.models.investigator import Investigator
-
-
-@step(u'And I have 2 districts with investigators')
-def and_i_have_2_districts_with_investigators(step):
-    district = LocationType.objects.create(
-        name=PRIME_LOCATION_TYPE, slug='district')
-    world.kampala = Location.objects.create(name="Kampala", type=district)
-    world.abim = Location.objects.create(name="Abim", type=district)
-    backend = Backend.objects.all()[0]
-
-    world.ea = EnumerationArea.objects.create(name="EA")
-    world.ea.locations.add(world.kampala)
-    world.ea_abim = EnumerationArea.objects.create(name="EA Abim")
-    world.ea_abim.locations.add(world.abim)
-
-    Investigator.objects.create(
-        name="Rajni", mobile_number="123456789", ea=world.ea, backend=backend)
-    Investigator.objects.create(
-        name="Rajni", mobile_number="123456780", ea=world.ea_abim, backend=backend)
-
-
-@step(u'And I visit bulk SMS page')
-def and_i_visit_bulk_sms_page(step):
-    world.page = BulkSMSPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I compose a SMS to send')
-def and_i_compose_a_sms_to_send(step):
-    world.page.compose_message("MESSAGE")
-
-
-@step(u'And I select the districts')
-def and_i_select_the_districts(step):
-    world.page.select_multiple(
-        '#bulk-sms-locations', world.kampala, world.abim)
-
-
-@step(u'And I click send')
-def and_i_click_send(step):
-    world.page.submit()
-
-
-@step(u'Then I should see a message saying SMS has been sent')
-def then_i_should_see_a_message_saying_sms_has_been_sent(step):
-    world.page.is_message_sent()
-
-
-@step(u'Then I should see error message for location')
-def then_i_should_see_error_message_for_location(step):
-    world.page.error_message_for('location')
-
-
-@step(u'Then I should see error message for text')
-def then_i_should_see_error_message_for_text(step):
-    world.page.error_message_for('text')
-
-
-@step(u'And I type message the message')
-def and_i_type_message_the_message(step):
-    world.text_length = 10
-    world.page.enter_text(length=world.text_length)
-
-
-@step(u'Then I should see the counter has changed')
-def then_i_should_see_the_counter_has_changed(step):
-    world.page.counter_updated(length=world.text_length)
diff --git a/survey/features/bulksms.feature b/survey/features/bulksms.feature
deleted file mode 100644
index d160cae5..00000000
--- a/survey/features/bulksms.feature
+++ /dev/null
@@ -1,22 +0,0 @@
-Feature: Bulk SMS
-
-  Scenario: Sending Bulk SMS to investigators in selected locations
-    Given I am logged in as researcher
-    And I have 2 districts with investigators
-    And I visit bulk SMS page
-    And I compose a SMS to send
-    And I select the districts
-    And I click send
-    Then I should see a message saying SMS has been sent
-
-  Scenario: Bulk SMS validations
-    Given I am logged in as researcher
-    And I have 2 districts with investigators
-    And I visit bulk SMS page
-    And I click send
-    Then I should see error message for location
-    And I select the districts
-    And I click send
-    Then I should see error message for text
-    And I type message the message
-    Then I should see the counter has changed
\ No newline at end of file
diff --git a/survey/features/ea.feature b/survey/features/ea.feature
deleted file mode 100644
index 75bfb399..00000000
--- a/survey/features/ea.feature
+++ /dev/null
@@ -1,17 +0,0 @@
-Feature: EA upload
-
-  Scenario: Upload EA
-    Given I am logged in as researcher
-    And I have some locations to add EA
-    And I have a survey
-    When I visit upload EA page
-    Then I should see EA upload form fields
-    When I click on the link for input file format
-    Then I should see table of EA layout
-    When I click on the link for input file format
-    Then said EA layout should collapse
-    When I have a EA csv file
-    And I input that file
-    And I select a survey
-    And I click the save button
-    Then I should see EA upload is in progress
diff --git a/survey/features/ea_steps.py b/survey/features/ea_steps.py
deleted file mode 100644
index e55bfd7b..00000000
--- a/survey/features/ea_steps.py
+++ /dev/null
@@ -1,94 +0,0 @@
-import csv
-from time import sleep
-from lettuce import step, world
-from rapidsms.contrib.locations.models import Location, LocationType
-from survey.features.page_objects.uploads import UploadEAPage
-from survey.models import LocationTypeDetails
-
-
-@step(u'And I have some locations to add EA')
-def and_i_have_some_locations_to_add_ea(step):
-    country = LocationType.objects.create(name='Country', slug='country')
-    uganda = Location.objects.create(name="Uganda", type=country)
-    LocationTypeDetails.objects.create(country=uganda, location_type=country)
-
-    region_type = LocationType.objects.create(
-        name="regiontype", slug="regiontype")
-    district_type = LocationType.objects.create(
-        name="districttype", slug='districttype')
-    county_type = LocationType.objects.create(
-        name="countytype", slug='countytype')
-    parish_type = LocationType.objects.create(
-        name="parishtype", slug='parishtype')
-
-    region = Location.objects.create(
-        name="region1", type=region_type, tree_parent=uganda)
-    district = Location.objects.create(
-        name="district1", tree_parent=region, type=district_type)
-    county_1 = Location.objects.create(
-        name="county1", tree_parent=district, type=county_type)
-    parish_1 = Location.objects.create(
-        name="parish_1", tree_parent=county_1, type=parish_type)
-    parish_1_b = Location.objects.create(
-        name="parish_1b", tree_parent=county_1, type=parish_type)
-
-    region = Location.objects.create(
-        name="region2", tree_parent=uganda, type=region_type)
-    district = Location.objects.create(
-        name="district2", tree_parent=region, type=district_type)
-    county_2 = Location.objects.create(
-        name="county2", tree_parent=district, type=county_type)
-    parish_2 = Location.objects.create(
-        name="parish_2", tree_parent=county_2, type=parish_type)
-
-
-@step(u'When I visit upload EA page')
-def when_i_visit_upload_ea_page(step):
-    world.page = UploadEAPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see EA upload form fields')
-def then_i_should_see_ea_upload_form_fields(step):
-    world.page.validate_fields_present(
-        ["Upload Enumeration Areas", "Survey", "EA file"])
-
-
-@step(u'Then I should see table of EA layout')
-def then_i_should_see_table_of_ea_layout(step):
-    world.type_names = [
-        type.name for type in LocationType.objects.exclude(name__iexact="country")]
-    world.page.validate_fields_present(world.type_names)
-
-
-@step(u'When I have a EA csv file')
-def when_i_have_a_ea_csv_file(step):
-    filedata = [
-        ['Regiontype', 'Districttype', 'Counttype',
-         'EA',                   'Parishtype', 'EA'],
-        ['region1',    'district1',    'county1',
-         'ea_containing_parish', 'parish_1',   ''],
-        ['region1',    'district1',    'county1',
-         'ea_containing_parish', 'parish_1b',  ''],
-        ['region2',    'district2',    'county2',   '',
-         'parish2',    'ea_under_parish'],
-        ['region2',    'district2',    'county2',   '',                     'parish2',    'ea_under_parish']]
-
-    write_to_csv('wb', filedata, 'test.csv')
-
-
-def write_to_csv(mode, data, csvfilename):
-    with open(csvfilename, mode) as fp:
-        file = csv.writer(fp, delimiter=',')
-        file.writerows(data)
-
-
-@step(u'Then I should see EA upload is in progress')
-def then_i_should_see_ea_upload_is_in_progress(step):
-    world.page.is_text_present('Upload in progress. This could take a while.')
-
-
-@step(u'Then said EA layout should collapse')
-def then_said_ea_layout_should_collapse(step):
-    sleep(3)
-    world.page.validate_layout_collapsed()
diff --git a/survey/features/home_page-step.py b/survey/features/home_page-step.py
deleted file mode 100644
index e984ebba..00000000
--- a/survey/features/home_page-step.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from lettuce import *
-
-from survey.features.page_objects.accounts import LogoutPage
-from survey.features.page_objects.root import AboutPage
-
-
-@step(u'Given I am not logged in')
-def given_i_am_not_logged_in(step):
-    world.page = LogoutPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see the completion map')
-def then_i_should_see_under_construction(step):
-    world.page.is_text_present("Completion rates:")
diff --git a/survey/features/home_page.feature b/survey/features/home_page.feature
deleted file mode 100644
index ad11c049..00000000
--- a/survey/features/home_page.feature
+++ /dev/null
@@ -1,6 +0,0 @@
-Feature: Homepage feature
-
-  Scenario: Home page
-    Given I am not logged in
-    And I am in the home page
-    Then I should see the completion map
\ No newline at end of file
diff --git a/survey/features/household_member-steps.py b/survey/features/household_member-steps.py
deleted file mode 100644
index 6c8ee857..00000000
--- a/survey/features/household_member-steps.py
+++ /dev/null
@@ -1,112 +0,0 @@
-from time import sleep
-from lettuce import *
-from rapidsms.contrib.locations.models import LocationType, Location
-from survey.features.page_objects.household_member import NewHouseholdMemberPage, EditHouseholdMemberPage, DeleteHouseholdMemberPage
-from survey.features.page_objects.households import HouseholdDetailsPage
-from survey.models import EnumerationArea
-from survey.models.households import HouseholdMember, HouseholdHead, Household
-from survey.models.investigator import Investigator
-
-
-@step(u'And I have a household')
-def and_i_have_a_household(step):
-    district = LocationType.objects.get(slug='district')
-    world.kampala = Location.objects.create(name='Kampala', type=district)
-    world.ea = EnumerationArea.objects.create(name="EA")
-    world.ea.locations.add(world.kampala_village)
-    world.investigator = Investigator.objects.create(
-        name="Investigator 1", mobile_number="1", ea=world.ea)
-    world.household = Household.objects.create(
-        investigator=world.investigator, ea=world.investigator.ea, uid=4)
-    HouseholdHead.objects.create(household=world.household, surname="Test", first_name="User",
-                                 date_of_birth="1980-09-01", male=True,
-                                 occupation='Agricultural labor', level_of_education='Primary',
-                                 resident_since_year=2013, resident_since_month=2)
-
-
-@step(u'And I visit new household member page')
-def and_i_visit_new_household_member_page(step):
-    world.page = NewHouseholdMemberPage(world.browser, world.household)
-    world.page.visit()
-
-
-@step(u'And I see all household member fields are present')
-def and_i_see_all_household_member_fields_are_present(step):
-    world.page.validate_fields()
-
-
-@step(u'Then I should see member successfully created message')
-def then_i_should_see_member_successfully_created_message(step):
-    world.page.see_success_message('Household member', 'created')
-
-
-@step(u'And I fill all member related fields')
-def and_i_fill_all_member_related_fields(step):
-    data = {'surname': 'xyz',
-            'male': True
-            }
-
-    world.page.fill_valid_member_values(data)
-    world.page.select_date("#id_date_of_birth")
-    sleep(3)
-
-
-@step(u'And also I have a household member')
-def and_also_i_have_a_household_member(step):
-    world.household_member = HouseholdMember.objects.create(surname='member1', date_of_birth='2013-08-30', male=True,
-                                                            household=world.household)
-
-
-@step(u'And I visit edit household member page')
-def and_i_visit_edit_household_member_page(step):
-    world.page = EditHouseholdMemberPage(
-        world.browser, world.household, world.household_member)
-    world.page.visit()
-
-
-@step(u'And I see all details of household member are present')
-def and_i_see_all_details_of_household_member_are_present(step):
-    world.page.validate_member_details(world.household_member)
-
-
-@step(u'And I edit member related fields')
-def and_i_edit_member_related_fields(step):
-    data = {'male': False,
-            'surname': 'member1edited'
-            }
-    world.page.fill_valid_member_values(data)
-    world.page.select_date("#id_date_of_birth")
-    sleep(3)
-
-
-@step(u'And I submit the form')
-def and_i_submit_the_form(step):
-    world.page.submit()
-
-
-@step(u'Then I should see member successfully edited message')
-def then_i_should_see_member_successfully_edited_message(step):
-    world.page.is_text_present('Household member successfully edited.')
-
-
-@step(u'And I visit that household details page')
-def and_i_visit_that_household_details_page(step):
-    world.page = HouseholdDetailsPage(world.browser, world.household)
-    world.page.visit()
-
-
-@step(u'And I click delete member')
-def and_i_click_delete_member(step):
-    world.page.click_delete_link(world.household_member.pk)
-
-
-@step(u'Then I should see a confirmation modal')
-def then_i_should_see_a_confirmation_modal(step):
-    world.page = DeleteHouseholdMemberPage(
-        world.browser, world.household, world.household_member)
-    world.page.see_delete_confirmation_modal()
-
-
-@step(u'Then that member is successfully deleted')
-def then_that_member_is_successfully_deleted(step):
-    world.page.see_success_message('Household member', 'deleted')
diff --git a/survey/features/household_member.feature b/survey/features/household_member.feature
deleted file mode 100644
index 20a277c6..00000000
--- a/survey/features/household_member.feature
+++ /dev/null
@@ -1,37 +0,0 @@
-Feature: Household Member feature
-
-    Scenario: Household Member new page
-      Given I am logged in as researcher
-      And I have locations
-      And I have an investigator in that location
-      And I have a household
-      And I visit new household member page
-      And I see all household member fields are present
-      And I fill all member related fields
-      And I submit the form
-      Then I should see member successfully created message
-
-    Scenario: Household member edit page
-      Given I am logged in as researcher
-      And I have locations
-      And I have an investigator in that location
-      And I have a household
-      And also I have a household member
-      And I visit edit household member page
-      And I see all details of household member are present
-      And I edit member related fields
-      And I submit the form
-      Then I should see member successfully edited message
-
-    Scenario: Delete Household Member
-        Given I am logged in as researcher
-        And I have locations
-        And I have an investigator in that location
-        And I have a household
-        And also I have a household member
-        And I visit that household details page
-        And I click delete member
-        Then I should see a confirmation modal
-        And if I click yes
-        Then that member is successfully deleted
-
diff --git a/survey/features/household_member_groups.feature b/survey/features/household_member_groups.feature
deleted file mode 100644
index 9a6462ab..00000000
--- a/survey/features/household_member_groups.feature
+++ /dev/null
@@ -1,120 +0,0 @@
-Feature: Group features
-
-  Scenario: List Conditions Page
-    Given I am logged in as researcher
-    And I visit conditions listing page
-    And I have 10 conditions
-    And I should see the conditions list
-    When I click the add button
-    Then I should see the new condition form
-
-  Scenario: List Groups Page
-    Given I am logged in as researcher
-    And I have a condition
-    And I have 100 groups with that condition
-    And I visit groups listing page
-    Then I should see the groups list paginated
-
-  Scenario: Add a group condition
-    Given I am logged in as researcher
-    And I visit the new condition page
-    When I fill in the condition details
-    And I click save button
-    Then I should see that the condition was saved on the condition list page
-
-  Scenario: Add household member a group
-    Given I am logged in as researcher
-    And I have a condition
-    And I visit the new group page
-    When I fill in the group details
-    And I select a condition
-    And I click save button
-    Then I should see that the group was saved successfully
-
-  Scenario: Create a condition on the group form
-    Given I am logged in as researcher
-    And I visit the new group page
-    When I click the add new condition
-    Then I should see the modal open
-    When I fill in the condition details
-    And I click the new condition form save button
-    Then I should see the condition saved on create group page
-    And I should see the new condition in the groups form
-
-  Scenario: Create a condition with invalid form on the group form
-    Given I am logged in as researcher
-    And I visit the new group page
-    When I click the add new condition
-    Then I should see the modal open
-    And I click the new condition form save button
-    Then I should see the form errors of required fields
-
-  Scenario: Create a group with multiple conditions
-    Given I am logged in as researcher
-    And I have 2 conditions
-    And I visit the new group page
-    When I fill name and order
-    And I select conditions
-    And I click save button
-    Then I should see that the group was saved successfully
-
-  Scenario: List group details
-    Given I am logged in as researcher
-    And I have member group with conditions
-    And I visit groups listing page
-    And I click view conditions link
-    Then I should see a list of conditions
-    When I click on add condition button
-    Then I should see a new condition form for this group
-    And When I fill condition details
-    And I submit the form
-    And I should see the newly added condition on that page
-
-  Scenario: Add group from group listing page
-    Given I am logged in as researcher
-    And I visit groups listing page
-    When I click the add group button
-    Then I should go to add group page
-
-  Scenario: JS validation for add group condition
-    Given I am logged in as researcher
-    And I visit the new condition page
-    When I select gender as attribute
-    Then I should see only Equals as available for condition
-    And male and female for values
-    When I select general as attribute
-    Then I should see only Equals as available for condition
-    And HEAD for values
-    When I select age as attribute
-    And If I add in a negative number
-    And I click the new condition form save button
-    Then I see error age cannot be negative
-
-  Scenario: Edit a member group
-    Given I am logged in as researcher
-    And I have member group with conditions
-    And I visit groups listing page
-    And I click edit group link
-    Then I should see the groups details in an edit group form
-    When I fill in edited group details
-    And I select new conditions
-    And I submit the form
-    Then I should see that the group was edited successfully
-
-  Scenario: Delete a member a group
-    Given I am logged in as researcher
-    And I have member group with conditions
-    And I visit groups listing page
-    And I click delete group link
-    Then I should see a delete confirmation modal 
-    When I click yes 
-    Then I should see that the group was deleted successfully
-
-  Scenario: Delete a group condition
-    Given I am logged in as researcher
-    And I have member group with conditions
-    And I visit conditions listing page
-    And I click delete condition link
-    Then I should see a delete condition confirmation modal 
-    When I click yes 
-    Then I should see that the condition was deleted successfully
\ No newline at end of file
diff --git a/survey/features/household_member_groups_steps.py b/survey/features/household_member_groups_steps.py
deleted file mode 100644
index 30a65382..00000000
--- a/survey/features/household_member_groups_steps.py
+++ /dev/null
@@ -1,358 +0,0 @@
-from random import randint
-
-from lettuce import *
-from survey.models.householdgroups import HouseholdMemberGroup, GroupCondition
-
-from survey.features.page_objects.household_member_groups import GroupConditionListPage, GroupsListingPage, AddConditionPage, AddGroupPage, GroupConditionModalPage, GroupDetailsPage, AddNewConditionToGroupPage, DeleteHouseholdMemberGroup
-
-
-@step(u'And I have 10 conditions')
-def and_i_have_10_conditions(step):
-    for i in xrange(10):
-        try:
-            GroupCondition.objects.create(
-                value=i, attribute='AGE', condition="EQUALS")
-        except Exception:
-            pass
-
-
-@step(u'And I visit conditions listing page')
-def and_i_visit_conditions_listing_page(step):
-    world.page = GroupConditionListPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I should see the conditions list')
-def and_i_should_see_the_conditions_list(step):
-    world.page.validate_fields()
-
-
-@step(u'And I have a condition')
-def and_i_have_a_condition(step):
-    world.condition = GroupCondition.objects.create(
-        value=5, attribute='AGE', condition="EQUALS")
-
-
-@step(u'And I have 100 groups with that condition')
-def and_i_have_100_groups_with_that_condition(step):
-    for i in xrange(100):
-        try:
-            HouseholdMemberGroup.objects.create(order=i, name="group %d" % i)
-        except Exception:
-            pass
-
-
-@step(u'And I visit groups listing page')
-def and_i_visit_groups_listing_page(step):
-    world.page = GroupsListingPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see the groups list paginated')
-def then_i_should_see_the_groups_list_paginated(step):
-    world.page.validate_fields()
-    world.page.validate_pagination()
-
-
-@step(u'When I click the add button')
-def when_i_click_the_add_button(step):
-    world.page.click_link_by_text(" Add Eligibility Criteria")
-
-
-@step(u'Then I should see the new condition form')
-def then_i_should_see_the_new_condition_form(step):
-    world.page.is_text_present("New Criteria")
-
-
-@step(u'And I visit the new condition page')
-def and_i_visit_the_new_condition_page(step):
-    world.page = AddConditionPage(world.browser)
-    world.page.visit()
-
-
-@step(u'When I fill in the condition details')
-def when_i_fill_in_the_condition_details(step):
-    data = {'attribute': 'AGE',
-            'value': '9'}
-    world.page.fill_valid_values(data)
-    world.page.fill('value', '9')
-
-
-@step(u'And I click save button')
-def and_i_click_save_button(step):
-    world.page.submit()
-
-
-@step(u'Then I should see that the condition was saved on the condition list page')
-def then_i_should_see_that_the_condition_was_saved_successfully(step):
-    world.page = GroupConditionListPage(world.browser)
-    world.page.validate_url()
-    world.page.see_success_message('Condition', 'added')
-
-
-@step(u'And I visit the new group page')
-def and_i_visit_the_new_group_page(step):
-    world.page = AddGroupPage(world.browser)
-    world.page.visit()
-
-
-@step(u'When I fill in the group details')
-def when_i_fill_in_the_group_details(step):
-    data = {'name': 'aged between 15 and 49',
-            'order': 1,
-            }
-    world.page.fill_valid_values(data)
-
-
-@step(u'Then I should see that the group was saved successfully')
-def then_i_should_see_that_the_group_was_saved_successfully(step):
-    world.page.see_success_message('Group', 'added')
-
-
-@step(u'When I click the add new condition')
-def when_i_click_the_add_new_condition(step):
-    world.page.click_link_by_text("Add Eligibility Criteria ")
-
-
-@step(u'Then I should see the modal open')
-def then_i_should_see_the_modal_open(step):
-    world.page = GroupConditionModalPage(world.browser)
-    world.page.validate_contents()
-
-
-@step(u'And I click the new condition form save button')
-def and_i_click_the_save_button(step):
-    world.page.click_button("save_condition_button")
-
-
-@step(u'Then I should see the condition saved on create group page')
-def then_i_should_see_the_condition_was_saved_successfully(step):
-    world.page = AddGroupPage(world.browser)
-    world.page.validate_url()
-    world.page.see_success_message("Condition", "added")
-
-
-@step(u'And I should see the new condition in the groups form')
-def and_i_should_see_the_new_condition_in_the_groups_form(step):
-    latest_condition = GroupCondition.objects.get(
-        value='9', attribute="AGE", condition="EQUALS")
-    world.page.validate_latest_condition(latest_condition)
-
-
-@step(u'And I have 2 conditions')
-def and_i_have_2_conditions(step):
-    world.condition_1 = GroupCondition.objects.create(
-        value=False, attribute="GENDER", condition="EQUALS")
-    world.condition_2 = GroupCondition.objects.create(
-        value=40, attribute="AGE", condition="EQUALS")
-
-
-@step(u'When I fill name and order')
-def when_i_fll_name_and_order(step):
-    data = {'name': 'aged between 15 and 49',
-            'order': 1}
-    world.page.fill_valid_values(data)
-
-
-@step(u'And I select conditions')
-def and_i_select_conditions(step):
-    world.page.select(
-        'conditions', [world.condition_1.pk, world.condition_2.pk])
-
-
-@step(u'Then I should see the form errors of required fields')
-def then_i_should_see_the_form_errors_of_required_fields(step):
-    world.page.is_text_present("This field is required.")
-
-
-@step(u'And I have member group with conditions')
-def and_i_have_member_group_with_conditions(step):
-    world.condition_1 = GroupCondition.objects.create(
-        value='True', attribute="GENDER", condition="EQUALS")
-    world.condition_2 = GroupCondition.objects.create(
-        value=35, attribute="AGE", condition="EQUALS")
-    world.group = HouseholdMemberGroup.objects.create(order=1, name="group 1")
-    world.condition_1.groups.add(world.group)
-    world.condition_2.groups.add(world.group)
-
-
-@step(u'And I click view conditions link')
-def and_i_click_view_conditions_link(step):
-    world.page.click_link_by_text(" Criteria")
-
-
-@step(u'Then I should see a list of conditions')
-def then_i_should_see_a_list_of_conditions(step):
-    world.page = GroupDetailsPage(world.browser, world.group)
-    world.page.validate_fields()
-
-
-@step(u'When I click Groups tab')
-def when_i_click_groups_tab(step):
-    world.page.click_link_by_text("Groups")
-
-
-@step(u'Then I should see group dropdown list')
-def then_i_should_see_group_dropdown_list(step):
-    reverse_url_links = ["household_member_groups_page",
-                         "new_household_member_groups_page"]
-    world.page.see_dropdown(reverse_url_links)
-
-
-@step(u'And I select a condition')
-def and_i_select_a_condition(step):
-    world.page.select("conditions", [world.condition.pk])
-
-
-@step(u'When I click the add group button')
-def when_i_click_the_add_group_button(step):
-    world.page.click_link_by_text(" Add Group")
-
-
-@step(u'Then I should go to add group page')
-def then_i_should_go_to_add_group_page(step):
-    world.page = AddGroupPage(world.browser)
-    world.page.validate_url()
-
-
-@step(u'When I select gender as attribute')
-def when_i_select_gender_as_attribute(step):
-    world.page.select('attribute', ['GENDER'])
-
-
-@step(u'Then I should see only Equals as available for condition')
-def then_i_should_see_only_equals_as_available_for_condition(step):
-    world.page.see_select_option(['EQUALS'], 'condition')
-
-
-@step(u'And male and female for values')
-def and_male_and_female_for_values(step):
-    world.page.see_select_option(['Male', 'Female'], 'value')
-
-
-@step(u'When I select general as attribute')
-def when_i_select_general_as_attribute(step):
-    world.page.select('attribute', ['GENERAL'])
-
-
-@step(u'And HEAD for values')
-def and_head_for_values(step):
-    world.page.find_by_css('input[name=value][readonly=readonly]', 'HEAD')
-
-
-@step(u'When I select age as attribute')
-def when_i_select_age_as_attribute(step):
-    world.page.select('attribute', ['AGE'])
-
-
-@step(u'And If I add in a negative number')
-def and_if_i_add_in_a_negative_number(step):
-    world.page.fill('value', '-8')
-
-
-@step(u'Then I see error age cannot be negative')
-def then_i_should_see_error(step):
-    world.page.is_text_present('Age must be a whole non negative number.')
-
-
-@step(u'When I click on add condition button')
-def when_i_click_on_add_condition_button(step):
-    world.page.click_link_by_text(" Add Eligibility Criteria")
-
-
-@step(u'Then I should see a new condition form for this group')
-def then_i_should_see_a_new_condition_form(step):
-    world.page = AddNewConditionToGroupPage(world.browser, world.group)
-    world.page.validate_url()
-    world.page.validate_fields_present(['Attribute', 'Condition', 'Value'])
-
-
-@step(u'And When I fill condition details')
-def and_when_i_fill_condition_details(step):
-    world.data = {
-        'attribute': 'AGE',
-        'condition': 'EQUALS'
-    }
-    world.page.fill_valid_values(world.data)
-    world.page.fill('value', '9')
-
-
-@step(u'And I should see the newly added condition on that page')
-def and_i_should_see_the_newly_added_condition_on_that_page(step):
-    world.page.see_success_message("Criteria", "added")
-    world.page.validate_fields_present(
-        [world.data['attribute'], world.data['condition'], '9'])
-
-
-@step(u'And I click edit group link')
-def and_i_click_edit_group_link(step):
-    world.page.click_link_by_text(" Edit")
-
-
-@step(u'When I fill in edited group details')
-def when_i_fill_in_edited_group_details(step):
-    data = {'name': 'aged between 15 and 39',
-            'order': 1,
-            }
-    world.page.fill_valid_values(data)
-
-
-@step(u'Then I should see that the group was edited successfully')
-def then_i_should_see_that_the_group_was_edited_successfully(step):
-    world.page.see_success_message("Group", "edited")
-
-
-@step(u'Then I should see the groups details in an edit group form')
-def then_i_should_see_the_groups_details_in_an_edit_group_form(step):
-    form = {'name': 'Name',
-            'order': 'Order'}
-    form_values = {'name': world.group.name,
-                   'order': world.group.order}
-    world.page.validate_form_present(form)
-    world.page.validate_form_values(form_values)
-
-
-@step(u'And I select new conditions')
-def and_i_select_new_conditions(step):
-    new_condition = GroupCondition.objects.create(
-        value=39, attribute='AGE', condition="LESS_THAN")
-    world.page.select_multiple(
-        '#id_conditions', world.condition_1, new_condition)
-
-
-@step(u'And I click delete group link')
-def and_i_click_delete_group_link(step):
-    world.page.click_link_by_text(" Delete")
-    world.page = DeleteHouseholdMemberGroup(world.browser, world.group)
-
-
-@step(u'Then I should see a delete confirmation modal')
-def then_i_should_see_a_delete_confirmation_modal(step):
-    world.page.see_confirm_modal_message(world.group.name)
-
-
-@step(u'When I click yes')
-def when_i_click_yes(step):
-    world.page.click_link_by_text("Yes")
-
-
-@step(u'Then I should see that the group was deleted successfully')
-def then_i_should_see_that_the_group_was_deleted_successfully(step):
-    world.page.see_success_message("Group", "deleted")
-
-
-@step(u'And I click delete condition link')
-def and_i_click_delete_condition_link(step):
-    world.page.click_link_by_text(" Delete")
-
-
-@step(u'Then I should see a delete condition confirmation modal')
-def then_i_should_see_a_delete_condition_confirmation_modal(step):
-    world.page.see_confirm_modal_message(str(world.condition_1))
-    world.page.is_text_present("It is attached to the following groups:")
-    world.page.find_link_by_text(world.group.name)
-
-
-@step(u'Then I should see that the condition was deleted successfully')
-def then_i_should_see_that_the_condition_was_deleted_successfully(step):
-    world.page.see_success_message("Criteria", "deleted")
diff --git a/survey/features/households-steps.py b/survey/features/households-steps.py
deleted file mode 100644
index 2ec04db9..00000000
--- a/survey/features/households-steps.py
+++ /dev/null
@@ -1,288 +0,0 @@
-# -*- coding: utf-8 -*-
-from random import randint
-from datetime import date
-from time import sleep
-
-from lettuce import *
-
-from rapidsms.contrib.locations.models import *
-from django.template.defaultfilters import slugify
-
-from survey.features.page_objects.households import NewHouseholdPage, HouseholdsListPage, HouseholdDetailsPage, EditHouseholdsPage
-from survey.models import EnumerationArea
-from survey.models.households import HouseholdMember, HouseholdHead, Household
-from survey.models.investigator import Investigator
-
-
-def random_text(text):
-    return text + str(randint(1, 999))
-
-
-@step(u'And I visit new household page')
-def and_i_visit_new_household_page(step):
-    world.page = NewHouseholdPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I fill household data')
-def and_i_fill_household_data(step):
-    values = {
-        'surname': random_text('house'),
-        'first_name': random_text('ayoyo'),
-        'date_of_birth': '1980-02-01',
-        'uid': '2'}
-    world.page.fill_valid_values(values, world.ea)
-
-
-@step(u'And I see all households fields are present')
-def and_i_see_all_households_fields_are_present(step):
-    world.page.valid_page()
-
-
-@step(u'And I have an investigator in that location')
-def and_i_have_an_investigator_in_that_location(step):
-    world.investigator = Investigator.objects.create(
-        name="Investigator name", ea=world.ea)
-
-
-@step(u'Then I should see that the household is created')
-def then_i_should_see_that_the_household_is_created(step):
-    world.household_uid = world.page.get_household_values()['uid']
-    world.page.validate_household_created()
-
-
-@step(u'And I click No to has children')
-def and_i_click_no_to_has_children(step):
-    world.page.has_children('False')
-
-
-@step(u'Then I should see children number fields disabled')
-def then_i_should_see_children_number_fields_disabled(step):
-    world.page.are_children_fields_disabled()
-
-
-@step(u'And No below 5 is also checked')
-def and_no_below_5_is_also_checked(step):
-    world.page.is_no_below_5_checked()
-
-
-@step(u'And checking below 5 to yes does not work')
-def and_checking_below_5_to_yes_does_not_work(step):
-    world.page.cannot_say_yes_to_below_5()
-
-
-@step(u'And Now If I click to Yes to has children')
-def and_now_if_i_click_to_yes_to_has_children(step):
-    world.page.has_children('True')
-
-
-@step(u'Then all children number fields are enabled back')
-def then_all_children_number_fields_are_enabled_back(step):
-    world.page.are_children_fields_disabled(is_disabled=False)
-
-
-@step(u'And I click No to has below 5')
-def and_i_click_no_to_has_below_5(step):
-    world.page.has_children_below_5('False')
-
-
-@step(u'Then I should see below 5 number fields disabled')
-def then_i_should_see_below_5_number_fields_disabled(step):
-    world.page.are_children_below_5_fields_disabled(is_disabled=True)
-
-
-@step(u'And Now If I click Yes to below 5')
-def and_now_if_i_click_yes_to_below_5(step):
-    world.page.has_children_below_5('True')
-
-
-@step(u'Then below 5 number fields are enabled back')
-def then_below_5_number_fields_are_enabled_back(step):
-    world.page.are_children_below_5_fields_disabled(is_disabled=False)
-
-
-@step(u'And I click No to has women')
-def and_i_click_no_to_has_women(step):
-    world.page.has_women('False')
-
-
-@step(u'Then I should see has women number fields disabled')
-def then_i_should_see_has_women_number_fields_disabled(step):
-    world.page.are_women_fields_disabled()
-
-
-@step(u'And Now If I click Yes to has women')
-def and_now_if_i_click_yes_to_has_women(step):
-    world.page.has_women('True')
-
-
-@step(u'Then has women number fields are enabled back')
-def then_has_women_number_fields_are_enabled_back(step):
-    world.page.are_women_fields_disabled(is_disabled=False)
-
-
-@step(u'And I fill in number_of_females lower than sum of 15_19 and 20_49')
-def and_i_fill_in_number_of_females_lower_than_sum_of_15_19_and_20_49(step):
-    world.page.fill_in_number_of_females_lower_than_sum_of_15_19_and_20_49()
-
-
-@step(u'Then I should see an error on number_of_females')
-def then_i_should_see_an_error_on_number_of_females(step):
-    world.page.see_an_error_on_number_of_females()
-
-
-@step(u'And Now If I choose Other as occupation')
-def and_now_if_i_choose_other_as_occupation(step):
-    world.page.choose_occupation('Other: ')
-
-
-@step(u'Then I have to specify one')
-def then_i_have_to_specify_one(step):
-    world.page.is_specify_visible(True)
-
-
-@step(u'And If I choose a different occupation')
-def and_if_i_choose_a_different_occupation(step):
-    world.page.choose_occupation('Business person')
-
-
-@step(u'Then Specify disappears')
-def then_specify_disappears(step):
-    world.page.is_specify_visible(False)
-
-
-@step(u'Given I have an investigator')
-def given_i_have_an_investigator(step):
-    country = LocationType.objects.create(
-        name="Country", slug=slugify("country"))
-    uganda = Location.objects.create(name="Uganda", type=country)
-    world.ea = EnumerationArea.objects.create(name="EA")
-    world.ea.locations.add(uganda)
-
-    world.investigator = Investigator.objects.create(name="Investigator ", mobile_number='987654321', age=20,
-                                                     level_of_education="Nursery", language="Luganda", ea=world.ea)
-
-
-@step(u'Given I have 100 households')
-def given_i_have_100_households(step):
-    for i in xrange(100):
-        random_number = str(randint(1, 99999))
-        try:
-            HouseholdHead.objects.create(surname="head" + random_number, date_of_birth='1980-06-01', male=False, household=Household.objects.create(
-                investigator=world.investigator, location=world.investigator.location, uid=i, ea=world.investigator.ea))
-        except Exception:
-            pass
-
-
-@step(u'And I visit households listing page')
-def and_i_visit_households_listing_page(step):
-    world.page = HouseholdsListPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I should see the households list paginated')
-def and_i_should_see_the_households_list_paginated(step):
-    world.page.validate_fields()
-    world.page.validate_pagination()
-
-
-@step(u'Given I have no households')
-def given_i_have_no_households(step):
-    Household.objects.all().delete()
-
-
-@step(u'And I should see no household message')
-def and_i_should_see_no_household_message(step):
-    world.page.no_registered_huseholds()
-
-
-@step(u'And I select list households')
-def and_i_select_list_households(step):
-    world.page.click_link_by_text("Households")
-    world.page = HouseholdsListPage(world.browser)
-
-
-@step(u'When I click add household button')
-def when_i_click_add_household_button(step):
-    world.page = HouseholdsListPage(world.browser)
-    world.page.visit()
-    world.page.click_by_css("#add-household")
-
-
-@step(u'Then I should see add household page')
-def then_i_should_see_add_household_page(step):
-    world.page = NewHouseholdPage(world.browser)
-    world.page.validate_url()
-
-
-@step(u'And then I click on that household ID')
-def and_when_i_click_on_that_household_id(step):
-    world.page.click_link_by_text(world.household.uid)
-
-
-@step(u'And I should see that household details, its head and members')
-def and_i_should_see_that_household_details_its_head_and_members(step):
-    world.page.validate_household_details()
-    world.page.validate_household_member_details()
-
-
-@step(u'And I have a member for that household')
-def and_i_have_a_member_for_that_household(step):
-    world.household = Household.objects.get(uid=world.household_uid)
-    fields_data = dict(surname='xyz', male=True, date_of_birth=date(
-        1980, 05, 01), household=world.household)
-    HouseholdMember.objects.create(**fields_data)
-
-
-@step(u'Then I should see household member title and add household member link')
-def then_i_should_see_household_member_title_and_add_household_member_link(step):
-    world.page = HouseholdDetailsPage(world.browser, world.household)
-    world.page.validate_household_member_title_and_add_household_member_link()
-
-
-@step(u'And I should see actions edit and delete member')
-def and_i_should_see_actions_edit_and_delete_member(step):
-    world.page.validate_actions_edit_and_delete_member()
-
-
-@step(u'And I have two other investigators')
-def and_i_have_two_other_investigators(step):
-    world.investigator_1 = Investigator.objects.create(
-        name="Investigator name", ea=world.ea, mobile_number="123456789")
-    world.investigator_2 = Investigator.objects.create(
-        name="Investigator name", ea=world.ea, mobile_number="123456782")
-
-
-@step(u'And I click on that household ID')
-def and_i_click_on_that_household_id(step):
-    world.page.click_link_by_text(world.household.uid)
-
-
-@step(u'Then I should be on the household details page')
-def then_i_should_be_on_the_household_details_page(step):
-    world.page = HouseholdDetailsPage(world.browser, world.household)
-    world.page.validate_url()
-
-
-@step(u'When I click edit household')
-def when_i_click_edit_household(step):
-    world.browser.find_link_by_text(' Edit Household').first.click()
-
-
-@step(u'Then I should see edit household form')
-def then_i_should_see_edit_household_form(step):
-    world.page = EditHouseholdsPage(world.browser, world.household)
-    world.related_location = world.household.get_related_location()
-    for key in world.related_location.keys()[:-1]:
-        world.page.is_text_present(world.related_location[key])
-
-
-@step(u'When I assign a new investigator')
-def when_i_assign_a_new_investigator(step):
-    world.page.fill_in_with_js(
-        '$("#household-investigator")', world.investigator_1.id)
-
-
-@step(u'Then I should see the investigator was saved successfully')
-def then_i_should_see_the_investigator_was_saved_successfully(step):
-    world.page.see_success_message('Household', 'edited')
diff --git a/survey/features/households.feature b/survey/features/households.feature
deleted file mode 100644
index 3837e767..00000000
--- a/survey/features/households.feature
+++ /dev/null
@@ -1,68 +0,0 @@
-Feature: Households feature
-
-    Scenario: Household new page
-      Given I am logged in as researcher
-      And I have locations
-      And I visit new household page
-      And I see all households fields are present
-      And I submit the form
-      Then I should see the error messages
-
-    Scenario: Create a household
-       Given I am logged in as researcher
-       And I have locations
-       And I have an investigator in that location
-       And I visit new household page
-       And I fill household data
-       And I submit the form
-       Then I should see that the household is created
-       And I have a member for that household
-       And I visit households listing page
-       And then I click on that household ID
-       Then I should see household member title and add household member link
-       And I should see that household details, its head and members
-       And I should see actions edit and delete member
-
-
-    Scenario: Create a household with other-specify occupation
-        Given I am logged in as researcher
-        And I have locations
-        And I visit new household page
-        And Now If I choose Other as occupation
-        Then I have to specify one
-        And If I choose a different occupation
-        Then Specify disappears
-
-    Scenario: List all households
-      Given I have an investigator
-      Given I have 100 households
-      Given I am logged in as researcher
-      And I have locations
-      And I am in the home page
-      And I click Survey Administration tab
-      And I select list households
-      And I should see the households list paginated
-      When I click add household button
-      Then I should see add household page
-
-    Scenario: No households list
-      Given I have no households
-      Given I am logged in as researcher
-      And I have locations
-      And I visit households listing page
-      And I should see no household message
-
-    Scenario: Edit households
-      Given I am logged in as researcher
-      And I have locations
-      And I have an investigator in that location
-      And I have a household
-      And I have two other investigators
-      And I visit households listing page
-      And then I click on that household ID
-      Then I should be on the household details page
-      When I click edit household
-      Then I should see edit household form
-      When I assign a new investigator
-      And I submit the form
-      Then I should see the investigator was saved successfully
\ No newline at end of file
diff --git a/survey/features/index-steps.py b/survey/features/index-steps.py
deleted file mode 100644
index 502c455f..00000000
--- a/survey/features/index-steps.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# -*- coding: utf-8 -*-
-import glob
-import os
-
-from lettuce import *
-from splinter import Browser
-from django.core.management import call_command
-from django.conf import settings
-
-from django.template.defaultfilters import slugify
-from survey.models.backend import Backend
-
-
-@before.each_scenario
-def flush_database(step):
-    call_command('flush', interactive=False)
-    create_backends()
-
-
-@before.all
-def clear_screenshots():
-    screenshots = glob.glob('./screenshots/*.png')
-    for screenshot in screenshots:
-        os.remove(screenshot)
-    open_browser()
-
-
-def open_browser():
-    world.browser = Browser("phantomjs")
-    world.browser.driver.maximize_window()
-
-
-@after.each_scenario
-def take_screenshot(scenario):
-    if scenario.failed:
-        world.browser.driver.save_screenshot(
-            'screenshots/%s.png' % slugify(scenario.name))
-
-
-@after.each_scenario
-def clear_cookies(scenario):
-    world.browser.cookies.delete()
-
-
-@after.all
-def close_browser(total):
-    world.browser.quit()
-    call_command('flush', interactive=False)
-
-
-def create_backends():
-    for backend in settings.INSTALLED_BACKENDS.keys():
-        Backend.objects.get_or_create(name=backend)
diff --git a/survey/features/indicators-steps.py b/survey/features/indicators-steps.py
deleted file mode 100644
index b6d2a68d..00000000
--- a/survey/features/indicators-steps.py
+++ /dev/null
@@ -1,196 +0,0 @@
-from time import sleep
-from django.utils.datastructures import SortedDict
-from lettuce import step, world
-from survey.features.page_objects.indicators import NewIndicatorPage, ListIndicatorPage
-from survey.models import QuestionModule, Batch, Indicator
-
-
-@step(u'And I visit new indicator page')
-def and_i_visit_new_indicator_page(step):
-    world.page = NewIndicatorPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I fill in the indicator details')
-def and_i_fill_in_the_indicator_details(step):
-    form_data = {'module': world.health_module.id,
-                 'name': 'Health',
-                 'description': 'some description',
-                 'measure': '%',
-                 'batch': world.batch.id}
-    world.page.fill_valid_values(form_data)
-
-
-@step(u'Then I should see that the indicator was successfully added')
-def then_i_should_see_that_the_indicator_was_successfully_added(step):
-    world.page.see_success_message("Indicator", "created")
-
-
-@step(u'And I have two indicators')
-def and_i_have_two_indicators(step):
-    health_module = QuestionModule.objects.create(name="Health")
-    batch = Batch.objects.create(name="Batch")
-    world.indicator_1 = Indicator.objects.create(name="indicator name", description="rajni indicator",
-                                                 measure='Percentage',
-                                                 module=health_module, batch=batch)
-    world.indicator_2 = Indicator.objects.create(name="indicator name 2", description="rajni indicator 2",
-                                                 measure='Percentage',
-                                                 module=health_module, batch=batch)
-
-
-@step(u'When I visit indicator listing page')
-def when_i_visit_indicator_listing_page(step):
-    world.page = ListIndicatorPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see all indicators listed')
-def then_i_should_see_indicators_listed(step):
-    world.page.see_indicators(
-        [world.indicator_1, world.indicator_2, world.indicator_3])
-
-
-@step(u'And I have three batches')
-def and_i_have_three_batches(step):
-    world.batch_1 = Batch.objects.create(
-        name="New Batch 1", survey=world.survey)
-    world.batch_2 = Batch.objects.create(
-        name="New Batch 2", survey=world.survey)
-    world.batch_3 = Batch.objects.create(name="New Batch 3")
-
-
-@step(u'And I have an indicator not in that survey')
-def and_i_have_an_indicator_not_in_that_survey(step):
-    world.indicator_3 = Indicator.objects.create(name="indicator name 3", description="rajni indicator 3",
-                                                 measure='Percentage',
-                                                 module=world.health_module_1, batch=world.batch_3)
-
-
-@step(u'And I have indicator in each batch')
-def and_i_have_indicator_in_each_batch(step):
-    world.indicator_1 = Indicator.objects.create(name="indicator name 1", description="rajni indicator 1",
-                                                 measure='Percentage',
-                                                 module=world.health_module_1, batch=world.batch_1)
-    world.indicator_1b = Indicator.objects.create(name="indicator name with different module",
-                                                  description="rajni indicator 1", measure='Percentage',
-                                                  module=world.health_module_2, batch=world.batch_1)
-    world.indicator_2 = Indicator.objects.create(name="indicator name 2", description="rajni indicator 2",
-                                                 measure='Percentage',
-                                                 module=world.health_module_2, batch=world.batch_2)
-
-
-@step(u'When I select a survey')
-def when_i_select_a_survey(step):
-    world.page.select('survey', [world.survey.id])
-
-
-@step(u'And I should see action buttons')
-def and_i_should_see_action_buttons(step):
-    world.page.validate_fields_present(
-        ["Delete", "Edit", "Formula", "Analysis"])
-
-
-@step(u'And I click on get list')
-def and_i_click_on_get_list(step):
-    world.page.click_by_css('#a-indicator-list')
-
-
-@step(u'Then I should see indicators in that survey')
-def then_i_should_see_indicators_in_that_survey(step):
-    world.page.see_indicators(
-        [world.indicator_1, world.indicator_1b, world.indicator_2])
-    world.page.is_text_present(world.indicator_3.name, False)
-
-
-@step(u'When I select a batch')
-def when_i_select_a_batch(step):
-    world.page.select('batch', [world.batch_1.id])
-
-
-@step(u'Then I should see indicators in that batch')
-def then_i_should_see_indicators_in_that_batch(step):
-    world.page.see_indicators([world.indicator_1, world.indicator_1b])
-    world.page.is_text_present(world.indicator_2.name, False)
-    world.page.is_text_present(world.indicator_3.name, False)
-
-
-@step(u'And I have two modules')
-def and_i_have_two_modules(step):
-    world.health_module_1 = QuestionModule.objects.create(name="Module")
-    world.health_module_2 = QuestionModule.objects.create(name="Module 2")
-
-
-@step(u'When I select a module')
-def when_i_select_a_module(step):
-    world.page.select('module', [world.health_module_1.id])
-
-
-@step(u'Then I should see indicators in that module')
-def then_i_should_see_indicators_in_that_module(step):
-    world.page.see_indicators([world.indicator_1])
-    world.page.is_text_present(world.indicator_1b.name, False)
-    world.page.is_text_present(world.indicator_2.name, False)
-    world.page.is_text_present(world.indicator_3.name, False)
-
-
-@step(u'When I click on add indicator button')
-def when_i_click_on_add_indicator_button(step):
-    world.page.click_by_css('#add_indicator')
-
-
-@step(u'Then I should see add indicator page')
-def then_i_should_see_add_indicator_page(step):
-    world.page = NewIndicatorPage(world.browser)
-    world.page.validate_url()
-
-
-@step(u'And I click the delete indicator link')
-def and_i_click_the_delete_indicator_link(step):
-    world.page.click_by_css("#delete-indicator_%s" % world.indicator_1.id)
-
-
-@step(u'Then I should see confirm indicator batch')
-def then_i_should_see_confirm_indicator_batch(step):
-    world.page.see_confirm_modal_message(world.indicator_1.name)
-
-
-@step(u'Then I should go back to indicator listing page')
-def then_i_should_go_back_to_indicator_listing_page(step):
-    world.page = ListIndicatorPage(world.browser)
-    world.page.validate_url()
-
-
-@step(u'And I should see the indicator successfully deleted')
-def and_i_should_see_the_indicator_successfully_deleted(step):
-    world.page.see_success_message("Indicator", "deleted")
-
-
-@step(u'And I click the edit indicator link')
-def and_i_click_the_edit_indicator_link(step):
-    world.page.click_by_css("#edit-indicator_%s" % world.indicator_1.id)
-
-
-@step(u'Then I should see the indicator details in the form')
-def then_i_should_see_the_indicator_details_in_the_form(step):
-    world.form_data = {'name': world.indicator_1.name,
-                       'description': world.indicator_1.description,
-                       'measure': '%'}
-    world.page.validate_form_values(world.form_data)
-    world.page.is_text_present(world.indicator_1.batch.name)
-    world.page.is_text_present(world.indicator_1.module.name)
-
-
-@step(u'When I fill in the new values for the indicator')
-def when_i_fill_in_the_new_values_for_the_indicator(step):
-    world.form_data = {'survey': world.survey.id,
-                       'batch': world.batch_1.id,
-                       'module': world.indicator_1.module.id,
-                       'name': "Indicator new nme ",
-                       'description': "Hoho description",
-                       'measure': '%'}
-    world.page.fill_valid_values(world.form_data)
-
-
-@step(u'Then I should see the indicator successfully edited')
-def then_i_should_see_the_indicator_successfully_edited(step):
-    world.page.see_success_message("Indicator", 'edited')
diff --git a/survey/features/indicators.feature b/survey/features/indicators.feature
deleted file mode 100644
index 98c480f9..00000000
--- a/survey/features/indicators.feature
+++ /dev/null
@@ -1,57 +0,0 @@
-Feature: Indicators feature
-
-    Scenario: Add indicators
-      Given I am logged in as researcher
-      And I have a survey
-      And I have a batch
-      And I have two question modules
-      And I visit new indicator page
-      And I visit new indicator page
-      And I fill in the indicator details
-      And I submit the form
-      Then I should see that the indicator was successfully added
-
-    Scenario: List indicators
-      Given I am logged in as researcher
-      And I have a survey
-      And I have three batches
-      And I have two modules
-      And I have an indicator not in that survey
-      And I have indicator in each batch
-      When I visit indicator listing page
-      Then I should see all indicators listed
-      And I should see action buttons
-      When I select a survey
-      And I click on get list
-      Then I should see indicators in that survey
-      When I select a batch
-      And I click on get list
-      Then I should see indicators in that batch
-      When I select a module
-      And I click on get list
-      Then I should see indicators in that module
-      When I click on add indicator button
-      Then I should see add indicator page
-
-  Scenario: Delete Indicator
-    Given I am logged in as researcher
-    And I have two indicators
-    When I visit indicator listing page
-    And I click the delete indicator link
-    Then I should see confirm indicator batch
-    And if I click yes
-    Then I should go back to indicator listing page
-    And I should see the indicator successfully deleted
-
-  Scenario: Edit Indicator
-    Given I am logged in as researcher
-    And I have a survey
-    And I have three batches
-    And I have two question modules
-    And I have two indicators
-    When I visit indicator listing page
-    And I click the edit indicator link
-    Then I should see the indicator details in the form
-    When I fill in the new values for the indicator
-    And I submit the form
-    Then I should see the indicator successfully edited
\ No newline at end of file
diff --git a/survey/features/investigators-steps.py b/survey/features/investigators-steps.py
deleted file mode 100644
index 5c244e42..00000000
--- a/survey/features/investigators-steps.py
+++ /dev/null
@@ -1,323 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# -*- coding: utf-8 -*-
-from random import randint
-
-from django.template.defaultfilters import slugify
-from django.contrib.auth.models import User, Group, Permission
-from django.contrib.contenttypes.models import ContentType
-from lettuce import *
-from rapidsms.contrib.locations.models import *
-
-from survey.features.page_objects.accounts import LoginPage
-
-from survey.features.page_objects.investigators import NewInvestigatorPage, InvestigatorsListPage, FilteredInvestigatorsListPage, EditInvestigatorPage, InvestigatorDetailsPage
-from survey.models import LocationTypeDetails, EnumerationArea
-from survey.models.investigator import Investigator
-
-
-def set_permissions(group, permissions_codename_list):
-    auth_content = ContentType.objects.get_for_model(Permission)
-    for codename in permissions_codename_list:
-        permission, out = Permission.objects.get_or_create(
-            codename=codename, content_type=auth_content)
-        group.permissions.add(permission)
-
-
-def create_reseacher():
-    researcher = Group.objects.create(name='researcher1')
-    user = User.objects.create_user('Rajni', 'rajni@kant.com', 'I_Rock')
-    researcher.user_set.add(user)
-    set_permissions(researcher, ['can_view_aggregates', 'can_view_households', 'can_view_batches',
-                                 'can_view_investigators', 'can_view_locations', 'can_view_household_groups'])
-
-    return user
-
-
-@step(u'Given I am logged in as researcher')
-def given_i_am_logged_in_as_researcher(step):
-    user = create_reseacher()
-    world.page = LoginPage(world.browser)
-    world.page.visit()
-    world.page.login(user)
-
-
-@step(u'And I have locations')
-def and_i_have_locations(step):
-    country = LocationType.objects.get_or_create(
-        name='Country', slug='country')[0]
-    uganda = Location.objects.get_or_create(name="Uganda", type=country)[0]
-    LocationTypeDetails.objects.create(country=uganda, location_type=country)
-
-    district = LocationType.objects.create(
-        name="District", slug=slugify("district"))
-    county = LocationType.objects.create(name="County", slug=slugify("county"))
-    subcounty = LocationType.objects.create(
-        name="Subcounty", slug=slugify("subcounty"))
-    parish = LocationType.objects.create(name="Parish", slug=slugify("parish"))
-    world.village = LocationType.objects.create(
-        name="Village", slug=slugify("village"))
-    LocationTypeDetails.objects.create(country=uganda, location_type=district)
-    LocationTypeDetails.objects.create(country=uganda, location_type=county)
-    LocationTypeDetails.objects.create(country=uganda, location_type=subcounty)
-    LocationTypeDetails.objects.create(country=uganda, location_type=parish)
-    LocationTypeDetails.objects.create(
-        country=uganda, location_type=world.village)
-
-    world.kampala_district = Location.objects.create(
-        name="Kampala", type=district, tree_parent=uganda)
-    world.kampala_county = Location.objects.create(
-        name="Kampala County", type=county, tree_parent=world.kampala_district)
-    world.kampala_subcounty = Location.objects.create(
-        name="Subcounty", type=subcounty, tree_parent=world.kampala_county)
-    world.kampala_parish = Location.objects.create(
-        name="Parish", type=parish, tree_parent=world.kampala_subcounty)
-    world.kampala_village = Location.objects.create(
-        name="Village", type=world.village, tree_parent=world.kampala_parish)
-    world.kampala_county_village = Location.objects.create(
-        name="Kampala County Village", type=world.village, tree_parent=world.kampala_parish)
-
-    world.ea = EnumerationArea.objects.create(name="EA")
-    world.ea.locations.add(world.kampala_village)
-
-
-@step(u'And I visit new investigator page')
-def and_i_visit_new_investigator_page(step):
-    world.page = NewInvestigatorPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I fill all necessary fields')
-def and_i_fill_all_necessary_fields(step):
-    values = {
-        'name': 'Investigator Name',
-        'mobile_number': "987654321",
-        'confirm_mobile_number': "987654321",
-        'male': 't',
-        'age': '25',
-        'level_of_education': 'Primary',
-        'language': 'Luo',
-    }
-    world.page.fill_valid_values(values, world.ea)
-
-
-@step(u'And I submit the form')
-def and_i_submit_the_form(step):
-    world.page.submit()
-
-
-@step(u'Then I should see that the investigator is created')
-def then_i_should_see_that_the_investigator_is_created(step):
-    index_page = InvestigatorsListPage(world.browser)
-    index_page.validate_presence_of_investigator(
-        world.page.get_investigator_values())
-
-
-@step(u'Given I have 100 investigators')
-def given_i_have_100_investigators(step):
-    uganda = Location.objects.create(name="Uganda")
-    for _ in xrange(100):
-        random_number = str(randint(1, 99999))
-        try:
-            Investigator.objects.create(name="Investigator " + random_number, mobile_number=random_number,
-                                        age=12, level_of_education="Nursery", language="Luganda", location=uganda)
-        except Exception:
-            pass
-
-
-@step(u'And I visit investigators listing page')
-def and_i_visit_investigators_listing_page(step):
-    world.page = InvestigatorsListPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I should see the investigators list paginated')
-def and_i_should_see_the_investigators_list_paginated(step):
-    world.page.validate_fields()
-    world.page.validate_pagination()
-    world.page.validate_fields()
-
-
-@step(u'And I fill in already registered mobile number')
-def and_i_fill_in_already_registered_mobile_number(step):
-    world.investigator = Investigator.objects.create(
-        name="investigator", mobile_number="987654321")
-    world.page.fill("mobile_number", world.investigator.mobile_number)
-
-
-@step(u'Then I should see that mobile number is already taken')
-def then_i_should_see_that_mobile_number_is_already_taken(step):
-    world.page.is_text_present(
-        world.investigator.mobile_number + " is already registered.")
-
-
-@step(u'And I see all the fields are present')
-def and_i_see_all_the_fields_are_present(step):
-    world.page.valid_page()
-
-
-@step(u'Then I should see the error messages')
-def then_i_should_see_the_error_messages(step):
-    world.page.is_text_present("This field is required.")
-
-
-@step(u'Given I have no investigators')
-def given_i_have_no_investigators(step):
-    Investigator.objects.all().delete()
-
-
-@step(u'And I should see no investigators registered message')
-def and_i_should_see_no_investigators_registered_message(step):
-    world.page.no_registered_invesitgators()
-
-
-@step(u'And I request filter list of a County with no associated investigator')
-def and_i_request_filter_list_for_another_county_with_no_investigator(step):
-    county_type = LocationType.objects.get(name='County')
-    new_county = Location.objects.create(
-        name="some county", type=county_type, tree_parent=world.kampala_district)
-    Investigator.objects.filter(ea__locations=new_county).delete()
-    world.page = FilteredInvestigatorsListPage(world.browser, new_county.id)
-    world.page.visit()
-
-
-@step(u'Then I should see no investigator for this County')
-def then_i_should_see_no_investigator_for_this_county(step):
-    world.page.no_registered_invesitgators()
-
-
-@step(u'And I have one investigator')
-def and_i_have_an_investigator(step):
-    country = LocationType.objects.create(
-        name="Country", slug=slugify("country"))
-    city = LocationType.objects.create(name="City", slug=slugify("city"))
-
-    uganda = Location.objects.create(name="Uganda", type=country)
-    kampala = Location.objects.create(
-        name="Kampala", type=city, tree_parent=uganda)
-
-    LocationTypeDetails.objects.create(country=uganda, location_type=country)
-    LocationTypeDetails.objects.create(country=uganda, location_type=city)
-
-    world.ea = EnumerationArea.objects.get_or_create(name="EA")[0]
-    world.ea.locations.add(kampala)
-
-    world.investigator = Investigator.objects.create(
-        name="Rajni", mobile_number="123456789", age=25, level_of_education="Nursery", language="Luganda", ea=world.ea)
-
-
-@step(u'And I visit investigators page')
-def and_i_visit_investigators_page(step):
-    world.page = InvestigatorsListPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I click on the investigators name')
-def and_i_click_on_the_investigators_name(step):
-    world.page.visit_investigator(world.investigator)
-
-
-@step(u'Then I should see his details displayed')
-def then_i_should_see_his_details_displayed(step):
-    world.page = InvestigatorDetailsPage(world.browser, world.investigator)
-    world.page.validate_page_content()
-
-
-@step(u'And I should see navigation links')
-def and_i_should_see_navigation_links(step):
-    world.page.validate_navigation_links()
-
-
-@step(u'Then back button should take back to Investigator Listing page')
-def then_back_button_should_take_back_to_investigator_listing_page(step):
-    world.page.validate_back_link()
-
-
-@step(u'And I click on the edit button')
-def and_i_click_on_the_edit_button(step):
-    world.page.click_link_by_text(" Edit")
-
-
-@step(u'Then it should be able to take me to edit form page')
-def then_it_should_be_able_to_take_me_to_edit_form_page(step):
-    world.page = EditInvestigatorPage(world.browser, world.investigator)
-    world.page.validate_edit_investigator_url()
-    world.page.visit()
-
-
-@step(u'And I change Name of investigator')
-def and_i_change_name_of_investigator(step):
-    world.page.change_name_of_investigator()
-
-
-@step(u'And I click on save')
-def and_i_click_on_save(step):
-    world.page.submit()
-
-
-@step(u'Then I should go back to investigator listing page')
-def then_i_should_go_back_to_investigator_listing_page(step):
-    world.page = InvestigatorsListPage(world.browser)
-    world.page.validate_page_url()
-
-
-@step(u'And I should see name of investigator updated')
-def and_i_should_see_name_of_investigator_updated(step):
-    world.page.validate_successful_edited_message()
-
-
-@step(u'And I click block the investigator')
-def and_i_click_block_the_investigator(step):
-    world.page.click_link_by_text(" Block")
-
-
-@step(u'Then I should see block investigator confirmation modal')
-def then_i_should_see_block_investigator_confirmation_modal(step):
-    world.page.see_confirm_block_message("block", world.investigator)
-
-
-@step(u'When I confirm block the investigator')
-def when_i_confirm_block_the_investigator(step):
-    world.page.click_by_css("#block-investigator-%s" % world.investigator.id)
-
-
-@step(u'Then I should see the investigator blocked successfully')
-def then_i_should_see_the_investigator_blocked_successfully(step):
-    world.page.see_success_message("Investigator", "blocked")
-
-
-@step(u'And I should see unblock investigator')
-def and_i_should_see_unblock_investigator(step):
-    world.page.is_text_present(" Unblock")
-
-
-@step(u'And I click unblock the investigator')
-def and_i_click_unblock_the_investigator(step):
-    world.page.click_link_by_text(" Unblock")
-
-
-@step(u'Then I should see unblock investigator confirmation modal')
-def then_i_should_see_unblock_investigator_confirmation_modal(step):
-    world.page.see_confirm_block_message("unblock", world.investigator)
-
-
-@step(u'When I confirm unblock the investigator')
-def when_i_confirm_unblock_the_investigator(step):
-    world.page.click_by_css("#unblock-investigator-%s" % world.investigator.id)
-
-
-@step(u'Then I should see the investigator unblocked successfully')
-def then_i_should_see_the_investigator_unblocked_successfully(step):
-    world.page.see_success_message("Investigator", "unblocked")
-
-
-@step(u'When I click add investigator button')
-def when_i_click_add_household_button(step):
-    world.page.click_by_css("#add-investigator")
-
-
-@step(u'Then I should see add investigator page')
-def then_i_should_see_add_household_page(step):
-    world.page = NewInvestigatorPage(world.browser)
-    world.page.validate_url()
diff --git a/survey/features/investigators.feature b/survey/features/investigators.feature
deleted file mode 100644
index 3afa5780..00000000
--- a/survey/features/investigators.feature
+++ /dev/null
@@ -1,82 +0,0 @@
-Feature: Investigators feature
-
-    Scenario: Investigator new page
-      Given I am logged in as researcher
-      And I have locations
-      And I visit new investigator page
-      And I see all the fields are present
-      And I submit the form
-      Then I should see the error messages
-    
-    Scenario: Create an investigator
-       Given I am logged in as researcher
-       And I have locations
-       And I visit new investigator page
-       And I fill all necessary fields
-       And I submit the form
-       Then I should see that the investigator is created
-    
-    Scenario: List investigators
-      Given I have 100 investigators
-      Given I am logged in as researcher
-      And I have locations
-      And I visit investigators listing page
-      And I should see the investigators list paginated
-      When I click add investigator button
-      Then I should see add investigator page
-
-
-    Scenario: No investigators list
-      Given I have no investigators
-      Given I am logged in as researcher
-      And I have locations
-      And I visit investigators listing page
-      And I should see no investigators registered message
-    
-    Scenario: No investigators filter list
-      Given I am logged in as researcher
-      And I have locations
-      And I request filter list of a County with no associated investigator
-      Then I should see no investigator for this County
-    
-    Scenario: Create an investigator - validation
-      Given I am logged in as researcher
-      And I have locations
-      And I visit new investigator page
-      And I fill in already registered mobile number
-      And I submit the form
-      Then I should see that mobile number is already taken
-
-    Scenario: View investigator details
-      Given I am logged in as researcher
-      And I have one investigator
-      And I visit investigators page
-      And I click on the investigators name
-      Then I should see his details displayed
-      And I should see navigation links
-      Then back button should take back to Investigator Listing page
-
-    Scenario: Edit investigator details
-      Given I am logged in as researcher
-      And I have one investigator
-      And I visit investigators page
-      And I click on the edit button
-      Then it should be able to take me to edit form page
-      And I change Name of investigator
-      And I click on save
-      Then I should go back to investigator listing page
-      And I should see name of investigator updated
-
-    Scenario: Block investigators
-      Given I am logged in as researcher
-      And I have one investigator
-      And I visit investigators listing page
-      And I click block the investigator
-      Then I should see block investigator confirmation modal
-      When I confirm block the investigator
-      Then I should see the investigator blocked successfully
-      And I should see unblock investigator
-      And I click unblock the investigator
-      Then I should see unblock investigator confirmation modal
-      When I confirm unblock the investigator
-      Then I should see the investigator unblocked successfully
\ No newline at end of file
diff --git a/survey/features/layout-steps.py b/survey/features/layout-steps.py
deleted file mode 100644
index 7cae3d5e..00000000
--- a/survey/features/layout-steps.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# vim: ai ts=4 sts=4 et sw=4 encoding=utf-8
-from lettuce import step, world
-
-
-@step(u'And I click Survey Administration tab')
-def and_i_click_survey_administration_tab(step):
-    world.page.click_tab("Survey Administration")
-
-
-@step(u'Then I should see survey administration dropdown list')
-def then_i_should_see_survey_administration_dropdown_list(step):
-    reverse_url_links = ["investigators_page", "list_household_page", "list_all_questions", "survey_list_page",
-                         "question_module_listing_page", "household_member_groups_page", "bulk_sms", "upload_ea"]
-    world.page.see_dropdown(reverse_url_links)
-
-
-@step(u'And I click Downloads Tab')
-def and_i_click_downloads_tab(step):
-    world.page.click_tab("Downloads")
-
-
-@step(u'Then I should see Downloads dropdown list')
-def then_i_should_see_downloads_dropdown_list(step):
-    reverse_url_links = ['download_excel', 'investigator_report_page']
-    world.page.see_dropdown(reverse_url_links)
-
-
-@step(u'And I click Analysis tab')
-def and_i_click_analysis_tab(step):
-    world.page.click_tab("Analysis")
-
-
-@step(u'Then I should see analysis dropdown list')
-def then_i_should_see_analysis_dropdown_list(step):
-    reverse_url_links = ["simulator_page", "list_indicator_page",
-                         "survey_completion_rates", "list_weights_page"]
-    world.page.see_dropdown(reverse_url_links)
-
-
-@step(u'And I click Settings tab')
-def and_i_click_settings_tab(step):
-    world.page.click_tab("Settings")
-
-
-@step(u'Then I should see Settings dropdown list')
-def then_i_should_see_settings_dropdown_list(step):
-    reverse_url_links = ["add_location_hierarchy",
-                         "upload_locations", "users_index"]
-    world.page.see_dropdown(reverse_url_links)
diff --git a/survey/features/location_hierarchy.feature b/survey/features/location_hierarchy.feature
deleted file mode 100644
index 2d33bcb1..00000000
--- a/survey/features/location_hierarchy.feature
+++ /dev/null
@@ -1,21 +0,0 @@
-Feature: Location hierarchy
-
-  Scenario: Add location hierarchy
-    Given I am logged in as admin
-    And I have a country
-    And I visit add location hierarchy page
-    Then I should see text message
-    And I should see country dropdown
-    And I should see country present in dropdown
-    And I should see a row for level details field
-    When I click add row icon
-    Then I should see anther row for levels details field
-    When I click remove row icon
-    Then I should see row for levels details field removed
-    And the code field is hidden
-    When I check has_code field
-    Then length of code field is shown
-    When I fill details
-    And I click the create hierarchy button
-    Then I should see location hierarchy successfully created
-
diff --git a/survey/features/location_hierarchy_steps.py b/survey/features/location_hierarchy_steps.py
deleted file mode 100644
index 16f1ccad..00000000
--- a/survey/features/location_hierarchy_steps.py
+++ /dev/null
@@ -1,92 +0,0 @@
-from lettuce import step, world
-from rapidsms.contrib.locations.models import Location, LocationType
-from survey.features.page_objects.location_hierarchy import AddLocationHierarchyPage
-
-
-@step(u'And I have a country')
-def and_i_have_a_country(step):
-    world.country = Location.objects.create(
-        name='SomeCountry', type=LocationType.objects.create(name='country', slug='country'))
-
-
-@step(u'And I visit add location hierarchy page')
-def and_i_visit_add_location_hierarchy_page(step):
-    world.page = AddLocationHierarchyPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see text message')
-def then_i_should_see_text_message(step):
-    world.page.is_text_present('Create geographical location hierarchy')
-
-
-@step(u'And I should see country dropdown')
-def and_i_should_see_country_dropdown(step):
-    assert world.browser.find_by_css('#id_country')
-
-
-@step(u'And I should see country present in dropdown')
-def and_i_should_see_country_present_in_dropdown(step):
-    world.page.see_select_option([world.country.name], 'country')
-
-
-@step(u'And I should see a row for level details field')
-def and_i_should_see_a_row_for_level_details_field(step):
-    world.page.see_field_details('Level 1', 'form-0')
-
-
-@step(u'When I click add row icon')
-def when_i_click_add_row_link(step):
-    world.page.click_by_css(".icon-plus")
-
-
-@step(u'Then I should see anther row for levels details field')
-def then_i_should_see_anther_row_for_levels_details_field(step):
-    world.page.see_field_details('Level 1', 'form-0')
-    world.page.see_field_details('Level 2', 'form-1')
-
-
-@step(u'When I click remove row icon')
-def when_i_click_remove_row_link(step):
-    world.browser.find_by_css('.icon-remove').last.click()
-
-
-@step(u'Then I should see row for levels details field removed')
-def then_i_should_see_row_for_levels_details_field_removed(step):
-    world.page.see_field_details('Level 1', 'form-0')
-    world.page.see_field_details('Level 2', 'form-1', False)
-
-
-@step(u'And the code field is hidden')
-def and_the_code_field_is_hidden(step):
-    world.page.is_hidden('form-0-length_of_code')
-
-
-@step(u'When I check has_code field')
-def when_i_check_has_code_field(step):
-    world.page.click_by_css('.has_code')
-
-
-@step(u'Then length of code field is shown')
-def then_code_field_is_shown(step):
-    world.page.is_hidden('code', False)
-
-
-@step(u'When I fill details')
-def when_i_fill_details(step):
-    data = {'country': world.country.id, 'form-0-levels': 'Region',
-            'form-0-levels': 'Hill', 'form-0-required': 'on',
-            'form-0-has_code': 'on', 'form-0-length_of_code': 2,
-            }
-    world.page.fill_valid_values(data)
-    world.page.fill('form-0-length_of_code', '2')
-
-
-@step(u'And I click the create hierarchy button')
-def and_i_click_the_create_hierarchy_button(step):
-    world.page.submit()
-
-
-@step(u'Then I should see location hierarchy successfully created')
-def then_i_should_see_location_hierarchy_successfully_created(step):
-    world.page.see_success_message('Location Hierarchy', 'created')
diff --git a/survey/features/location_weights.feature b/survey/features/location_weights.feature
deleted file mode 100644
index 7a5a6a2a..00000000
--- a/survey/features/location_weights.feature
+++ /dev/null
@@ -1,58 +0,0 @@
-Feature: Location Weights upload
-
-  Scenario: Upload locations weights
-    Given I am logged in as researcher
-    And I have some locations to add weights
-    And I have a survey
-    When I visit upload locations weights page
-    Then I should see upload weights form fields
-    When I click on the link for input file format
-    Then I should see table of location weights layout
-    When I click on the link for input file format
-    Then said weight layout should collapse
-    When I have a locations weights csv file
-    And I input that file
-    And I select a survey
-    And I click the save button
-    Then I should see location weights upload is in progress
-
-  Scenario: List locations weights
-    Given I am logged in as researcher
-    And I have a survey
-    And I have some locations with weights
-    And I have error logs from location weights upload
-    And I visit the list location weights page
-    Then I should see the locations weights
-    When I click the view error logs link
-    Then I should see the error logs from previous the month
-
-  Scenario: List locations weights -- pagination and upload link
-    Given I am logged in as researcher
-    And I have a survey
-    And I have some 100 locations with weights
-    And I visit the list location weights page
-    Then I see locations weights paginated
-    When I click the upload weights link
-    Then I should see upload weights page
-
-
-  Scenario: Filter list locations weights
-    Given I am logged in as researcher
-    And I have two surveys
-    And I have a number of locations and weights in each survey
-    And I visit the list location weights page
-    And I select one survey
-    And I click get list
-    Then I should see the location weights in that survey
-    When I select a location
-    And I click get list
-    Then I should see the weights for that location and survey
-
-  Scenario: List and filter upload weights error logs
-    Given I am logged in as researcher
-    And I have some error logs for upload weights
-    And I visit the weights error logs page
-    Then I should see all error logs
-    When I select from and to dates
-    And I click the filter link
-    Then I should see only error logs between those dates
\ No newline at end of file
diff --git a/survey/features/location_weights_steps.py b/survey/features/location_weights_steps.py
deleted file mode 100644
index 54defd7d..00000000
--- a/survey/features/location_weights_steps.py
+++ /dev/null
@@ -1,300 +0,0 @@
-import csv
-from time import sleep
-import datetime
-from lettuce import step, world
-from rapidsms.contrib.locations.models import Location, LocationType
-from survey.features.page_objects.uploads import UploadWeightsPage
-from survey.features.page_objects.weights import ListLocationWeightsPage, ListLocationWeightsErrorLogPage
-from survey.models import LocationWeight, UploadErrorLog, Survey, LocationTypeDetails
-
-
-@step(u'And I have some locations to add weights')
-def and_i_have_a_locations(step):
-    country = LocationType.objects.create(name='Country', slug='country')
-    uganda = Location.objects.create(name="Uganda", type=country)
-    LocationTypeDetails.objects.create(country=uganda, location_type=country)
-
-    region_type = LocationType.objects.create(name="region1", slug="region1")
-    district_type = LocationType.objects.create(
-        name="district1", slug='district1')
-    county_type = LocationType.objects.create(name="county1", slug='county1')
-
-    region = Location.objects.create(
-        name="region1", type=region_type, tree_parent=uganda)
-    district = Location.objects.create(
-        name="district1", tree_parent=region, type=district_type)
-    Location.objects.create(
-        name="county1", tree_parent=district, type=county_type)
-
-    region = Location.objects.create(
-        name="region2", tree_parent=uganda, type=region_type)
-    district = Location.objects.create(
-        name="district2", tree_parent=region, type=district_type)
-    Location.objects.create(
-        name="county2", tree_parent=district, type=county_type)
-
-
-@step(u'When I visit upload locations weights page')
-def when_i_visit_upload_locations_weights_page(step):
-    world.page = UploadWeightsPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see upload weights form fields')
-def then_i_should_see_upload_weights_form_fields(step):
-    world.page.validate_fields_present(
-        ["Upload Location Weights", "Survey", "Location weights file"])
-
-
-@step(u'Then I should see table of location weights layout')
-def then_i_should_see_table_of_location_weights_layout(step):
-    world.type_names = [type.name.capitalize(
-    ) + 'Name' for type in LocationType.objects.all()]
-    world.page.validate_fields_present(world.type_names)
-
-
-@step(u'And I select a survey')
-def and_i_select_a_survey(step):
-    world.page.select('survey', [world.survey.id])
-
-
-@step(u'When I have a locations weights csv file')
-def when_i_have_a_locations_weights_csv_file(step):
-    filedata = [world.type_names,
-                ['region1', 'district1', 'county1', '0.02'],
-                ['region2', 'district2', 'county2', '0.1']]
-    write_to_csv('wb', filedata, 'test.csv')
-
-
-def write_to_csv(mode, data, csvfilename):
-    with open(csvfilename, mode) as fp:
-        file = csv.writer(fp, delimiter=',')
-        file.writerows(data)
-
-
-@step(u'Then I should see location weights upload is in progress')
-def then_i_should_see_location_weights_successfully_added(step):
-    world.page.is_text_present('Upload in progress. This could take a while.')
-
-
-@step(u'Then said weight layout should collapse')
-def then_said_weight_layout_should_collapse(step):
-    sleep(3)
-    world.page.validate_layout_collapsed()
-
-
-@step(u'And I have some locations with weights')
-def and_i_have_some_locations_to_with_weights(step):
-    country = LocationType.objects.create(name="Country", slug="country")
-    uganda = Location.objects.create(name="Uganda", type=country)
-    LocationTypeDetails.objects.create(country=uganda, location_type=country)
-
-    reqion_type = LocationType.objects.create(name="region1", slug="region1")
-    district_type = LocationType.objects.create(
-        name="district1", slug='district1')
-    county_type = LocationType.objects.create(name="county1", slug='county1')
-
-    region = Location.objects.create(
-        name="region1", type=reqion_type, tree_parent=uganda)
-    district = Location.objects.create(
-        name="district1", tree_parent=region, type=district_type)
-    county = Location.objects.create(
-        name="county1", tree_parent=district, type=county_type)
-
-    region = Location.objects.create(
-        name="region2", type=reqion_type, tree_parent=uganda)
-    district = Location.objects.create(
-        name="district2", tree_parent=region, type=district_type)
-    county1 = Location.objects.create(
-        name="county2", tree_parent=district, type=county_type)
-    world.weight_1 = LocationWeight.objects.create(
-        location=county, selection_probability=0.1, survey=world.survey)
-    world.weight_2 = LocationWeight.objects.create(
-        location=county1, selection_probability=0.2, survey=world.survey)
-
-
-@step(u'And I have error logs from location weights upload')
-def and_i_have_error_logs_from_location_weights_upload(step):
-    world.error_log = UploadErrorLog.objects.create(
-        model="WEIGHTS", filename="some_file.csv", error="Some error")
-    world.error_log2 = UploadErrorLog.objects.create(
-        model="WEIGHTS", filename="some_file.csv", error="Some error two")
-
-
-@step(u'And I visit the list location weights page')
-def and_i_visit_the_list_location_weights_page(step):
-    world.page = ListLocationWeightsPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see the locations weights')
-def then_i_should_see_the_locations_weights(step):
-    weight_1_details = [world.weight_1.location.name, str(world.weight_1.selection_probability),
-                        str(world.weight_1.survey.get_total_respondents()), str(world.weight_1.survey.sample_size)]
-    weight_2_details = [world.weight_2.location.name, str(world.weight_2.selection_probability),
-                        str(world.weight_2.survey.get_total_respondents()), str(world.weight_1.survey.sample_size)]
-    world.page.validate_fields_present(weight_1_details)
-    world.page.validate_fields_present(weight_2_details)
-
-
-@step(u'When i click the view error logs link')
-def when_i_click_the_view_error_logs_link(step):
-    world.page.click_by_css("#view_error_log")
-
-
-@step(u'Then I should see the error logs from previous the month')
-def then_i_should_see_the_error_logs_from_previous_the_month(step):
-    world.page = ListLocationWeightsErrorLogPage(world.browser)
-    world.page.validate_fields_present(
-        [world.error_log.filename, world.error_log.error])
-    world.page.validate_fields_present(
-        [world.error_log2.filename, world.error_log2.error])
-
-
-@step(u'And I have two surveys')
-def and_i_have_two_surveys(step):
-    world.survey_1 = Survey.objects.create(name="Survey 1")
-    world.survey_2 = Survey.objects.create(name="Survey 2")
-
-
-@step(u'And I select one survey')
-def and_i_select_one_survey(step):
-    world.page.select("survey", [world.survey_2.id])
-
-
-@step(u'And I have a number of locations and weights in each survey')
-def and_i_have_a_number_of_locations_and_weights_in_each_survey(step):
-    country = LocationType.objects.create(name='Country', slug='country')
-    uganda = Location.objects.create(name="Uganda", type=country)
-    LocationTypeDetails.objects.create(country=uganda, location_type=country)
-
-    county = LocationType.objects.create(name="County", slug="county")
-    world.county1 = Location.objects.create(
-        name="county1", type=county, tree_parent=uganda)
-    world.county2 = Location.objects.create(
-        name="county2", type=county, tree_parent=uganda)
-    world.weight_1 = LocationWeight.objects.create(
-        location=world.county1, selection_probability=0.1, survey=world.survey_1)
-    world.weight_2 = LocationWeight.objects.create(
-        location=world.county2, selection_probability=0.2, survey=world.survey_2)
-    world.weight_3 = LocationWeight.objects.create(
-        location=world.county1, selection_probability=0.22, survey=world.survey_2)
-
-
-@step(u'And I click get list')
-def and_i_click_get_list(step):
-
-    world.page.click_by_css("#get_list")
-
-
-@step(u'Then I should see the location weights in that survey')
-def then_i_should_see_the_location_weights_in_that_survey(step):
-    weight_3_details = [world.weight_3.location.name, str(world.weight_3.selection_probability),
-                        str(world.weight_3.survey.get_total_respondents()), str(world.weight_3.survey.sample_size)]
-    weight_2_details = [world.weight_2.location.name, str(world.weight_2.selection_probability),
-                        str(world.weight_2.survey.get_total_respondents()), str(world.weight_1.survey.sample_size)]
-    world.page.validate_fields_present(weight_3_details)
-    world.page.validate_fields_present(weight_2_details)
-
-
-@step(u'When I select a location')
-def when_i_select_a_location(step):
-    world.page.fill_in_with_js('$("#location-county")', world.county1.id)
-
-
-@step(u'Then I should see the weights for that location and survey')
-def then_i_should_see_the_weights_for_that_location_and_survey(step):
-    weight_3_details = [world.weight_3.location.name, str(world.weight_3.selection_probability),
-                        str(world.weight_3.survey.get_total_respondents()), str(world.weight_3.survey.sample_size)]
-    world.page.validate_fields_present(weight_3_details)
-
-
-@step(u'When I click the upload weights link')
-def when_i_click_the_upload_weights_link(step):
-    world.page.click_by_css('#upload_weights')
-
-
-@step(u'Then I should see upload weights page')
-def then_i_should_see_upload_weights_page(step):
-    world.page = UploadWeightsPage(world.browser)
-    world.page.validate_url()
-
-
-@step(u'And I have some 100 locations with weights')
-def and_i_have_some_100_locations_with_weights(step):
-    country = LocationType.objects.create(name="Country", slug="country")
-    uganda = Location.objects.create(name="Uganda", type=country)
-    LocationTypeDetails.objects.create(country=uganda, location_type=country)
-
-    reqion_type = LocationType.objects.create(name="region1", slug="region1")
-    district_type = LocationType.objects.create(
-        name="district1", slug='district1')
-    county_type = LocationType.objects.create(name="county1", slug='county1')
-
-    region = Location.objects.create(
-        name="region1", type=reqion_type, tree_parent=uganda)
-    district = Location.objects.create(
-        name="district1", tree_parent=region, type=district_type)
-    county = Location.objects.create(
-        name="county1", tree_parent=district, type=county_type)
-
-    for i in xrange(100):
-        location = Location.objects.create(
-            name=str(i), tree_parent=district, type=county_type)
-        LocationWeight.objects.create(
-            location=location, selection_probability=i / 100.0, survey=world.survey)
-
-
-@step(u'Then I see locations weights paginated')
-def then_i_see_locations_weights_paginated(step):
-    world.page.validate_pagination()
-
-
-@step(u'And I have some error logs for upload weights')
-def and_i_have_some_error_logs_for_upload_weights(step):
-    world.error_log = UploadErrorLog.objects.create(
-        model="WEIGHTS", filename="some_file.csv", error="Some error")
-    world.error_log2 = UploadErrorLog.objects.create(
-        model="WEIGHTS", filename="some_file_1.csv", error="Some error two")
-    world.error_log3 = UploadErrorLog.objects.create(
-        model="LOCATION", filename="some_file_2.csv", error="Some error three")
-
-    world.timedelta = datetime.timedelta(days=4)
-    world.error_log2.created += world.timedelta
-    world.error_log2.save()
-
-
-@step(u'And I visit the weights error logs page')
-def and_i_visit_the_weights_error_logs_page(step):
-    world.page = ListLocationWeightsErrorLogPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see all error logs')
-def then_i_should_see_all_error_logs(step):
-    world.page.validate_fields_present(
-        [world.error_log.filename, world.error_log.error])
-    world.page.validate_fields_present(
-        [world.error_log2.filename, world.error_log2.error])
-    world.page.validate_fields_present(
-        [world.error_log3.filename, world.error_log3.error], False)
-
-
-@step(u'When I select from and to dates')
-def when_i_select_from_and_to_dates(step):
-    dates = {'from_date': world.error_log.created.strftime('%Y-%m-%d'),
-             'to_date': world.error_log.created.strftime('%Y-%m-%d')}
-    world.page.fill_valid_values(dates)
-
-
-@step(u'Then I should see only error logs between those dates')
-def then_i_should_see_only_error_logs_between_those_dates(step):
-    world.page.validate_fields_present(
-        [world.error_log.filename, world.error_log.error])
-    world.page.validate_fields_present(
-        [world.error_log2.filename, world.error_log2.error], False)
-
-
-@step(u'And I click the filter link')
-def and_i_click_the_filter_link(step):
-    world.page.click_by_css('#get_logs')
diff --git a/survey/features/login-steps.py b/survey/features/login-steps.py
deleted file mode 100644
index 29a9797f..00000000
--- a/survey/features/login-steps.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from lettuce import *
-from django.contrib.auth.models import User
-
-from survey.features.page_objects.accounts import LoginPage
-
-from survey.features.page_objects.aggregates import AggregateStatusPage, DownloadExcelPage
-from survey.features.page_objects.households import NewHouseholdPage
-from survey.features.page_objects.investigators import NewInvestigatorPage, InvestigatorsListPage
-from survey.features.page_objects.root import HomePage
-from survey.features.page_objects.users import NewUserPage
-from survey.models.users import UserProfile
-
-
-@step(u'Given I have a user')
-def given_i_have_a_user(step):
-    world.user = User.objects.create_user(
-        'Rajni', 'rajni@kant.com', 'I_Rock', first_name='Rajin', last_name="Kant")
-    profile = UserProfile.objects.create(
-        user=world.user, mobile_number='2222222223')
-
-
-@step(u'And I visit the login page')
-def and_i_visit_the_login_page(step):
-    world.page = LoginPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I login a user')
-def and_i_login_a_user(step):
-    world.page = LoginPage(world.browser)
-    world.page.login(world.user)
-
-
-@step(u'Then I should see that I am logged in as given username')
-def then_i_should_see_that_i_am_logged_in_as_given_username(step):
-    world.page.see_home_page_and_logged_in_status(world.user)
-
-
-@step(u'And I am in the home page')
-def and_i_am_in_the_home_page(step):
-    world.page = HomePage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I click the login link')
-def and_i_click_the_login_link(step):
-    world.page.click_the_login_link()
-
-
-@step(u'Then I should see new investigator with logout link')
-def then_i_should_see_new_investigator_with_logout_link(step):
-    world.page = NewInvestigatorPage(world.browser)
-    world.page.see_username_link()
-
-
-@step(u'Then I should see list investigator with logout link')
-def then_i_should_see_list_investigator_with_logout_link(step):
-    world.page = InvestigatorsListPage(world.browser)
-    world.page.see_username_link()
-
-
-@step(u'Then I should see new household page with logout link')
-def then_i_should_see_new_household_page_with_logout_link(step):
-    world.page = NewHouseholdPage(world.browser)
-    world.page.see_username_link()
-
-
-@step(u'Then I should see aggregate status page with logout link')
-def then_i_should_see_aggregate_status_page_with_logout_link(step):
-    world.page = AggregateStatusPage(world.browser)
-    world.page.see_username_link()
-
-
-@step(u'Then I should see download excel page with logout link')
-def then_i_should_see_download_excel_page_with_logout_link(step):
-    world.page = DownloadExcelPage(world.browser)
-    world.page.see_username_link()
-
-
-@step(u'Then I should see new user page with logout link')
-def then_i_should_see_new_user_page_with_logout_link(step):
-    world.page = NewUserPage(world.browser)
-    world.page.see_username_link()
diff --git a/survey/features/login.feature b/survey/features/login.feature
deleted file mode 100644
index 76307db4..00000000
--- a/survey/features/login.feature
+++ /dev/null
@@ -1,50 +0,0 @@
-Feature: Login feature
-
-  Scenario: Login page
-    Given I have a user
-    And And I visit the login page
-    And I login a user
-    Then I should see that I am logged in as given username
-
-  Scenario: Login from Home page
-    Given I have a user
-    And I am in the home page
-    And I click the login link
-    And I login a user
-    Then I should see that I am logged in as given username
-
-  Scenario: Login from new investigator page
-    Given I have a user
-    And I have locations
-    And I visit new investigator page
-    And I login a user
-    Then I should see new investigator with logout link
-
-  Scenario: Login from list investigator page
-    Given I have a user
-    And I have locations
-    And I visit investigators listing page
-    And I login a user
-    Then I should see list investigator with logout link
-
-  Scenario: Login from new household page
-    Given I have a user
-    And I have locations
-    And I have an investigator in that location
-    And I visit new household page
-    And I login a user
-    Then I should see new household page with logout link
-
-  Scenario: Login from download excel report page
-    Given I have a user
-     And I have three surveys
-     And I have batches in those surveys
-     And I visit download excel page
-     And I login a user
-     Then I should see download excel page with logout link
-
-  Scenario: Login from users page
-     Given I have a user
-     And I visit new user page
-     And I login a user
-     Then I should see new user page with logout link
diff --git a/survey/features/page_objects/__init__.py b/survey/features/page_objects/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/survey/features/page_objects/accounts.py b/survey/features/page_objects/accounts.py
deleted file mode 100644
index 63f105f4..00000000
--- a/survey/features/page_objects/accounts.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# vim: ai ts=4 sts=4 et sw=4 encoding=utf-8
-from survey.features.page_objects.base import PageObject
-from survey.features.page_objects.root import AboutPage, HomePage
-
-from lettuce.django import django_url
-
-
-class ResetPasswordPage(PageObject):
-    url = '/accounts/reset_password/'
-
-    def is_change_password_form_visble(self):
-        self.browser.is_text_present("Old password")
-        self.browser.is_text_present("New password")
-        self.browser.is_text_present("New password confirmation")
-
-    def click_change_password_button(self):
-        self.browser.find_by_name("save_changes").first.click()
-
-    def is_incorrect_oldpassword_error_visible(self):
-        self.is_text_present(
-            "Your old password was entered incorrectly. Please enter it again.")
-
-    def is_password_mismatch(self):
-        self.is_text_present("The two password fields didn't match.")
-
-
-class LogoutPage(PageObject):
-    url = "/accounts/logout"
-
-    def check_browser_is_in_about_page(self):
-        assert self.browser.url == django_url(AboutPage.url)
-
-
-class LoginPage(PageObject):
-    url = "/accounts/login"
-
-    def login(self, user):
-        self.is_text_present('Type your username and password to login')
-
-        user.set_password('secret')
-        user.save()
-        details = {'username': user.username,
-                   'password': 'secret',
-                   }
-
-        self.browser.fill_form(details)
-        self.submit()
-
-    def see_home_page_and_logged_in_status(self, user):
-        assert self.browser.url == django_url(HomePage.url)
-        self.see_logged_in_status(user)
diff --git a/survey/features/page_objects/aggregates.py b/survey/features/page_objects/aggregates.py
deleted file mode 100644
index 2c35a08e..00000000
--- a/survey/features/page_objects/aggregates.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# vim: ai ts=4 sts=4 et sw=4 encoding=utf-8
-import os
-from rapidsms.contrib.locations.models import Location
-from survey.features.page_objects.base import PageObject
-
-
-class AggregateStatusPage(PageObject):
-    url = "/aggregates/status"
-
-    def choose_location(self, locations):
-        for key, value in locations.items():
-            object_id = "location-%s" % key
-            assert self.browser.is_element_present_by_id(object_id)
-            jquery_id = '$("#%s")' % object_id
-            location = Location.objects.get(name=value)
-            self.fill_in_with_js(jquery_id, location.pk)
-
-    def check_if_batches_present(self, batches):
-        all_options = self.browser.find_by_id(
-            'id_batch')[0].find_by_tag('option')
-        all_options = [option.text for option in all_options]
-        for batch in batches:
-            assert batch.name in all_options
-
-    def check_get_status_button_presence(self):
-        assert self.browser.find_by_css(
-            "#aggregates-form")[0].find_by_tag('button')[0].text == "Get status"
-
-    def choose_batch(self, batch):
-        self.browser.select('batch', batch.pk)
-
-    def assert_status_count(self, pending_households, completed_housesholds, pending_clusters, completed_clusters):
-        assert self.browser.find_by_id(
-            'pending-households-count')[0].text == str(pending_households)
-        assert self.browser.find_by_id(
-            'completed-households-count')[0].text == str(completed_housesholds)
-        assert self.browser.find_by_id(
-            'pending-clusters-count')[0].text == str(pending_clusters)
-        assert self.browser.find_by_id(
-            'completed-clusters-count')[0].text == str(completed_clusters)
-
-    def check_presence_of_investigators(self, *investigators):
-        for investigator in investigators:
-            self.is_text_present(investigator.name)
-            self.is_text_present(investigator.mobile_number)
-            self.is_text_present("10")
-
-    def assert_presence_of_batch_is_closed_message(self):
-        self.is_text_present(
-            "This batch is currently closed for this location.")
-
-    def select_all_district(self):
-        self.browser.execute_script(
-            "$('#location-district').val('').change().trigger('liszt:updated').chosen().change();")
-
-    def see_all_districts_location_selected(self):
-        assert self.browser.find_by_css('input[name=location]')[0].value == ''
-
-
-class DownloadExcelPage(PageObject):
-    url = "/aggregates/download_spreadsheet"
-
-
-class InvestigatorReportPage(PageObject):
-
-    def __init__(self, browser):
-        super(InvestigatorReportPage, self).__init__(browser)
-        self.url = '/investigator_report/'
-
-    def validate_select_option(self, batch):
-        element_value = self.browser.find_by_css("#id_batch")[0].value
-        assert int(element_value) == int(batch.id)
diff --git a/survey/features/page_objects/base.py b/survey/features/page_objects/base.py
deleted file mode 100644
index 8e4d0cc6..00000000
--- a/survey/features/page_objects/base.py
+++ /dev/null
@@ -1,228 +0,0 @@
-# vim: ai ts=4 sts=4 et sw=4 encoding=utf-8
-from random import randint
-from time import sleep
-from django.core.urlresolvers import reverse
-from lettuce.django import django_url
-from nose.tools import assert_equals
-
-
-class PageObject(object):
-
-    def __init__(self, browser):
-        self.browser = browser
-
-    def visit(self):
-        self.browser.visit(django_url(self.url))
-
-    def validate_url(self):
-        assert self.browser.url == django_url(self.url)
-
-    def fill(self, name, value):
-        self.browser.fill(name, value)
-
-    def is_text_present(self, text, status=True):
-        assert_equals(status, self.browser.is_text_present(text))
-
-    def is_disabled(self, element_id):
-        try:
-            element = self.browser.find_by_css(
-                '#%s[disabled]' % element_id).first
-            return True
-        except Exception, e:
-            return False
-
-    def fill_in_with_js(self, jquery_id, object_id):
-        script = '%s.val(%s).change(); %s.trigger("liszt:updated").chosen().change()' % (
-            jquery_id, object_id, jquery_id)
-        self.browser.execute_script(script)
-        sleep(2)
-
-    def submit(self):
-        self.browser.find_by_css("form button").first.click()
-
-    def see_username_link(self):
-        self.click_by_css('.btn-navbar')
-        assert self.browser.find_by_css("#drop-user-settings")
-
-    def see_logged_in_status(self, user):
-        assert self.browser.find_link_by_partial_text(
-            "Logged in as: %s" % user.get_full_name())
-
-    def see_the_about_link(self):
-        assert self.browser.find_link_by_text('About')
-
-    def find_link_by_text(self, text):
-        assert self.browser.find_link_by_text(text)
-
-    def click_the_about_link(self):
-        self.browser.click_link_by_text('About')
-
-    def check_anonymous_user_allowed_tabs(self):
-        assert self.browser.find_link_by_text('About')
-        assert self.browser.find_link_by_text('mMICS')
-        assert self.browser.find_link_by_text('Login')
-
-    def check_data_entry_allowed_tabs(self):
-        assert self.browser.find_link_by_text('About')
-        assert self.browser.find_link_by_text('mMICS')
-        assert self.browser.find_link_by_text('Survey Administration')
-
-    def check_researcher_allowed_tabs(self):
-        self.check_data_entry_allowed_tabs()
-        assert self.browser.find_link_by_text('Downloads')
-        assert self.browser.find_link_by_text('Analysis')
-
-    def check_all_tabs(self):
-        self.check_researcher_allowed_tabs()
-        assert self.browser.find_link_by_text('Settings')
-
-    def check_researcher_not_allowed_tabs(self):
-        assert not self.browser.find_link_by_text('Settings')
-
-    def check_data_entry_not_allowed_tabs(self):
-        self.check_researcher_not_allowed_tabs()
-        assert not self.browser.find_link_by_text('Downloads')
-        assert not self.browser.find_link_by_text('Analysis')
-
-    def check_anonymous_user_not_allowed_tabs(self):
-        self.check_data_entry_not_allowed_tabs()
-        assert not self.browser.find_link_by_text('Survey Administration')
-
-    def check_notify_investigators_drop_down_is_not_present(self):
-        self.browser.click_link_by_text('Survey Administration')
-        assert not self.browser.find_link_by_text('Notifications')
-
-    def choose_radio(self, name, value):
-        js = "$('input:radio[name=%s][value=%s]').prop('checked', true).change()" % (
-            name, value)
-        self.browser.execute_script(js)
-
-    def see_user_settings_link(self, user):
-        assert self.browser.find_link_by_partial_text(
-            "%s" % str(user.get_full_name()))
-
-    def click_user_settings(self):
-        self.click_by_css("#fold-menu")
-        sleep(3)
-        self.click_by_css("#drop-user-settings")
-
-    def assert_user_can_see_profile_and_logout_link(self):
-        links = ["Edit Profile", "Change Password", "Logout"]
-        for link in links:
-            assert self.browser.find_link_by_partial_text(link)
-
-    def click_reset_password_form(self):
-        self.browser.find_link_by_partial_text("Change Password").click()
-
-    def assert_password_successfully_reset(self):
-        self.browser.is_text_present("Your password was reset successfully!!")
-
-    def click_actions_button(self):
-        self.browser.find_by_css('#action_caret').first.click()
-
-    def click_link_by_text(self, text):
-        self.browser.click_link_by_text(text)
-
-    def fill_valid_values(self, data):
-        self.browser.fill_form(data)
-        sleep(2)
-
-    def validate_pagination(self):
-        self.browser.click_link_by_text("2")
-
-    def is_radio_selected(self, name, value):
-        js = "$('input[name=%s]:radio').prop('checked')" % name
-        return self.browser.execute_script(js) == value
-
-    def see_success_message(self, object_name, action_str):
-        self.is_text_present('%s successfully %s.' % (object_name, action_str))
-
-    def select_multiple(self, field_id=None, *data):
-        for item in data:
-            script = "$('%s').multiSelect('select', '%s')" % (
-                field_id, item.pk)
-            self.browser.execute_script(script)
-
-    def validate_fields_present(self, fields, status=True):
-        for field in fields:
-            self.is_text_present(field, status)
-
-    def select_date(self, field_id):
-        script = "$('%s').focus()" % field_id
-        self.browser.execute_script(script)
-        script = "$('.ui-state-default').first().click()"
-        self.browser.execute_script(script)
-
-    def click_tab(self, tab_name):
-        self.browser.click_link_by_text(tab_name)
-
-    def see_dropdown(self, links):
-        for url_name in links:
-            assert self.browser.find_link_by_partial_href(reverse(url_name))
-
-    def select(self, name, values):
-        for value in values:
-            self.browser.select(name, value)
-
-    def click_by_css(self, css_selector):
-        self.browser.find_by_css(css_selector).first.click()
-
-    def click_link_by_partial_href(self, modal_id):
-        self.browser.click_link_by_partial_href(modal_id)
-
-    def click_link_by_href(self, modal_id):
-        self.browser.click_link_by_href(modal_id)
-
-    def click_button(self, name):
-        self.browser.find_by_name(name).first.click()
-
-    def find_by_css(self, css_selector, text):
-        assert self.browser.find_by_css(css_selector).first.value == text
-
-    def see_select_option(self, option_list, field_name):
-        for option in option_list:
-            assert option in self.browser.find_by_name(field_name).first.text
-
-    def option_not_present(self, option_list, field_name):
-        for option in option_list:
-            assert not option in self.browser.find_by_name(
-                field_name).first.text
-
-    def see_message(self, text):
-        assert self.browser.is_text_present(text)
-
-    def see_confirm_modal_message(self, name, action_str="delete"):
-        self.is_text_present(
-            "Confirm: Are you sure you want to %s %s?" % (action_str, name))
-
-    def validate_form_present(self, form):
-        for key in form.keys():
-            assert self.browser.find_by_name(key).first
-            self.is_text_present(form[key])
-
-    def validate_form_values(self, form_values):
-        for key in form_values.keys():
-            assert self.browser.find_by_name(
-                key).first.value == str(form_values[key])
-
-    def field_not_present(self, field_name):
-        assert not self.browser.find_by_name(field_name)
-
-    def field_is_visible(self, field_name):
-        return self.browser.find_by_name(field_name).first.visible
-
-    def find_element_by_css(self, selector):
-        assert self.browser.find_by_css(selector).first
-
-    def is_hidden(self, field, status=True):
-        assert_equals(status, not self.browser.find_by_css(
-            '.hide').first.visible)
-
-    def find_by_name(self, name):
-        assert self.browser.find_by_name(name)
-
-    def click_by_name(self, name):
-        self.browser.find_by_name(name).first.click()
-
-    def input_file(self, filename):
-        self.browser.attach_file('file', filename)
diff --git a/survey/features/page_objects/batches.py b/survey/features/page_objects/batches.py
deleted file mode 100644
index 66d28f80..00000000
--- a/survey/features/page_objects/batches.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# vim: ai ts=4 sts=4 et sw=4 encoding=utf-8
-from time import sleep
-from survey.features.page_objects.base import PageObject
-from nose.tools import assert_equals
-
-
-class AddBatchPage(PageObject):
-
-    def __init__(self, browser, survey):
-        self.browser = browser
-        self.survey = survey
-        self.url = '/surveys/%s/batches/new/' % survey.pk
-
-    def validate_error_message_on_fields(self):
-        self.is_text_present("This field is required.")
-
-
-class BatchShowPage(PageObject):
-
-    def __init__(self, browser, batch):
-        super(BatchShowPage, self).__init__(browser)
-        self.url = "/batches/" + str(batch.pk)
-
-    def batch_closed_for_all_locations(self):
-        assert len(self.browser.find_by_css('input[checked=checked]')) == 0
-
-    def open_batch_for(self, location):
-        self.browser.execute_script(
-            '$("#open_close_switch_%s").parent().bootstrapSwitch("toggleState")' % location.id)
-        sleep(2)
-
-    def close_batch_for(self, location):
-        self.browser.execute_script(
-            '$("#open_close_switch_%s").parent().bootstrapSwitch("toggleState")' % location.id)
-        sleep(2)
-
-    def activate_non_response_for_batch_and(self, location):
-        self.browser.execute_script(
-            '$("#activate_non_response_switch_%s").parent().bootstrapSwitch("toggleState")' % location.id)
-        sleep(2)
-
-    def deactivate_non_response_for_batch_and(self, location):
-        self.browser.execute_script(
-            '$("#activate_non_response_switch_%s").parent().bootstrapSwitch("toggleState")' % location.id)
-        sleep(2)
-
-
-class EditBatchPage(PageObject):
-
-    def __init__(self, browser, batch, survey):
-        self.browser = browser
-        self.batch = batch
-        self.survey = survey
-        self.url = '/surveys/%s/batches/%s/edit/' % (
-            self.survey.id, self.batch.id)
-
-
-class BatchListPage(PageObject):
-
-    def __init__(self, browser, survey):
-        self.browser = browser
-        self.survey = survey
-        self.url = '/surveys/' + str(self.survey.id) + '/batches/'
-
-    def visit_batch(self, batch):
-        self.browser.click_link_by_text(" Open/Close")
-        return BatchShowPage(self.browser, batch)
-
-    def click_add_batch_button(self):
-        self.browser.click_link_by_text("Add Batch")
-
-    def validate_fields(self):
-        self.validate_fields_present(
-            [self.survey.name.capitalize(), "Batch Name", "Description", "Actions"])
-
-    def click_link_by_text(self, text):
-        self.browser.click_link_by_text(text)
-
-    def validate_page_got_survey_id(self):
-        assert self.browser.find_by_css(
-            '#survey_id').first.value == str(self.survey.id)
-
-
-class AssignQuestionToBatchPage(PageObject):
-
-    def __init__(self, browser, batch):
-        self.browser = browser
-        self.batch = batch
-        self.url = '/batches/' + str(self.batch.id) + '/assign_questions/'
-
-    def see_the_question(self, status, question_id):
-        assert_equals(status, self.browser.find_by_id(
-            "%s-selectable" % question_id).visible)
-
-    def see_the_selected_question(self, status, question_id):
-        assert_equals(status, self.browser.find_by_id(
-            "%s-selection" % question_id).visible)
diff --git a/survey/features/page_objects/ea.py b/survey/features/page_objects/ea.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/survey/features/page_objects/household_member.py b/survey/features/page_objects/household_member.py
deleted file mode 100644
index ad613ccb..00000000
--- a/survey/features/page_objects/household_member.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# vim: ai ts=4 sts=4 et sw=4 encoding=utf-8
-from survey.features.page_objects.base import PageObject
-
-
-class NewHouseholdMemberPage(PageObject):
-
-    def __init__(self, browser, household):
-        self.browser = browser
-        self.household = household
-        self.url = '/households/%d/member/new/' % household.id
-
-    def validate_fields(self):
-        self.validate_fields_present(
-            ['Family Name', 'Sex', 'Date of birth', 'Create', 'Cancel'])
-
-    def fill_valid_member_values(self, data):
-        self.browser.fill_form(data)
-
-
-class EditHouseholdMemberPage(PageObject):
-
-    def __init__(self, browser, household, member):
-        self.browser = browser
-        self.household = household
-        self.member = member
-        self.url = '/households/%d/member/%d/edit/' % (household.id, member.id)
-
-    def validate_member_details(self, household_member):
-        self.browser.is_text_present(household_member.surname)
-        self.browser.is_text_present(household_member.date_of_birth)
-        self.is_radio_selected('male', True)
-
-    def fill_valid_member_values(self, data):
-        self.browser.fill_form(data)
-
-
-class DeleteHouseholdMemberPage(PageObject):
-
-    def __init__(self, browser, household, member):
-        self.browser = browser
-        self.household = household
-        self.member = member
-        self.url = '/households/%d/member/%d/delete/' % (
-            household.id, member.id)
-
-    def see_delete_confirmation_modal(self):
-        self.is_text_present("Delete Household Member")
-        self.is_text_present(
-            "Confirm: Are you sure you want to delete %s?" % self.member.surname)
diff --git a/survey/features/page_objects/household_member_groups.py b/survey/features/page_objects/household_member_groups.py
deleted file mode 100644
index b1c59d86..00000000
--- a/survey/features/page_objects/household_member_groups.py
+++ /dev/null
@@ -1,65 +0,0 @@
-from survey.features.page_objects.base import PageObject
-
-
-class GroupConditionListPage(PageObject):
-    url = '/conditions/'
-
-    def validate_fields(self):
-        self.validate_fields_present(
-            ["Group Criteria List", "Condition", "Attribute", "Value"])
-
-
-class GroupsListingPage(PageObject):
-    url = '/groups/'
-
-    def validate_fields(self):
-        self.validate_fields_present(
-            ["Groups List", "Order", "Group name", "Actions"])
-
-
-class AddConditionPage(PageObject):
-    url = "/conditions/new/"
-
-
-class AddGroupPage(PageObject):
-    url = '/groups/new/'
-
-    def validate_latest_condition(self, condition):
-        self.browser.find_by_value("%s > %s > %s" % (
-            condition.attribute, condition.condition, condition.value))
-
-
-class GroupConditionModalPage(PageObject):
-    url = ''
-
-    def validate_contents(self):
-        self.validate_fields_present(
-            ["Value", "Attribute", "Eligibility Criteria", "New Eligibility Criteria"])
-
-
-class GroupDetailsPage(PageObject):
-
-    def __init__(self, browser, group):
-        super(GroupDetailsPage, self).__init__(browser)
-        self.group = group
-        self.url = '/groups/%s/' % group.id
-
-    def validate_fields(self):
-        self.validate_fields_present(
-            ["Group %s Criteria List" % self.group.name, "Condition", "Attribute", "Value"])
-
-
-class AddNewConditionToGroupPage(PageObject):
-
-    def __init__(self, browser, group):
-        super(AddNewConditionToGroupPage, self).__init__(browser)
-        self.group = group
-        self.url = '/groups/%d/conditions/new/' % group.id
-
-
-class DeleteHouseholdMemberGroup(PageObject):
-
-    def __init__(self, browser, group):
-        super(DeleteHouseholdMemberGroup, self).__init__(browser)
-        self.group = group
-        self.url = '/groups/%d/delete/' % group.id
diff --git a/survey/features/page_objects/households.py b/survey/features/page_objects/households.py
deleted file mode 100644
index ff000cdb..00000000
--- a/survey/features/page_objects/households.py
+++ /dev/null
@@ -1,173 +0,0 @@
-# vim: ai ts=4 sts=4 et sw=4 encoding=utf-8
-from rapidsms.contrib.locations.models import Location
-from survey.features.page_objects.base import PageObject
-from survey.investigator_configs import MONTHS
-from survey.models import EnumerationArea
-from survey.models.investigator import Investigator
-from lettuce.django import django_url
-
-
-class NewHouseholdPage(PageObject):
-    url = "/households/new/"
-
-    def valid_page(self):
-        fields = ['investigator', 'surname', 'first_name', 'male', 'date_of_birth', 'occupation',
-                  'level_of_education', 'resident_since_month', 'resident_since_year']
-        fields += ['uid']
-        for field in fields:
-            assert self.browser.is_element_present_by_name(field)
-
-    def get_household_values(self):
-        return self.values
-
-    def fill_valid_values(self, values, ea=None):
-        kampala = Location.objects.get(name="Kampala")
-        kampala_county = Location.objects.get(name="Kampala County")
-        kampala_subcounty = Location.objects.get(name="Subcounty")
-        kampala_parish = Location.objects.get(name="Parish")
-        kampala_village = Location.objects.get(name="Village")
-        self.fill_in_with_js('$("#location-district")', kampala.id)
-        self.fill_in_with_js('$("#location-county")', kampala_county.id)
-        self.fill_in_with_js('$("#location-subcounty")', kampala_subcounty.id)
-        self.fill_in_with_js('$("#location-parish")', kampala_parish.id)
-        self.fill_in_with_js('$("#location-village")', kampala_village.id)
-        self.fill_in_with_js('$("#widget_ea")', ea.id)
-
-        investigator = Investigator.objects.get(name="Investigator name")
-        self.fill_in_with_js('$("#household-investigator")', investigator.id)
-        self.fill_in_with_js('$("#household-extra_resident_since_year")', 1984)
-        self.fill_in_with_js('$("#household-extra_resident_since_month")', 1)
-        self.values = values
-        self.browser.fill_form(self.values)
-
-    def validate_household_created(self):
-        assert self.browser.is_text_present(
-            "Household successfully registered.")
-
-    def has_children(self, value):
-        self.choose_radio('has_children', value)
-
-    def are_children_fields_disabled(self, is_disabled=True):
-        for element_id in ['aged_between_5_12_years', 'aged_between_13_17_years']:
-            element_id = 'household-children-' + element_id
-            assert self.is_disabled(element_id) == is_disabled
-        self.are_children_below_5_fields_disabled(is_disabled=is_disabled)
-
-    def is_no_below_5_checked(self):
-        assert self.browser.find_by_id(
-            'household-children-has_children_below_5_1').selected == True
-
-    def cannot_say_yes_to_below_5(self):
-        assert self.is_disabled(
-            "household-children-has_children_below_5_0") == True
-        self.are_children_fields_disabled()
-
-    def has_children_below_5(self, value):
-        self.choose_radio('has_children_below_5', value)
-
-    def are_children_below_5_fields_disabled(self, is_disabled=True):
-        for element_id in ['aged_between_0_5_months', 'aged_between_6_11_months', 'aged_between_12_23_months',
-                           'aged_between_24_59_months']:
-            element_id = 'household-children-' + element_id
-            assert self.is_disabled(element_id) == is_disabled
-
-    def has_women(self, value):
-        self.choose_radio('has_women', value)
-
-    def are_women_fields_disabled(self, is_disabled=True):
-        for element_id in ['aged_between_15_19_years', 'aged_between_20_49_years']:
-            element_id = 'household-women-' + element_id
-            assert self.is_disabled(element_id) == is_disabled
-
-    def fill_in_number_of_females_lower_than_sum_of_15_19_and_20_49(self):
-        self.browser.fill('number_of_females', '1')
-        self.browser.fill('aged_between_15_19_years', '2')
-        self.browser.fill('aged_between_20_49_years', '3')
-
-    def see_an_error_on_number_of_females(self):
-        self.is_text_present(
-            'Please enter a value that is greater or equal to the total number of women above 15 years age.')
-
-    def choose_occupation(self, occupation_value):
-        self.browser.select('occupation', occupation_value)
-
-    def is_specify_visible(self, status=True):
-        extra = self.browser.find_by_css('#extra-occupation-field')
-        if status:
-            assert len(extra) == 1
-        else:
-            assert len(extra) == 0
-
-
-class HouseholdDetailsPage(PageObject):
-
-    def __init__(self, browser, household):
-        super(HouseholdDetailsPage, self).__init__(browser)
-        self.browser = browser
-        self.household = household
-        self.url = '/households/' + str(household.id) + '/'
-
-    def validate_household_details(self):
-        household_head = self.household.get_head()
-        details = {
-            'Family Name': household_head.surname,
-            'Other Names': household_head.first_name,
-            'Age': str(household_head.get_age()),
-            'Gender': 'Male' if household_head.male else 'Female',
-            'Occupation / Main Livelihood': household_head.occupation,
-            'Highest level of education completed': household_head.level_of_education,
-            'Since when have you lived here': str(household_head.resident_since_year),
-        }
-        for label, text in details.items():
-            self.is_text_present(label)
-            self.is_text_present(text)
-        self.is_text_present(MONTHS[household_head.resident_since_month][1])
-
-    def validate_household_member_details_table_headings(self):
-        member_details_headings = ['Family Name', 'Date of birth', 'Sex']
-        for heading in member_details_headings:
-            self.is_text_present(heading)
-
-    def validate_household_member_details_values(self):
-        for member in self.household.household_member.all():
-            for detail in [member.surname, member.date_of_birth.strftime('%b %d, %Y'), 'Male' if member.male else 'Female']:
-                self.is_text_present(str(detail))
-
-    def validate_household_member_details(self):
-        self.validate_household_member_details_table_headings()
-        self.validate_household_member_details_values()
-
-    def validate_household_member_title_and_add_household_member_link(self):
-        self.is_text_present('Household Members:')
-        self.browser.find_link_by_text('Add Member')
-
-    def validate_actions_edit_and_delete_member(self):
-        self.browser.find_link_by_text('Edit')
-        self.browser.find_link_by_text('Delete')
-
-    def click_delete_link(self, member_id):
-        self.browser.click_link_by_partial_href(
-            "#delete_member_%d" % member_id)
-
-
-class HouseholdsListPage(PageObject):
-    url = '/households/'
-
-    def validate_fields(self):
-        self.validate_fields_present(["Households List", "Household ID", "Household Head",
-                                      "District", "County", "Sub County", "Parish", "Village", "Investigator"])
-
-    def validate_pagination(self):
-        self.browser.click_link_by_text("2")
-
-    def no_registered_huseholds(self):
-        self.browser.is_text_present(
-            'There are  no households currently registered  for this country.')
-
-
-class EditHouseholdsPage(PageObject):
-
-    def __init__(self, browser, household):
-        self.browser = browser
-        self.household = household
-        self.url = '/households/%s/edit/' % str(household.id)
diff --git a/survey/features/page_objects/indicators.py b/survey/features/page_objects/indicators.py
deleted file mode 100644
index c08b6694..00000000
--- a/survey/features/page_objects/indicators.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from survey.features.page_objects.base import PageObject
-
-
-class NewIndicatorPage(PageObject):
-    url = '/indicators/new/'
-
-
-class ListIndicatorPage(PageObject):
-    url = '/indicators/'
-
-    def see_indicators(self, indicators):
-        list_titles = ['Indicator', 'Description',
-                       'Module', 'Measure', 'Actions']
-        values = [[field.name, field.description, field.module.name,
-                   field.measure] for field in indicators]
-        values.append(list_titles)
-        fields = [field for fields in values for field in fields]
-        self.validate_fields_present(fields)
-
-
-class SimpleIndicatorGraphPage(PageObject):
-
-    def __init__(self, browser, indicator):
-        self.indicator = indicator
-        self.browser = browser
-        self.url = "/indicators/%s/simple/" % self.indicator.id
diff --git a/survey/features/page_objects/investigators.py b/survey/features/page_objects/investigators.py
deleted file mode 100644
index ff5842be..00000000
--- a/survey/features/page_objects/investigators.py
+++ /dev/null
@@ -1,149 +0,0 @@
-# vim: ai ts=4 sts=4 et sw=4 encoding=utf-8
-from time import sleep
-from survey.features.page_objects.base import PageObject
-from survey.investigator_configs import COUNTRY_PHONE_CODE
-from rapidsms.contrib.locations.models import Location
-from lettuce.django import django_url
-from survey.models import EnumerationArea
-
-
-class NewInvestigatorPage(PageObject):
-    url = "/investigators/new/"
-
-    def valid_page(self):
-        fields = ['name', 'mobile_number',
-                  'confirm_mobile_number', 'male', 'age', 'backend']
-        for field in fields:
-            assert self.browser.is_element_present_by_name(field)
-        assert self.browser.find_by_css(
-            "span.add-on")[0].text == COUNTRY_PHONE_CODE
-
-    def get_investigator_values(self):
-        return self.values
-
-    def validate_detail_page_url(self):
-        assert self.browser.url == django_url(self.url)
-
-    def fill_valid_values(self, values, ea):
-        self.browser.find_by_id(
-            "location-value").value = Location.objects.create(name="Uganda").id
-        kampala = Location.objects.get(name="Kampala")
-        kampala_county = Location.objects.get(name="Kampala County")
-        kampala_subcounty = Location.objects.get(name="Subcounty")
-        kampala_parish = Location.objects.get(name="Parish")
-        kampala_village = Location.objects.get(name="Village")
-        ea = EnumerationArea.objects.get(name="EA")
-        self.fill_in_with_js('$("#location-district")', kampala.id)
-        self.fill_in_with_js('$("#location-county")', kampala_county.id)
-        self.fill_in_with_js('$("#location-subcounty")', kampala_subcounty.id)
-        self.fill_in_with_js('$("#location-parish")', kampala_parish.id)
-        self.fill_in_with_js('$("#location-village")', kampala_village.id)
-        self.fill_in_with_js('$("#widget_ea")', ea.id)
-
-        self.values = values
-        self.browser.fill_form(self.values)
-
-
-class InvestigatorsListPage(PageObject):
-    url = '/investigators/'
-
-    def validate_fields(self):
-        self.validate_fields_present(
-            ["Investigators List", "Name", "Mobile Number", "Action"])
-
-    def validate_pagination(self):
-        self.browser.click_link_by_text("2")
-
-    def validate_presence_of_investigator(self, values):
-        assert self.browser.is_text_present(values['name'])
-        assert self.browser.is_text_present(values['mobile_number'])
-
-    def no_registered_invesitgators(self):
-        assert self.browser.is_text_present(
-            "There are no investigators currently registered for this location.")
-
-    def visit_investigator(self, investigator):
-        self.browser.click_link_by_text(investigator.name)
-
-    def see_confirm_block_message(self, confirmation_type, investigator):
-        self.is_text_present("Confirm: Are you sure you want to %s investigator %s" % (
-            confirmation_type, investigator.name))
-
-    def validate_successful_edited_message(self):
-        self.is_text_present("Investigator successfully edited.")
-
-    def validate_page_url(self):
-        assert self.browser.url == django_url(self.url)
-
-
-class FilteredInvestigatorsListPage(InvestigatorsListPage):
-
-    def __init__(self, browser, location_id):
-        self.browser = browser
-        self.url = '/investigators/?location=' + str(location_id)
-
-    def no_registered_invesitgators(self):
-        assert self.browser.is_text_present(
-            "There are no investigators currently registered for this county.")
-
-
-class EditInvestigatorPage(PageObject):
-
-    def __init__(self, browser, investigator):
-        self.browser = browser
-        self.investigator = investigator
-        self.url = '/investigators/' + str(investigator.id) + '/edit/'
-
-    def validate_edit_investigator_url(self):
-        assert self.browser.url == django_url(self.url)
-
-    def change_name_of_investigator(self):
-        self.values = {
-            'name': 'Updated Name',
-            'mobile_number': self.investigator.mobile_number,
-            'confirm_mobile_number': self.investigator.mobile_number,
-            'male': self.investigator.male,
-            'age': self.investigator.age,
-            'level_of_education': self.investigator.level_of_education,
-            'language': self.investigator.language,
-            'location': self.investigator.location,
-        }
-        self.browser.fill_form(self.values)
-
-    def assert_user_saved_sucessfully(self):
-        self.is_text_present("User successfully edited.")
-
-
-class InvestigatorDetailsPage(PageObject):
-
-    def __init__(self, browser, investigator):
-        self.browser = browser
-        self.investigator = investigator
-        self.url = '/investigators/' + str(investigator.id) + '/'
-
-    def validate_page_content(self):
-        details = {
-            'Name': self.investigator.name,
-            'Mobile Number': self.investigator.mobile_number,
-            'Age': str(self.investigator.age),
-            'Sex': 'Male' if self.investigator.male else 'Female',
-            'Highest Level of Education': self.investigator.level_of_education,
-            'Preferred Language of Communication': self.investigator.language,
-            'Country': 'Uganda',
-            'City': 'Kampala',
-        }
-        for label, text in details.items():
-            self.is_text_present(label)
-            self.is_text_present(text)
-
-    def validate_navigation_links(self):
-        assert self.browser.find_link_by_text(' Back')
-
-    def validate_back_link(self):
-        self.browser.find_link_by_href(django_url(InvestigatorsListPage.url))
-
-    def validate_detail_page_url(self):
-        assert self.browser.url == django_url(self.url)
-
-    def validate_successful_edited_message(self):
-        self.is_text_present("Investigator successfully edited.")
diff --git a/survey/features/page_objects/location_hierarchy.py b/survey/features/page_objects/location_hierarchy.py
deleted file mode 100644
index 0b1ca00e..00000000
--- a/survey/features/page_objects/location_hierarchy.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from survey.features.page_objects.base import PageObject
-from nose.tools import assert_equals
-
-
-class AddLocationHierarchyPage(PageObject):
-
-    def __init__(self, browser):
-        super(AddLocationHierarchyPage, self).__init__(browser)
-        self.url = '/locations/hierarchy/add/'
-
-    def see_field_details(self, level, form, status=True):
-        assert_equals(status, self.browser.is_text_present(level))
-        status = 1 if status else 0
-        for field in ['levels', 'required', 'has_code', 'code']:
-            assert_equals(status, len(
-                self.browser.find_by_name(form + '-levels')))
-
-    def submit(self):
-        self.browser.find_by_name('save_button').first.click()
diff --git a/survey/features/page_objects/question.py b/survey/features/page_objects/question.py
deleted file mode 100644
index 386bddd3..00000000
--- a/survey/features/page_objects/question.py
+++ /dev/null
@@ -1,91 +0,0 @@
-from survey.features.page_objects.base import PageObject
-from nose.tools import assert_equals
-
-
-class BatchQuestionsListPage(PageObject):
-
-    def __init__(self, browser, batch):
-        self.browser = browser
-        self.batch = batch
-        self.url = '/batches/%d/questions/' % batch.id
-
-    def validate_fields(self):
-        self.is_text_present('%s Question' % self.batch.name.capitalize())
-        self.is_text_present('Code')
-        self.is_text_present('Question')
-        self.is_text_present('Type')
-        self.is_text_present('Group')
-        self.is_text_present('Actions')
-
-    def validate_pagination(self, status=True):
-        assert_equals(not status, not self.browser.find_link_by_text("2"))
-
-    def validate_back_to_questions_list_page(self):
-        assert_equals(False, self.browser.is_text_present("Text"))
-        assert_equals(False, self.browser.is_text_present("Order"))
-        assert_equals(False, self.browser.is_text_present("Close"))
-
-    def validate_only_view_options_action_exists(self):
-        self.find_link_by_text("View options/Logic")
-        self.find_link_by_text("View Logic")
-
-        self.is_text_present("Add Subquestion", False)
-        self.is_text_present("Add Logic", False)
-        self.is_text_present("Edit", False)
-        self.is_text_present("Remove", False)
-
-
-class ListAllQuestionsPage(PageObject):
-    url = "/questions/"
-
-    def validate_fields(self):
-        self.validate_fields_present(
-            ['Questions List', 'Question', 'Type', 'Group', 'Actions'])
-
-    def click_delete_subquestion(self):
-        self.click_by_css("#delete_subquestion")
-
-
-class AddQuestionPage(PageObject):
-
-    def __init__(self, browser, batch):
-        self.browser = browser
-        self.batch = batch
-        self.url = '/batches/%d/questions/new/' % batch.id
-
-
-class CreateNewQuestionPage(PageObject):
-    url = "/questions/new/"
-
-    def see_one_option_field(self, option):
-        self.is_text_present(option)
-
-    def see_option_add_and_remove_buttons(self, length):
-        assert len(self.browser.find_by_css(".icon-plus")) == length
-        assert len(self.browser.find_by_css(".icon-remove")) == length
-
-    def option_not_present(self, option):
-        assert not self.browser.is_text_present(option)
-
-    def see_option_text(self, option_text, field_name):
-        elements = self.browser.find_by_name(field_name)
-        for element in elements:
-            if option_text in element.text:
-                return True
-        return False
-
-
-class CreateNewSubQuestionPage(PageObject):
-
-    def __init__(self, browser, question):
-        self.browser = browser
-        self.question = question
-        self.url = "/questions/%d/sub_questions/new/" % question.id
-
-
-class EditQuestionPage(PageObject):
-
-    def __init__(self, browser, question):
-        super(EditQuestionPage, self).__init__(browser)
-        self.question = question
-        self.url = "/questions/%d/edit/" % question.id
diff --git a/survey/features/page_objects/question_module.py b/survey/features/page_objects/question_module.py
deleted file mode 100644
index 840bcf64..00000000
--- a/survey/features/page_objects/question_module.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from survey.features.page_objects.base import PageObject
-
-
-class QuestionModuleList(PageObject):
-    url = "/modules/"
-
-
-class NewQuestionModule(PageObject):
-    url = "/modules/new/"
-
-
-class EditQuestionModulePage(PageObject):
-
-    def __init__(self, browser, module):
-        super(EditQuestionModulePage, self).__init__(browser)
-        self.module = module
-        self.url = '/modules/%s/edit/' % module.id
diff --git a/survey/features/page_objects/root.py b/survey/features/page_objects/root.py
deleted file mode 100644
index f1de3002..00000000
--- a/survey/features/page_objects/root.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# -*- coding: utf-8 -*-
-from time import sleep
-
-from rapidsms.backends.database.models import BackendMessage
-
-from survey.features.page_objects.base import PageObject
-from survey.models.investigator import Investigator
-
-
-class HomePage(PageObject):
-    url = "/"
-
-    def click_the_login_link(self):
-        self.browser.click_link_by_text('Login')
-
-    def see_under_construction(self):
-        self.is_text_present('Under Construction')
-
-
-class AboutPage(PageObject):
-    url = "/about/"
-
-    def see_the_about_text_provided_by_panwar(self):
-        self.is_text_present('Multiple Indicator Cluster Survey (MICS)')
-        self.is_text_present('Survey tools')
-        self.is_text_present(
-            'Mobile-based Multiple Indicator Cluster Survey (MICS)')
-
-    def assert_edit_link_absent(self):
-        assert not self.browser.find_by_css("#edit-about_us")
-
-
-class EditAboutUsPage(PageObject):
-    url = '/about/edit/'
-
-    def fill_wywget_textarea(self, data):
-        script = '$("#content-editor").show().css("visibility","")'
-        self.browser.execute_script(script)
-        sleep(2)
-        self.fill_valid_values(data)
-
-
-class BulkSMSPage(PageObject):
-    url = "/bulk_sms"
-    messages = {
-        "location": "Please select a location.",
-        "text": "Please enter the message to send.",
-    }
-
-    def compose_message(self, message):
-        self.message = message
-        self.fill('text', message)
-
-    def is_message_sent(self):
-        self.is_text_present("Your message has been sent to investigators.")
-        for investgator in Investigator.objects.all():
-            assert BackendMessage.objects.filter(
-                identity=investgator.identity, text=self.message).count() == 1
-
-    def error_message_for(self, field):
-        self.is_text_present(self.messages[field])
-
-    def enter_text(self, length):
-        message = "*" * length
-        self.fill('text', message)
-
-    def counter_updated(self, length):
-        counter = str(length) + "/480"
-        self.is_text_present(counter)
diff --git a/survey/features/page_objects/rules.py b/survey/features/page_objects/rules.py
deleted file mode 100644
index 545994c8..00000000
--- a/survey/features/page_objects/rules.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from survey.features.page_objects.base import PageObject
-
-
-class AddLogicToBatchQuestionPage(PageObject):
-
-    def __init__(self, browser, batch, question):
-        super(AddLogicToBatchQuestionPage, self).__init__(browser)
-        self.question = question
-        self.batch = batch
-        self.url = '/batches/%s/questions/%s/add_logic/' % (
-            batch.id, question.id)
-
-    def validate_fields(self):
-        fields = ['Question: %s' % str(
-            self.question), 'Eligibility criteria', 'Attribute', 'Then']
-
-        self.validate_fields_present(fields)
diff --git a/survey/features/page_objects/survey_completion_rates.py b/survey/features/page_objects/survey_completion_rates.py
deleted file mode 100644
index c27923a7..00000000
--- a/survey/features/page_objects/survey_completion_rates.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from rapidsms.contrib.locations.models import Location
-from survey.features.page_objects.base import PageObject
-
-
-class SurveyCompletionRatesPage(PageObject):
-
-    def __init__(self, browser):
-        super(SurveyCompletionRatesPage, self).__init__(browser)
-        self.url = '/surveys/completion/'
-
-    def choose_location(self, locations):
-        for key, value in locations.items():
-            object_id = "location-%s" % key
-            assert self.browser.is_element_present_by_id(object_id)
-            jquery_id = '$("#%s")' % object_id
-            location = Location.objects.get(name=value)
-            self.fill_in_with_js(jquery_id, location.pk)
-
-    def choose_ea(self, ea):
-        jquery_id = '$("#widget_ea")'
-        self.fill_in_with_js(jquery_id, ea.id)
-
-    def check_if_batches_present(self, batches):
-        all_options = self.browser.find_by_id(
-            'id_batch')[0].find_by_tag('option')
-        all_options = [option.text for option in all_options]
-        for batch in batches:
-            assert batch.name in all_options
-
-    def check_get_status_button_presence(self):
-        assert self.browser.find_by_css(
-            "#aggregates-form")[0].find_by_tag('button')[0].text == "Get status"
-
-    def choose_batch(self, batch):
-        self.browser.select('batch', batch.pk)
-
-    def see_completion_rates_table(self):
-        assert self.browser.is_text_present('Location')
-        assert self.browser.is_text_present('Total Household')
-        assert self.browser.is_text_present('% Completed')
-
-    def see_houdehold_completion_table(self):
-        assert self.browser.is_text_present('HH Code')
-        assert self.browser.is_text_present('Household Head')
-        assert self.browser.is_text_present(
-            'Total number of members in Household')
-        assert self.browser.is_text_present('Total Interviewed')
-        assert self.browser.is_text_present('Date Completed')
diff --git a/survey/features/page_objects/surveys.py b/survey/features/page_objects/surveys.py
deleted file mode 100644
index 0d7d8866..00000000
--- a/survey/features/page_objects/surveys.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from survey.features.page_objects.base import PageObject
-
-
-class SurveyListPage(PageObject):
-    url = '/surveys/'
-
-    def validate_fields(self):
-        self.validate_fields_present(
-            ["Survey List", "Name", "Description", "Type", "Number of Households", "Actions"])
-
-
-class AddSurveyPage(PageObject):
-    url = '/surveys/new/'
-
-
-class EditSurveyPage(PageObject):
-
-    def __init__(self, browser, survey):
-        super(EditSurveyPage, self).__init__(browser)
-        self.survey = survey
-        self.url = '/surveys/%d/edit/' % survey.id
diff --git a/survey/features/page_objects/uploads.py b/survey/features/page_objects/uploads.py
deleted file mode 100644
index 085a797d..00000000
--- a/survey/features/page_objects/uploads.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# vim: ai ts=4 sts=4 et sw=4 encoding=utf-8
-from survey.features.page_objects.base import PageObject
-
-
-class UploadLocationsPage(PageObject):
-
-    def __init__(self, browser):
-        super(UploadLocationsPage, self).__init__(browser)
-        self.url = '/locations/upload/'
-
-    def validate_typecode_appear_before_typename(self, type_name, length_of_code):
-        headers = self.browser.find_by_css('th')
-        assert headers[0].value == type_name.capitalize() + 'Code'
-        assert headers[1].value == type_name.capitalize() + 'Name'
-
-        self.is_text_present('0' * length_of_code)
-
-
-class UploadBase(PageObject):
-
-    def submit(self):
-        self.browser.find_by_name('save_button').first.click()
-
-    def validate_layout_collapsed(self):
-        collapse_element = self.browser.find_by_css("#collapse_table")
-        assert not 'in' in collapse_element.first['class']
-
-
-class UploadWeightsPage(UploadBase):
-
-    def __init__(self, browser):
-        super(UploadWeightsPage, self).__init__(browser)
-        self.url = '/locations/weights/upload/'
-
-
-class UploadEAPage(UploadBase):
-
-    def __init__(self, browser):
-        super(UploadEAPage, self).__init__(browser)
-        self.url = '/locations/enumeration_area/upload/'
diff --git a/survey/features/page_objects/users.py b/survey/features/page_objects/users.py
deleted file mode 100644
index 4e68bdea..00000000
--- a/survey/features/page_objects/users.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# vim: ai ts=4 sts=4 et sw=4 encoding=utf-8
-from time import sleep
-from lettuce.django import django_url
-from survey.features.page_objects.base import PageObject
-
-
-class UsersListPage(PageObject):
-    url = "/users/"
-
-    def validate_users_listed(self):
-        self.is_text_present('Users List')
-
-    def validate_displayed_headers(self):
-        self.validate_fields_present(
-            ["Full name", "Role", "Mobile number", "Actions"])
-
-    def validate_users_paginated(self):
-        self.browser.click_link_by_text("2")
-
-    def assert_user_saved_sucessfully(self):
-        self.is_text_present("User successfully edited.")
-
-
-class EditUserDetailsPage(PageObject):
-
-    def set_user(self, user):
-        self.user = user
-        self.url = "/users/" + str(user.pk) + "/edit/"
-
-    def assert_form_has_infomation(self):
-        assert self.browser.find_by_name(
-            "username").first.value == self.user.username
-        assert self.browser.find_by_name(
-            "mobile_number").first.value == self.user.userprofile.mobile_number
-        assert self.browser.find_by_name(
-            "email").first.value == self.user.email
-
-    def modify_users_information(self):
-        assert self.browser.find_by_name('mobile_number')
-        self.fill('mobile_number', '994747474')
-
-    def click_update_button(self):
-        self.browser.find_by_name("save_button").first.click()
-
-    def assert_user_saved_sucessfully(self):
-        self.is_text_present("User successfully edited.")
-
-    def assert_username_is_readonly(self):
-        try:
-            assert self.browser.find_by_css('#id_username[readonly]').first
-        except Exception, e:
-            return False
-
-    def is_group_input_field_visible(self, status=True):
-        if status:
-            assert self.browser.find_by_name("groups")
-        else:
-            assert not self.browser.find_by_name("groups")
-
-
-class UserDetailsPage(PageObject):
-
-    def __init__(self, browser, user):
-        self.browser = browser
-        self.user = user
-        self.url = '/users/%d/' % user.id
-
-    def validate_page_content(self):
-        details = {
-            'Username': self.user.username,
-            'First name': self.user.first_name,
-            'Last name': self.user.last_name,
-            'Mobile number': self.user.userprofile.mobile_number,
-            'Email': self.user.email,
-            'Groups': ", ".join(self.user.groups.all().values_list('name', flat=True)),
-        }
-        for label, text in details.items():
-            self.is_text_present(label)
-            self.is_text_present(text)
-
-    def validate_back_link(self):
-        self.browser.find_link_by_href(django_url(UsersListPage.url))
-
-
-class NewUserPage(PageObject):
-    url = "/users/new/"
-
-    def valid_page(self):
-        sleep(5)
-        self.is_text_present('New User')
-        fields = ['username', 'password1', 'password2', 'first_name', 'last_name',
-                  'mobile_number', 'email', 'groups']
-        for field in fields:
-            assert self.browser.is_element_present_by_name(field)
-
-    def see_user_successfully_registered(self):
-        sleep(0.5)
-        self.is_text_present('User successfully registered.')
diff --git a/survey/features/page_objects/weights.py b/survey/features/page_objects/weights.py
deleted file mode 100644
index 59d73a2e..00000000
--- a/survey/features/page_objects/weights.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from survey.features.page_objects.base import PageObject
-
-__author__ = 'mnandri'
-
-
-class ListLocationWeightsPage(PageObject):
-    url = "/locations/weights/"
-
-
-class ListLocationWeightsErrorLogPage(PageObject):
-    url = "/locations/weights/error_logs/"
diff --git a/survey/features/permissions-steps.py b/survey/features/permissions-steps.py
deleted file mode 100644
index 3ba8207a..00000000
--- a/survey/features/permissions-steps.py
+++ /dev/null
@@ -1,94 +0,0 @@
-from lettuce import *
-from django.contrib.auth.models import User, Group, Permission
-from django.contrib.contenttypes.models import ContentType
-
-from survey.features.page_objects.accounts import LogoutPage, LoginPage
-
-
-@step(u'Given I  am an anonymous user')
-def given_i_am_an_anonymous_user(step):
-    world.page = LogoutPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see anonymous user allowed tabs')
-def then_i_should_see_anonymous_user_allowed_tabs(step):
-    world.page = LoginPage(world.browser)
-    world.page.check_anonymous_user_allowed_tabs()
-
-
-@step(u'And I should not be seeing above anonymous user level tabs')
-def and_i_should_not_be_seeing_above_anonymous_user_level_tabs(step):
-    world.page.check_anonymous_user_not_allowed_tabs()
-
-
-def set_permissions(group, permissions_codename_list):
-    auth_content = ContentType.objects.get_for_model(Permission)
-    for codename in permissions_codename_list:
-        permission, out = Permission.objects.get_or_create(
-            codename=codename, content_type=auth_content)
-        group.permissions.add(permission)
-
-
-@step(u'Given I have a data entry user')
-def given_i_have_a_data_entry_user(step):
-    data_entry = Group.objects.create(name='data_entry')
-    world.user = User.objects.create_user('Rajni', 'rajni@kant.com', 'I_Rock')
-    data_entry.user_set.add(world.user)
-    set_permissions(
-        data_entry, ['can_view_households', 'can_view_investigators'])
-
-
-@step(u'And I login that user')
-def and_i_login_that_user(step):
-    world.page = LoginPage(world.browser)
-    world.page.visit()
-    world.page.login(world.user)
-
-
-@step(u'Then I should see data entry allowed tabs')
-def then_i_should_see_data_entry_allowed_tabs(step):
-    world.page.check_data_entry_allowed_tabs()
-
-
-@step(u'And I should not be seeing above data entry level tabs')
-def and_i_should_not_be_seeing_above_data_entry_level_tabs(step):
-    world.page.check_data_entry_not_allowed_tabs()
-
-
-@step(u'Given I have a researcher user')
-def given_i_have_a_researcher_user(step):
-    researcher = Group.objects.create(name='researcher')
-    world.user = User.objects.create_user('Rajni', 'rajni@kant.com', 'I_Rock')
-    researcher.user_set.add(world.user)
-    set_permissions(researcher, [
-                    'can_view_aggregates', 'can_view_households', 'can_view_batches', 'can_view_investigators'])
-
-
-@step(u'And I should not be seeing above researcher level tabs')
-def and_i_should_not_be_seeing_above_researcher_level_tabs(step):
-    world.page.check_researcher_not_allowed_tabs()
-
-
-@step(u'Then I should see researcher allowed tabs')
-def then_i_should_see_researcher_allowed_tabs(step):
-    world.page.check_researcher_allowed_tabs()
-
-
-@step(u'Given I have a admin user')
-def given_i_have_a_admin_user(step):
-    admin = Group.objects.create(name='mics_admin')
-    world.user = User.objects.create_user('rajni', 'Rajni@kant.com', 'I_Rock')
-    admin.user_set.add(world.user)
-    set_permissions(admin, ['can_view_aggregates', 'can_view_households',
-                            'can_view_batches', 'can_view_investigators', 'can_view_users'])
-
-
-@step(u'Then I should all tabs')
-def then_i_should_all_tabs(step):
-    world.page.check_all_tabs()
-
-
-@step(u'Then I should not see notify investigators drop-down')
-def then_i_should_not_see_notify_investigators_drop_down(step):
-    world.page.check_notify_investigators_drop_down_is_not_present()
diff --git a/survey/features/permissions.feature b/survey/features/permissions.feature
deleted file mode 100644
index 10c3e90d..00000000
--- a/survey/features/permissions.feature
+++ /dev/null
@@ -1,33 +0,0 @@
-Feature: Permissions feature
-
-  Scenario: Anonymous user tabs
-      Given I  am an anonymous user
-      And I visit the login page
-      Then I should see anonymous user allowed tabs
-      And I should not be seeing above anonymous user level tabs
-
-  Scenario: Data entry Tabs
-      Given I have a data entry user
-      And And I visit the login page
-      And I login that user
-      Then I should see data entry allowed tabs
-      And I should not be seeing above data entry level tabs
-
-  Scenario: Researcher Tabs
-      Given I have a researcher user
-      And And I visit the login page
-      And I login that user
-      Then I should see researcher allowed tabs
-      And I should not be seeing above researcher level tabs
-
-  Scenario: Admin Tabs
-      Given I have a admin user
-      And And I visit the login page
-      And I login that user
-      Then I should all tabs
-
-  Scenario: Notify investigators
-      Given I have a data entry user
-      And And I visit the login page
-      And I login that user
-      Then I should not see notify investigators drop-down
\ No newline at end of file
diff --git a/survey/features/question_module-steps.py b/survey/features/question_module-steps.py
deleted file mode 100644
index 84897ed4..00000000
--- a/survey/features/question_module-steps.py
+++ /dev/null
@@ -1,86 +0,0 @@
-from lettuce import step, world
-from survey.features.page_objects.question_module import QuestionModuleList, NewQuestionModule, EditQuestionModulePage
-from survey.models import QuestionModule
-
-
-@step(u'And I have two question modules')
-def and_i_have_two_question_modules(step):
-    world.health_module = QuestionModule.objects.create(name="Health")
-    world.education_module = QuestionModule.objects.create(name="Education")
-
-
-@step(u'When I visit the list questions modules page')
-def when_i_visit_the_list_questions_modules_page(step):
-    world.page = QuestionModuleList(browser=world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see the questions modules listed')
-def then_i_should_see_the_questions_modules_listed(step):
-    fields = [world.health_module.name, world.education_module.name,
-              'Number', 'Module Name', 'Module Lists']
-    world.page.validate_fields_present(fields)
-
-
-@step(u'When I visit the create questions module page')
-def when_i_visit_the_create_questions_module_page(step):
-    world.page = NewQuestionModule(world.browser)
-    world.page.visit()
-    world.page.validate_url()
-
-
-@step(u'And I fill in the question module details')
-def and_i_fill_in_the_question_module_details(step):
-    world.page.fill_valid_values({'name': 'Education'})
-
-
-@step(u'Then I should see that the question module on the listing page')
-def then_i_should_see_that_the_question_module_on_the_listing_page(step):
-    world.page = QuestionModuleList(browser=world.browser)
-    fields = ['Education', 'Number', 'Module Name', 'Module Lists']
-    world.page.validate_fields_present(fields)
-
-
-@step(u'And I click delete module')
-def and_i_click_delete_module(step):
-    world.page.click_by_css("#delete-question-module_%s" %
-                            world.health_module.id)
-
-
-@step(u'I should see a delete module confirmation modal')
-def i_should_see_a_confirmation_modal(step):
-    world.page.see_confirm_modal_message(world.health_module.name)
-
-
-@step(u'When I confirm delete')
-def when_i_confirm_delete(step):
-    world.page.click_by_css("#delete-module-%s" % world.health_module.id)
-
-
-@step(u'Then I should see the module was deleted')
-def then_i_should_see_the_module_was_deleted(step):
-    world.page.see_success_message("Module", "deleted")
-
-
-@step(u'And I click edit module')
-def and_i_click_edit_module(step):
-    world.page.click_by_css("#edit-module_%s" % world.health_module.id)
-
-
-@step(u'I should see a edit module page')
-def i_should_see_a_edit_module_page(step):
-    world.page = EditQuestionModulePage(world.browser, world.health_module)
-    world.page.validate_url()
-
-
-@step(u'When I fill in valid values')
-def when_i_fill_in_valid_values(step):
-    world.page.fill_valid_values({'name': 'Edited Module'})
-
-
-@step(u'Then I should see the edited question module')
-def then_i_should_see_the_edited_question_module(step):
-    world.page = QuestionModuleList(world.browser)
-    assert not world.page.browser.find_link_by_text(world.health_module.name)
-    world.page.is_text_present('Edited Module')
-    world.page.see_success_message("Question module", "edited")
diff --git a/survey/features/question_module.feature b/survey/features/question_module.feature
deleted file mode 100644
index 86ba122c..00000000
--- a/survey/features/question_module.feature
+++ /dev/null
@@ -1,33 +0,0 @@
-Feature: Question module
-
-    Scenario: List Question modules
-      Given I am logged in as researcher
-      And I have two question modules
-      When I visit the list questions modules page
-      Then I should see the questions modules listed
-
-    Scenario: Create a Question module
-      Given I am logged in as researcher
-      When I visit the create questions module page
-      And I fill in the question module details
-      And I submit the form
-      Then I should see that the question module on the listing page
-
-    Scenario: Delete a Question module
-      Given I am logged in as researcher
-      And I have two question modules
-      When I visit the list questions modules page
-      And I click delete module
-      I should see a delete module confirmation modal
-      When I confirm delete
-      Then I should see the module was deleted
-
-    Scenario: Edit a Question module
-      Given I am logged in as researcher
-      And I have two question modules
-      When I visit the list questions modules page
-      And I click edit module
-      I should see a edit module page
-      When I fill in valid values
-      And I submit the form
-      Then I should see the edited question module
\ No newline at end of file
diff --git a/survey/features/rule_to_batch-steps.py b/survey/features/rule_to_batch-steps.py
deleted file mode 100644
index 3599110f..00000000
--- a/survey/features/rule_to_batch-steps.py
+++ /dev/null
@@ -1,364 +0,0 @@
-from time import sleep
-from lettuce import *
-from survey.features.page_objects.question import BatchQuestionsListPage
-from survey.features.page_objects.rules import AddLogicToBatchQuestionPage
-from survey.models import Question, AnswerRule, BatchQuestionOrder
-from survey.models.question import QuestionOption
-
-
-def save_batch_to_question(question, batch):
-    question.batches.add(batch)
-    BatchQuestionOrder.objects.create(question=question, batch=batch, order=1)
-
-
-@step(u'And I have a question')
-def and_i_have_a_question(step):
-    world.question = Question.objects.create(text="question1", answer_type=Question.NUMBER, order=1,
-                                             group=world.household_member_group, module=world.module, identifier='ID 1')
-
-
-@step(u'And I assign batch to these questions')
-def and_i_assign_batch_to_these_questions(step):
-    save_batch_to_question(world.question, world.batch)
-
-
-@step(u'And I visit batches question list page')
-def and_i_visit_batches_question_list_page(step):
-    world.page = BatchQuestionsListPage(world.browser, world.batch)
-    world.page.visit()
-
-
-@step(u'And I click on add logic link')
-def and_i_click_on_add_logic_link(step):
-    sleep(5)
-    world.page.click_link_by_text(" Add Logic")
-
-
-@step(u'Then I should see the add logic page')
-def then_i_should_see_the_add_logic_page(step):
-    world.page = AddLogicToBatchQuestionPage(
-        world.browser, world.batch, world.question)
-    world.page.validate_url()
-    world.page.validate_fields()
-
-
-@step(u'When I fill in skip rule details')
-def when_i_fill_in_skip_rule_details(step):
-    form_data = {'condition': 'EQUALS',
-                 'attribute': 'value',
-                 'value': '0',
-                 'action': 'END_INTERVIEW',
-                 }
-    world.page.fill_valid_values(form_data)
-
-
-@step(u'Then I should see the logic was successfully added to the question')
-def then_i_should_see_the_logic_was_successfully_added_to_the_question(step):
-    world.page.see_success_message('Logic', 'added')
-
-
-@step(u'And I assign batch to multichoice question')
-def and_i_assign_batch_to_multichoice_question(step):
-    world.question = world.multi_choice_question
-    save_batch_to_question(world.question, world.batch)
-
-
-@step(u'And I should see in multichoice if field defaulted to equals option')
-def and_i_should_see_if_field_defaulted_to_equals_option(step):
-    form_data = {'condition': 'EQUALS_OPTION'}
-    world.page.validate_form_values(form_data)
-
-
-@step(u'And I should see if field is disabled')
-def and_i_should_see_if_field_is_disabled(step):
-    assert world.page.is_disabled("id_condition")
-
-
-@step(u'And I should see attribute field defaulted to option')
-def and_i_should_see_attribute_field_defaulted_to_option(step):
-    form_data = {'attribute': 'option'}
-    world.page.validate_form_values(form_data)
-
-
-@step(u'And I should see attribute field is disabled')
-def and_i_should_see_attribute_field_is_disabled(step):
-    assert world.page.is_disabled("id_attribute")
-
-
-@step(u'And I should see dropdown of all available options')
-def and_i_should_see_dropdown_of_all_available_options(step):
-    options = [option.text for option in QuestionOption.objects.filter(
-        question=world.question)]
-    world.page.see_select_option(options, 'option')
-
-
-@step(u'And I should not see value text box and questions dropdown')
-def and_i_should_not_see_value_text_box_and_questions_dropdown(step):
-    world.page.field_not_present('value')
-    world.page.field_not_present('validate_with_question')
-
-
-@step(u'When I select option from dropdown')
-def when_i_select_option_from_dropdown(step):
-    world.page.select('option', [world.option1.id])
-
-
-@step(u'And I select skip to from then field')
-def and_i_select_skip_to_from_then_field(step):
-    world.page.select('action', ['SKIP_TO'])
-
-
-@step(u'Then I should see field for next question next to the then field')
-def then_i_should_see_field_for_next_question_next_to_the_then_field(step):
-    assert world.page.field_is_visible('next_question')
-
-
-@step(u'When I select end interview from then field')
-def when_i_select_end_interview_from_then_field(step):
-    world.page.select('action', ['END_INTERVIEW'])
-
-
-@step(u'Then I should not see field for next question next to the then field')
-def then_i_should_not_see_field_for_next_question_next_to_the_then_field(step):
-    assert not world.page.field_is_visible('next_question')
-
-
-@step(u'And I should see if field defaulted to equals')
-def and_i_should_see_if_field_defaulted_to_equals(step):
-    form_data = {'condition': 'EQUALS'}
-    world.page.validate_form_values(form_data)
-
-
-@step(u'And I should see if field is not disabled')
-def and_i_should_see_if_field_is_not_disabled(step):
-    assert not world.page.is_disabled("id_condition")
-
-
-@step(u'And I should also have all other conditions in the dropdown')
-def and_i_should_also_have_all_other_conditions_in_the_dropdown(step):
-    condition_options = ['> QUESTION RESPONSE', '> VALUE', '< QUESTION RESPONSE', '< VALUE',
-                         'EQUALS']
-    world.page.see_select_option(condition_options, 'condition')
-
-
-@step(u'And I should see attribute field defaulted to value')
-def and_i_should_see_attribute_field_defaulted_to_value(step):
-    form_data = {'attribute': 'value'}
-    world.page.validate_form_values(form_data)
-
-
-@step(u'And I should also have question in the attribute field dropdown')
-def and_i_should_also_have_question_in_the_attribute_field_dropdown(step):
-    world.page.see_select_option(['Value', 'Question'], 'attribute')
-
-
-@step(u'And I should see attribute field is not disabled')
-def and_i_should_see_attribute_field_is_not_disabled(step):
-    assert not world.page.is_disabled("id_attribute")
-
-
-@step(u'And I should see value text field')
-def and_i_should_see_value_text_field(step):
-    world.browser.find_by_name('value')
-    assert world.page.field_is_visible('value')
-
-
-@step(u'And I should not see option dropdown box and questions dropdown')
-def and_i_should_not_see_option_dropdown_box_and_questions_dropdown(step):
-    world.page.field_not_present('option')
-    assert not world.page.field_is_visible('validate_with_question')
-
-
-@step(u'When I select question option from dropdown')
-def when_i_select_question_option_from_dropdown(step):
-    world.page.select('attribute', ['validate_with_question'])
-
-
-@step(u'Then I should see questions dropdown')
-def then_i_should_see_questions_dropdown(step):
-    assert world.page.field_is_visible('validate_with_question')
-
-
-@step(u'And I should not see option dropdown box and value text box')
-def and_i_should_not_see_option_dropdown_box_and_value_text_box(step):
-    world.page.field_not_present('option')
-    assert not world.page.field_is_visible('value')
-
-
-@step(u'And I should see all the action dropdown options')
-def and_i_should_see_all_the_action_dropdown_options(step):
-    action_options = ['RECONFIRM', 'END INTERVIEW',
-                      'ASK SUBQUESTION', 'SKIP TO']
-    world.page.see_select_option(action_options, 'action')
-
-
-@step(u'And I have two subquestions for this question')
-def and_i_have_two_subquestions_for_this_question(step):
-    world.sub_question1 = Question.objects.create(text="sub question1", answer_type=Question.NUMBER, subquestion=True,
-                                                  parent=world.question, identifier='ID 2')
-    world.sub_question2 = Question.objects.create(text="sub question2", answer_type=Question.NUMBER, subquestion=True,
-                                                  parent=world.question, identifier='ID 3')
-    world.sub_question1.batches.add(world.batch)
-    world.sub_question2.batches.add(world.batch)
-
-
-@step(u'When I select ask subquestion from then field')
-def when_i_select_ask_subquestion_from_then_field(step):
-    world.page.select('action', ['ASK_SUBQUESTION'])
-
-
-@step(u'Then I should see next question populated with subquestions')
-def then_i_should_see_next_question_populated_with_subquestions(step):
-    next_question_options = [
-        world.sub_question1.text, world.sub_question2.text]
-    world.page.see_select_option(next_question_options, 'next_question')
-
-
-@step(u'Then I should see add subquestion button')
-def then_i_should_see_add_subquestion_button(step):
-    world.page.find_link_by_text("Add Subquestion")
-
-
-@step(u'When I click add subquestion button')
-def when_i_click_add_subquestion_button(step):
-    world.page.click_link_by_partial_href("#add_sub_question_modal")
-
-
-@step(u'Then I should see a modal for add subquestion')
-def then_i_should_see_a_modal_for_add_subquestion(step):
-    world.page.validate_fields_present(
-        ["New Sub Question", "Text", "Group", "Answer type"])
-
-
-@step(u'When I fill the subquestion details')
-def when_i_fill_the_subquestion_details(step):
-    world.data = {'text': 'hritik question',
-                  'answer_type': Question.NUMBER,
-                  'identifier': 'Q234'}
-
-    world.page.fill_valid_values(world.data)
-
-
-@step(u'And I click save question button on the form')
-def and_i_click_save_question_button_on_the_form(step):
-    world.page.click_by_css('#modal_sub_question_button')
-
-
-@step(u'Then I should see the recent subquestion in next question dropdown')
-def then_i_should_see_the_recent_subquestion_in_next_question_dropdown(step):
-    sleep(2)
-    world.page.see_select_option(world.data['text'], 'next_question')
-
-
-@step(u'And I should not see the add subquestion button')
-def and_i_should_not_see_the_add_subquestion_button(step):
-    assert not world.page.field_is_visible("add_subquestion_button")
-
-
-@step(u'And I should not the add subquestion button')
-def and_i_should_not_the_add_subquestion_button(step):
-    assert world.page.field_is_visible("add_subquestion_button")
-
-
-@step(u'When I select greater than value from the drop down')
-def when_i_select_greater_than_value_from_the_drop_down(step):
-    world.page.select('condition', ['GREATER_THAN_VALUE'])
-
-
-@step(u'Then I should see attribute field defaulted to value')
-def then_i_should_see_attribute_field_defaulted_to_value(step):
-    form_data = {'attribute': 'value'}
-    world.page.validate_form_values(form_data)
-
-
-@step(u'And I should not see question in the attribute')
-def and_i_should_not_see_question_in_the_attribute(step):
-    world.page.option_not_present(['Question'], 'attribute')
-
-
-@step(u'When I select less than value from the drop down')
-def when_i_select_less_than_value_from_the_drop_down(step):
-    world.page.select('condition', ['LESS_THAN_VALUE'])
-
-
-@step(u'When I select greater than question from the drop down')
-def when_i_select_greater_than_question_from_the_drop_down(step):
-    world.page.select('condition', ['GREATER_THAN_QUESTION'])
-
-
-@step(u'Then I should see attribute field defaulted to question')
-def then_i_should_see_attribute_field_defaulted_to_question(step):
-    form_data = {'attribute': 'validate_with_question'}
-    world.page.validate_form_values(form_data)
-
-
-@step(u'And I should not see value in the attribute')
-def and_i_should_not_see_value_in_the_attribute(step):
-    world.page.option_not_present(['Value'], 'attribute')
-
-
-@step(u'When I select less than question from the drop down')
-def when_i_select_less_than_question_from_the_drop_down(step):
-    world.page.select('condition', ['LESS_THAN_QUESTION'])
-
-
-@step(u'When I select equals from drop down')
-def when_i_select_equals_from_drop_down(step):
-    world.page.select('condition', ['EQUALS'])
-
-
-@step(u'And I should see question in the attribute')
-def and_i_should_see_question_in_the_attribute(step):
-    world.page.see_select_option(['Question'], 'attribute')
-
-
-@step(u'And I have a subquestion under this question')
-def and_i_have_a_subquestion_under_this_question(step):
-    world.sub_question = Question.objects.create(subquestion=True, parent=world.question,
-                                                 text="this is a subquestion", identifier='ID 4')
-
-
-@step(u'When I fill the  duplicate subquestion details')
-def when_i_fill_the_duplicate_subquestion_details(step):
-    world.page.fill_valid_values({'text': world.sub_question.text,
-                                  'identifier': world.sub_question.identifier})
-    world.page.select('group', [world.household_member_group.pk])
-    world.page.select('answer_type', [Question.NUMBER])
-
-
-@step(u'And I should see error on the form text field')
-def and_i_should_see_error_on_the_form_text_field(step):
-    sleep(5)
-    world.page.is_text_present(
-        "Sub question for this question with this text already exists.")
-
-
-@step(u'When I refill the form with valid values')
-def when_i_refill_the_form_with_valid_values(step):
-    world.data = {
-        'text': world.sub_question.text + "edited text",
-        'answer_type': Question.NUMBER,
-        'identifier': 'ID 5',
-        'group': world.household_member_group.pk
-    }
-    world.page.fill_valid_values(
-        {'text': world.data['text'], 'identifier': world.data['identifier']})
-    world.page.select('group', [world.data['group']])
-    world.page.select('answer_type', [world.data['answer_type']])
-
-
-@step(u'And I should see already existing logic for the question')
-def and_i_should_see_already_existing_logic_for_the_question(step):
-    world.page.validate_fields_present(
-        [world.question.text, "Eligibility Criteria", "Question/Value/Option", "Action"])
-
-
-@step(u'When I select between from the drop down')
-def when_i_select_between_from_the_drop_down(step):
-    world.page.select('condition', ['BETWEEN'])
-
-
-@step(u'And I should see two text fields for min and max value')
-def and_i_should_see_two_text_fields_for_min_and_max_value(step):
-    assert world.browser.find_by_css('#id_min_value')
-    assert world.browser.find_by_css('#id_max_value')
diff --git a/survey/features/rules_to_batch.feature b/survey/features/rules_to_batch.feature
deleted file mode 100644
index f338e533..00000000
--- a/survey/features/rules_to_batch.feature
+++ /dev/null
@@ -1,158 +0,0 @@
-Feature: Batch related features
-
-  Scenario: Add new answer rule to batch question
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have a member group
-    And I have a module
-    And I have a question
-    And I assign batch to these questions
-    And I visit batches question list page
-    And I click on add logic link
-    Then I should see the add logic page
-    And I should see already existing logic for the question
-    When I fill in skip rule details
-    And I submit the form
-    Then I should see the logic was successfully added to the question
-
-  Scenario: Add answer rule to multichoice question - Javascript
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have a module
-    And I have a member group
-    And I have a multichoice question
-    And I assign batch to multichoice question
-    And I visit batches question list page
-    And I click on add logic link
-    Then I should see the add logic page
-    And I should see in multichoice if field defaulted to equals option
-    And I should see if field is disabled
-    And I should see attribute field defaulted to option
-    And I should see attribute field is disabled
-    And I should see dropdown of all available options
-    And I should not see value text box and questions dropdown
-    When I select option from dropdown
-    And I select skip to from then field
-    Then I should see field for next question next to the then field
-    When I select end interview from then field
-    Then I should not see field for next question next to the then field
-
-  Scenario: Add answer rule to question which is not multichoice - Javascript
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have a member group
-    And I have a module
-    And I have a question
-    And I assign batch to these questions
-    And I visit batches question list page
-    And I click on add logic link
-    Then I should see the add logic page
-    And I should see if field defaulted to equals
-    And I should see if field is not disabled
-    And I should also have all other conditions in the dropdown
-    And I should see attribute field defaulted to value
-    And I should also have question in the attribute field dropdown
-    And I should see attribute field is not disabled
-    And I should see value text field
-    When I select greater than value from the drop down
-    Then I should see attribute field defaulted to value
-    And I should not see question in the attribute
-    When I select less than value from the drop down
-    Then I should see attribute field defaulted to value
-    And I should not see question in the attribute
-    When I select greater than question from the drop down
-    Then I should see attribute field defaulted to question
-    And I should not see value in the attribute
-    When I select less than question from the drop down
-    Then I should see attribute field defaulted to question
-    And I should not see value in the attribute
-    When I select equals from drop down
-    And I should see attribute field defaulted to value
-    And I should see question in the attribute
-    And I should not see option dropdown box and questions dropdown
-    When I select question option from dropdown
-    Then I should see questions dropdown
-    And I should not see option dropdown box and value text box
-    And I should see all the action dropdown options
-
-   Scenario: Add Between condition- Javascript
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have a member group
-    And I have a module
-    And I have a question
-    And I assign batch to these questions
-    And I visit batches question list page
-    And I click on add logic link
-    Then I should see the add logic page
-    When I select between from the drop down
-    Then I should see attribute field defaulted to value
-    And I should not see question in the attribute
-    And I should see two text fields for min and max value
-    And I should not see option dropdown box and value text box
-
-
-  Scenario: add rule ask subquestion to a question
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have a member group
-    And I have a module
-    And I have a question
-    And I assign batch to these questions
-    And I have two subquestions for this question
-    And I visit batches question list page
-    And I click on add logic link
-    Then I should see the add logic page
-    And I should not see the add subquestion button
-    When I select ask subquestion from then field
-    Then I should see next question populated with subquestions
-    And I should not the add subquestion button
-
-  Scenario: Add subquestion modal
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have a member group
-    And I have a module
-    And I have a question
-    And I assign batch to these questions
-    And I visit batches question list page
-    And I click on add logic link
-    Then I should see the add logic page
-    When I select ask subquestion from then field
-    Then I should see add subquestion button
-    When I click add subquestion button
-    Then I should see a modal for add subquestion
-    And I click save question button on the form
-    Then I should see field required error message
-    When I fill the subquestion details
-    And I click save question button on the form
-    Then I should see the recent subquestion in next question dropdown
-
-  Scenario: Add duplicate subquestion on modal
-    Given I am logged in as researcher
-    And I have a survey
-    And I have a batch
-    And I have a member group
-    And I have a module
-    And I have a question
-    And I assign batch to these questions
-    And I have a subquestion under this question
-    And I visit batches question list page
-    And I click on add logic link
-    Then I should see the add logic page
-    When I select ask subquestion from then field
-    Then I should see add subquestion button
-    When I click add subquestion button
-    Then I should see a modal for add subquestion
-    When I fill the  duplicate subquestion details
-    And I click save question button on the form
-    And I should see error on the form text field
-    When I refill the form with valid values
-    And I click save question button on the form
-    Then I should see the recent subquestion in next question dropdown
\ No newline at end of file
diff --git a/survey/features/simple_indicator_chart.feature b/survey/features/simple_indicator_chart.feature
deleted file mode 100644
index c5c5c584..00000000
--- a/survey/features/simple_indicator_chart.feature
+++ /dev/null
@@ -1,27 +0,0 @@
-Feature: Simple indicator results
-
-    Background:
-        Given I am logged in as researcher
-        And I have a survey
-        And I have three batches
-        And I have two modules
-
-    Scenario: Simple Indicator - Transition from List to analysis
-        And I have regions and districts
-        And I have investigators in those districts
-        And I have households in in those districts
-        And I have a multichoice  question
-        And I have an indicator in that survey
-        And I have responses for that question
-        Given I am on the indicator listing page
-        And I click the analysis link of the indicator
-        Then I should see indicator result page
-        And I should see indicator graph for the country
-        And I should see indicator data table for the country
-
-    Scenario: Simple Indicator - Transition from List to analysis when no formula is added
-        And I have regions and districts
-        And I have an indicator with out a formula
-        Given I am on the indicator listing page
-        And I click that indicators analysis link
-        Then I should see indicator has no formula message
\ No newline at end of file
diff --git a/survey/features/simple_indicator_chart_step.py b/survey/features/simple_indicator_chart_step.py
deleted file mode 100644
index 95637e34..00000000
--- a/survey/features/simple_indicator_chart_step.py
+++ /dev/null
@@ -1,185 +0,0 @@
-from random import randint
-from lettuce import *
-from survey.models.locations import *
-from survey.features.page_objects.indicators import ListIndicatorPage, SimpleIndicatorGraphPage
-from survey.models import HouseholdMemberGroup, Indicator, LocationTypeDetails, EnumerationArea
-from survey.models.households import HouseholdHead, Household
-from survey.models.backend import Backend
-from survey.models.interviewer import Interviewer
-from survey.models.formula import *
-from survey.models.questions import Question, QuestionOption
-
-
-def create_household_head(uid, investigator, household_listing, survey_householdlisting):
-    household = Household.objects.create(house_number=uid, listing=household_listing, physical_address='Test address' + str(randint(1, 9999)),
-                                         last_registrar=investigator, registration_channel="ODK Access", head_desc="Head", head_sex='MALE')
-    return HouseholdHead.objects.create(surname="sur" + str(randint(1, 9999)), first_name='fir' + str(randint(1, 9999)), gender='MALE', date_of_birth="1988-01-01",
-                                        household=household, survey_listing=survey_householdlisting,
-                                        registrar=investigator, registration_channel="ODK Access",
-                                        occupation="Agricultural labor", level_of_education="Primary",
-                                        resident_since='1989-02-02')
-
-
-@step(u'And I have regions and districts')
-def and_i_have_regions_and_districts(step):
-    world.country = LocationType.objects.create(name='Country', slug='country')
-    world.region = LocationType.objects.create(name='Region', slug='region')
-    world.district = LocationType.objects.create(
-        name='District', slug='district')
-    world.village = LocationType.objects.create(name='Village', slug='village')
-
-    world.uganda = Location.objects.create(name="Uganda", type=world.country)
-
-    LocationTypeDetails.objects.create(
-        location_type=world.country, country=world.uganda)
-    LocationTypeDetails.objects.create(
-        location_type=world.region, country=world.uganda)
-    LocationTypeDetails.objects.create(
-        location_type=world.district, country=world.uganda)
-    LocationTypeDetails.objects.create(
-        location_type=world.village, country=world.uganda)
-
-    world.west = Location.objects.create(
-        name="WEST", type=world.region, tree_parent=world.uganda)
-    world.central = Location.objects.create(
-        name="CENTRAL", type=world.region, tree_parent=world.uganda)
-    world.kampala = Location.objects.create(
-        name="Kampala", tree_parent=world.central, type=world.district)
-    world.mbarara = Location.objects.create(
-        name="Mbarara", tree_parent=world.west, type=world.district)
-
-    world.kibungo = Location.objects.create(
-        name="Kibungo", type=world.district, tree_parent=world.west)
-    world.mpigi = Location.objects.create(
-        name="Mpigi", type=world.district, tree_parent=world.central)
-
-    world.ea = EnumerationArea.objects.get_or_create(name="EA")[0]
-    world.ea.locations.add(world.kampala)
-
-    world.ea_mbarara = EnumerationArea.objects.get_or_create(name="EA2")[0]
-    world.ea_mbarara.locations.add(world.mbarara)
-
-
-@step(u'And I have an indicator in that survey')
-def and_i_have_an_indicator_in_that_survey(step):
-    world.indicator = Indicator.objects.create(name="ITN 1.23", description="rajni indicator",
-                                               measure='Percentage',
-                                               batch=world.batch_1, module=world.health_module_1)
-
-    world.formula = Formula.objects.create(
-        count=world.question_3, indicator=world.indicator)
-
-
-@step(u'And I have a multichoice  question')
-def and_i_have_a_multichoice_question(step):
-    world.member_group = HouseholdMemberGroup.objects.create(
-        name="GENERAL", order=1)
-    world.question_3 = Question.objects.create(text="This is a question", module=world.health_module_1,
-                                               answer_type=Question.MULTICHOICE, order=3, group=world.member_group)
-
-    world.yes_option = QuestionOption.objects.create(
-        question=world.question_3, text="Yes", order=1)
-    world.no_option = QuestionOption.objects.create(
-        question=world.question_3, text="No", order=2)
-    world.question_3.batches.add(world.batch_1)
-    BatchQuestionOrder.objects.create(
-        question=world.question_3, batch=world.batch_1, order=1)
-
-
-@step(u'And I have households in in those districts')
-def and_i_have_households_in_in_those_districts(step):
-    world.household_head_1 = create_household_head(0, world.investigator)
-    world.household_head_2 = create_household_head(1, world.investigator)
-    world.household_head_3 = create_household_head(2, world.investigator)
-    world.household_head_4 = create_household_head(3, world.investigator)
-    world.household_head_5 = create_household_head(4, world.investigator)
-
-    world.household_head_6 = create_household_head(5, world.investigator_2)
-    world.household_head_7 = create_household_head(6, world.investigator_2)
-    world.household_head_8 = create_household_head(7, world.investigator_2)
-    world.household_head_9 = create_household_head(8, world.investigator_2)
-
-
-@step(u'And I have investigators in those districts')
-def and_i_have_investigators_in_those_districts(step):
-    backend = Backend.objects.create(name="Backend")
-    world.investigator = Investigator.objects.create(name="Investigator 1", mobile_number="1", ea=world.ea,
-                                                     backend=backend)
-    world.investigator_2 = Investigator.objects.create(name="Investigator 1", mobile_number="33331",
-                                                       ea=world.ea_mbarara,
-                                                       backend=backend)
-
-
-@step(u'And I have responses for that question')
-def and_i_have_responses_for_that_question(step):
-    world.investigator.member_answered(
-        world.question_3, world.household_head_1, world.yes_option.order, world.batch_1)
-    world.investigator.member_answered(
-        world.question_3, world.household_head_2, world.yes_option.order, world.batch_1)
-    world.investigator.member_answered(
-        world.question_3, world.household_head_3, world.yes_option.order, world.batch_1)
-    world.investigator.member_answered(
-        world.question_3, world.household_head_4, world.no_option.order, world.batch_1)
-    world.investigator.member_answered(
-        world.question_3, world.household_head_5, world.no_option.order, world.batch_1)
-
-    world.investigator_2.member_answered(
-        world.question_3, world.household_head_6, world.yes_option.order, world.batch_1)
-    world.investigator_2.member_answered(
-        world.question_3, world.household_head_7, world.yes_option.order, world.batch_1)
-    world.investigator_2.member_answered(
-        world.question_3, world.household_head_8, world.no_option.order, world.batch_1)
-    world.investigator_2.member_answered(
-        world.question_3, world.household_head_9, world.no_option.order, world.batch_1)
-
-
-@step(u'Given I am on the indicator listing page')
-def given_i_am_on_the_indicator_listing_page(step):
-    world.page = ListIndicatorPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I click the analysis link of the indicator')
-def and_i_click_the_analysis_link_of_the_indicator(step):
-    world.page.click_by_css("#analyse-indicator_%s" % world.indicator.id)
-
-
-@step(u'Then I should see indicator result page')
-def then_i_should_see_indicator_result_page(step):
-    world.page = SimpleIndicatorGraphPage(world.browser, world.indicator)
-    world.page.validate_url()
-
-
-@step(u'And I should see indicator graph for the country')
-def and_i_should_see_indicator_graph_for_the_country(step):
-    world.page.validate_fields_present(
-        ['Yes', 'No', '%s Count per %s' % (world.indicator.name, world.region.name)])
-
-
-@step(u'And I should see indicator data table for the country')
-def and_i_should_see_indicator_data_table_for_the_country(step):
-    world.page.validate_fields_present(
-        [world.region.name, world.district.name, world.yes_option.text, world.no_option.text, 'Total'])
-
-    central_tabulated_data_results = ['3', '2', '5']
-    west_tabulated_data_results = ['2', '2', '4']
-    world.page.validate_fields_present(central_tabulated_data_results)
-    world.page.validate_fields_present(west_tabulated_data_results)
-
-
-@step(u'And I have an indicator with out a formula')
-def and_i_have_an_indicator_with_out_a_formula(step):
-    world.indicator_with_no_formula = Indicator.objects.create(name="ITN 1.23", description="rajni indicator",
-                                                               measure='Percentage',
-                                                               batch=world.batch_1, module=world.health_module_1)
-
-
-@step(u'Then I should see indicator has no formula message')
-def then_i_should_see_indicator_has_no_formula_message(step):
-    world.page.see_message("No formula was found in this indicator")
-
-
-@step(u'And I click that indicators analysis link')
-def and_i_click_that_indicators_analysis_link(step):
-    world.page.click_by_css("#analyse-indicator_%s" %
-                            world.indicator_with_no_formula.id)
diff --git a/survey/features/survey.feature b/survey/features/survey.feature
deleted file mode 100644
index 03630ac3..00000000
--- a/survey/features/survey.feature
+++ /dev/null
@@ -1,55 +0,0 @@
-Feature: Survey features
-
-  Scenario: List survey page
-    Given I am logged in as researcher
-    And I have 100 surveys
-    And I visit surveys listing page
-    Then I should see the survey list paginated
-    And when I click on add batch action for first survey
-    Then I should go to add batch page
-
-  Scenario: Link to batch list
-    Given I am logged in as researcher
-    And I have a survey
-    And I visit surveys listing page
-    And I click on a survey name
-    Then I should see a list of the batches under the survey
-
-  Scenario: Add a survey
-    Given I am logged in as researcher
-    And I visit the new survey page
-    When I fill in the survey details
-    And I click save button
-    Then I should see that the survey was saved successfully
-
-  Scenario: Add survey modal
-    Given I am logged in as researcher
-    And I visit surveys listing page
-    And I click on create new survey button
-    Then I should see the create new survey modal
-    When I fill in the survey details
-    And I click the modal save button
-    Then I should see that the survey was saved successfully
-
-  Scenario: Edit a survey
-    Given I am logged in as researcher
-    And I have a survey
-    And I visit surveys listing page
-    And I click on edit link for this survey
-    Then I should see the edit survey page
-    When I fill in the survey details
-    And I submit the form
-    Then I should see that the survey was edited successfully
-
-  Scenario: Delete a survey
-    Given I am logged in as researcher
-    And I have a survey
-    And I visit surveys listing page
-    And I click on delete link for this survey
-    Then I should see confirm delete survey
-    And if I click yes
-    Then I should go back to survey listing page
-    And I should see that the survey was deleted successfully
-
-
-
diff --git a/survey/features/survey_steps.py b/survey/features/survey_steps.py
deleted file mode 100644
index ba9d5a9e..00000000
--- a/survey/features/survey_steps.py
+++ /dev/null
@@ -1,153 +0,0 @@
-from random import randint
-
-from lettuce import *
-from survey.features.page_objects.batches import BatchListPage, AddBatchPage
-from survey.models.surveys import Survey
-from survey.features.page_objects.surveys import SurveyListPage, AddSurveyPage, EditSurveyPage
-
-
-@step(u'And I visit surveys listing page')
-def and_i_visit_surveys_listing_page(step):
-    world.page = SurveyListPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I have 100 surveys')
-def and_i_have_100_surveys(step):
-    world.survey = list()
-    for i in xrange(100):
-        try:
-            world.survey.append(Survey.objects.create(name='survey %d' % i, description='survey descrpition %d' % i,
-                                                      type=(True if i % 2 else False), sample_size=i))
-        except Exception:
-            pass
-
-
-@step(u'Then I should see the survey list paginated')
-def then_i_should_see_the_survey_list_paginated(step):
-    world.page.validate_fields()
-    world.page.validate_pagination()
-    world.page.validate_fields()
-
-
-@step(u'And if I click the add survey button')
-def and_if_i_click_the_add_survey_button(step):
-    world.page.click_link_by_text("Create New Survey")
-
-
-@step(u'Then I should see the new survey form')
-def then_i_should_see_the_new_survey_form(step):
-    world.page = AddSurveyPage(world.browser)
-    world.page.validate_url()
-    world.page.validate_fields_present(
-        ["New Survey", "Name", "Description", "Type", "Number of Households"])
-
-
-@step(u'And I visit the new survey page')
-def and_i_visit_the_new_survey_page(step):
-    world.page = AddSurveyPage(world.browser)
-    world.page.visit()
-
-
-@step(u'When I fill in the survey details')
-def when_i_fill_in_the_survey_details(step):
-    world.data = {'name': 'survey rajni',
-                  'description': 'survey description rajni',
-                  'sample_size': 10,
-                  'type': True,
-                  }
-    world.page.fill_valid_values(world.data)
-
-
-@step(u'And I select the questions')
-def and_i_select_the_questions(step):
-    world.page.select_multiple('#id_questions', world.question)
-
-
-@step(u'Then I should see that the survey was saved successfully')
-def then_i_should_see_that_the_survey_was_saved_successfully(step):
-    world.page.see_success_message('Survey', 'added')
-
-
-@step(u'And I have a survey')
-def and_i_have_a_survey(step):
-    world.survey = Survey.objects.create(
-        name='survey name', description='survey descrpition', type=False, sample_size=10)
-
-
-@step(u'And I click on a survey name')
-def and_i_click_on_a_survey_name(step):
-    world.page.click_link_by_text(world.survey.name)
-
-
-@step(u'Then I should see a list of the batches under the survey')
-def then_i_should_see_a_list_of_the_batches_under_the_survey(step):
-    world.page = BatchListPage(world.browser, world.survey)
-    world.page.validate_url()
-
-
-@step(u'And I click on create new survey button')
-def and_i_click_on_create_new_survey_button(step):
-    world.page.click_link_by_partial_href("#new_survey")
-
-
-@step(u'Then I should see the create new survey modal')
-def then_i_should_see_the_create_new_survey_modal(step):
-    world.page.validate_fields_present(
-        ["New Survey", "Name", "Description", "Type", "Number of Households"])
-
-
-@step(u'And I click the modal save button')
-def and_i_click_the_modal_save_button(step):
-    world.page.click_button("save_button")
-
-
-@step(u'And when I click on add batch action for first survey')
-def and_when_i_click_on_add_batch_action_for_first_survey(step):
-    world.page = SurveyListPage(world.browser)
-    world.page.visit()
-    world.page.click_by_css(".add_batch")
-
-
-@step(u'Then I should go to add batch page')
-def then_i_should_go_to_add_batch_page(step):
-    world.page = AddBatchPage(world.browser, world.survey[0])
-    world.page.validate_url()
-
-
-@step(u'And I click on edit link for this survey')
-def and_i_click_on_edit_link_for_this_survey(step):
-    world.page.click_link_by_text(" Edit")
-
-
-@step(u'Then I should see the edit survey page')
-def then_i_should_see_the_edit_survey_page(step):
-    world.page = EditSurveyPage(world.browser, world.survey)
-    world.page.validate_url()
-
-
-@step(u'Then I should see that the survey was edited successfully')
-def then_i_should_see_that_the_survey_was_edited_successfully(step):
-    world.page.is_text_present(world.data['name'])
-    world.page.see_success_message("Survey", "edited")
-
-
-@step(u'And I click on delete link for this survey')
-def and_i_click_on_delete_link_for_this_survey(step):
-    world.page.click_link_by_text(" Delete")
-
-
-@step(u'Then I should go back to survey listing page')
-def then_i_should_go_back_to_survey_listing_page(step):
-    world.page = SurveyListPage(world.browser)
-    world.page.validate_url()
-
-
-@step(u'And I should see that the survey was deleted successfully')
-def and_i_should_see_that_the_survey_was_deleted_successfully(step):
-    world.page.see_success_message("Survey", "deleted")
-
-
-@step(u'Then I should see confirm delete survey')
-def then_i_should_see_confirm_delete_survey(step):
-    world.page.see_confirm_modal_message(world.survey.name)
diff --git a/survey/features/upload_location-steps.py b/survey/features/upload_location-steps.py
deleted file mode 100644
index 0549a006..00000000
--- a/survey/features/upload_location-steps.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# -*- coding: utf-8 -*-
-import csv
-from time import sleep
-from django.contrib.auth.models import Group, User, Permission
-from django.contrib.contenttypes.models import ContentType
-from lettuce import step, world
-from rapidsms.contrib.locations.models import LocationType
-from survey.features.page_objects.accounts import LoginPage
-from survey.features.page_objects.location_hierarchy import AddLocationHierarchyPage
-from survey.features.page_objects.uploads import UploadLocationsPage
-from survey.models import LocationTypeDetails
-from survey.tests.base_test import BaseTest
-
-
-def set_permissions(group, permissions_codename_list):
-    auth_content = ContentType.objects.get_for_model(Permission)
-    for codename in permissions_codename_list:
-        permission, out = Permission.objects.get_or_create(
-            codename=codename, content_type=auth_content)
-        group.permissions.add(permission)
-
-
-def create_admin():
-    admin = Group.objects.create(name='mics_admin')
-    user = User.objects.create_user('rajni', 'Rajni@kant.com', 'I_Rock')
-    admin.user_set.add(user)
-    set_permissions(admin, ['can_view_aggregates', 'can_view_households', 'can_view_batches',
-                            'can_view_investigators', 'can_view_users', 'can_add_location_types'])
-
-    return user
-
-
-@step(u'Given I am logged in as admin')
-def given_i_am_logged_in_as_admin(step):
-    user = create_admin()
-    world.page = LoginPage(world.browser)
-    world.page.visit()
-    world.page.login(user)
-
-
-@step(u'When I visit upload locations page')
-def and_i_visit_upload_locations_page(step):
-    world.page = UploadLocationsPage(world.browser)
-    world.page.visit()
-
-
-@step(u'And I have location type and location details objects')
-def and_i_have_location_type_and_location_details_objects(step):
-    world.location_type1 = LocationType.objects.create(
-        name='type1', slug='type1')
-    world.location_type_details1 = LocationTypeDetails.objects.create(required=False, has_code=True,
-                                                                      location_type=world.location_type1,
-                                                                      length_of_code=3, country=world.country)
-
-    world.location_type2 = LocationType.objects.create(
-        name='type2', slug='type2')
-    world.location_type_details2 = LocationTypeDetails.objects.create(
-        required=False, has_code=False, location_type=world.location_type2, country=world.country)
-
-
-@step(u'Then I should see the page title')
-def then_i_should_see_the_text_message(step):
-    world.page.is_text_present('Upload Geographical Locations')
-
-
-@step(u'And I should see name of the country for which details were added')
-def and_i_should_see_name_of_the_country_for_which_details_were_added(step):
-    world.page.find_by_css('#id_country', world.country.name)
-
-
-@step(u'And I should see link for input file format')
-def and_i_should_see_link_for_input_file_format(step):
-    world.page.find_link_by_text("Location Input File Format")
-
-
-@step(u'When I click on the link for input file format')
-def when_i_click_on_the_link_for_input_file_format(step):
-    sleep(3)
-    world.page.click_link_by_partial_href('#collapse_table')
-
-
-@step(u'Then I should see table of all location types')
-def then_i_should_see_table_of_all_location_types(step):
-    world.page.is_text_present(
-        world.location_type1.name.capitalize() + 'Name', True)
-    world.page.is_text_present(
-        world.location_type2.name.capitalize() + 'Name', True)
-
-
-@step(u'And Type code should be in front of any type that has code')
-def and_type_code_should_be_in_front_of_any_type_that_has_code(step):
-    world.page.validate_typecode_appear_before_typename(world.location_type1.name,
-                                                        world.location_type_details1.length_of_code)
-    world.page.is_text_present(
-        world.location_type2.name.capitalize() + 'Code', False)
-
-
-@step(u'Then Table should collapse')
-def then_table_should_collapse(step):
-    sleep(3)
-    world.page.is_text_present(
-        world.location_type1.name.capitalize() + 'Name', False)
-    world.page.is_text_present(
-        world.location_type2.name.capitalize() + 'Name', False)
-
-
-@step(u'Then I should see no hierarchy message')
-def then_i_should_see_no_hierarchy_message(step):
-    world.page.is_text_present('No location hierarchy added yet.')
-
-
-@step(u'And I should see the button to add hierarchy')
-def and_i_should_see_the_button_to_add_hierarchy(step):
-    world.page.find_by_name('add_hierarchy')
-
-
-@step(u'When I click on add hierarchy button')
-def when_i_click_on_add_hierarchy_button(step):
-    world.page.click_by_name('add_hierarchy')
-
-
-@step(u'And I should go to add hierarchy page')
-def and_i_should_go_to_add_hierarchy_page(step):
-    world.page = AddLocationHierarchyPage(world.browser)
-    world.page.validate_url()
-
-
-@step(u'When I have a csv locations file')
-def when_i_have_a_csv_locations_file(step):
-    data = [[world.location_type1.name + 'Code', world.location_type1.name + 'Name', world.location_type2.name + 'Name'],
-            ['001', 'district1', 'county1'],
-            ['003', 'district2', 'county2']]
-    write_to_csv('wb', data, 'test.csv')
-
-
-def write_to_csv(mode, data, csvfilename):
-    with open(csvfilename, mode) as fp:
-        file = csv.writer(fp, delimiter=',')
-        file.writerows(data)
-
-
-@step(u'And I input that file')
-def and_i_input_that_file(step):
-    world.page.input_file('test.csv')
-
-
-@step(u'And I click the save button')
-def when_i_click_the_save_button(step):
-    world.page.submit()
-
-
-@step(u'Then I should see locations uploads processing')
-def then_i_should_see_locations_successfully_added(step):
-    world.page.is_text_present("Upload in progress. This could take a while.")
diff --git a/survey/features/upload_locations.feature b/survey/features/upload_locations.feature
deleted file mode 100644
index 2c66c626..00000000
--- a/survey/features/upload_locations.feature
+++ /dev/null
@@ -1,28 +0,0 @@
-Feature: Location upload
-
-  Scenario: Upload locations
-    Given I am logged in as admin
-    And I have a country
-    And I have location type and location details objects
-    When I visit upload locations page
-    Then I should see the page title
-    And I should see name of the country for which details were added
-    And I should see link for input file format
-    When I click on the link for input file format
-    Then I should see table of all location types
-    And Type code should be in front of any type that has code
-    When I click on the link for input file format
-    Then Table should collapse
-    When I have a csv locations file
-    And I input that file
-    And I click the save button
-    Then I should see locations uploads processing
-
-  Scenario: Upload locations- when no details object present
-    Given I am logged in as admin
-    And I have a country
-    When I visit upload locations page
-    Then I should see no hierarchy message
-    And I should see the button to add hierarchy
-    When I click on add hierarchy button
-    And I should go to add hierarchy page
diff --git a/survey/features/user_settings-steps.py b/survey/features/user_settings-steps.py
deleted file mode 100644
index 2260de3a..00000000
--- a/survey/features/user_settings-steps.py
+++ /dev/null
@@ -1,54 +0,0 @@
-from lettuce import *
-from survey.features.page_objects.accounts import ResetPasswordPage
-
-
-@step(u'And I click user settings link')
-def and_i_click_user_settings_link(step):
-    world.page.click_user_settings()
-
-
-@step(u'Then I see edit profile, change password and logout link')
-def then_i_see_edit_profile_change_password_and_logout_link(step):
-    world.page.assert_user_can_see_profile_and_logout_link()
-
-
-@step(u'Then I click change password link')
-def then_i_click_change_password_link(step):
-    world.page.click_reset_password_form()
-
-
-@step(u'Then I should see a form asking me to add old password and new password')
-def then_i_should_see_a_form_asking_me_to_add_old_password_and_new_password(step):
-    world.page = ResetPasswordPage(world.browser)
-    world.page.visit()
-    world.page.is_change_password_form_visble()
-
-
-@step(u'Then I fill in the old password and new password')
-def then_i_fill_in_the_old_password_and_new_password(step):
-    world.page.fill('old_password', 'kant')
-    world.page.fill('new_password1', 'pass')
-    world.page.fill('new_password2', 'pass')
-
-
-@step(u'And I click the change my password button')
-def and_i_click_the_change_my_password_button(step):
-    world.page.click_change_password_button()
-
-
-@step(u'Then I should see password reset successfully')
-def then_i_should_see_password_reset_successfully(step):
-    world.page.assert_password_successfully_reset()
-
-
-@step(u'Then I fill in the wrong old password and correct new password')
-def then_i_fill_in_the_wrong_old_password_and_correct_new_password(step):
-    world.page.fill('old_password', 'kantus')
-    world.page.fill('new_password1', 'pass')
-    world.page.fill('new_password2', 'pasq')
-
-
-@step(u'Then I should error that my old password is incorrect')
-def then_i_should_error_that_my_old_password_is_incorrect(step):
-    world.page.is_incorrect_oldpassword_error_visible()
-    world.page.is_password_mismatch()
diff --git a/survey/features/user_settings.feature b/survey/features/user_settings.feature
deleted file mode 100644
index 3d73de39..00000000
--- a/survey/features/user_settings.feature
+++ /dev/null
@@ -1,35 +0,0 @@
-Feature: User Settings feature
-
-    Scenario: user settings links    
-      Given I have a user
-      And I visit the login page
-      And I login a user
-      And I am in the home page
-      And I click user settings link
-      Then I see edit profile, change password and logout link
-    
-    Scenario: Change use password 
-      Given I have a user
-      And I visit the login page
-      And I login a user
-      And I am in the home page
-      And I click user settings link
-      Then I see edit profile, change password and logout link
-      Then I click change password link
-      Then I should see a form asking me to add old password and new password
-      Then I fill in the old password and new password
-      And I click the change my password button
-      Then I should see password reset successfully
-      
-    Scenario: Change user password with errors 
-      Given I have a user
-      And I visit the login page
-      And I login a user
-      And I am in the home page
-      And I click user settings link
-      Then I see edit profile, change password and logout link
-      Then I click change password link
-      Then I should see a form asking me to add old password and new password
-      Then I fill in the wrong old password and correct new password
-      And I click the change my password button
-      Then I should error that my old password is incorrect and password mismatch
\ No newline at end of file
diff --git a/survey/features/users-steps.py b/survey/features/users-steps.py
deleted file mode 100644
index f9774e8f..00000000
--- a/survey/features/users-steps.py
+++ /dev/null
@@ -1,305 +0,0 @@
-from random import randint
-from time import sleep
-
-from lettuce import *
-from django.contrib.auth.models import User, Group
-
-from survey.features.page_objects.accounts import LoginPage, LogoutPage
-from survey.features.page_objects.users import NewUserPage, UsersListPage, EditUserDetailsPage, UserDetailsPage
-from survey.models.users import UserProfile
-
-
-@step(u'Given I am logged in as a superuser')
-def given_i_am_logged_in_as_a_superuser(step):
-    world.user = User.objects.create(username='Rajni', email='rajni@kant.com',
-                                     password='I_Rock', first_name='some name', last_name='last_name')
-    world.user.is_superuser = True
-    world.user.save()
-    UserProfile.objects.create(user=world.user, mobile_number='123456666')
-    world.page = LoginPage(world.browser)
-    world.page.visit()
-    world.page.login(world.user)
-
-
-@step(u'And I visit new user page')
-def and_i_visit_new_user_page(step):
-    world.page = NewUserPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I see all  new user fields')
-def then_i_see_all_new_user_fields(step):
-    world.page.valid_page()
-
-
-@step(u'And I click submit')
-def and_i_click_submit(step):
-    world.page.submit()
-
-
-@step(u'And I have a group')
-def and_i_have_a_group(step):
-    world.group = Group.objects.create(name="kojo")
-
-
-@step(u'Then I fill all necessary new user fields')
-def then_i_fill_all_necessary_new_user_fields(step):
-    world.user_data = {
-        'username': 'babyrajni',
-        'password1': 'baby_kant',
-        'password2': 'baby_kant',
-        'first_name': 'Baby',
-        'last_name': 'Kant',
-        'mobile_number': '123456789',
-        'email': 'baby@ka.nt',
-        'groups': world.group.id,
-    }
-
-    world.page.fill_valid_values(world.user_data)
-
-
-@step(u'Then I should see user successfully registered')
-def then_i_should_see_user_successfully_registered(step):
-    world.page.see_user_successfully_registered()
-
-
-def logout(world):
-    world.page = LogoutPage(world.browser)
-    world.page.visit()
-
-
-def login(user, world):
-    world.page = LoginPage(world.browser)
-    world.page.visit()
-    world.page.login(user)
-
-
-@step(u'And I can login that user successfully')
-def and_i_can_login_that_user_successfully(step):
-    logout(world)
-    user = User.objects.get(username=world.user_data['username'])
-    login(user, world)
-    world.page.see_home_page_and_logged_in_status(user)
-
-
-@step(u'Then I fill an existing mobile number')
-def then_i_fill_an_existing_mobile_number(step):
-    world.user_data = {
-        'mobile_number': '123456789',
-    }
-
-    user = User.objects.create(username='some_other_name')
-    UserProfile.objects.create(
-        user=user, mobile_number=world.user_data['mobile_number'])
-
-    world.page.fill_valid_values(world.user_data)
-    world.page.submit()
-
-
-@step(u'Then I should see existing mobile number error message')
-def then_i_should_see_existing_mobile_number_error_message(step):
-    world.page.is_text_present(
-        '%s is already associated with a different user.' % world.user_data['mobile_number'])
-
-
-@step(u'Then I fill an existing username')
-def then_i_fill_an_existing_username(step):
-    world.user_data = {
-        'username': 'babyrajni',
-    }
-    User.objects.create(username=world.user_data['username'])
-
-    world.page.fill_valid_values(world.user_data)
-    world.page.submit()
-
-
-@step(u'Then I should see existing username error message')
-def then_i_should_see_existing_username_error_message(step):
-    world.page.is_text_present(
-        '%s is no longer available.' % world.user_data['username'])
-
-
-@step(u'Then I fill an existing email')
-def then_i_fill_an_existing_email(step):
-    world.user_data = {
-        'email': 'haha@ha.ha',
-    }
-    User.objects.create(email=world.user_data['email'])
-
-    world.page.fill_valid_values(world.user_data)
-    world.page.submit()
-
-
-@step(u'Then I should see existing email error message')
-def then_i_should_see_existing_email_error_message(step):
-    world.page.is_text_present(
-        '%s is already associated with a different user.' % world.user_data['email'])
-
-
-@step(u'Then I fill a not allowed username')
-def then_i_fill_a_not_allowed_username(step):
-    not_allowed_username = 'haha#%&&**!'
-    world.user_data = {
-        'username': not_allowed_username,
-    }
-    User.objects.create(username=world.user_data['username'])
-
-    world.page.fill_valid_values(world.user_data)
-    world.page.submit()
-
-
-@step(u'Then I should see not allowed username error message')
-def then_i_should_see_not_allowed_username_error_message(step):
-    world.page.is_text_present("username may contain only letters characters.")
-
-
-@step(u'And I have 100 users')
-def and_i_have_users(step):
-    for i in range(100):
-        random_suffix_number = str(randint(1, 99999))
-        try:
-            user = User.objects.create_user('user' + random_suffix_number, random_suffix_number + "@gmail.com",
-                                            'pass' + random_suffix_number)
-            UserProfile.objects.create(
-                mobile_number=random_suffix_number, user=user)
-        except:
-            pass
-
-
-@step(u'And I visit the users list page')
-def and_i_visit_the_users_list_page(step):
-    world.page = UsersListPage(world.browser)
-    world.page.visit()
-
-
-@step(u'Then I should see a list of users')
-def then_i_should_see_a_list_of_users(step):
-    world.page.validate_users_listed()
-    world.page.validate_displayed_headers()
-    world.page.validate_users_paginated()
-
-
-@step(u'Then I should see the users information in a form')
-def then_i_should_see_the_users_information_in_a_form(step):
-    world.page = EditUserDetailsPage(world.browser)
-    world.page.set_user(world.user)
-    world.page.assert_form_has_infomation()
-
-
-@step(u'When I modify the users information')
-def when_i_modify_the_users_information(step):
-    world.page.modify_users_information()
-
-
-@step(u'And I click the update button')
-def and_i_click_the_update_button(step):
-    world.page.click_update_button()
-
-
-@step(u'Then I should see user information updated successfully')
-def then_i_should_see_user_information_saved_successfully(step):
-    world.page.assert_user_saved_sucessfully()
-
-
-@step(u'And I see that username is readonly')
-def and_i_see_that_username_is_readonly(step):
-    world.page.assert_username_is_readonly()
-
-
-@step(u'Then I should not see the groups field')
-def then_i_should_not_see_the_groups_field(step):
-    world.page.is_group_input_field_visible(False)
-
-
-@step(u'Then I should see the groups field')
-def then_i_should_see_the_groups_field(step):
-    world.page.is_group_input_field_visible(True)
-
-
-@step(u'And I select edit action')
-def and_i_select_edit_action(step):
-    world.page.click_by_css("#edit_user")
-    world.page = EditUserDetailsPage(world.browser)
-
-
-@step(u'When I click add user button')
-def when_i_click_add_user_button(step):
-    world.page = UsersListPage(world.browser)
-    world.page.visit()
-    world.page.click_by_css("#add-user")
-
-
-@step(u'Then I should see add user page')
-def then_i_should_see_add_user_page(step):
-    world.page = NewUserPage(world.browser)
-    world.page.validate_url()
-
-
-@step(u'And I have one user')
-def and_i_have_one_user(step):
-    world.user = User.objects.create_user(username='hahaRajni', email='rarajni@kant.com',
-                                          password='I_Rock_0', first_name='some rajni name', last_name='last_name')
-    UserProfile.objects.create(user=world.user, mobile_number='123456667')
-    admin = Group.objects.get(name='mics_admin')
-    admin.user_set.add(world.user)
-
-
-@step(u'And I click the user details link')
-def and_i_click_the_user_details_link(step):
-    world.page.click_link_by_href("/users/%d/" % world.user.id)
-
-
-@step(u'Then I should see the user details displayed')
-def then_i_should_see_the_user_details_displayed(step):
-    world.page = UserDetailsPage(world.browser, world.user)
-    world.page.validate_url()
-    world.page.validate_page_content()
-
-
-@step(u'Then back button should take back to users page')
-def then_back_button_should_take_back_to_users_page(step):
-    world.page.validate_back_link()
-
-
-@step(u'And I click the user deactivate link')
-def and_i_click_the_user_deactivate_link(step):
-    world.page.click_link_by_partial_href(
-        "#deactivate_user_%d" % world.user.id)
-
-
-@step(u'Then I should see a deactivate user confirmation modal')
-def then_i_should_see_a_deactivate_user_confirmation_modal(step):
-    world.page.see_confirm_modal_message(world.user.username, "deactivate")
-
-
-@step(u'Then I should see the user is deactivated')
-def then_i_should_see_the_user_is_deactivated(step):
-    world.page.see_success_message(
-        "User %s" % world.user.username, "deactivated")
-
-
-@step(u'When I click the activate link for that user')
-def when_i_click_the_activate_link_for_that_user(step):
-    world.page.click_link_by_partial_href(
-        "#re-activate_user_%d" % world.user.id)
-
-
-@step(u'Then I should see a reactivate user confirmation modal')
-def then_i_should_see_a_reactivate_user_confirmation_modal(step):
-    world.page.see_confirm_modal_message(world.user.username, "re-activate")
-
-
-@step(u'Then I should see the user is reactivated')
-def then_i_should_see_the_user_is_reactivated(step):
-    world.page.see_success_message(
-        "User %s" % world.user.username, "re-activated")
-
-
-@step(u'When I confirm deactivate')
-def when_i_confirm_deactivate(step):
-    world.page.click_by_css("#deactivate-user-%d" % world.user.id)
-
-
-@step(u'When I confirm reactivate')
-def when_i_confirm_reactivate(step):
-    world.page.click_by_css("#re-activate-user-%d" % world.user.id)
diff --git a/survey/features/users.feature b/survey/features/users.feature
deleted file mode 100644
index 33b28a19..00000000
--- a/survey/features/users.feature
+++ /dev/null
@@ -1,114 +0,0 @@
-Feature: Users feature
-
-    Scenario: new user page
-      Given I am logged in as a superuser
-      And I visit new user page
-      Then I see all  new user fields
-      And I click submit
-      Then I should see the error messages
-
-    Scenario: create user
-       Given I am logged in as a superuser
-       And I have a group
-       And I visit new user page
-       Then I fill all necessary new user fields
-       And I click submit
-       Then I should see user successfully registered
-       And I can login that user successfully
-
-    Scenario: create user with already existing mobile
-      Given I am logged in as a superuser
-      And I have a group
-      And I visit new user page
-      Then I fill an existing mobile number
-      And I click submit
-      Then I should see existing mobile number error message
-
-    Scenario: create user with already existing username
-      Given I am logged in as a superuser
-      And I have a group
-      And I visit new user page
-      Then I fill an existing username
-      And I click submit
-      Then I should see existing username error message
-
-    Scenario: create user with already existing email
-      Given I am logged in as a superuser
-      And I have a group
-      And I visit new user page
-      Then I fill an existing email
-      And I click submit
-      Then I should see existing email error message
-
-    Scenario: create user with prohibited username
-      Given I am logged in as a superuser
-      And I have a group
-      And I visit new user page
-      Then I fill a not allowed username
-      And I click submit
-      Then I should see not allowed username error message
-      
-    Scenario: List users
-      Given I am logged in as a superuser
-      And I have 100 users
-      And I visit the users list page
-      Then I should see a list of users
-      When I click add user button
-      Then I should see add user page
-
-      
-    Scenario: Edit a user
-      Given I am logged in as a superuser
-      And I visit the users list page
-      And I click on the edit button
-      Then I should see the users information in a form
-      And I see that username is readonly
-      When I modify the users information
-      And I click the update button
-      Then I should see user information updated successfully
-    
-
-    Scenario: Edit a user when logged in with no permissions
-      Given I have a user
-      And I visit the login page
-      And I login a user
-      And I am in the home page
-      And I click user settings link
-      And I select edit action
-      Then I should not see the groups field
-      When I modify the users information
-      And I click the update button
-      Then I should see user information updated successfully
-
-    Scenario: Edit a user when logged as superuser
-      Given I am logged in as a superuser
-      And I visit the login page
-      And I login a user
-      And I am in the home page
-      And I click user settings link
-      And I select edit action
-      Then I should see the groups field
-      When I modify the users information
-      And I click the update button
-      Then I should see user information updated successfully
-
-    Scenario: View user details
-      Given I am logged in as admin
-      And I have one user
-      And I visit the users list page
-      And I click the user details link
-      Then I should see the user details displayed
-      Then back button should take back to users page
-
-  Scenario: Deactivate/Activate a user
-    Given I am logged in as admin
-    And I have one user
-    And I visit the users list page
-    And I click the user deactivate link
-    Then I should see a deactivate user confirmation modal
-    When I confirm deactivate
-    Then I should see the user is deactivated
-    When I click the activate link for that user
-    Then I should see a reactivate user confirmation modal
-    When I confirm reactivate
-    Then I should see the user is reactivated
\ No newline at end of file
diff --git a/survey/fixtures/__init__.py b/survey/fixtures/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/survey/fixtures/about_us.json b/survey/fixtures/about_us.json
deleted file mode 100644
index 9deaeb9b..00000000
--- a/survey/fixtures/about_us.json
+++ /dev/null
@@ -1,11 +0,0 @@
-[
-    {
-        "pk": 2,
-        "model": "survey.aboutus",
-        "fields": {
-            "content": "

Multiple Indicator Cluster Survey (MICS)

UNICEF assists countries in collecting and analyzing data in order to fill data gaps for monitoring the situation of children and women through its international household survey initiative the Multiple Indicator Cluster Surveys (MICS). Since the mid-1990s, the MICS has enabled many countries to produce statistically sound and internationally comparable estimates of a range of indicators in the areas of health, education child protection and HIV/AIDS. MICS findings have been used extensively as a basis for policy decisions and programme interventions, and for the purpose of influencing public opinion on the situation of children and women around the world.

Survey tools

The MICS survey tools are developed by UNICEF after consultations with relevant experts from various UN organizations as well as with interagency monitoring groups. UNICEF works closely with other household survey programmes, in particular the Demographic and Health Surveys (DHS) programme, to harmonize survey questions and modules and to ensure a coordinated approach to survey implementation, with the objective to provide comparability across surveys and to avoid duplication of efforts. The survey questionnaires are modular tools that can be adapted to the needs of the country

Mobile-based Multiple Indicator Cluster Survey (MICS)

UNICEF Uganda has developed a mobile-based tool to work over the USSD technology to collect household-level.
The advantage of the mobile-based approach is: ", - "modified": "2013-12-23T14:36:35.805Z", - "created": "2013-12-23T14:36:35.805Z" - } - } -] \ No newline at end of file diff --git a/survey/fixtures/batches_a_to_e.json b/survey/fixtures/batches_a_to_e.json deleted file mode 100644 index b2b6e8b0..00000000 --- a/survey/fixtures/batches_a_to_e.json +++ /dev/null @@ -1,1921 +0,0 @@ -[ - { - "pk": 1, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.317Z", - "text": "How many people usually live in this household?", - "batch": 1, - "modified": "2013-08-26T11:34:27.317Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 1 - } - }, - { - "pk": 2, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.319Z", - "text": "What is the households main source of drinking water?", - "batch": 1, - "modified": "2013-08-26T11:34:27.319Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 2 - } - }, - { - "pk": 3, - "model": "survey.question", - "fields": { - "parent": 2, - "created": "2013-08-26T11:34:27.331Z", - "text": "Describe the source of drinking water", - "batch": 1, - "modified": "2013-08-26T11:34:27.331Z", - "subquestion": true, - "answer_type": "text", - "identifier": null, - "order": null - } - }, - { - "pk": 4, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.332Z", - "text": "What is the type of toilet that is mainly used in this household?", - "batch": 1, - "modified": "2013-08-26T11:34:27.333Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 3 - } - }, - { - "pk": 5, - "model": "survey.question", - "fields": { - "parent": 4, - "created": "2013-08-26T11:34:27.340Z", - "text": "Describe the type of toilet being used", - "batch": 1, - "modified": "2013-08-26T11:34:27.340Z", - "subquestion": true, - "answer_type": "text", - "identifier": null, - "order": null - } - }, - { - "pk": 6, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.341Z", - "text": "Does this household have a hand-washing facility at the toilet?", - "batch": 1, - "modified": "2013-08-26T11:34:27.341Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 4 - } - }, - { - "pk": 7, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.345Z", - "text": "Does your household have any mosquito nets that are used while sleeping?", - "batch": 1, - "modified": "2013-08-26T11:34:27.345Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 5 - } - }, - { - "pk": 8, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.347Z", - "text": "Are these insecticide-treated mosquito nets?", - "batch": 1, - "modified": "2013-08-26T11:34:27.347Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 6 - } - }, - { - "pk": 9, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.351Z", - "text": "How many children are aged under 5 (FIVE) years in this household?", - "batch": 1, - "modified": "2013-08-26T11:34:27.351Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 7 - } - }, - { - "pk": 10, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.352Z", - "text": "How many of them slept under a mosquito net last night?", - "batch": 1, - "modified": "2013-08-26T11:34:27.352Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 8 - } - }, - { - "pk": 11, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.353Z", - "text": "How many pregnant women aged between 15-49 years are in this household?", - "batch": 1, - "modified": "2013-08-26T11:34:27.353Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 9 - } - }, - { - "pk": 12, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.354Z", - "text": "How many of the currently pregnant women slept under an Insecticide-Treated mosquito net last night?", - "batch": 1, - "modified": "2013-08-26T11:34:27.354Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 10 - } - }, - { - "pk": 13, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.367Z", - "text": "How many women aged between 15-49 years are in this household?", - "batch": 2, - "modified": "2013-08-26T11:34:27.368Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 1 - } - }, - { - "pk": 14, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.369Z", - "text": "**ASK TO SPEAK TO ONE OF THESE WOMEN** \n How many of these women has had a live birth in the last 2 years?", - "batch": 2, - "modified": "2013-08-26T11:34:27.369Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 2 - } - }, - { - "pk": 15, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.370Z", - "text": "How many of these child births were delivered in a health facility?", - "batch": 2, - "modified": "2013-08-26T11:34:27.370Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 3 - } - }, - { - "pk": 16, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.371Z", - "text": "How many of these child births were assisted by a skilled health personnel?", - "batch": 2, - "modified": "2013-08-26T11:34:27.371Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 4 - } - }, - { - "pk": 17, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.372Z", - "text": "How many of these new borns were breast fed within 1 hour of birth?", - "batch": 2, - "modified": "2013-08-26T11:34:27.373Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 5 - } - }, - { - "pk": 18, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.382Z", - "text": "How many times did you receive antenatal care during the last pregnancy?", - "batch": 2, - "modified": "2013-08-26T11:34:27.382Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 6 - } - }, - { - "pk": 19, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.383Z", - "text": "During the last pregnancy, did you receive any injection on the arm or shoulder to prevent the baby from getting tetanus?", - "batch": 2, - "modified": "2013-08-26T11:34:27.383Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 7 - } - }, - { - "pk": 20, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.386Z", - "text": "How many times did you receive this tetanus injection during your pregnancy?", - "batch": 2, - "modified": "2013-08-26T11:34:27.386Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 8 - } - }, - { - "pk": 21, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.394Z", - "text": "How many women aged between 15-49 years are in this household?", - "batch": 3, - "modified": "2013-08-26T11:34:27.394Z", - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 1 - } - }, - { - "pk": 22, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.395Z", - "text": "**ASK TO SPEAK TO ONE OF THE WOMEN** \n Have you ever heard of an illness called AIDS?", - "batch": 3, - "modified": "2013-08-26T11:34:27.395Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 2 - } - }, - { - "pk": 23, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.397Z", - "text": "Do you know of a place where people can go to get tested for the AIDS virus?", - "batch": 3, - "modified": "2013-08-26T11:34:27.398Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 3 - } - }, - { - "pk": 24, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.400Z", - "text": "Have you ever been tested to see if you have the AIDS virus?", - "batch": 3, - "modified": "2013-08-26T11:34:27.400Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 4 - } - }, - { - "pk": 25, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.403Z", - "text": "When was the most recent time you were tested?", - "batch": 3, - "modified": "2013-08-26T11:34:27.403Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 5 - } - }, - { - "pk": 26, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.407Z", - "text": "Can the virus that causes AIDS be transmitted from a mother to her baby during pregnancy?", - "batch": 3, - "modified": "2013-08-26T11:34:27.407Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 6 - } - }, - { - "pk": 27, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.410Z", - "text": "Can the virus that causes AIDS be transmitted from a mother to her baby during delivery?", - "batch": 3, - "modified": "2013-08-26T11:34:27.410Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 7 - } - }, - { - "pk": 28, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.412Z", - "text": "Can the virus that causes AIDS be transmitted from a mother to her baby by breastfeeding?", - "batch": 3, - "modified": "2013-08-26T11:34:27.412Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 8 - } - }, - { - "pk": 29, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.415Z", - "text": "How often do you read a newspaper or magazine?", - "batch": 3, - "modified": "2013-08-26T11:34:27.415Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 9 - } - }, - { - "pk": 30, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.419Z", - "text": "How often do you listen to the radio?", - "batch": 3, - "modified": "2013-08-26T11:34:27.420Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 10 - } - }, - { - "pk": 31, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.424Z", - "text": "How often do you watch television?", - "batch": 3, - "modified": "2013-08-26T11:34:27.424Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 11 - } - }, - { - "pk": 32, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.432Z", - "text": "Can people reduce their chance of getting the AIDS virus by having just one uninfected sex partner who has no other sex partners?", - "batch": 3, - "modified": "2013-08-26T11:34:27.432Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 12 - } - }, - { - "pk": 33, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.436Z", - "text": "Can people get the AIDS virus because of witchcraft or other supernatural means?", - "batch": 3, - "modified": "2013-08-26T11:34:27.436Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 13 - } - }, - { - "pk": 34, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.439Z", - "text": "Can people reduce their chance of getting the AIDS virus by using a condom every time they have sex?", - "batch": 3, - "modified": "2013-08-26T11:34:27.439Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 14 - } - }, - { - "pk": 35, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.443Z", - "text": "Can people get the AIDS virus from mosquito bites?", - "batch": 3, - "modified": "2013-08-26T11:34:27.443Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 15 - } - }, - { - "pk": 36, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.446Z", - "text": "Can people get the AIDS virus by sharing food with a person who has the AIDS virus?", - "batch": 3, - "modified": "2013-08-26T11:34:27.447Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 16 - } - }, - { - "pk": 37, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.450Z", - "text": "Is it possible for a healthy-looking person to have the AIDS virus?", - "batch": 3, - "modified": "2013-08-26T11:34:27.450Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 17 - } - }, - { - "pk": 38, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.453Z", - "text": "In the last 12 months, have you used a computer?", - "batch": 3, - "modified": "2013-08-26T11:34:27.453Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 18 - } - }, - { - "pk": 39, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.456Z", - "text": "During the last one month, how often did you use a computer?", - "batch": 3, - "modified": "2013-08-26T11:34:27.456Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 19 - } - }, - { - "pk": 40, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.460Z", - "text": "In the last 12 months, have you used the internet?", - "batch": 3, - "modified": "2013-08-26T11:34:27.460Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 20 - } - }, - { - "pk": 41, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.463Z", - "text": "During the last one month, how often did you use the internet?", - "batch": 3, - "modified": "2013-08-26T11:34:27.463Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 21 - } - }, - { - "pk": 42, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.467Z", - "text": "Compared to this time last year, would you say that your life has improved, stayed more or less the same, or worsened, overall?", - "batch": 3, - "modified": "2013-08-26T11:34:27.467Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 22 - } - }, - { - "pk": 43, - "model": "survey.question", - "fields": { - "parent": null, - "created": "2013-08-26T11:34:27.472Z", - "text": "And in one year from now, do you expect that your life will be better, will be more or less the same, or will be worse, overall?", - "batch": 3, - "modified": "2013-08-26T11:34:27.472Z", - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 23 - } - }, - { - "pk": 1, - "model": "survey.questionoption", - "fields": { - "text": "Piped tap water", - "order": 1, - "question": 2, - "modified": "2013-08-26T11:34:27.321Z", - "created": "2013-08-26T11:34:27.320Z" - } - }, - { - "pk": 2, - "model": "survey.questionoption", - "fields": { - "text": "Borehole", - "order": 2, - "question": 2, - "modified": "2013-08-26T11:34:27.322Z", - "created": "2013-08-26T11:34:27.322Z" - } - }, - { - "pk": 3, - "model": "survey.questionoption", - "fields": { - "text": "Protected well/spring", - "order": 3, - "question": 2, - "modified": "2013-08-26T11:34:27.323Z", - "created": "2013-08-26T11:34:27.323Z" - } - }, - { - "pk": 4, - "model": "survey.questionoption", - "fields": { - "text": "Unprotected well/spring", - "order": 4, - "question": 2, - "modified": "2013-08-26T11:34:27.324Z", - "created": "2013-08-26T11:34:27.324Z" - } - }, - { - "pk": 5, - "model": "survey.questionoption", - "fields": { - "text": "Public tap", - "order": 5, - "question": 2, - "modified": "2013-08-26T11:34:27.325Z", - "created": "2013-08-26T11:34:27.324Z" - } - }, - { - "pk": 6, - "model": "survey.questionoption", - "fields": { - "text": "Gravity Flow scheme", - "order": 6, - "question": 2, - "modified": "2013-08-26T11:34:27.325Z", - "created": "2013-08-26T11:34:27.325Z" - } - }, - { - "pk": 7, - "model": "survey.questionoption", - "fields": { - "text": "Bottled water", - "order": 7, - "question": 2, - "modified": "2013-08-26T11:34:27.326Z", - "created": "2013-08-26T11:34:27.326Z" - } - }, - { - "pk": 8, - "model": "survey.questionoption", - "fields": { - "text": "Tanker-truck/cart", - "order": 8, - "question": 2, - "modified": "2013-08-26T11:34:27.327Z", - "created": "2013-08-26T11:34:27.327Z" - } - }, - { - "pk": 9, - "model": "survey.questionoption", - "fields": { - "text": "Surface water (river, stream, dam, lake, pond, canal, irrigation channel)", - "order": 9, - "question": 2, - "modified": "2013-08-26T11:34:27.329Z", - "created": "2013-08-26T11:34:27.328Z" - } - }, - { - "pk": 10, - "model": "survey.questionoption", - "fields": { - "text": "Others", - "order": 10, - "question": 2, - "modified": "2013-08-26T11:34:27.330Z", - "created": "2013-08-26T11:34:27.330Z" - } - }, - { - "pk": 11, - "model": "survey.questionoption", - "fields": { - "text": "Flush/Pour flush", - "order": 1, - "question": 4, - "modified": "2013-08-26T11:34:27.334Z", - "created": "2013-08-26T11:34:27.334Z" - } - }, - { - "pk": 12, - "model": "survey.questionoption", - "fields": { - "text": "Ventilated Improved Pit (VIP) latrine", - "order": 2, - "question": 4, - "modified": "2013-08-26T11:34:27.335Z", - "created": "2013-08-26T11:34:27.335Z" - } - }, - { - "pk": 13, - "model": "survey.questionoption", - "fields": { - "text": "Pit latrine with slab /covered pit", - "order": 3, - "question": 4, - "modified": "2013-08-26T11:34:27.336Z", - "created": "2013-08-26T11:34:27.335Z" - } - }, - { - "pk": 14, - "model": "survey.questionoption", - "fields": { - "text": "Pit latrine without slab /uncovered pit", - "order": 4, - "question": 4, - "modified": "2013-08-26T11:34:27.336Z", - "created": "2013-08-26T11:34:27.336Z" - } - }, - { - "pk": 15, - "model": "survey.questionoption", - "fields": { - "text": "ECOSAN/Composting toilet", - "order": 5, - "question": 4, - "modified": "2013-08-26T11:34:27.337Z", - "created": "2013-08-26T11:34:27.337Z" - } - }, - { - "pk": 16, - "model": "survey.questionoption", - "fields": { - "text": "Hanging toilet/latrine", - "order": 6, - "question": 4, - "modified": "2013-08-26T11:34:27.338Z", - "created": "2013-08-26T11:34:27.338Z" - } - }, - { - "pk": 17, - "model": "survey.questionoption", - "fields": { - "text": "No facility, Bush, Field", - "order": 7, - "question": 4, - "modified": "2013-08-26T11:34:27.339Z", - "created": "2013-08-26T11:34:27.339Z" - } - }, - { - "pk": 18, - "model": "survey.questionoption", - "fields": { - "text": "Others", - "order": 8, - "question": 4, - "modified": "2013-08-26T11:34:27.340Z", - "created": "2013-08-26T11:34:27.339Z" - } - }, - { - "pk": 19, - "model": "survey.questionoption", - "fields": { - "text": "Yes, with water and soap", - "order": 1, - "question": 6, - "modified": "2013-08-26T11:34:27.342Z", - "created": "2013-08-26T11:34:27.342Z" - } - }, - { - "pk": 20, - "model": "survey.questionoption", - "fields": { - "text": "Yes, with water only", - "order": 2, - "question": 6, - "modified": "2013-08-26T11:34:27.343Z", - "created": "2013-08-26T11:34:27.343Z" - } - }, - { - "pk": 21, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 3, - "question": 6, - "modified": "2013-08-26T11:34:27.344Z", - "created": "2013-08-26T11:34:27.344Z" - } - }, - { - "pk": 22, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 7, - "modified": "2013-08-26T11:34:27.346Z", - "created": "2013-08-26T11:34:27.346Z" - } - }, - { - "pk": 23, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 7, - "modified": "2013-08-26T11:34:27.347Z", - "created": "2013-08-26T11:34:27.346Z" - } - }, - { - "pk": 24, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 8, - "modified": "2013-08-26T11:34:27.348Z", - "created": "2013-08-26T11:34:27.348Z" - } - }, - { - "pk": 25, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 8, - "modified": "2013-08-26T11:34:27.349Z", - "created": "2013-08-26T11:34:27.349Z" - } - }, - { - "pk": 26, - "model": "survey.questionoption", - "fields": { - "text": "Dont Know", - "order": 3, - "question": 8, - "modified": "2013-08-26T11:34:27.350Z", - "created": "2013-08-26T11:34:27.350Z" - } - }, - { - "pk": 27, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 19, - "modified": "2013-08-26T11:34:27.385Z", - "created": "2013-08-26T11:34:27.384Z" - } - }, - { - "pk": 28, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 19, - "modified": "2013-08-26T11:34:27.385Z", - "created": "2013-08-26T11:34:27.385Z" - } - }, - { - "pk": 29, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 22, - "modified": "2013-08-26T11:34:27.396Z", - "created": "2013-08-26T11:34:27.396Z" - } - }, - { - "pk": 30, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 22, - "modified": "2013-08-26T11:34:27.397Z", - "created": "2013-08-26T11:34:27.397Z" - } - }, - { - "pk": 31, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 23, - "modified": "2013-08-26T11:34:27.399Z", - "created": "2013-08-26T11:34:27.399Z" - } - }, - { - "pk": 32, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 23, - "modified": "2013-08-26T11:34:27.400Z", - "created": "2013-08-26T11:34:27.400Z" - } - }, - { - "pk": 33, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 24, - "modified": "2013-08-26T11:34:27.401Z", - "created": "2013-08-26T11:34:27.401Z" - } - }, - { - "pk": 34, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 24, - "modified": "2013-08-26T11:34:27.402Z", - "created": "2013-08-26T11:34:27.402Z" - } - }, - { - "pk": 35, - "model": "survey.questionoption", - "fields": { - "text": "Less than 12 months ago", - "order": 1, - "question": 25, - "modified": "2013-08-26T11:34:27.405Z", - "created": "2013-08-26T11:34:27.404Z" - } - }, - { - "pk": 36, - "model": "survey.questionoption", - "fields": { - "text": "12-23 months ago", - "order": 2, - "question": 25, - "modified": "2013-08-26T11:34:27.405Z", - "created": "2013-08-26T11:34:27.405Z" - } - }, - { - "pk": 37, - "model": "survey.questionoption", - "fields": { - "text": "2 or more years ago", - "order": 3, - "question": 25, - "modified": "2013-08-26T11:34:27.406Z", - "created": "2013-08-26T11:34:27.406Z" - } - }, - { - "pk": 38, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 26, - "modified": "2013-08-26T11:34:27.408Z", - "created": "2013-08-26T11:34:27.408Z" - } - }, - { - "pk": 39, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 26, - "modified": "2013-08-26T11:34:27.409Z", - "created": "2013-08-26T11:34:27.409Z" - } - }, - { - "pk": 40, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 27, - "modified": "2013-08-26T11:34:27.411Z", - "created": "2013-08-26T11:34:27.410Z" - } - }, - { - "pk": 41, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 27, - "modified": "2013-08-26T11:34:27.411Z", - "created": "2013-08-26T11:34:27.411Z" - } - }, - { - "pk": 42, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 28, - "modified": "2013-08-26T11:34:27.413Z", - "created": "2013-08-26T11:34:27.413Z" - } - }, - { - "pk": 43, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 28, - "modified": "2013-08-26T11:34:27.414Z", - "created": "2013-08-26T11:34:27.414Z" - } - }, - { - "pk": 44, - "model": "survey.questionoption", - "fields": { - "text": "Almost every day", - "order": 1, - "question": 29, - "modified": "2013-08-26T11:34:27.416Z", - "created": "2013-08-26T11:34:27.416Z" - } - }, - { - "pk": 45, - "model": "survey.questionoption", - "fields": { - "text": "At least once a week", - "order": 2, - "question": 29, - "modified": "2013-08-26T11:34:27.417Z", - "created": "2013-08-26T11:34:27.417Z" - } - }, - { - "pk": 46, - "model": "survey.questionoption", - "fields": { - "text": "Less than once a week", - "order": 3, - "question": 29, - "modified": "2013-08-26T11:34:27.418Z", - "created": "2013-08-26T11:34:27.418Z" - } - }, - { - "pk": 47, - "model": "survey.questionoption", - "fields": { - "text": "Not at all", - "order": 4, - "question": 29, - "modified": "2013-08-26T11:34:27.419Z", - "created": "2013-08-26T11:34:27.418Z" - } - }, - { - "pk": 48, - "model": "survey.questionoption", - "fields": { - "text": "Almost every day", - "order": 1, - "question": 30, - "modified": "2013-08-26T11:34:27.420Z", - "created": "2013-08-26T11:34:27.420Z" - } - }, - { - "pk": 49, - "model": "survey.questionoption", - "fields": { - "text": "At least once a week", - "order": 2, - "question": 30, - "modified": "2013-08-26T11:34:27.421Z", - "created": "2013-08-26T11:34:27.421Z" - } - }, - { - "pk": 50, - "model": "survey.questionoption", - "fields": { - "text": "Less than once a week", - "order": 3, - "question": 30, - "modified": "2013-08-26T11:34:27.422Z", - "created": "2013-08-26T11:34:27.422Z" - } - }, - { - "pk": 51, - "model": "survey.questionoption", - "fields": { - "text": "Not at all", - "order": 4, - "question": 30, - "modified": "2013-08-26T11:34:27.423Z", - "created": "2013-08-26T11:34:27.423Z" - } - }, - { - "pk": 52, - "model": "survey.questionoption", - "fields": { - "text": "Almost every day", - "order": 1, - "question": 31, - "modified": "2013-08-26T11:34:27.425Z", - "created": "2013-08-26T11:34:27.425Z" - } - }, - { - "pk": 53, - "model": "survey.questionoption", - "fields": { - "text": "At least once a week", - "order": 2, - "question": 31, - "modified": "2013-08-26T11:34:27.426Z", - "created": "2013-08-26T11:34:27.426Z" - } - }, - { - "pk": 54, - "model": "survey.questionoption", - "fields": { - "text": "Less than once a week", - "order": 3, - "question": 31, - "modified": "2013-08-26T11:34:27.427Z", - "created": "2013-08-26T11:34:27.426Z" - } - }, - { - "pk": 55, - "model": "survey.questionoption", - "fields": { - "text": "Not at all", - "order": 4, - "question": 31, - "modified": "2013-08-26T11:34:27.427Z", - "created": "2013-08-26T11:34:27.427Z" - } - }, - { - "pk": 56, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 32, - "modified": "2013-08-26T11:34:27.433Z", - "created": "2013-08-26T11:34:27.433Z" - } - }, - { - "pk": 57, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 32, - "modified": "2013-08-26T11:34:27.434Z", - "created": "2013-08-26T11:34:27.434Z" - } - }, - { - "pk": 58, - "model": "survey.questionoption", - "fields": { - "text": "Dont know", - "order": 3, - "question": 32, - "modified": "2013-08-26T11:34:27.435Z", - "created": "2013-08-26T11:34:27.435Z" - } - }, - { - "pk": 59, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 33, - "modified": "2013-08-26T11:34:27.437Z", - "created": "2013-08-26T11:34:27.437Z" - } - }, - { - "pk": 60, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 33, - "modified": "2013-08-26T11:34:27.438Z", - "created": "2013-08-26T11:34:27.437Z" - } - }, - { - "pk": 61, - "model": "survey.questionoption", - "fields": { - "text": "Dont know", - "order": 3, - "question": 33, - "modified": "2013-08-26T11:34:27.438Z", - "created": "2013-08-26T11:34:27.438Z" - } - }, - { - "pk": 62, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 34, - "modified": "2013-08-26T11:34:27.440Z", - "created": "2013-08-26T11:34:27.440Z" - } - }, - { - "pk": 63, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 34, - "modified": "2013-08-26T11:34:27.441Z", - "created": "2013-08-26T11:34:27.441Z" - } - }, - { - "pk": 64, - "model": "survey.questionoption", - "fields": { - "text": "Dont know", - "order": 3, - "question": 34, - "modified": "2013-08-26T11:34:27.442Z", - "created": "2013-08-26T11:34:27.442Z" - } - }, - { - "pk": 65, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 35, - "modified": "2013-08-26T11:34:27.444Z", - "created": "2013-08-26T11:34:27.444Z" - } - }, - { - "pk": 66, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 35, - "modified": "2013-08-26T11:34:27.445Z", - "created": "2013-08-26T11:34:27.445Z" - } - }, - { - "pk": 67, - "model": "survey.questionoption", - "fields": { - "text": "Dont know", - "order": 3, - "question": 35, - "modified": "2013-08-26T11:34:27.446Z", - "created": "2013-08-26T11:34:27.445Z" - } - }, - { - "pk": 68, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 36, - "modified": "2013-08-26T11:34:27.447Z", - "created": "2013-08-26T11:34:27.447Z" - } - }, - { - "pk": 69, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 36, - "modified": "2013-08-26T11:34:27.448Z", - "created": "2013-08-26T11:34:27.448Z" - } - }, - { - "pk": 70, - "model": "survey.questionoption", - "fields": { - "text": "Dont know", - "order": 3, - "question": 36, - "modified": "2013-08-26T11:34:27.449Z", - "created": "2013-08-26T11:34:27.449Z" - } - }, - { - "pk": 71, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 37, - "modified": "2013-08-26T11:34:27.451Z", - "created": "2013-08-26T11:34:27.451Z" - } - }, - { - "pk": 72, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 37, - "modified": "2013-08-26T11:34:27.452Z", - "created": "2013-08-26T11:34:27.451Z" - } - }, - { - "pk": 73, - "model": "survey.questionoption", - "fields": { - "text": "Dont know", - "order": 3, - "question": 37, - "modified": "2013-08-26T11:34:27.452Z", - "created": "2013-08-26T11:34:27.452Z" - } - }, - { - "pk": 74, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 38, - "modified": "2013-08-26T11:34:27.454Z", - "created": "2013-08-26T11:34:27.454Z" - } - }, - { - "pk": 75, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 38, - "modified": "2013-08-26T11:34:27.455Z", - "created": "2013-08-26T11:34:27.455Z" - } - }, - { - "pk": 76, - "model": "survey.questionoption", - "fields": { - "text": "Almost every day", - "order": 1, - "question": 39, - "modified": "2013-08-26T11:34:27.457Z", - "created": "2013-08-26T11:34:27.457Z" - } - }, - { - "pk": 77, - "model": "survey.questionoption", - "fields": { - "text": "At least once a week", - "order": 2, - "question": 39, - "modified": "2013-08-26T11:34:27.458Z", - "created": "2013-08-26T11:34:27.457Z" - } - }, - { - "pk": 78, - "model": "survey.questionoption", - "fields": { - "text": "Less than once a week", - "order": 3, - "question": 39, - "modified": "2013-08-26T11:34:27.458Z", - "created": "2013-08-26T11:34:27.458Z" - } - }, - { - "pk": 79, - "model": "survey.questionoption", - "fields": { - "text": "Not at all", - "order": 4, - "question": 39, - "modified": "2013-08-26T11:34:27.459Z", - "created": "2013-08-26T11:34:27.459Z" - } - }, - { - "pk": 80, - "model": "survey.questionoption", - "fields": { - "text": "Yes", - "order": 1, - "question": 40, - "modified": "2013-08-26T11:34:27.461Z", - "created": "2013-08-26T11:34:27.461Z" - } - }, - { - "pk": 81, - "model": "survey.questionoption", - "fields": { - "text": "No", - "order": 2, - "question": 40, - "modified": "2013-08-26T11:34:27.462Z", - "created": "2013-08-26T11:34:27.462Z" - } - }, - { - "pk": 82, - "model": "survey.questionoption", - "fields": { - "text": "Almost every day", - "order": 1, - "question": 41, - "modified": "2013-08-26T11:34:27.464Z", - "created": "2013-08-26T11:34:27.464Z" - } - }, - { - "pk": 83, - "model": "survey.questionoption", - "fields": { - "text": "At least once a week", - "order": 2, - "question": 41, - "modified": "2013-08-26T11:34:27.464Z", - "created": "2013-08-26T11:34:27.464Z" - } - }, - { - "pk": 84, - "model": "survey.questionoption", - "fields": { - "text": "Less than once a week", - "order": 3, - "question": 41, - "modified": "2013-08-26T11:34:27.465Z", - "created": "2013-08-26T11:34:27.465Z" - } - }, - { - "pk": 85, - "model": "survey.questionoption", - "fields": { - "text": "Not at all", - "order": 4, - "question": 41, - "modified": "2013-08-26T11:34:27.466Z", - "created": "2013-08-26T11:34:27.466Z" - } - }, - { - "pk": 86, - "model": "survey.questionoption", - "fields": { - "text": "Improved", - "order": 1, - "question": 42, - "modified": "2013-08-26T11:34:27.468Z", - "created": "2013-08-26T11:34:27.468Z" - } - }, - { - "pk": 87, - "model": "survey.questionoption", - "fields": { - "text": "More or less the same", - "order": 2, - "question": 42, - "modified": "2013-08-26T11:34:27.469Z", - "created": "2013-08-26T11:34:27.469Z" - } - }, - { - "pk": 88, - "model": "survey.questionoption", - "fields": { - "text": "Worsened", - "order": 3, - "question": 42, - "modified": "2013-08-26T11:34:27.471Z", - "created": "2013-08-26T11:34:27.471Z" - } - }, - { - "pk": 89, - "model": "survey.questionoption", - "fields": { - "text": "Better", - "order": 1, - "question": 43, - "modified": "2013-08-26T11:34:27.473Z", - "created": "2013-08-26T11:34:27.473Z" - } - }, - { - "pk": 90, - "model": "survey.questionoption", - "fields": { - "text": "More or less the same", - "order": 2, - "question": 43, - "modified": "2013-08-26T11:34:27.474Z", - "created": "2013-08-26T11:34:27.474Z" - } - }, - { - "pk": 91, - "model": "survey.questionoption", - "fields": { - "text": "Worsened", - "order": 3, - "question": 43, - "modified": "2013-08-26T11:34:27.475Z", - "created": "2013-08-26T11:34:27.475Z" - } - }, - { - "pk": 1, - "model": "survey.batch", - "fields": { - "description": null, - "order": 1, - "modified": "2013-08-26T11:34:27.316Z", - "name": "Batch A", - "created": "2013-08-26T11:34:27.314Z" - } - }, - { - "pk": 2, - "model": "survey.batch", - "fields": { - "description": null, - "order": 2, - "modified": "2013-08-26T11:34:27.366Z", - "name": "Batch B and C", - "created": "2013-08-26T11:34:27.366Z" - } - }, - { - "pk": 3, - "model": "survey.batch", - "fields": { - "description": null, - "order": 3, - "modified": "2013-08-26T11:34:27.393Z", - "name": "Batch D and E", - "created": "2013-08-26T11:34:27.392Z" - } - }, - { - "pk": 1, - "model": "survey.answerrule", - "fields": { - "validate_with_option": 10, - "created": "2013-08-26T11:34:27.355Z", - "next_question": 3, - "question": 2, - "modified": "2013-08-26T11:34:27.356Z", - "validate_with_question": null, - "action": "ASK_SUBQUESTION", - "validate_with_value": null, - "condition": "EQUALS_OPTION" - } - }, - { - "pk": 2, - "model": "survey.answerrule", - "fields": { - "validate_with_option": 18, - "created": "2013-08-26T11:34:27.357Z", - "next_question": 5, - "question": 4, - "modified": "2013-08-26T11:34:27.358Z", - "validate_with_question": null, - "action": "ASK_SUBQUESTION", - "validate_with_value": null, - "condition": "EQUALS_OPTION" - } - }, - { - "pk": 3, - "model": "survey.answerrule", - "fields": { - "validate_with_option": null, - "created": "2013-08-26T11:34:27.359Z", - "next_question": 11, - "question": 9, - "modified": "2013-08-26T11:34:27.359Z", - "validate_with_question": null, - "action": "SKIP_TO", - "validate_with_value": 0, - "condition": "EQUALS" - } - }, - { - "pk": 4, - "model": "survey.answerrule", - "fields": { - "validate_with_option": 23, - "created": "2013-08-26T11:34:27.360Z", - "next_question": null, - "question": 7, - "modified": "2013-08-26T11:34:27.361Z", - "validate_with_question": null, - "action": "END_INTERVIEW", - "validate_with_value": null, - "condition": "EQUALS" - } - }, - { - "pk": 5, - "model": "survey.answerrule", - "fields": { - "validate_with_option": null, - "created": "2013-08-26T11:34:27.361Z", - "next_question": null, - "question": 10, - "modified": "2013-08-26T11:34:27.362Z", - "validate_with_question": 9, - "action": "REANSWER", - "validate_with_value": null, - "condition": "GREATER_THAN_QUESTION" - } - }, - { - "pk": 6, - "model": "survey.answerrule", - "fields": { - "validate_with_option": null, - "created": "2013-08-26T11:34:27.363Z", - "next_question": null, - "question": 11, - "modified": "2013-08-26T11:34:27.363Z", - "validate_with_question": null, - "action": "END_INTERVIEW", - "validate_with_value": 0, - "condition": "EQUALS" - } - }, - { - "pk": 7, - "model": "survey.answerrule", - "fields": { - "validate_with_option": null, - "created": "2013-08-26T11:34:27.364Z", - "next_question": null, - "question": 12, - "modified": "2013-08-26T11:34:27.364Z", - "validate_with_question": 11, - "action": "REANSWER", - "validate_with_value": null, - "condition": "GREATER_THAN_QUESTION" - } - }, - { - "pk": 8, - "model": "survey.answerrule", - "fields": { - "validate_with_option": null, - "created": "2013-08-26T11:34:27.374Z", - "next_question": null, - "question": 14, - "modified": "2013-08-26T11:34:27.374Z", - "validate_with_question": 13, - "action": "REANSWER", - "validate_with_value": null, - "condition": "GREATER_THAN_QUESTION" - } - }, - { - "pk": 9, - "model": "survey.answerrule", - "fields": { - "validate_with_option": null, - "created": "2013-08-26T11:34:27.375Z", - "next_question": null, - "question": 15, - "modified": "2013-08-26T11:34:27.376Z", - "validate_with_question": 14, - "action": "REANSWER", - "validate_with_value": null, - "condition": "GREATER_THAN_QUESTION" - } - }, - { - "pk": 10, - "model": "survey.answerrule", - "fields": { - "validate_with_option": null, - "created": "2013-08-26T11:34:27.377Z", - "next_question": null, - "question": 16, - "modified": "2013-08-26T11:34:27.377Z", - "validate_with_question": 14, - "action": "REANSWER", - "validate_with_value": null, - "condition": "GREATER_THAN_QUESTION" - } - }, - { - "pk": 11, - "model": "survey.answerrule", - "fields": { - "validate_with_option": null, - "created": "2013-08-26T11:34:27.378Z", - "next_question": null, - "question": 17, - "modified": "2013-08-26T11:34:27.378Z", - "validate_with_question": 14, - "action": "REANSWER", - "validate_with_value": null, - "condition": "GREATER_THAN_QUESTION" - } - }, - { - "pk": 12, - "model": "survey.answerrule", - "fields": { - "validate_with_option": 28, - "created": "2013-08-26T11:34:27.387Z", - "next_question": null, - "question": 19, - "modified": "2013-08-26T11:34:27.387Z", - "validate_with_question": null, - "action": "END_INTERVIEW", - "validate_with_value": null, - "condition": "EQUALS_OPTION" - } - }, - { - "pk": 13, - "model": "survey.answerrule", - "fields": { - "validate_with_option": null, - "created": "2013-08-26T11:34:27.390Z", - "next_question": 18, - "question": 13, - "modified": "2013-08-26T11:34:27.390Z", - "validate_with_question": null, - "action": "SKIP_TO", - "validate_with_value": 0, - "condition": "EQUALS" - } - }, - { - "pk": 14, - "model": "survey.answerrule", - "fields": { - "validate_with_option": 34, - "created": "2013-08-26T11:34:27.428Z", - "next_question": 26, - "question": 24, - "modified": "2013-08-26T11:34:27.428Z", - "validate_with_question": null, - "action": "SKIP_TO", - "validate_with_value": null, - "condition": "EQUALS_OPTION" - } - }, - { - "pk": 15, - "model": "survey.answerrule", - "fields": { - "validate_with_option": 75, - "created": "2013-08-26T11:34:27.476Z", - "next_question": 40, - "question": 38, - "modified": "2013-08-26T11:34:27.476Z", - "validate_with_question": null, - "action": "SKIP_TO", - "validate_with_value": null, - "condition": "EQUALS_OPTION" - } - }, - { - "pk": 16, - "model": "survey.answerrule", - "fields": { - "validate_with_option": null, - "created": "2013-08-26T11:34:27.479Z", - "next_question": 32, - "question": 21, - "modified": "2013-08-26T11:34:27.479Z", - "validate_with_question": null, - "action": "SKIP_TO", - "validate_with_value": 0, - "condition": "EQUALS" - } - } -] \ No newline at end of file diff --git a/survey/fixtures/data_seeds/about_us b/survey/fixtures/data_seeds/about_us deleted file mode 100644 index 6ab07896..00000000 --- a/survey/fixtures/data_seeds/about_us +++ /dev/null @@ -1,49 +0,0 @@ -from django.core import serializers -from survey.models import AboutUs -content = """ -

Multiple Indicator Cluster Survey (MICS)

-

UNICEF assists countries in collecting and analyzing data in order to fill data gaps for monitoring the - situation of children and women through its international household survey initiative the Multiple Indicator - Cluster Surveys (MICS). Since the mid-1990s, the MICS has enabled many countries to produce statistically - sound and internationally comparable estimates of a range of indicators in the areas of health, education, - child protection and HIV/AIDS. MICS findings have been used extensively as a basis for policy decisions - and programme interventions, and for the purpose of influencing public opinion on the situation of - children and women around the world.

- -

Survey tools

- The MICS survey tools are developed by UNICEF after consultations with relevant experts from various UN - organizations as well as with interagency monitoring groups. UNICEF works closely with other household - survey programmes, in particular the Demographic and Health Surveys (DHS) programme, to harmonize survey - questions and modules and to ensure a coordinated approach to survey implementation, with the objective to - provide comparability across surveys and to avoid duplication of efforts. - The survey questionnaires are modular tools that can be adapted to the needs of the country -

Mobile-based Multiple Indicator Cluster Survey (MICS)

- UNICEF Uganda has developed a mobile-based tool to work over the USSD technology to collect household-level. -
- The advantage of the mobile-based approach is: - - """ - -about_us = AboutUs.objects.create(content=content) -data = serializers.serialize("json", [about_us]) -print data diff --git a/survey/fixtures/data_seeds/about_us.json b/survey/fixtures/data_seeds/about_us.json deleted file mode 100644 index 9deaeb9b..00000000 --- a/survey/fixtures/data_seeds/about_us.json +++ /dev/null @@ -1,11 +0,0 @@ -[ - { - "pk": 2, - "model": "survey.aboutus", - "fields": { - "content": "

Multiple Indicator Cluster Survey (MICS)

UNICEF assists countries in collecting and analyzing data in order to fill data gaps for monitoring the situation of children and women through its international household survey initiative the Multiple Indicator Cluster Surveys (MICS). Since the mid-1990s, the MICS has enabled many countries to produce statistically sound and internationally comparable estimates of a range of indicators in the areas of health, education child protection and HIV/AIDS. MICS findings have been used extensively as a basis for policy decisions and programme interventions, and for the purpose of influencing public opinion on the situation of children and women around the world.

Survey tools

The MICS survey tools are developed by UNICEF after consultations with relevant experts from various UN organizations as well as with interagency monitoring groups. UNICEF works closely with other household survey programmes, in particular the Demographic and Health Surveys (DHS) programme, to harmonize survey questions and modules and to ensure a coordinated approach to survey implementation, with the objective to provide comparability across surveys and to avoid duplication of efforts. The survey questionnaires are modular tools that can be adapted to the needs of the country

Mobile-based Multiple Indicator Cluster Survey (MICS)

UNICEF Uganda has developed a mobile-based tool to work over the USSD technology to collect household-level.
The advantage of the mobile-based approach is: ", - "modified": "2013-12-23T14:36:35.805Z", - "created": "2013-12-23T14:36:35.805Z" - } - } -] \ No newline at end of file diff --git a/survey/fixtures/data_seeds/batch_a b/survey/fixtures/data_seeds/batch_a deleted file mode 100644 index de0473a1..00000000 --- a/survey/fixtures/data_seeds/batch_a +++ /dev/null @@ -1,76 +0,0 @@ -from survey.models import * - -Question.objects.all().delete() -QuestionOption.objects.all().delete() -Batch.objects.all().delete() -AnswerRule.objects.all().delete() - -batch = Batch.objects.create(order=1, name="Batch A") - -question_1 = Question.objects.create(batch=batch, text="How many people usually live in this household?", answer_type=Question.NUMBER, order=1) - -question_2 = Question.objects.create(batch=batch, text="What is the households main source of drinking water?", answer_type=Question.MULTICHOICE, order=2) -QuestionOption.objects.create(question=question_2, text="Piped tap water", order=1) -QuestionOption.objects.create(question=question_2, text="Borehole", order=2) -QuestionOption.objects.create(question=question_2, text="Protected well/spring", order=3) -QuestionOption.objects.create(question=question_2, text="Unprotected well/spring", order=4) -QuestionOption.objects.create(question=question_2, text="Public tap", order=5) -QuestionOption.objects.create(question=question_2, text="Gravity Flow scheme", order=6) -QuestionOption.objects.create(question=question_2, text="Bottled water", order=7) -QuestionOption.objects.create(question=question_2, text="Tanker-truck/cart", order=8) -QuestionOption.objects.create(question=question_2, text="Surface water (river, stream, dam, lake, pond, canal, irrigation channel)", order=9) -other_option_1 = QuestionOption.objects.create(question=question_2, text="Others", order=10) -sub_question_1 = Question.objects.create(batch=batch, text="Describe the source of drinking water", answer_type=Question.TEXT, subquestion=True, parent=question_2) - -question_3 = Question.objects.create(batch=batch, text="What is the type of toilet that is mainly used in this household?", answer_type=Question.MULTICHOICE, order=3) -QuestionOption.objects.create(question=question_3, text="Flush/Pour flush", order=1) -QuestionOption.objects.create(question=question_3, text="Ventilated Improved Pit (VIP) latrine", order=2) -QuestionOption.objects.create(question=question_3, text="Pit latrine with slab /covered pit", order=3) -QuestionOption.objects.create(question=question_3, text="Pit latrine without slab /uncovered pit", order=4) -QuestionOption.objects.create(question=question_3, text="ECOSAN/Composting toilet", order=5) -QuestionOption.objects.create(question=question_3, text="Hanging toilet/latrine", order=6) -QuestionOption.objects.create(question=question_3, text="No facility, Bush, Field", order=7) -other_option_2 = QuestionOption.objects.create(question=question_3, text="Others", order=8) -sub_question_2 = Question.objects.create(batch=batch, text="Describe the type of toilet being used", answer_type=Question.TEXT, subquestion=True, parent=question_3) - -question_4 = Question.objects.create(batch=batch, text="Does this household have a hand-washing facility at the toilet?", answer_type=Question.MULTICHOICE, order=4) -QuestionOption.objects.create(question=question_4, text="Yes, with water and soap", order=1) -QuestionOption.objects.create(question=question_4, text="Yes, with water only", order=2) -QuestionOption.objects.create(question=question_4, text="No", order=3) - -question_5 = Question.objects.create(batch=batch, text="Does your household have any mosquito nets that are used while sleeping?", answer_type=Question.MULTICHOICE, order=5) -QuestionOption.objects.create(question=question_5, text="Yes", order=1) -option_5_2 = QuestionOption.objects.create(question=question_5, text="No", order=2) - -question_6 = Question.objects.create(batch=batch, text="Are these insecticide-treated mosquito nets?", answer_type=Question.MULTICHOICE, order=6) -QuestionOption.objects.create(question=question_6, text="Yes", order=1) -QuestionOption.objects.create(question=question_6, text="No", order=2) -QuestionOption.objects.create(question=question_6, text="Dont Know", order=3) - -question_7 = Question.objects.create(batch=batch, text="How many children are aged under 5 (FIVE) years in this household?", answer_type=Question.NUMBER, order=7) - -question_8 = Question.objects.create(batch=batch, text="How many of them slept under a mosquito net last night?", answer_type=Question.NUMBER, order=8) - -question_9 = Question.objects.create(batch=batch, text="How many pregnant women aged between 15-49 years are in this household?", answer_type=Question.NUMBER, order=9) - -question_10 = Question.objects.create(batch=batch, text="How many of the currently pregnant women slept under an Insecticide-Treated mosquito net last night?", answer_type=Question.NUMBER, order=10 ) - -# Rules -AnswerRule.objects.create(question=question_2, action=AnswerRule.ACTIONS['ASK_SUBQUESTION'], condition=AnswerRule.CONDITIONS['EQUALS_OPTION'], validate_with_option=other_option_1, next_question=sub_question_1) - -AnswerRule.objects.create(question=question_3, action=AnswerRule.ACTIONS['ASK_SUBQUESTION'], condition=AnswerRule.CONDITIONS['EQUALS_OPTION'], validate_with_option=other_option_2, next_question=sub_question_2) - -AnswerRule.objects.create(question=question_7, action=AnswerRule.ACTIONS['SKIP_TO'], condition=AnswerRule.CONDITIONS['EQUALS'], validate_with_value=0, next_question=question_9) - -AnswerRule.objects.create(question=question_5, action=AnswerRule.ACTIONS['END_INTERVIEW'], condition=AnswerRule.CONDITIONS['EQUALS'], validate_with_option=option_5_2) - -AnswerRule.objects.create(question=question_8, action=AnswerRule.ACTIONS['REANSWER'], condition=AnswerRule.CONDITIONS['GREATER_THAN_QUESTION'], validate_with_question=question_7) - -AnswerRule.objects.create(question=question_9, action=AnswerRule.ACTIONS['END_INTERVIEW'], condition=AnswerRule.CONDITIONS['EQUALS'], validate_with_value=0) - -AnswerRule.objects.create(question=question_10, action=AnswerRule.ACTIONS['REANSWER'], condition=AnswerRule.CONDITIONS['GREATER_THAN_QUESTION'], validate_with_question=question_9) - -import batch_b -import batch_c -import batch_d -import batch_e diff --git a/survey/fixtures/data_seeds/batch_b b/survey/fixtures/data_seeds/batch_b deleted file mode 100644 index ccfe9e4a..00000000 --- a/survey/fixtures/data_seeds/batch_b +++ /dev/null @@ -1,21 +0,0 @@ -from survey.models import * -batch = Batch.objects.create(order=2, name="Batch B and C") - -question_1 = Question.objects.create(batch=batch, text="How many women aged between 15-49 years are in this household?", answer_type=Question.NUMBER, order=1) -question_2 = Question.objects.create(batch=batch, text="**ASK TO SPEAK TO ONE OF THESE WOMEN** \n How many of these women has had a live birth in the last 2 years?", answer_type=Question.NUMBER, order=2) - -question_3 = Question.objects.create(batch=batch, text="How many of these child births were delivered in a health facility?", answer_type=Question.NUMBER, order=3) -question_4 = Question.objects.create(batch=batch, text="How many of these child births were assisted by a skilled health personnel?", answer_type=Question.NUMBER, order=4) - -question_5 = Question.objects.create(batch=batch, text="How many of these new borns were breast fed within 1 hour of birth?", answer_type=Question.NUMBER, order=5) - -# Rules -# AnswerRule.objects.create(question=question_1, action=AnswerRule.ACTIONS['END_INTERVIEW'], condition=AnswerRule.CONDITIONS['EQUALS'], validate_with_value=0) - -AnswerRule.objects.create(question=question_2, action=AnswerRule.ACTIONS['REANSWER'], condition=AnswerRule.CONDITIONS['GREATER_THAN_QUESTION'], validate_with_question=question_1) - -AnswerRule.objects.create(question=question_3, action=AnswerRule.ACTIONS['REANSWER'], condition=AnswerRule.CONDITIONS['GREATER_THAN_QUESTION'], validate_with_question=question_2) - -AnswerRule.objects.create(question=question_4, action=AnswerRule.ACTIONS['REANSWER'], condition=AnswerRule.CONDITIONS['GREATER_THAN_QUESTION'], validate_with_question=question_2) - -AnswerRule.objects.create(question=question_5, action=AnswerRule.ACTIONS['REANSWER'], condition=AnswerRule.CONDITIONS['GREATER_THAN_QUESTION'], validate_with_question=question_2) \ No newline at end of file diff --git a/survey/fixtures/data_seeds/batch_c b/survey/fixtures/data_seeds/batch_c deleted file mode 100644 index 04ec87f9..00000000 --- a/survey/fixtures/data_seeds/batch_c +++ /dev/null @@ -1,17 +0,0 @@ -from survey.models import * -batch = Batch.objects.get(order=2, name="Batch B and C") - -question_1 = Question.objects.create(batch=batch, text="How many times did you receive antenatal care during the last pregnancy?", answer_type=Question.NUMBER, order=6) - -question_3 = Question.objects.create(batch=batch, text="During the last pregnancy, did you receive any injection on the arm or shoulder to prevent the baby from getting tetanus?", answer_type=Question.MULTICHOICE, order=7) -QuestionOption.objects.create(question=question_3, text="Yes", order=1) -no_option = QuestionOption.objects.create(question=question_3, text="No", order=2) - -question_4 = Question.objects.create(batch=batch, text="How many times did you receive this tetanus injection during your pregnancy?", answer_type=Question.NUMBER, order=8) - -# Rules -AnswerRule.objects.create(question=question_3, action=AnswerRule.ACTIONS['END_INTERVIEW'], condition=AnswerRule.CONDITIONS['EQUALS_OPTION'], validate_with_option=no_option) - -## extra rule for B and C -question_1_of_b = Question.objects.get(batch=batch, text="How many women aged between 15-49 years are in this household?", answer_type=Question.NUMBER, order=1) -AnswerRule.objects.create(question=question_1_of_b, action=AnswerRule.ACTIONS['SKIP_TO'], condition=AnswerRule.CONDITIONS['EQUALS'], validate_with_value=0, next_question=question_1) \ No newline at end of file diff --git a/survey/fixtures/data_seeds/batch_d b/survey/fixtures/data_seeds/batch_d deleted file mode 100644 index c3351266..00000000 --- a/survey/fixtures/data_seeds/batch_d +++ /dev/null @@ -1,55 +0,0 @@ -from survey.models import * -batch = Batch.objects.create(order=3, name="Batch D and E") - -question_1 = Question.objects.create(batch=batch, text="How many women aged between 15-49 years are in this household?", answer_type=Question.NUMBER, order=1) - -question_2 = Question.objects.create(batch=batch, text="**ASK TO SPEAK TO ONE OF THE WOMEN** \n Have you ever heard of an illness called AIDS?", answer_type=Question.MULTICHOICE, order=2) -QuestionOption.objects.create(question=question_2, text="Yes", order=1) -QuestionOption.objects.create(question=question_2, text="No", order=2) - -question_3 = Question.objects.create(batch=batch, text="Do you know of a place where people can go to get tested for the AIDS virus?", answer_type=Question.MULTICHOICE, order=3) -QuestionOption.objects.create(question=question_3, text="Yes", order=1) -QuestionOption.objects.create(question=question_3, text="No", order=2) - -question_4 = Question.objects.create(batch=batch, text="Have you ever been tested to see if you have the AIDS virus?", answer_type=Question.MULTICHOICE, order=4) -QuestionOption.objects.create(question=question_4, text="Yes", order=1) -no_option_4 = QuestionOption.objects.create(question=question_4, text="No", order=2) - -question_5 = Question.objects.create(batch=batch, text="When was the most recent time you were tested?", answer_type=Question.MULTICHOICE, order=5) -QuestionOption.objects.create(question=question_5, text="Less than 12 months ago", order=1) -QuestionOption.objects.create(question=question_5, text="12-23 months ago", order=2) -QuestionOption.objects.create(question=question_5, text="2 or more years ago", order=3) - -question_6 = Question.objects.create(batch=batch, text="Can the virus that causes AIDS be transmitted from a mother to her baby during pregnancy?", answer_type=Question.MULTICHOICE, order=6) -QuestionOption.objects.create(question=question_6, text="Yes", order=1) -QuestionOption.objects.create(question=question_6, text="No", order=2) - -question_7 = Question.objects.create(batch=batch, text="Can the virus that causes AIDS be transmitted from a mother to her baby during delivery?", answer_type=Question.MULTICHOICE, order=7) -QuestionOption.objects.create(question=question_7, text="Yes", order=1) -QuestionOption.objects.create(question=question_7, text="No", order=2) - -question_8 = Question.objects.create(batch=batch, text="Can the virus that causes AIDS be transmitted from a mother to her baby by breastfeeding?", answer_type=Question.MULTICHOICE, order=8) -QuestionOption.objects.create(question=question_8, text="Yes", order=1) -QuestionOption.objects.create(question=question_8, text="No", order=2) - -question_9 = Question.objects.create(batch=batch, text="How often do you read a newspaper or magazine?", answer_type=Question.MULTICHOICE, order=9) -QuestionOption.objects.create(question=question_9, text="Almost every day", order=1) -QuestionOption.objects.create(question=question_9, text="At least once a week", order=2) -QuestionOption.objects.create(question=question_9, text="Less than once a week", order=3) -QuestionOption.objects.create(question=question_9, text="Not at all", order=4) - -question_10 = Question.objects.create(batch=batch, text="How often do you listen to the radio?", answer_type=Question.MULTICHOICE, order=10) -QuestionOption.objects.create(question=question_10, text="Almost every day", order=1) -QuestionOption.objects.create(question=question_10, text="At least once a week", order=2) -QuestionOption.objects.create(question=question_10, text="Less than once a week", order=3) -QuestionOption.objects.create(question=question_10, text="Not at all", order=4) - -question_11 = Question.objects.create(batch=batch, text="How often do you watch television?", answer_type=Question.MULTICHOICE, order=11) -QuestionOption.objects.create(question=question_11, text="Almost every day", order=1) -QuestionOption.objects.create(question=question_11, text="At least once a week", order=2) -QuestionOption.objects.create(question=question_11, text="Less than once a week", order=3) -QuestionOption.objects.create(question=question_11, text="Not at all", order=4) - -# Rules -# AnswerRule.objects.create(question=question_1, action=AnswerRule.ACTIONS['END_INTERVIEW'], condition=AnswerRule.CONDITIONS['EQUALS'], validate_with_value=0) -AnswerRule.objects.create(question=question_4, action=AnswerRule.ACTIONS['SKIP_TO'], condition=AnswerRule.CONDITIONS['EQUALS_OPTION'], validate_with_option=no_option_4, next_question=question_6) diff --git a/survey/fixtures/data_seeds/batch_e b/survey/fixtures/data_seeds/batch_e deleted file mode 100644 index a10ff48d..00000000 --- a/survey/fixtures/data_seeds/batch_e +++ /dev/null @@ -1,70 +0,0 @@ -from survey.models import * -batch = Batch.objects.get(order=3, name="Batch D and E") - -question_1 = Question.objects.create(batch=batch, text="Can people reduce their chance of getting the AIDS virus by having just one uninfected sex partner who has no other sex partners?", answer_type=Question.MULTICHOICE, order=12) -QuestionOption.objects.create(question=question_1, text="Yes", order=1) -QuestionOption.objects.create(question=question_1, text="No", order=2) -QuestionOption.objects.create(question=question_1, text="Dont know", order=3) - -question_2 = Question.objects.create(batch=batch, text="Can people get the AIDS virus because of witchcraft or other supernatural means?", answer_type=Question.MULTICHOICE, order=13) -QuestionOption.objects.create(question=question_2, text="Yes", order=1) -QuestionOption.objects.create(question=question_2, text="No", order=2) -QuestionOption.objects.create(question=question_2, text="Dont know", order=3) - -question_3 = Question.objects.create(batch=batch, text="Can people reduce their chance of getting the AIDS virus by using a condom every time they have sex?", answer_type=Question.MULTICHOICE, order=14) -QuestionOption.objects.create(question=question_3, text="Yes", order=1) -QuestionOption.objects.create(question=question_3, text="No", order=2) -QuestionOption.objects.create(question=question_3, text="Dont know", order=3) - -question_4 = Question.objects.create(batch=batch, text="Can people get the AIDS virus from mosquito bites?", answer_type=Question.MULTICHOICE, order=15) -QuestionOption.objects.create(question=question_4, text="Yes", order=1) -QuestionOption.objects.create(question=question_4, text="No", order=2) -QuestionOption.objects.create(question=question_4, text="Dont know", order=3) - -question_5 = Question.objects.create(batch=batch, text="Can people get the AIDS virus by sharing food with a person who has the AIDS virus?", answer_type=Question.MULTICHOICE, order=16) -QuestionOption.objects.create(question=question_5, text="Yes", order=1) -QuestionOption.objects.create(question=question_5, text="No", order=2) -QuestionOption.objects.create(question=question_5, text="Dont know", order=3) - -question_6 = Question.objects.create(batch=batch, text="Is it possible for a healthy-looking person to have the AIDS virus?", answer_type=Question.MULTICHOICE, order=17) -QuestionOption.objects.create(question=question_6, text="Yes", order=1) -QuestionOption.objects.create(question=question_6, text="No", order=2) -QuestionOption.objects.create(question=question_6, text="Dont know", order=3) - -question_7 = Question.objects.create(batch=batch, text="In the last 12 months, have you used a computer?", answer_type=Question.MULTICHOICE, order=18) -QuestionOption.objects.create(question=question_7, text="Yes", order=1) -no_option_7 = QuestionOption.objects.create(question=question_7, text="No", order=2) - -question_8 = Question.objects.create(batch=batch, text="During the last one month, how often did you use a computer?", answer_type=Question.MULTICHOICE, order=19) -QuestionOption.objects.create(question=question_8, text="Almost every day", order=1) -QuestionOption.objects.create(question=question_8, text="At least once a week", order=2) -QuestionOption.objects.create(question=question_8, text="Less than once a week", order=3) -QuestionOption.objects.create(question=question_8, text="Not at all", order=4) - -question_9 = Question.objects.create(batch=batch, text="In the last 12 months, have you used the internet?", answer_type=Question.MULTICHOICE, order=20) -QuestionOption.objects.create(question=question_9, text="Yes", order=1) -QuestionOption.objects.create(question=question_9, text="No", order=2) - -question_10 = Question.objects.create(batch=batch, text="During the last one month, how often did you use the internet?", answer_type=Question.MULTICHOICE, order=21) -QuestionOption.objects.create(question=question_10, text="Almost every day", order=1) -QuestionOption.objects.create(question=question_10, text="At least once a week", order=2) -QuestionOption.objects.create(question=question_10, text="Less than once a week", order=3) -QuestionOption.objects.create(question=question_10, text="Not at all", order=4) - -question_11 = Question.objects.create(batch=batch, text="Compared to this time last year, would you say that your life has improved, stayed more or less the same, or worsened, overall?", answer_type=Question.MULTICHOICE, order=22) -QuestionOption.objects.create(question=question_11, text="Improved", order=1) -QuestionOption.objects.create(question=question_11, text="More or less the same", order=2) -QuestionOption.objects.create(question=question_11, text="Worsened", order=3) - -question_12 = Question.objects.create(batch=batch, text="And in one year from now, do you expect that your life will be better, will be more or less the same, or will be worse, overall?", answer_type=Question.MULTICHOICE, order=23) -QuestionOption.objects.create(question=question_12, text="Better", order=1) -QuestionOption.objects.create(question=question_12, text="More or less the same", order=2) -QuestionOption.objects.create(question=question_12, text="Worsened", order=3) - -# Rules - -AnswerRule.objects.create(question=question_7, action=AnswerRule.ACTIONS['SKIP_TO'], condition=AnswerRule.CONDITIONS['EQUALS_OPTION'], validate_with_option=no_option_7, next_question=question_9) - -## extra rule for B and C -question_1_of_d = Question.objects.get(batch=batch, text="How many women aged between 15-49 years are in this household?", answer_type=Question.NUMBER, order=1) -AnswerRule.objects.create(question=question_1_of_d, action=AnswerRule.ACTIONS['SKIP_TO'], condition=AnswerRule.CONDITIONS['EQUALS'], validate_with_value=0, next_question=question_1) \ No newline at end of file diff --git a/survey/fixtures/data_seeds/non_response_questions b/survey/fixtures/data_seeds/non_response_questions deleted file mode 100644 index 829ec2a4..00000000 --- a/survey/fixtures/data_seeds/non_response_questions +++ /dev/null @@ -1,26 +0,0 @@ -from survey.models import HouseholdMemberGroup, QuestionModule, Question, QuestionOption - -none_response_group = HouseholdMemberGroup.objects.create(name="NON_RESPONSE", order=18) -module = QuestionModule.objects.create(name='NON RESPONSE') -non_response_question = Question.objects.create(module=module, - text="Why did HH-%s-%s not take the survey", - answer_type=Question.MULTICHOICE, order=1, - group=none_response_group) - -QuestionOption.objects.create(question=non_response_question, text="Refused", order=1) -QuestionOption.objects.create(question=non_response_question, text="No competent respondent at time of visit", order=2) -QuestionOption.objects.create(question=non_response_question, text="H/H not known/not found", order=3) -QuestionOption.objects.create(question=non_response_question, text="HH/Disintegrated", order=4) -QuestionOption.objects.create(question=non_response_question, text="Not at home for extended period", order=5) -QuestionOption.objects.create(question=non_response_question, text="Dwelling destroyed", order=6) -QuestionOption.objects.create(question=non_response_question, text="Moved to another vilage/town/district", order=7) -QuestionOption.objects.create(question=non_response_question, text="Moved to a neighboring country", order=8) -QuestionOption.objects.create(question=non_response_question, text="Shifted to unknown location", order=9) -QuestionOption.objects.create(question=non_response_question, text="Transferred due to work/ education", order=10) -QuestionOption.objects.create(question=non_response_question, text="Resettled home from the camp", order=11) -QuestionOption.objects.create(question=non_response_question, text="Moved to another camp", order=12) - -member_non_response_question = Question.objects.create(module=module, text="Why did %s not take the survey", - answer_type=Question.MULTICHOICE, order=2, group=none_response_group) -QuestionOption.objects.create(question=member_non_response_question, text="Member Refused", order=1) -QuestionOption.objects.create(question=member_non_response_question, text="Reason", order=2) diff --git a/survey/fixtures/data_seeds/registration_questions b/survey/fixtures/data_seeds/registration_questions deleted file mode 100644 index b9aaa6a1..00000000 --- a/survey/fixtures/data_seeds/registration_questions +++ /dev/null @@ -1,47 +0,0 @@ -from survey.models import HouseholdMemberGroup, QuestionModule, Question, QuestionOption -from django.core import serializers - -registration_group = HouseholdMemberGroup.objects.get_or_create(name="REGISTRATION GROUP", order=0)[0] - -module = QuestionModule.objects.create(name='Registration') - -Question.objects.create(module=module, text="Please Enter the name", - answer_type=Question.TEXT, order=1, group=registration_group) - -question_2 = Question.objects.create(module=module, text="Please Enter the age", - answer_type=Question.NUMBER, order=2, group=registration_group) - -month = Question.objects.create(module=module, text="Please Enter the month of birth\n(enter 99 if not known)", - answer_type=Question.MULTICHOICE, group=registration_group, order=3) - -QuestionOption.objects.create(question=month, text="January", order=1) -QuestionOption.objects.create(question=month, text="February", order=2) -QuestionOption.objects.create(question=month, text="March", order=3) -QuestionOption.objects.create(question=month, text="April", order=4) -QuestionOption.objects.create(question=month, text="May", order=5) -QuestionOption.objects.create(question=month, text="June", order=6) -QuestionOption.objects.create(question=month, text="July", order=7) -QuestionOption.objects.create(question=month, text="August", order=8) -QuestionOption.objects.create(question=month, text="September", order=9) -QuestionOption.objects.create(question=month, text="October", order=10) -QuestionOption.objects.create(question=month, text="November", order=11) -QuestionOption.objects.create(question=month, text="December", order=12) -QuestionOption.objects.create(question=month, text="DONT KNOW", order=99) - -year = Question.objects.create(module=module, text="Please Enter the year of birth\n(enter 99 if not known)", - answer_type=Question.NUMBER, group=registration_group, order=4) - -Question.objects.create(module=module, text="Please Enter the gender:\n1.Male\n2.Female", - answer_type=Question.NUMBER, order=5, group=registration_group) -Registration_questions = Question.objects.filter(group=registration_group) -month_options = QuestionOption.objects.filter(question=month) - - -data = serializers.serialize("json", [registration_group]) -print data -data = serializers.serialize("json", [module]) -print data -data = serializers.serialize("json", Registration_questions) -print data -data = serializers.serialize("json", month_options) -print data diff --git a/survey/fixtures/permission.json b/survey/fixtures/permission.json deleted file mode 100644 index 6a5f3633..00000000 --- a/survey/fixtures/permission.json +++ /dev/null @@ -1,114 +0,0 @@ -[ - - { - "pk": 91, - "model": "auth.permission", - "fields": { - "codename": "can_view_aggregates", - "name": "Can view aggregates tab", - "content_type": ["auth", "user"] - } - }, - - { - "pk": 92, - "model": "auth.permission", - "fields": { - "codename": "can_view_users", - "name": "Can view users tab", - "content_type": ["auth", "user"] - } - }, - - { - "pk": 93, - "model": "auth.permission", - "fields": { - "codename": "can_view_batches", - "name": "Can view batches tab", - "content_type": ["auth", "user"] - } - }, - - { - "pk": 94, - "model": "auth.permission", - "fields": { - "codename": "can_view_investigators", - "name": "Can view investigators tab", - "content_type": ["auth", "user"] - } - }, - - { - "pk": 95, - "model": "auth.permission", - "fields": { - "codename": "can_view_households", - "name": "Can view households tab", - "content_type": ["auth", "user"] - } - }, - - { - "pk": 96, - "model": "auth.permission", - "fields": { - "codename": "can_view_locations", - "name": "Can view locations tab", - "content_type": ["auth", "user"] - } - }, - - { - "pk": 97, - "model": "auth.permission", - "fields": { - "codename": "can_view_household_groups", - "name": "Can view groups tab", - "content_type": ["auth", "user"] - } - }, - - { - "pk": 98, - "model": "auth.permission", - "fields": { - "codename": "can_add_location_types", - "name": "can add location types", - "content_type": ["auth", "user"] - } - }, - - { - "pk": 1, - "model": "auth.group", - "fields": { - "name": "admin", - "permissions": [19, 20, 21, 4, 5, 6, 1, 2, 3, 7, 8, 9, 10, 11, 12, 31, 32, 33, 28, 29, 30, 25, - 26, 27, 13, 14, 15, 16, 17, 18, 22, 23, 24, 88, 89, 90, 37, 38, 39, 61, 62, 63, 64, 65, 66, 52, 53, 54, 46, 47, 48, 67, 68, 69, 49, 50, 51, 70, 71, 72, 40, - 41, 42, 43, 44, 45, 85, 86, 87, 79, 80, 81, 73, 74, 75, 76, 77, 78, 58, 59, 60, 82, 83, 84, 34, 35, 36, 55, 56, 57, 91, 92, 93, 94, 95, - 96, 97, 98] - } - }, - - { - "pk": 2, - "model": "auth.group", - "fields": { - "name": "researcher", - "permissions": [31, 32, 33, 28, 29, 30, 25, 26, 27, 88, 89, 90, 37, 38, 39, 61, 62, 63, - 64, 65, 66, 52, 53, 54, 46, 47, 48, 67, 68, 69, 49, 50, 51, 70, 71, 72, 40, 41, 42, 43, 44, 45, 85, 86, 87, 79, 80, 81, 73, 74, 75, 76, 77, 78, 58, - 59, 60, 82, 83, 84, 34, 35, 36, 55, 56, 57, 95, 94, 93, 91, 96, 97] - } - }, - - { - "pk": 3, - "model": "auth.group", - "fields": { - "name": "data entry", - "permissions": [52, 53, 54, 46, 47, 48, 49, 50, 51, 40, 41, 42, 55, 56, 57, 95, 94, 96] - } - } -] \ No newline at end of file diff --git a/survey/fixtures/registration_questions.json b/survey/fixtures/registration_questions.json deleted file mode 100644 index 224fc922..00000000 --- a/survey/fixtures/registration_questions.json +++ /dev/null @@ -1,250 +0,0 @@ -[ - { - "pk": 22, - "model": "survey.householdmembergroup", - "fields": { - "order": 0, - "modified": "2013-11-27T09:15:56.680Z", - "name": "REGISTRATION GROUP", - "created": "2013-11-27T09:15:56.672Z" - } - }, - { - "pk": 37, - "model": "survey.questionmodule", - "fields": { - "description": null, - "modified": "2013-11-27T09:18:21.163Z", - "name": "Registration", - "created": "2013-11-27T09:18:21.163Z" - } - }, - { - "pk": 394, - "model": "survey.question", - "fields": { - "batches": [], - "group": 22, - "parent": null, - "created": "2013-11-27T09:18:21.168Z", - "text": "Please Enter the name", - "modified": "2013-11-27T09:18:21.169Z", - "module": 37, - "subquestion": false, - "answer_type": "text", - "identifier": null, - "order": 1 - } - }, - { - "pk": 395, - "model": "survey.question", - "fields": { - "batches": [], - "group": 22, - "parent": null, - "created": "2013-11-27T09:18:21.177Z", - "text": "Please Enter the age", - "modified": "2013-11-27T09:18:21.177Z", - "module": 37, - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 2 - } - }, - { - "pk": 396, - "model": "survey.question", - "fields": { - "batches": [], - "group": 22, - "parent": null, - "created": "2013-11-27T09:18:21.181Z", - "text": "Please Enter the month of birth\n(enter 99 if not known)", - "modified": "2013-11-27T09:18:21.181Z", - "module": 37, - "subquestion": false, - "answer_type": "multichoice", - "identifier": null, - "order": 3 - } - }, - { - "pk": 397, - "model": "survey.question", - "fields": { - "batches": [], - "group": 22, - "parent": null, - "created": "2013-11-27T09:18:44.311Z", - "text": "Please Enter the year of birth\n(enter 99 if not known)", - "modified": "2013-11-27T09:18:44.312Z", - "module": 37, - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 4 - } - }, - { - "pk": 398, - "model": "survey.question", - "fields": { - "batches": [], - "group": 22, - "parent": null, - "created": "2013-11-27T09:18:44.318Z", - "text": "Please Enter the gender:\n1.Male\n2.Female", - "modified": "2013-11-27T09:18:44.319Z", - "module": 37, - "subquestion": false, - "answer_type": "number", - "identifier": null, - "order": 5 - } - }, - { - "pk": 948, - "model": "survey.questionoption", - "fields": { - "text": "DONT KNOW", - "order": 99, - "question": 396, - "modified": "2013-11-27T09:18:33.748Z", - "created": "2013-11-27T09:18:33.748Z" - } - }, - { - "pk": 947, - "model": "survey.questionoption", - "fields": { - "text": "December", - "order": 12, - "question": 396, - "modified": "2013-11-27T09:18:33.745Z", - "created": "2013-11-27T09:18:33.744Z" - } - }, - { - "pk": 946, - "model": "survey.questionoption", - "fields": { - "text": "November", - "order": 11, - "question": 396, - "modified": "2013-11-27T09:18:33.734Z", - "created": "2013-11-27T09:18:33.734Z" - } - }, - { - "pk": 945, - "model": "survey.questionoption", - "fields": { - "text": "October", - "order": 10, - "question": 396, - "modified": "2013-11-27T09:18:33.731Z", - "created": "2013-11-27T09:18:33.731Z" - } - }, - { - "pk": 944, - "model": "survey.questionoption", - "fields": { - "text": "September", - "order": 9, - "question": 396, - "modified": "2013-11-27T09:18:33.728Z", - "created": "2013-11-27T09:18:33.728Z" - } - }, - { - "pk": 943, - "model": "survey.questionoption", - "fields": { - "text": "August", - "order": 8, - "question": 396, - "modified": "2013-11-27T09:18:33.725Z", - "created": "2013-11-27T09:18:33.725Z" - } - }, - { - "pk": 942, - "model": "survey.questionoption", - "fields": { - "text": "July", - "order": 7, - "question": 396, - "modified": "2013-11-27T09:18:33.722Z", - "created": "2013-11-27T09:18:33.722Z" - } - }, - { - "pk": 941, - "model": "survey.questionoption", - "fields": { - "text": "June", - "order": 6, - "question": 396, - "modified": "2013-11-27T09:18:33.720Z", - "created": "2013-11-27T09:18:33.719Z" - } - }, - { - "pk": 940, - "model": "survey.questionoption", - "fields": { - "text": "May", - "order": 5, - "question": 396, - "modified": "2013-11-27T09:18:33.717Z", - "created": "2013-11-27T09:18:33.716Z" - } - }, - { - "pk": 939, - "model": "survey.questionoption", - "fields": { - "text": "April", - "order": 4, - "question": 396, - "modified": "2013-11-27T09:18:33.714Z", - "created": "2013-11-27T09:18:33.714Z" - } - }, - { - "pk": 938, - "model": "survey.questionoption", - "fields": { - "text": "March", - "order": 3, - "question": 396, - "modified": "2013-11-27T09:18:33.711Z", - "created": "2013-11-27T09:18:33.711Z" - } - }, - { - "pk": 937, - "model": "survey.questionoption", - "fields": { - "text": "February", - "order": 2, - "question": 396, - "modified": "2013-11-27T09:18:33.707Z", - "created": "2013-11-27T09:18:33.707Z" - } - }, - { - "pk": 936, - "model": "survey.questionoption", - "fields": { - "text": "January", - "order": 1, - "question": 396, - "modified": "2013-11-27T09:18:33.702Z", - "created": "2013-11-27T09:18:33.702Z" - } - } -] diff --git a/survey/forms/__init__.py b/survey/forms/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/survey/forms/aboutus_form.py b/survey/forms/aboutus_form.py deleted file mode 100644 index ff21722d..00000000 --- a/survey/forms/aboutus_form.py +++ /dev/null @@ -1,12 +0,0 @@ -from django.forms import ModelForm, forms -from survey.models import AboutUs - - -class AboutUsForm(ModelForm): - - class Meta: - model = AboutUs - widgets = { - 'content': forms.Textarea(attrs={"rows": 10, 'cols': 40, "id": "content-editor"}) - } - exclude = [] diff --git a/survey/forms/aggregates.py b/survey/forms/aggregates.py deleted file mode 100644 index 192e7113..00000000 --- a/survey/forms/aggregates.py +++ /dev/null @@ -1,19 +0,0 @@ -from django import forms -from django.core.exceptions import ValidationError -from django.forms import ModelForm -from survey.models import Batch, Survey -from django.utils.safestring import mark_safe -from survey.models.formula import * - - -class InterviewerReportForm(forms.Form): - survey = forms.ModelChoiceField( - queryset=Survey.objects.all(), empty_label='----') - batch = forms.ModelChoiceField( - queryset=Batch.objects.none(), empty_label='----', required=False) - - def __init__(self, *args, **kwargs): - super(InterviewerReportForm, self).__init__(*args, **kwargs) - if self.data.get('survey'): - survey = Survey.objects.get(id=self.data['survey']) - self.fields['batch'].queryset = survey.batches.all() diff --git a/survey/forms/batch.py b/survey/forms/batch.py deleted file mode 100644 index 8af42116..00000000 --- a/survey/forms/batch.py +++ /dev/null @@ -1,61 +0,0 @@ -from django import forms -from django.core.exceptions import ValidationError -from django.forms import ModelForm -from survey.models import Batch, BatchChannel, QuestionTemplate, WebAccess -from django.utils.safestring import mark_safe -from survey.models.formula import * - - -class BatchForm(ModelForm): - access_channels = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple(attrs={'class': 'access_channels'}), - choices=[opt for opt in BatchChannel.ACCESS_CHANNELS - if not opt[0] == WebAccess.choice_name()]) - - def __init__(self, *args, **kwargs): - if kwargs.get('instance'): - initial = kwargs.setdefault('initial', {}) - initial['access_channels'] = [ - c.channel for c in kwargs['instance'].access_channels.all()] - forms.ModelForm.__init__(self, *args, **kwargs) - - class Meta: - model = Batch - fields = ['name', 'description', 'survey', ] - - widgets = { - 'description': forms.Textarea(attrs={"rows": 4, "cols": 50}), - 'survey': forms.HiddenInput(), - } - - def save(self, commit=True, **kwargs): - batch = super(BatchForm, self).save(commit=commit) - bc = BatchChannel.objects.filter(batch=batch) - bc.delete() - for val in kwargs['access_channels']: - BatchChannel.objects.create(batch=batch, channel=val) - - -class BatchQuestionsForm(ModelForm): - questions = forms.ModelMultipleChoiceField(label=u'', queryset=QuestionTemplate.objects.filter(), - widget=forms.SelectMultiple(attrs={'class': 'multi-select'})) - - class Meta: - model = Batch - fields = [] - - def __init__(self, batch=None, *args, **kwargs): - super(BatchQuestionsForm, self).__init__(*args, **kwargs) - -# def save_question_to_batch(self, batch): -# for question in self.cleaned_data['questions']: -# question.save() -# order = BatchQuestionOrder.next_question_order_for(batch) -# BatchQuestionOrder.objects.create(question=question, batch=batch, order=order) -# question.batches.add(batch) -# -# def save(self, commit=True, *args, **kwargs): -# batch = super(BatchQuestionsForm, self).save(commit=commit, *args, **kwargs) -# -# if commit: -# batch.save() -# self.save_question_to_batch(batch) diff --git a/survey/forms/enumeration_area.py b/survey/forms/enumeration_area.py deleted file mode 100644 index c4453925..00000000 --- a/survey/forms/enumeration_area.py +++ /dev/null @@ -1,126 +0,0 @@ -from django import forms -from django.core.exceptions import ValidationError -from django.forms import ModelForm, Form -from django.conf import settings -from survey.models import EnumerationArea, Location, LocationType -from django.http import QueryDict - - -class EnumerationAreaForm(ModelForm): - - def __init__(self, locations=None, *args, **kwargs): - super(EnumerationAreaForm, self).__init__(*args, **kwargs) - self.fields.keyOrder = ['name', ] - locations = locations or LocationsFilterForm().get_locations() - self.fields['locations'].queryset = locations - self.fields.keyOrder.append('locations') - - class Meta: - model = EnumerationArea - exclude = [] - widgets = { - 'name': forms.TextInput(attrs={"id": 'ea_name', "class": 'enumeration_area'}), - 'total_households': forms.TextInput({'id': 'total_households', "class": 'enumeration_area'}), - 'locations': forms.SelectMultiple(attrs={'class': 'multi-select enumeration_area', 'id': 'ea-locations'}) - } - - -class LocationsFilterForm(Form): - ''' - 1. Used to filter out locations under a given main location (eg states under a country) - 2. Also to filter out locations under given ea if defined - ''' - - last_location_selected = None - - def __init__(self, *args, **kwargs): - include_ea = kwargs.pop('include_ea', False) - super(LocationsFilterForm, self).__init__(*args, **kwargs) - data = self.data - if not isinstance(self.data, QueryDict): - self.data = QueryDict('', mutable=True) - self.data.update(data) - self.data._mutable = True - last_selected_pk = None - largest_unit = LocationType.largest_unit() - locations = Location.objects.none() - for location_type in LocationType.objects.all(): - if location_type.parent is not None and location_type.is_leaf_node() == False: - kw = {'type': location_type} - parent_selection = data.get(location_type.parent.name, None) - if (locations and parent_selection) or location_type == largest_unit: - if parent_selection: - last_selected_pk = data.get( - location_type.name, None) or parent_selection - kw['parent__pk'] = parent_selection - locations = Location.objects.filter(**kw).order_by('name') - else: - self.data[location_type.name] = '' - locations = Location.objects.none() - # choices = [(loc.pk, loc.name) for loc in locations] - # choices.insert(0, ('', '--- Select %s ---' % location_type.name)) - self.fields[location_type.name] = forms.ModelChoiceField( - queryset=locations) # forms.ChoiceField(choices=choices) - self.fields[location_type.name].required = False - self.fields[location_type.name].widget.attrs[ - 'class'] = 'location_filter ea_filters chzn-select' - # self.fields[location_type.name].widget.attrs['style'] = 'width: 100px;' - if last_selected_pk: - self.last_location_selected = Location.objects.get( - pk=last_selected_pk) - if include_ea: - if self.last_location_selected: - eas = EnumerationArea.objects.filter(locations__in=get_leaf_locs( - loc=self.last_location_selected)).distinct() - else: - eas = EnumerationArea.objects.none() - choices = [(ea.pk, ea.name) for ea in eas] - choices.insert(0, ('', '--- Select EA ---')) - self.fields['enumeration_area'] = forms.ModelChoiceField( - queryset=eas) # ChoiceField(choices=choices) - self.fields['enumeration_area'].widget.attrs[ - 'class'] = 'location_filter chzn-select' - # self.fields['enumeration_area'].widget.attrs['style'] = 'width: 100px;' - self.fields['enumeration_area'].required = False - self.data._mutable = False - - def get_locations(self): - loc = None - ea = None - if self.is_valid(): - for key in self.fields.keys(): - if key is not 'enumeration_area': - val = self.cleaned_data[key] - if val: - loc = val - else: - ea = self.cleaned_data.get(key, None) - return get_leaf_locs(loc, ea) - - def get_enumerations(self): - return EnumerationArea.objects.filter(locations__in=self.get_locations()).distinct().order_by('name') - - -def get_leaf_locs(loc=None, ea=None): - if Location.objects.exists(): - if not loc: - location = Location.objects.get(parent=None) - else: - if isinstance(loc, Location): - location = loc - else: - location = Location.objects.get(pk=loc) - locations = location.get_leafnodes(True) - if ea: - locations = locations.filter(enumeration_areas=ea) - return locations.distinct() - else: - return Location.objects.none() - -# -# def save(self, commit=True, **kwargs): -# batch = super(EnumerationAreaForm, self).save(commit=commit) -# bc = BatchChannel.objects.filter(batch=batch) -# bc.delete() -# for val in kwargs['access_channels']: -# BatchChannel.objects.create(batch=batch, channel=val) diff --git a/survey/forms/filters.py b/survey/forms/filters.py deleted file mode 100644 index f4309033..00000000 --- a/survey/forms/filters.py +++ /dev/null @@ -1,121 +0,0 @@ -from django import forms -from survey.models import HouseholdMemberGroup, QuestionModule, Question, Batch, Survey, EnumerationArea, SurveyAllocation, Location -from django.contrib.auth.handlers.modwsgi import groups_for_user -MAX_NUMBER_OF_QUESTION_DISPLAYED_PER_PAGE = 1000 -DEFAULT_NUMBER_OF_QUESTION_DISPLAYED_PER_PAGE = 20 - - -class QuestionFilterForm(forms.Form): - - groups = forms.ChoiceField( - label='Group', widget=forms.Select(), choices=[], required=False) - modules = forms.ChoiceField( - label='Module', widget=forms.Select(), choices=[], required=False) - question_types = forms.ChoiceField( - label='Question Type', widget=forms.Select(), choices=[], required=False) - - def __init__(self, data=None, initial=None, read_only=[], batch=None): - super(QuestionFilterForm, self).__init__(data=data, initial=initial) - group_choices = [('All', 'All')] - module_choices = [('All', 'All')] - question_type_choices = [('All', 'All')] - map(lambda group: group_choices.append((group.id, group.name)), - HouseholdMemberGroup.objects.all().exclude(name='REGISTRATION GROUP')) - map(lambda question_module: module_choices.append( - (question_module.id, question_module.name)), QuestionModule.objects.all()) - if batch is None: - map(lambda question_type: question_type_choices.append( - question_type), list(Question.ANSWER_TYPES)) - else: - map(lambda question_type: question_type_choices.append(question_type), - [(name, name) for name in batch.answer_types]) - self.fields['groups'].choices = group_choices - self.fields['modules'].choices = module_choices - self.fields['question_types'].choices = question_type_choices - for field in read_only: - self.fields[field].widget.attrs['readonly'] = True - self.fields[field].widget.attrs['disabled'] = True - - def filter(self, questions): - query_dict = None - if self.is_valid(): - query_dict = {'group': self.cleaned_data['groups'], - 'module': self.cleaned_data['modules'], - 'answer_type': self.cleaned_data['question_types']} - for key, val in query_dict.items(): - if val == 'All' or not val: - del query_dict[key] - if query_dict is None: - query_dict = {'group__in': [key for key, val in self.fields['groups'].choices if not val == 'All'], - 'module__in': [key for key, val in self.fields['modules'].choices if not val == 'All'], - 'answer_type__in': [key for key, val in self.fields['question_types'].choices if not val == 'All']} - return questions.filter(**query_dict) - - -class IndicatorFilterForm(forms.Form): - survey = forms.ChoiceField(label='Survey', widget=forms.Select( - attrs={'id': 'id_filter_survey'}), choices=[], required=False) - batch = forms.ChoiceField( - label='Batch', widget=forms.Select(), choices=[], required=False) - module = forms.ChoiceField( - label='Module', widget=forms.Select(), choices=[], required=False) - - def __init__(self, data=None, initial=None): - super(IndicatorFilterForm, self).__init__(data=data, initial=initial) - all_surveys, all_batches, all_modules = self.set_all_choices(data) - self.fields['survey'].choices = all_surveys - self.fields['batch'].choices = all_batches - self.fields['module'].choices = all_modules - - def set_all_choices(self, data=None): - all_batches = [('All', 'All')] - all_surveys = [('All', 'All')] - all_modules = [('All', 'All')] - batches = Batch.objects.all() - if data and data.get('survey', None).isdigit(): - batches = batches.filter(survey__id=int(data.get('survey', None))) - map(lambda batch: all_batches.append((batch.id, batch.name)), batches) - map(lambda survey: all_surveys.append( - (survey.id, survey.name)), Survey.objects.all()) - map(lambda module: all_modules.append( - (module.id, module.name)), QuestionModule.objects.all()) - - return all_surveys, all_batches, all_modules - - -class LocationFilterForm(forms.Form): - survey = forms.ModelChoiceField( - queryset=Survey.objects.all().order_by('name'), empty_label='----') - batch = forms.ModelChoiceField( - queryset=Batch.objects.none(), empty_label='----') - location = forms.ModelChoiceField( - queryset=Location.objects.all(), widget=forms.HiddenInput(), required=False) - ea = forms.ModelChoiceField( - queryset=None, widget=forms.HiddenInput(), required=False) - - def __init__(self, *args, **kwargs): - super(LocationFilterForm, self).__init__(*args, **kwargs) - if self.data.get('survey'): - survey = Survey.objects.get(id=self.data['survey']) - self.fields[ - 'batch'].queryset = survey.batches.all().order_by('name') - self.fields['ea'].queryset = EnumerationArea.objects.filter( - survey_allocations__survey=survey) - - -class SurveyBatchFilterForm(forms.Form): - AS_TEXT = 1 - AS_LABEL = 0 - survey = forms.ModelChoiceField( - queryset=Survey.objects.all().order_by('name'), empty_label='----') - batch = forms.ModelChoiceField(queryset=Batch.objects.all( - ).order_by('name'), empty_label='----', required=False) - multi_option = forms.ChoiceField( - choices=[(AS_TEXT, 'As Text'), (AS_LABEL, 'As Value')]) - - def __init__(self, *args, **kwargs): - super(SurveyBatchFilterForm, self).__init__(*args, **kwargs) - # import pdb; pdb.set_trace() - if self.data.get('survey'): - survey = Survey.objects.get(id=self.data['survey']) - self.fields['batch'].queryset = survey.batches.all() diff --git a/survey/forms/formula.py b/survey/forms/formula.py deleted file mode 100644 index e8306eb0..00000000 --- a/survey/forms/formula.py +++ /dev/null @@ -1,87 +0,0 @@ -from django.core.exceptions import ValidationError -from django.forms import ModelForm -from django import forms -from survey.models import Formula, HouseholdMemberGroup - - -class FormulaForm(ModelForm): - OPTIONS = ( - ("GROUP", "GROUP"), - ("QUESTION", "QUESTION"), - ) - - def __init__(self, indicator=None, *args, **kwargs): - super(FormulaForm, self).__init__(*args, **kwargs) - self.indicator = indicator - self.fields['denominator'].label = "" - self.fields['count'].label = "" - self.fields['groups'].label = "" - - question_choices = [] - if indicator.batch: - for question in indicator.batch.survey_questions: - if question.module == indicator.module: - question_choices.append((question.id, question.text)) - - groups = HouseholdMemberGroup.objects.all() - self.fields['numerator'].choices = question_choices - self.fields['denominator'].choices = question_choices - self.fields['count'].choices = question_choices - self.fields['groups'].choices = [ - (group.id, group.name) for group in groups] - - if indicator: - self.delete_fields_based_on(indicator) - - def delete_fields_based_on(self, indicator): - if indicator.is_percentage_indicator(): - deleted_fields = ['count'] - denominator_label = "Denominator" - else: - deleted_fields = ['numerator', 'denominator', 'numerator_options'] - denominator_label = "Count" - - for field in deleted_fields: - del self.fields[field] - self.fields['denominator_type'].label = denominator_label - - def clean(self): - cleaned_data = self.cleaned_data - is_question_selected = cleaned_data['denominator_type'] == 'QUESTION' - denominator = cleaned_data.get('denominator', None) - groups = cleaned_data.get('groups', None) - count = cleaned_data.get('count', None) - - error_message = 'Formula already exist for indicator %s.' % self.indicator.name - existing_formula = [] - - if self.indicator and self.indicator.is_percentage_indicator(): - - if denominator: - existing_formula = Formula.objects.filter(indicator=self.indicator, numerator=cleaned_data['numerator'], - denominator=denominator) - if groups and not is_question_selected: - existing_formula = Formula.objects.filter(indicator=self.indicator, numerator=cleaned_data['numerator'], - groups=groups) - - if self.indicator and not self.indicator.is_percentage_indicator(): - if count: - existing_formula = Formula.objects.filter( - indicator=self.indicator, count=count) - - if groups and not is_question_selected: - existing_formula = Formula.objects.filter( - indicator=self.indicator, groups=groups) - - if existing_formula: - raise ValidationError(error_message) - - return self.cleaned_data - - class Meta: - model = Formula - fields = ['numerator', 'numerator_options', 'denominator_type', 'groups', 'denominator', 'count', - 'denominator_options'] - - denominator_type = forms.CharField( - label="Denominator", widget=forms.Select(choices=OPTIONS)) diff --git a/survey/forms/group_condition.py b/survey/forms/group_condition.py deleted file mode 100644 index 8abfc093..00000000 --- a/survey/forms/group_condition.py +++ /dev/null @@ -1,83 +0,0 @@ -from django.forms import ModelForm -from django import forms -from survey.models.householdgroups import GroupCondition - - -class GroupConditionForm(ModelForm): - - def clean_gender_condition(self): - self.condition_should_be_equal_for('GENDER') - - def clean_general_condition(self): - self.condition_should_be_equal_for('GENERAL') - - def condition_should_be_equal_for(self, attribute_str): - if self.cleaned_data.get('condition', None) and self.cleaned_data.get('attribute', None): - condition = self.cleaned_data['condition'] - attribute = self.cleaned_data['attribute'] - - if attribute == GroupCondition.GROUP_TYPES[attribute_str] and condition != GroupCondition.CONDITIONS['EQUALS']: - message = "%s can only have condition: %s." % ( - GroupCondition.GROUP_TYPES[attribute_str], GroupCondition.CONDITIONS['EQUALS']) - self._errors['condition'] = self.error_class([message]) - del self.cleaned_data['condition'] - return self.cleaned_data - - def clean_gender_values(self): - if self.cleaned_data.get('condition', None) and self.cleaned_data.get('value', None): - value = self.cleaned_data['value'] - attribute = self.cleaned_data['attribute'] - - if attribute == GroupCondition.GROUP_TYPES['GENDER'] and str(value) not in ['1', '0']: - message = "%s can only have male or female values." % GroupCondition.GROUP_TYPES[ - 'GENDER'] - self._errors['value'] = self.error_class([message]) - del self.cleaned_data['value'] - return self.cleaned_data - - def set_value_error_message(self, message): - self._errors['value'] = self.error_class([message]) - del self.cleaned_data['value'] - - def clean_age_values(self): - if self.cleaned_data.get('condition', None) and self.cleaned_data.get('value', None): - value = self.cleaned_data['value'] - attribute = self.cleaned_data['attribute'] - - if attribute == GroupCondition.GROUP_TYPES['AGE']: - try: - if int(value) < 0: - message = "Age cannot be negative." - self.set_value_error_message(message) - except ValueError: - message = "Age must be a whole number." - self.set_value_error_message(message) - - return self.cleaned_data - - def clean_general_values(self): - if self.cleaned_data.get('condition', None) and self.cleaned_data.get('value', None): - value = self.cleaned_data['value'] - attribute = self.cleaned_data['attribute'] - if attribute == 'GENERAL' and str(value) not in ['1', '0']: - message = "%s can only have the value: HEAD. or Not HEAD" % GroupCondition.GROUP_TYPES[ - 'GENERAL'] - self._errors['value'] = self.error_class([message]) - del self.cleaned_data['value'] - return self.cleaned_data - - def clean(self): - self.clean_gender_values() - self.clean_age_values() - self.clean_gender_condition() - self.clean_general_values() - self.clean_general_condition() - self.cleaned_data = super(GroupConditionForm, self).clean() - return self.cleaned_data - - class Meta: - model = GroupCondition - fields = ['attribute', 'condition', 'value'] - widgets = { - 'value': forms.TextInput(attrs={'type': 'number', 'min': 0, 'max': 100}), - } diff --git a/survey/forms/household.py b/survey/forms/household.py deleted file mode 100644 index 2d28158f..00000000 --- a/survey/forms/household.py +++ /dev/null @@ -1,49 +0,0 @@ -from django.core.exceptions import ValidationError -from django.forms import ModelForm -from survey.models.households import Household -from survey.models import WebAccess, Interviewer, SurveyAllocation -from django import forms - - -class HouseholdForm(ModelForm): - - class Meta: - model = Household - exclude = ['listing', 'head_desc'] - widgets = { - 'ea': forms.HiddenInput(), - 'registration_channel': forms.HiddenInput(), - 'physical_address': forms.Textarea(attrs={"rows": 4, "cols": 100, "maxlength": "150"}), - } - - def __init__(self, is_edit=False, eas=[], survey=None, *args, **kwargs): - super(HouseholdForm, self).__init__(*args, **kwargs) - self.is_editing = is_edit - self.fields['registration_channel'].initial = WebAccess.choice_name() - if eas: - self.fields['last_registrar'].queryset = Interviewer.objects.filter( - ea__pk__in=[ea.pk for ea in eas]) - -# if not self.is_editing: -# self.fields['uid'].initial = Household.next_uid(survey) -# else: -# self.fields['uid'].initial = self.instance.uid -# self.fields['uid'].widget.attrs['disabled'] = 'disabled' - def clean_registrar(self): - if SurveyAllocation.get_allocation(self.cleaned_data['last_registrar']) is None: - raise ValidationError( - "No open survey available for this Interviewer yet.") - return self.cleaned_data['registrar'] - - def clean_house_number(self): - if self.instance is None: - try: - house_number = self.cleaned_data['house_number'] - household = Household.objects.filter( - house_number=int(house_number)) - if household: - raise ValidationError( - "Household with this Household Number already exists.") - except TypeError: - raise ValidationError("This field is required.") - return self.cleaned_data['house_number'] diff --git a/survey/forms/householdHead.py b/survey/forms/householdHead.py deleted file mode 100644 index e19800ed..00000000 --- a/survey/forms/householdHead.py +++ /dev/null @@ -1,40 +0,0 @@ -from datetime import datetime -from django import forms -from django.forms import ModelForm, DateInput -from survey.models.households import HouseholdHead -from survey.interviewer_configs import OCCUPATION, MONTHS -from widgets import InlineRadioSelect -from django.conf import settings -from django.core.exceptions import ValidationError - - -class HouseholdHeadForm(ModelForm): - - class Meta: - model = HouseholdHead - fields = ['surname', 'first_name', 'date_of_birth', 'gender', - 'level_of_education', 'resident_since', 'occupation'] - widgets = { - 'surname': forms.TextInput(attrs={'placeholder': 'Family Name'}), - 'first_name': forms.TextInput(attrs={'placeholder': 'Other Names'}), - 'male': InlineRadioSelect(choices=((True, 'Male'), (False, 'Female'))), - 'occupation': forms.Select(choices=OCCUPATION), - } - date_of_birth = forms.DateField(label="Date of birth", required=True, - input_formats=[settings.DATE_FORMAT, ], - widget=forms.DateInput(attrs={'placeholder': 'Date Of Birth', - 'class': 'datepicker'}, - format=settings.DATE_FORMAT),) - resident_since = forms.DateField(required=True, input_formats=[settings.DATE_FORMAT, ], - widget=forms.DateInput(attrs={'placeholder': 'Date Of Birth', - 'class': 'datepicker'}, - format=settings.DATE_FORMAT), - help_text='Date the person started living there') - - def clean_resident_since(self): - resident_since = self.cleaned_data['resident_since'] - date_of_birth = self.cleaned_data['date_of_birth'] - if date_of_birth > resident_since: - raise ValidationError( - 'Member cannot be resident before date of birth') - return self.cleaned_data['resident_since'] diff --git a/survey/forms/householdMember.py b/survey/forms/householdMember.py deleted file mode 100644 index 8a3a1ba9..00000000 --- a/survey/forms/householdMember.py +++ /dev/null @@ -1,22 +0,0 @@ -from django.forms import ModelForm, DateInput -from django import forms -from survey.models.households import HouseholdMember -from django.conf import settings - - -class HouseholdMemberForm(ModelForm): - - def __init__(self, *args, **kwargs): - super(HouseholdMemberForm, self).__init__(*args, **kwargs) - - class Meta: - model = HouseholdMember - fields = ['surname', 'first_name', 'date_of_birth', 'gender'] - widgets = { - 'surname': forms.TextInput(attrs={'placeholder': 'Family Name'}), - 'first_name': forms.TextInput(attrs={'placeholder': 'Other Names'}), - 'male': forms.RadioSelect(choices=((True, 'Male'), (False, 'Female'))), - } - date_of_birth = forms.DateField(label="Date of birth", required=True, input_formats=[settings.DATE_FORMAT, ], - widget=forms.DateInput(attrs={'placeholder': 'Date Of Birth', - 'class': 'datepicker'}, format=settings.DATE_FORMAT)) diff --git a/survey/forms/household_member_group.py b/survey/forms/household_member_group.py deleted file mode 100644 index 6912ed4c..00000000 --- a/survey/forms/household_member_group.py +++ /dev/null @@ -1,49 +0,0 @@ -from django.forms import ModelForm -from django import forms -from survey.models.householdgroups import HouseholdMemberGroup, GroupCondition - - -class HouseholdMemberGroupForm(ModelForm): - - conditions = forms.ModelMultipleChoiceField(label=u'Eligibility Criteria', queryset=GroupCondition.objects.all(), - widget=forms.SelectMultiple(attrs={'class': 'multi-select'})) - - class Meta: - model = HouseholdMemberGroup - exclude = [] - - def clean(self): - cleaned_data = super(HouseholdMemberGroupForm, self).clean() - selected_conditions = cleaned_data.get('conditions', []) - self_pk = None - if self.instance: - self_pk = self.instance.pk - hmgroups = HouseholdMemberGroup.objects.filter( - conditions__in=selected_conditions).exclude(pk=self_pk) - total_selected = len(selected_conditions) - for group in hmgroups: - if group.conditions.count() == total_selected: - raise forms.ValidationError( - 'Same conditions exist with %s group' % group) - return cleaned_data - - def add_conditions(self, group): - group.conditions.clear() - for condition in self.cleaned_data['conditions']: - condition.groups.add(group) - - def save(self, commit=True, *args, **kwargs): - group = super(HouseholdMemberGroupForm, self).save( - commit=commit, *args, **kwargs) - if commit: - self.add_conditions(group) - return group - - def clean_order(self): - order = self.cleaned_data['order'] - if HouseholdMemberGroup.objects.filter(order=order).count() > 0 and self.initial.get('order', None) != int(order): - message = 'This order already exists. The minimum available is %d.' % ( - HouseholdMemberGroup.max_order() + 1) - self._errors['order'] = self.error_class([message]) - del self.cleaned_data['order'] - return order diff --git a/survey/forms/indicator.py b/survey/forms/indicator.py deleted file mode 100644 index 9884acfc..00000000 --- a/survey/forms/indicator.py +++ /dev/null @@ -1,42 +0,0 @@ -from django import forms -from django.forms import ModelForm -from survey.models import Indicator, Batch, QuestionModule, Survey -from django.core.exceptions import ValidationError - - -class IndicatorForm(ModelForm): - survey = forms.ModelChoiceField( - queryset=Survey.objects.all(), empty_label=None) - batch = forms.ModelChoiceField( - queryset=Batch.objects.none(), empty_label='Select Batch', required=False) - - def __init__(self, *args, **kwargs): - super(IndicatorForm, self).__init__(*args, **kwargs) - if kwargs.get('instance'): - batch = kwargs['instance'].batch - survey = batch.survey - self.fields['survey'].initial = survey - self.fields['batch'].queryset = survey.batches - self.fields['batch'].initial = batch - if self.data.get('survey'): - self.fields['batch'].queryset = Batch.objects.filter( - survey=self.data['survey']) - self.fields[ - 'module'].queryset = QuestionModule.objects.order_by('name') - self.fields['name'].label = 'Indicator' - - def clean(self): - super(IndicatorForm, self).clean() - batch = self.cleaned_data.get('batch', None) - survey = self.cleaned_data.get('survey', None) - if batch and batch.survey != survey: - message = "Batch %s does not belong to the selected Survey." % ( - batch.name) - self._errors['batch'] = self.error_class([message]) - del self.cleaned_data['batch'] - return self.cleaned_data - - class Meta: - model = Indicator - fields = ['survey', 'batch', 'module', - 'name', 'description', 'measure'] diff --git a/survey/forms/interviewer.py b/survey/forms/interviewer.py deleted file mode 100644 index 88b7bd0d..00000000 --- a/survey/forms/interviewer.py +++ /dev/null @@ -1,139 +0,0 @@ -from django import forms -from django.forms import ModelForm -from survey.models import Interviewer, ODKAccess, USSDAccess, BatchLocationStatus, Survey, EnumerationArea, SurveyAllocation -from django.forms.models import inlineformset_factory -from django.conf import settings -from django.core.exceptions import ValidationError -import phonenumbers - - -class InterviewerForm(ModelForm): - survey = forms.ModelChoiceField( - queryset=Survey.objects.all(), required=False) - date_of_birth = forms.DateField(label="Date of birth", required=True, input_formats=[settings.DATE_FORMAT, ], - widget=forms.DateInput(attrs={'placeholder': 'Date Of Birth', - 'class': 'datepicker'}, format=settings.DATE_FORMAT)) - - def __init__(self, eas, data=None, *args, **kwargs): - super(InterviewerForm, self).__init__(data=data, *args, **kwargs) - self.fields.keyOrder = ['name', 'gender', 'date_of_birth', - 'level_of_education', 'language', 'ea', 'survey'] - if self.instance: - try: - self.fields['survey'].initial = SurveyAllocation.objects.filter(interviewer=self.instance, - status__in=[SurveyAllocation.PENDING, - SurveyAllocation.COMPLETED])[0].survey.pk - except IndexError: - pass - self.fields['ea'].queryset = eas - - class Meta: - model = Interviewer - fields = ['name', 'date_of_birth', 'gender', - 'level_of_education', 'language', 'ea'] - widgets = { - 'name': forms.TextInput(attrs={'placeholder': 'Name'}), - 'gender': forms.RadioSelect(choices=((True, 'Male'), (False, 'Female'))), - 'ea': forms.Select(attrs={'class': 'chzn-select ea_filter'}), - } - - def clean_survey(self): - ea = self.data.get('ea', '') - if not ea: - raise ValidationError('No Enumeration Area selected') - survey = self.cleaned_data['survey'] - if survey: - # check if this has already been allocated to someone else - allocs = SurveyAllocation.objects.filter(survey=survey, status__in=[SurveyAllocation.PENDING, - SurveyAllocation.COMPLETED], - interviewer__ea=ea, - allocation_ea=ea) - if self.instance and self.instance.pk: - allocs = allocs.exclude(interviewer=self.instance) - if allocs.exists(): - raise ValidationError( - 'Survey already active in %s for Interviewer %s' % (ea, allocs[0].interviewer)) - return survey - - # def clean(self): - # ea_id = self.data.get('ea') - # #check if interviewer is already active with survey in current EA - # if self.instance and self.instance.pk and ea_id != self.instance.ea.pk and self.instance.has_survey(): - # raise ValidationError('Interviewer is having an active survey') - # return self.cleaned_data - - def save(self, commit=True, **kwargs): - interviewer = super(InterviewerForm, self).save( - commit=commit, **kwargs) - if commit: - survey = self.cleaned_data['survey'] - if survey: - ea = self.cleaned_data['ea'] - interviewer.assignments.update( - status=SurveyAllocation.DEALLOCATED) - SurveyAllocation.objects.create(survey=survey, - interviewer=interviewer, - allocation_ea=ea) - return interviewer - - -class USSDAccessForm(ModelForm): - user_identifier = forms.CharField(label='Mobile Number', - max_length=settings.MOBILE_NUM_MAX_LENGTH, - min_length=settings.MOBILE_NUM_MIN_LENGTH, - widget=forms.TextInput(attrs={'placeholder': 'Format: 771234567', - 'style': "width:172px;", - 'maxlength': settings.MOBILE_NUM_MAX_LENGTH, - 'minlength': settings.MOBILE_NUM_MIN_LENGTH})) - - def __init__(self, *args, **kwargs): - super(USSDAccessForm, self).__init__(*args, **kwargs) - self.fields.keyOrder = ['is_active', 'user_identifier', ] - - class Meta: - model = USSDAccess - exclude = ['reponse_timeout', 'duration', 'interviewer', 'aggregator'] - - def clean_user_identifier(self): - identifier = self.cleaned_data.get('user_identifier', '') - try: - identifier = phonenumbers.parse(identifier, settings.COUNTRY_CODE) - if phonenumbers.is_valid_number_for_region(identifier, settings.COUNTRY_CODE): - self.cleaned_data[ - 'user_identifier'] = identifier.national_number - else: - raise ValidationError('Invalid mobile number for your region') - except phonenumbers.NumberParseException: - raise ValidationError('Invalid mobile number') - accesses = USSDAccess.objects.filter( - user_identifier=identifier.national_number) - if self.instance and accesses.exclude(interviewer=self.instance.interviewer).exists(): - raise ValidationError('This mobile number is already in use by %s' % accesses. - exclude(interviewer=self.instance.interviewer)[0].interviewer.name) - return self.cleaned_data['user_identifier'] - - -class ODKAccessForm(ModelForm): - user_identifier = forms.CharField(label='ODK ID') - - def __init__(self, *args, **kwargs): - super(ODKAccessForm, self).__init__(*args, **kwargs) - self.fields.keyOrder = ['is_active', 'user_identifier', 'odk_token', ] - - def clean_user_identifier(self): - identifier = self.cleaned_data.get('user_identifier', '') - accesses = ODKAccess.objects.filter(user_identifier=identifier) - if self.instance: - try: - accesses = accesses.exclude( - interviewer=self.instance.interviewer) - except Interviewer.DoesNotExist: - pass - if accesses.exists(): - raise ValidationError('This ODK ID is already in use by %s' % accesses[ - 0].interviewer.name) - return self.cleaned_data['user_identifier'] - - class Meta: - model = ODKAccess - exclude = ['reponse_timeout', 'duration', 'interviewer'] diff --git a/survey/forms/location_details.py b/survey/forms/location_details.py deleted file mode 100644 index d6186a17..00000000 --- a/survey/forms/location_details.py +++ /dev/null @@ -1,15 +0,0 @@ -from django import forms -from django.forms import ModelForm -from survey.models.location_type_details import LocationTypeDetails - - -class LocationDetailsForm(ModelForm): - levels = forms.CharField(label='Level1', max_length=50, required=True) - - class Meta: - model = LocationTypeDetails - fields = ['required', 'has_code', 'length_of_code'] - - widgets = { - 'has_code': forms.CheckboxInput(attrs={'class': 'has_code'}), - } diff --git a/survey/forms/location_hierarchy.py b/survey/forms/location_hierarchy.py deleted file mode 100644 index 49058107..00000000 --- a/survey/forms/location_hierarchy.py +++ /dev/null @@ -1,42 +0,0 @@ -from django import forms -from django.forms.formsets import BaseFormSet -from survey.models.locations import * -from survey.models import LocationTypeDetails - - -class BaseArticleFormSet(BaseFormSet): - - def clean(self): - for form_count in range(0, self.total_form_count()): - form = self.forms[form_count] - has_code = form.cleaned_data.get('has_code', None) - code = form.cleaned_data.get('length_of_code', '') - levels = form.cleaned_data.get('levels', '') - - if len(levels.strip()) == 0: - message = "field cannot be empty." - form._errors["levels"] = form.error_class([message]) - raise forms.ValidationError(message) - - if has_code: - if not code: - message = "length of code cannot be blank if has code is checked." - form._errors["length_of_code"] = form.error_class([ - message]) - raise forms.ValidationError(message) - - -class LocationHierarchyForm(forms.Form): - - def __init__(self, data=None): - super(LocationHierarchyForm, self).__init__(data=data) - self.fields['country'] = forms.ChoiceField( - label='Country', choices=self.get_country_choices(), widget=forms.Select, required=True) - - def get_country_choices(self): - existing_country_details = LocationTypeDetails.objects.exclude( - country=None) - if existing_country_details: - existing_country = existing_country_details[0].country - return [(existing_country.id, existing_country.name)] - return [(country.id, country.name) for country in Location.objects.filter(type__name__iexact='country')] diff --git a/survey/forms/locations.py b/survey/forms/locations.py deleted file mode 100644 index 4307a4e2..00000000 --- a/survey/forms/locations.py +++ /dev/null @@ -1,64 +0,0 @@ -from django import forms -from django.forms import ModelForm -from survey.models.locations import * -from django.template.defaultfilters import slugify -from django.core.exceptions import ValidationError - -from survey.models.formula import * - - -class LocationTypeForm(ModelForm): - - def clean_name(self): - name = self.cleaned_data['name'] - types_with_same_name = LocationType.objects.filter(name=name) - if types_with_same_name and self.initial.get('name', None) != str(name): - message = "%s already exists" % name - self._errors['name'] = self.error_class([message]) - del self.cleaned_data['name'] - return name - - def save(self, commit=True, *args, **kwargs): - a_type = super(LocationTypeForm, self).save( - commit=False, *args, **kwargs) - a_type.slug = slugify(a_type.name) - if commit: - a_type.save() - - return a_type - - class Meta: - model = LocationType - exclude = ['slug'] - - -class LocationForm(ModelForm): - - def editing_instance(self, cleaned_data): - for field in cleaned_data.keys(): - if self.initial.get(field, None) != cleaned_data[field]: - return False - return True - - def clean_type(self): - a_type = self.cleaned_data['type'] - if not a_type: - message = "This field is required." - self._errors['type'] = self.error_class([message]) - del self.cleaned_data['type'] - return a_type - - def clean(self): - cleaned_data = super(LocationForm, self).clean() - locations_with_same_attributes = Location.objects.filter( - **cleaned_data) - if locations_with_same_attributes and not self.editing_instance(cleaned_data): - raise ValidationError('This location already exists.') - return cleaned_data - - class Meta: - model = Location - exclude = ['point', 'parent_type', 'parent_id'] - widgets = { - 'tree_parent': forms.Select(attrs={'class': 'chzn-select', 'data-placeholder': 'Select or Type District'}), - } diff --git a/survey/forms/logic.py b/survey/forms/logic.py deleted file mode 100644 index ed6ad215..00000000 --- a/survey/forms/logic.py +++ /dev/null @@ -1,157 +0,0 @@ -from django import forms -from django.core.exceptions import ValidationError -from survey.models import Answer, MultiChoiceAnswer, MultiSelectAnswer, DateAnswer, QuestionFlow, Question, TextArgument -from survey.models.helper_constants import CONDITIONS -from django.db import IntegrityError - - -class LogicForm(forms.Form): - SKIP_TO = 'SKIP_TO' - END_INTERVIEW = 'END_INTERVIEW' - REANSWER = 'REANSWER' - ASK_SUBQUESTION = 'ASK_SUBQUESTION' - BACK_TO = 'BACK_TO' - BACK_TO_ACTION = 'BACK TO' - SUBQUESTION_ACTION = 'ASK SUBQUESTION' - ACTIONS = { - END_INTERVIEW: 'END INTERVIEW', - SKIP_TO: 'SKIP TO', - REANSWER: 'RECONFIRM', - BACK_TO: BACK_TO_ACTION, - ASK_SUBQUESTION: SUBQUESTION_ACTION, - } - - def __init__(self, question, initial=None, *args, **kwargs): - super(LogicForm, self).__init__(initial=initial, *args, **kwargs) - data = kwargs.get('data', None) - batch = question.batch - self.question = question - self.batch = batch - self.fields['condition'] = forms.ChoiceField(label='Eligibility criteria', choices=[], widget=forms.Select, - required=False) - self.fields['attribute'] = forms.ChoiceField(label='Attribute', choices=[('value', 'Value'), ], - widget=forms.Select, required=False) - self.fields['condition'].choices = [(validator.__name__, validator.__name__.upper()) - for validator in Answer.get_class(question.answer_type).validators()] - if question.answer_type in [MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name()]: - self.fields['option'] = forms.ChoiceField( - label='', choices=[], widget=forms.Select, required=True) - self.fields['option'].choices = [ - (option.order, option.text) for option in question.options.all()] - else: - self.fields['value'] = forms.CharField(label='', required=False) - self.fields['min_value'] = forms.CharField(label='', required=False, - widget=forms.TextInput(attrs={'placeholder': 'Min Value'})) - self.fields['max_value'] = forms.CharField(label='', required=False, - widget=forms.TextInput(attrs={'placeholder': 'Max Value'})) - if question.answer_type == DateAnswer.choice_name(): - self.fields['value'].widget.attrs['class'] = 'datepicker' - self.fields['min_value'].widget.attrs['class'] = 'datepicker' - self.fields['max_value'].widget.attrs['class'] = 'datepicker' - # validate_with_question = forms.ChoiceField(label='', choices=[], widget=forms.Select, required=False) - self.fields['action'] = forms.ChoiceField( - label='Then', choices=[], widget=forms.Select, required=True) - flows = self.question.flows.all() - existing_nexts = [f.next_question.pk for f in flows if f.next_question] - next_q_choices = [(q.pk, q.text) for q in batch.questions_inline( - ) if q.pk is not self.question.pk] - # and q.pk not in existing_nexts] - next_q_choices.extend([(q.pk, q.text) - for q in batch.zombie_questions()]) - self.fields['next_question'] = forms.ChoiceField(label='', choices=next_q_choices, widget=forms.Select, - required=False) - self.fields['next_question'].widget.attrs['class'] = 'chzn-select' - self.fields['action'].choices = self.ACTIONS.items() - - action_sent = data.get('action', None) if data else None - - def clean_value(self): - - if self.question.answer_type not in [MultiSelectAnswer.choice_name(), MultiChoiceAnswer.choice_name()] \ - and self.cleaned_data['condition'] != 'between' and \ - len(self.cleaned_data['value'].strip()) == 0: - raise ValidationError("Field is required.") - value = self.cleaned_data.get('value', '') - if value: - Answer.get_class( - self.question.answer_type).validate_test_value(value) - return self.cleaned_data.get('value', '') - - def clean_min_value(self): - if (self.cleaned_data['condition'] == 'between') and len(self.cleaned_data['min_value'].strip()) == 0: - raise ValidationError("Field is required.") - value = self.cleaned_data.get('min_value', '') - if value: - Answer.get_class( - self.question.answer_type).validate_test_value(value) - return self.cleaned_data.get('min_value', '') - - def clean_max_value(self): - if (self.cleaned_data['condition'] == 'between') and len(self.cleaned_data['max_value'].strip()) == 0: - raise ValidationError("Field is required.") - value = self.cleaned_data.get('max_value') - if value: - Answer.get_class( - self.question.answer_type).validate_test_value(value) - return self.cleaned_data.get('max_value', '') - - def _make_desc(self): - # return '%s-%s' % (self.cleaned_data['condition'], - # self.ACTIONS[self.cleaned_data['action']]) - return self.ACTIONS[self.cleaned_data['action']] - - def clean(self): - field_name = "" - rule = [] - desc = self._make_desc() - flows = QuestionFlow.objects.filter(question=self.question) - - if len(flows) > 0: - for flow in flows: - if self.cleaned_data['condition'] == 'between': - min_val = self.cleaned_data.get('min_value') - max_val = self.cleaned_data.get('max_value') - min_arg = flow.text_arguments.filter( - position=0, param=min_val).exists() - max_arg = flow.text_arguments.filter( - position=0, param=max_val).exists() - if max_arg and min_arg: - raise ValidationError("This rule already exists.") - elif flow.text_arguments.filter(position=0, param=self.cleaned_data.get('value', '').strip()).exists(): - raise ValidationError("This rule already exists.") - if flow.next_question and flow.next_question.pk == self.cleaned_data.get('next_question', ''): - raise ValidationError( - "Logic rule already exists to selected next question.") - - return self.cleaned_data - - def save(self, *args, **kwargs): - next_question = None - desc = self._make_desc() - if self.cleaned_data['action'] in [self.ASK_SUBQUESTION, self.SKIP_TO, self.BACK_TO]: - next_question = Question.objects.get( - pk=self.cleaned_data['next_question']) - if self.cleaned_data['action'] == self.REANSWER: - next_question = self.question - flow = QuestionFlow.objects.create(question=self.question, - validation_test=self.cleaned_data[ - 'condition'], - next_question=next_question, - desc=desc) - if self.cleaned_data['action'] == self.ASK_SUBQUESTION: - # connect back to next inline question of the main - QuestionFlow.objects.create(question=next_question, - desc=desc, - next_question=self.batch.next_inline(self.question)) - if self.cleaned_data['condition'] == 'between': - TextArgument.objects.create( - flow=flow, position=0, param=self.cleaned_data['min_value']) - TextArgument.objects.create( - flow=flow, position=1, param=self.cleaned_data['max_value']) - else: - value = self.cleaned_data.get('value', '') - if self.question.answer_type in [MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name()]: - value = self.cleaned_data['option'] - TextArgument.objects.create(flow=flow, position=0, param=value) - # clean up now, remove all zombie questions - self.question.batch.zombie_questions().delete() diff --git a/survey/forms/question.py b/survey/forms/question.py deleted file mode 100644 index 0ffb04da..00000000 --- a/survey/forms/question.py +++ /dev/null @@ -1,141 +0,0 @@ -from django import forms -from django.forms import ModelForm -import re -from django.core.exceptions import ValidationError -from survey.models import Question, QuestionOption, Batch, Answer, QuestionModule, \ - HouseholdMemberGroup, MultiChoiceAnswer, MultiSelectAnswer, QuestionFlow, AnswerAccessDefinition -from django.conf import settings - - -class QuestionForm(ModelForm): - - options = forms.CharField( - max_length=50, widget=forms.HiddenInput(), required=False) - - def __init__(self, batch, data=None, initial=None, parent_question=None, instance=None): - super(QuestionForm, self).__init__( - data=data, initial=initial, instance=instance) - self.fields['identifier'].label = "Variable name" - self.fields['batch'].widget = forms.HiddenInput() - self.fields['batch'].initial = batch.pk - self.batch = batch - # depending on type of ussd/odk access of batch restrict the answer - # type - self.fields['answer_type'].choices = [choice for choice in self.fields['answer_type'].choices - if choice[0] in batch.answer_types or choice[0] == ''] - if instance: - self.help_text = ' and '.join( - AnswerAccessDefinition.access_channels(instance.answer_type)) - self.fields['answer_type'].help_text = self.help_text - self.answer_map = {} - definitions = AnswerAccessDefinition.objects.all() - for defi in definitions: - self.answer_map[defi.answer_type] = self.answer_map.get( - defi.answer_type, []) - self.answer_map[defi.answer_type].append(defi.channel) - - self.parent_question = parent_question - - class Meta: - model = Question - fields = ['batch', 'module', 'text', - 'identifier', 'group', 'answer_type'] - widgets = { - 'text': forms.Textarea(attrs={"rows": 4, "cols": 100, "maxlength": "150"}), - } - - def clean_options(self): - options = dict(self.data).get('options') - if options: - options = filter(lambda text: text.strip(), options) - # options = map(lambda option: re.sub("[%s]" % settings.USSD_IGNORED_CHARACTERS, '', option), options) - options = map(lambda option: re.sub(" ", ' ', option), options) - options = map(lambda option: option.strip(), options) - self.cleaned_data['options'] = options - return options - - def clean_identifier(self): - identifier = self.cleaned_data['identifier'] - if Question.objects.filter(identifier=identifier, batch=self.batch).exists(): - if self.instance and self.instance.identifier == identifier: - pass - else: - raise ValidationError( - 'Identifier already in use for this batch') - return self.cleaned_data['identifier'] - - def clean(self): - answer_type = self.cleaned_data.get('answer_type', None) - options = self.cleaned_data.get('options', None) - text = self.cleaned_data.get('text', None) - self._check__multichoice_and_options_compatibility( - answer_type, options) - self._strip_special_characters_for_ussd(text) - self._prevent_duplicate_subquestions(text) - return self.cleaned_data - - def _check__multichoice_and_options_compatibility(self, answer_type, options): - if answer_type in [MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name()] and not options: - message = 'Question Options missing.' - self._errors['answer_type'] = self.error_class([message]) - del self.cleaned_data['answer_type'] - - if answer_type not in [MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name()] and options: - del self.cleaned_data['options'] - - def _strip_special_characters_for_ussd(self, text): - if text: - text = re.sub("[%s]" % settings.USSD_IGNORED_CHARACTERS, '', text) - self.cleaned_data['text'] = re.sub(" ", ' ', text) - - def _prevent_duplicate_subquestions(self, text): - if self.parent_question: - duplicate_sub_question = self.parent_question.get_subquestions().filter(text__iexact=text) - has_instance_id_different = ( - self.instance.id and self.instance.id != duplicate_sub_question[0].id) - - if duplicate_sub_question.exists() and (not self.instance.id or has_instance_id_different): - self._errors['text'] = self.error_class( - ["Sub question for this question with this text already exists."]) - del self.cleaned_data['text'] - - def kwargs_has_batch(self, **kwargs): - return kwargs.has_key('batch') and isinstance(kwargs['batch'], Batch) - - def options_supplied(self, commit): - return commit and self.cleaned_data.get('options', None) - - def save_question_options(self, question): - order = 0 - options = self.cleaned_data['options'] - question.options.all().delete() - # options.sort() - for text in options: - order += 1 - QuestionOption.objects.create( - question=question, text=text, order=order) - - def save(self, commit=True, zombie=False, **kwargs): - question = super(QuestionForm, self).save(commit=False) - if commit: - if question.pk is None: - question.save() - # get the last question inline - # create a inline flow with current batch - batch = question.batch - last_question = batch.last_question_inline() - if last_question: - if zombie is False: - # incase, inline flow with no next quest already exists - flow, _ = QuestionFlow.objects.get_or_create( - question=last_question, validation_test__isnull=True) - flow.next_question = question - flow.save() - elif batch.start_question is None: - batch.start_question = question - batch.save() - else: - question.save() - if self.options_supplied(commit): - self.save_question_options(question) - return question diff --git a/survey/forms/question_module_form.py b/survey/forms/question_module_form.py deleted file mode 100644 index 7c48b26c..00000000 --- a/survey/forms/question_module_form.py +++ /dev/null @@ -1,21 +0,0 @@ -from django.core.exceptions import ValidationError -from django.forms import ModelForm, forms -from survey.models import QuestionModule - - -class QuestionModuleForm(ModelForm): - - def clean_name(self): - name = self.cleaned_data['name'] - creating_new_module = not self.instance.id - editing_module = self.instance.name and self.instance.name != name - if (creating_new_module or editing_module) and QuestionModule.objects.filter(name=name): - raise ValidationError("Module with name %s already exists." % name) - return self.cleaned_data['name'] - - class Meta: - model = QuestionModule - exclude = [] - widgets = { - 'description': forms.Textarea(attrs={"rows": 3, "cols": 30}) - } diff --git a/survey/forms/question_template.py b/survey/forms/question_template.py deleted file mode 100644 index c96bc187..00000000 --- a/survey/forms/question_template.py +++ /dev/null @@ -1,99 +0,0 @@ -from django import forms -from django.forms import ModelForm, ValidationError -import re -from django.conf import settings -from survey.models import QuestionTemplate, TemplateOption, Answer, QuestionModule, \ - HouseholdMemberGroup, MultiChoiceAnswer, MultiSelectAnswer, QuestionFlow, AnswerAccessDefinition - - -class QuestionTemplateForm(ModelForm): - - options = forms.CharField( - max_length=50, widget=forms.HiddenInput(), required=False) - - def __init__(self, *args, **kwargs): - super(QuestionTemplateForm, self).__init__(*args, **kwargs) - self.fields.keyOrder = ['module', 'text', - 'identifier', 'group', 'answer_type'] - instance = kwargs.get('instance', None) - if instance: - self.help_text = ' and '.join( - AnswerAccessDefinition.access_channels(instance.answer_type)) - self.fields['answer_type'].help_text = self.help_text - self.answer_map = {} - definitions = AnswerAccessDefinition.objects.all() - for defi in definitions: - self.answer_map[defi.answer_type] = self.answer_map.get( - defi.answer_type, []) - self.answer_map[defi.answer_type].append(defi.channel) - - class Meta: - model = QuestionTemplate - exclude = [] - widgets = { - 'text': forms.Textarea(attrs={"rows": 4, "cols": 100, "maxlength": "150"}), - } - - def clean_identifier(self): - answer_type = self.cleaned_data.get('identifier', None) - qts = QuestionTemplate.objects.filter(identifier__iexact=answer_type) - if qts.exists(): - if not self.instance or not (self.instance.identifier == answer_type): - raise ValidationError( - 'Identifier already in use the question library') - return self.cleaned_data['identifier'] - - def clean_options(self): - options = dict(self.data).get('options') - if options: - options = filter(lambda text: text.strip(), options) - options = map(lambda option: re.sub( - "[%s]" % settings.USSD_IGNORED_CHARACTERS, '', option), options) - options = map(lambda option: re.sub(" ", ' ', option), options) - self.cleaned_data['options'] = options - return options - - def clean(self): - self.clean_options() - answer_type = self.cleaned_data.get('answer_type', None) - options = self.cleaned_data.get('options', None) - text = self.cleaned_data.get('text', None) - self._check__multichoice_and_options_compatibility( - answer_type, options) - self._strip_special_characters_for_ussd(text) -# import pdb; pdb.set_trace() - return self.cleaned_data - - def _check__multichoice_and_options_compatibility(self, answer_type, options): - if answer_type in [MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name()] and not options: - message = 'Question Options missing.' - self._errors['answer_type'] = self.error_class([message]) - del self.cleaned_data['answer_type'] - - if answer_type not in [MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name()] and options: - del self.cleaned_data['options'] - - def _strip_special_characters_for_ussd(self, text): - if text: - text = re.sub("[%s]" % settings.USSD_IGNORED_CHARACTERS, '', text) - self.cleaned_data['text'] = re.sub(" ", ' ', text) - - def options_supplied(self, commit): - return commit and self.cleaned_data.get('options', None) - - def save_question_options(self, question): - order = 0 - options = self.cleaned_data['options'] - question.options.all().delete() - # options.sort() - for text in options: - order += 1 - TemplateOption.objects.create( - question=question, text=text, order=order) - - def save(self, commit=True, *args, **kwargs): - question = super(QuestionTemplateForm, self).save( - commit=commit, *args, **kwargs) - if self.options_supplied(commit): - self.save_question_options(question) - return question diff --git a/survey/forms/surveys.py b/survey/forms/surveys.py deleted file mode 100644 index da7dfa0d..00000000 --- a/survey/forms/surveys.py +++ /dev/null @@ -1,55 +0,0 @@ -from django.core.exceptions import ValidationError -from django.forms import ModelForm -from django import forms -from survey.forms.widgets import InlineRadioSelect -from survey.models import Survey, BatchCommencement, SurveyHouseholdListing - - -class SurveyForm(ModelForm): - # survey_listing = forms.CharField(choices=[(survey.pk, survey.name) for survey in Survey.objects.all()], - # help_text='Select survey household listing to reuse. Leave empty for fresh listing', - # required=False) - - class Meta: - model = Survey - fields = ['name', 'description', 'has_sampling', - 'sample_size', 'preferred_listing'] - widgets = { - 'description': forms.Textarea(attrs={"rows": 4, "cols": 50}), - 'has_sampling': InlineRadioSelect(choices=((True, 'Sampled'), (False, 'Census')), attrs={'class': 'has_sampling'}), - } - - def __init__(self, *args, **kwargs): - super(SurveyForm, self).__init__(*args, **kwargs) - if kwargs.get('instance', None) and kwargs['instance'].has_sampling is False: - self.fields['preferred_listing'].widget.attrs[ - 'disabled'] = 'disabled' - else: - preferred_listings = [('', '------ None, Create new -------'), ] - survey_listings = SurveyHouseholdListing.objects.all() - preferred_listings.extend( - set([(l.survey.pk, l.survey.name) for l in survey_listings])) - self.fields['preferred_listing'].choices = preferred_listings - - def clean(self): - cleaned_data = self.cleaned_data - - has_sampling = cleaned_data.get('has_sampling', None) - - if has_sampling and not cleaned_data.get('sample_size', None): - raise ValidationError( - 'Sample size must be specified if has sampling is selected.') - - return cleaned_data - - def clean_name(self): - name = self.cleaned_data['name'] - survey = Survey.objects.filter(name=name) - instance_id = self.instance.id - - if not instance_id and survey: - raise ValidationError("Survey with name %s already exist." % name) - elif instance_id and survey and survey[0].id != instance_id: - raise ValidationError("Survey with name %s already exist." % name) - - return self.cleaned_data['name'] diff --git a/survey/forms/upload_csv_file.py b/survey/forms/upload_csv_file.py deleted file mode 100644 index 44f602e6..00000000 --- a/survey/forms/upload_csv_file.py +++ /dev/null @@ -1,130 +0,0 @@ -from django import forms -from django.core.exceptions import ValidationError -from django.template.defaultfilters import slugify -from survey.models import Location, LocationType -from survey.models import Survey, LocationTypeDetails -from survey.services.csv_uploader import CSVUploader, UploadService -from survey.services.location_upload import UploadLocation -from survey.services.location_weights_upload import UploadLocationWeights -from survey.services.ea_upload import UploadEA - - -class UploadCSVFileForm(forms.Form): - file = forms.FileField(label='Location Input File', required=True) - - def __init__(self, uploader, *args, **kwargs): - super(UploadCSVFileForm, self).__init__(*args, **kwargs) - self.uploader = uploader - - def clean_file(self): - _file = self.cleaned_data['file'] - if self._is_not_csv(_file): - message = '%s is not a valid csv file.' % _file.name - self._errors['file'] = self.error_class([message]) - del self.cleaned_data['file'] - return _file - - def _is_not_csv(self, _file): - return '\0' in _file.read() - - def upload(self): - _file = self.cleaned_data['file'] - uploader = self.uploader(_file) - return uploader.upload() - - -class UploadLocationsForm(UploadCSVFileForm): - - def __init__(self, *args, **kwargs): - super(UploadLocationsForm, self).__init__( - UploadLocation, *args, **kwargs) - - def clean(self, *args, **kwargs): - super(UploadLocationsForm, self).clean(*args, **kwargs) - if self.cleaned_data.get('file', None): - self.clean_headers() - return self.cleaned_data - - def clean_headers(self): - _file = self.cleaned_data['file'] - headers = CSVUploader(_file).headers() - headers = [header for header in headers if header] - # - if headers[-2].lower().replace('name', '') == 'ea': - # import pdb;pdb.set_trace() - EA_INDEX = len(headers) - 2 - headers.pop(EA_INDEX) - headers.pop(EA_INDEX) # this is total households - for index, header in enumerate(headers): - header = header.strip() - if not header.endswith('Code'): - header = header.replace('Name', '') - location_type = self.clean_location_type(header) - self.clean_headers_location_type_order(headers) - return self.cleaned_data - - def clean_location_type(self, header): - location_type = LocationType.objects.filter( - name__iexact=header, slug=slugify(header)) - if not location_type.exists(): - raise ValidationError('Location type - %s not found.' % header) - return location_type[0] - - def clean_location_detail(self, location_type, header): - detail = location_type.details.all() - if not detail: - raise ValidationError( - 'Location type details for %s not found.' % header) - return detail[0] - - def clean_has_code(self, type, location_detail, headers, index): - if location_detail.has_code: - if index == 0 or headers[index - 1].replace('Code', '') != type.name: - raise ValidationError( - '%sCode column should be before %sName column. Please refer to input file format.' % (type.name, type.name)) - else: - if headers[index - 1].replace('Code', '') == type.name: - raise ValidationError( - '%s has no code. The column %sCode should be removed. Please refer to input file format.' % (type.name, type.name)) - - def clean_headers_location_type_order(self, headers): - headers = UploadService.remove_trailing( - 'Name', in_array=headers, exclude='Code') - headers = [header.upper() for header in headers] - if headers[0] == 'COUNTRY': - headers.pop(0) - location_types = [ - loc.name.upper() for loc in LocationType.all().exclude(name__iexact='COUNTRY')] - if not location_types == headers: - raise ValidationError( - 'Location types not in order. Please refer to input file format.') - - -class UploadWithSurveyForm(UploadCSVFileForm): - survey = forms.ModelChoiceField( - queryset=Survey.objects.all(), empty_label=None) - - def __init__(self, uploader, *args, **kwargs): - super(UploadWithSurveyForm, self).__init__(uploader, *args, **kwargs) - self.fields.keyOrder = ['survey', 'file'] - - def upload(self): - _file = self.cleaned_data['file'] - survey = self.cleaned_data['survey'] - uploader = self.uploader(_file) - return uploader.upload(survey) - - -class UploadWeightsForm(UploadWithSurveyForm): - - def __init__(self, *args, **kwargs): - super(UploadWeightsForm, self).__init__( - UploadLocationWeights, *args, **kwargs) - self.fields['file'].label = 'Location weights file' - - -class UploadEAForm(UploadWithSurveyForm): - - def __init__(self, *args, **kwargs): - super(UploadEAForm, self).__init__(UploadEA, *args, **kwargs) - self.fields['file'].label = 'EA file' diff --git a/survey/forms/users.py b/survey/forms/users.py deleted file mode 100644 index 4f0040f1..00000000 --- a/survey/forms/users.py +++ /dev/null @@ -1,158 +0,0 @@ -from django import forms -from django.contrib.auth.models import User, Group -from django.forms import ModelForm - -from django.contrib.auth.forms import UserCreationForm - -from survey.models.users import UserProfile - - -class UserForm(UserCreationForm): - mobile_number = forms.DecimalField(min_value=100000000, - max_digits=9, - widget=forms.TextInput(attrs={'placeholder': 'Format: 771234567', - 'style': "width:172px;", - 'maxlength': '10'})) - - def __init__(self, *args, **kwargs): - super(UserForm, self).__init__(*args, **kwargs) - self.fields['password2'].label = 'Confirm Password' - self.fields.keyOrder = ['username', 'password1', 'password2', 'first_name', 'last_name', - 'mobile_number', 'email', 'groups'] - self.fields['groups'].queryset = Group.objects.all().order_by('name') - - def _clean_attribute(self, Klass, **kwargs): - attribute_name = kwargs.keys()[0] - data_attr = kwargs[attribute_name] - users_with_same_attr = Klass.objects.filter(**kwargs) - if users_with_same_attr and self.initial.get(attribute_name, None) != str(data_attr): - message = "%s is already associated to a different user." % data_attr - self._errors[attribute_name] = self.error_class([message]) - del self.cleaned_data[attribute_name] - return data_attr - - def clean_username(self): - username = self.cleaned_data['username'] - return self._clean_attribute(User, username=username) - - def clean_mobile_number(self): - mobile_number = self.cleaned_data['mobile_number'] - return self._clean_attribute(UserProfile, mobile_number=mobile_number) - - def clean_email(self): - email = self.cleaned_data['email'] - return self._clean_attribute(User, email=email) - - def save(self, commit=True, *args, **kwargs): - user = super(UserForm, self).save(commit=commit, *args, **kwargs) - if commit: - self.save_m2m() - user_profile, b = UserProfile.objects.get_or_create(user=user) - user_profile.mobile_number = self.cleaned_data['mobile_number'] - user_profile.save() - - return user - - class Meta: - model = User - fields = ("username", "first_name", "last_name", "email", "groups") - - -class EditUserForm(ModelForm): - mobile_number = forms.DecimalField(min_value=100000000, - max_digits=9, - widget=forms.TextInput(attrs={'placeholder': 'Format: 771234567', - 'style': "width:172px;", - 'maxlength': '10'})) - password = forms.CharField(label="Password", required=False, - widget=forms.PasswordInput()) - - confirm_password = forms.CharField(label="Confirm Password", - required=False, - widget=forms.PasswordInput()) - - def __init__(self, user, *args, **kwargs): - super(EditUserForm, self).__init__(*args, **kwargs) - self.set_form_fields_order(user) - - def set_form_fields_order(self, user): - self.fields.keyOrder = ['username', 'first_name', - 'last_name', 'mobile_number', 'email', ] - if user.has_perm("auth.can_view_users"): - self.fields.keyOrder.extend( - ['password', 'confirm_password', 'groups']) - - def _clean_attribute(self, Klass, **kwargs): - attribute_name = kwargs.keys()[0] - data_attr = kwargs[attribute_name] - users_with_same_attr = Klass.objects.filter(**kwargs) - if users_with_same_attr and self.initial.get(attribute_name, None) != str(data_attr): - message = "%s is already associated to a different user." % data_attr - self._errors[attribute_name] = self.error_class([message]) - del self.cleaned_data[attribute_name] - return data_attr - - def clean_username(self): - username = self.cleaned_data['username'] - users_with_same_username = User.objects.filter(username=username) - if self.initial.get('username', None) != str(username) or not users_with_same_username: - message = "username cannot be changed." - self._errors['username'] = self.error_class([message]) - del self.cleaned_data['username'] - - return username - - def clean_mobile_number(self): - mobile_number = self.cleaned_data['mobile_number'] - return self._clean_attribute(UserProfile, mobile_number=mobile_number) - - def clean_email(self): - email = self.cleaned_data['email'] - return self._clean_attribute(User, email=email) - - def _clean_passwords(self, cleaned_data): - password = cleaned_data.get('password', '') - confirm_password = cleaned_data.get('confirm_password', '') - if password != confirm_password: - message = "passwords must match." - self._errors['confirm_password'] = self.error_class([message]) - del cleaned_data['password'] - del cleaned_data['confirm_password'] - return password - - def clean(self): - cleaned_data = super(EditUserForm, self).clean() - self._clean_passwords(cleaned_data) - return cleaned_data - - def save(self, commit=True, *args, **kwargs): - user = super(EditUserForm, self).save(commit=commit, *args, **kwargs) - self._change_password(user) - if commit: - self._create_or_update_profile(user) - user.save() - return user - - def _change_password(self, user): - password = self.cleaned_data.get("password", None) - if password: - user.set_password(password) - - def _create_or_update_profile(self, user): - user_profile, created = UserProfile.objects.get_or_create(user=user) - user_profile.mobile_number = self.cleaned_data['mobile_number'] - user_profile.save() - - class Meta: - model = User - fields = ("username", "first_name", "last_name", "email", "groups") - widgets = { - 'username': forms.TextInput(attrs={'readonly': 'readonly'}), - } - - -class UserProfileForm(ModelForm): - - class Meta: - model = UserProfile - exclude = ['user'] diff --git a/survey/forms/widgets.py b/survey/forms/widgets.py deleted file mode 100644 index 80d258fe..00000000 --- a/survey/forms/widgets.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.forms import widgets -from django.utils.encoding import force_unicode -from django.utils.safestring import mark_safe - - -class InlineRadioFieldRenderer(widgets.RadioFieldRenderer): - - def render(self): - return mark_safe(u'\n %s \n' % u'\n'.join([u' %s   ' % force_unicode(w) for w in self])) - - -class InlineRadioSelect(widgets.RadioSelect): - renderer = InlineRadioFieldRenderer diff --git a/survey/interviewer_configs.py b/survey/interviewer_configs.py deleted file mode 100644 index f2f5db43..00000000 --- a/survey/interviewer_configs.py +++ /dev/null @@ -1,100 +0,0 @@ -LEVEL_OF_EDUCATION = (("Did not attend school", "Did not attend school"), - ("Nursery", "Nursery"), - ("Primary", "Primary"), - ("'O' Level", "'O' Level"), - ("'A' Level", "'A' Level"), - ("Tertiary", "Tertiary"), - ("University", "University"), - ) - -LANGUAGES = (("English", "English"), - ("Luganda", "Luganda"), - ("Runyankore-Rukiga", "Runyankore-Rukiga"), - ("Runyoro-Rutoro", "Runyoro-Rutoro"), - ("Swahili", "Swahili"), - ("Ateso-Karimojong", "Ateso-Karimojong"), - ("Luo", "Luo"), - ("Lugbara", "Lugbara"), - ) -MONTHS = ((1, 'January'), - (2, 'February'), - (3, 'March'), - (4, 'April'), - (5, 'May'), - (6, 'June'), - (7, 'July'), - (8, 'August'), - (9, 'September'), - (10, 'October'), - (11, 'November'), - (12, 'December'), - ) -OCCUPATION = ( - ("Agricultural labor", "Agricultural labor"), - ("Livestock herding", "Livestock herding"), - ("Own farm labor", "Own farm labor"), - ("Employed(salaried)", "Employed(salaried)"), - ("Waged labor (Casual)", "Waged labor (Casual)"), - ("Petty trade", "Petty trade"), - ("Unemployed", "Unemployed"), - ("Student", "Student"), - ("Business person", "Business person"), - ("Retired pensioner", "Retired pensioner"), - ("Retired (no pension)", "Retired (no pension)"), - ("Housewife", "Housewife"), - ("Domestic help", "Domestic help"), - ("Hunting, gathering", "Hunting, gathering"), - ("Firewood/charcoal", "Firewood/charcoal"), - ("Brewing", "Brewing"), - ("Weaving/basketry", "Weaving/basketry"), - ("Fishing", "Fishing"), - ("Other: ", "Other"), -) - -MESSAGES = { - 'UNKNOWN_RESPONSE': "Pls enter a valid reply", - 'SUCCESS_MESSAGE': "This survey has come to an end. Your responses have been received. Thank you.", - 'BATCH_5_MIN_TIMEDOUT_MESSAGE': "This batch is already completed and 5 minutes have passed. You may no longer retake it.", - 'USER_NOT_REGISTERED': "Sorry, your mobile number is not registered for any surveys.", - 'START': "Welcome {{interviewer}} to the survey. Current Survey is: {{survey}}", - 'HOUSE_REGISTRATION': 'House Registration', - 'REGISTER_MEMBER': 'Member Registration', - 'START_LISTING': 'Start Listing', - 'START_SURVEY': 'Start Survey', - 'SELECT_HOUSEHOLD': 'To Select Household', - 'NEW_HOUSEHOLD': 'New Household', - 'HOUSEHOLD_LIST': "Household Listing.", - 'HOUSEHOLD_LIST_PRE': 'Please enter details for this household', - 'MEMBERS_LIST': "Please select a member from the list", - 'MEMBERS_LIST_EMPTY': "Sorry no pending member in this household", - 'SUCCESS_MESSAGE_FOR_COMPLETING_ALL_HOUSEHOLDS': "Survey Completed. Thank you.", - 'RETAKE_SURVEY': "You have already completed this household. Would you like to start again?\n1: Yes\n2: No", - 'NO_HOUSEHOLDS': "Sorry, you have no households registered.", - 'NO_OPEN_BATCH': "Sorry, currently there are no open surveys in your Enumeration Area.", - 'NO_MORE_OPEN_BATCH': 'Sorry, no pending batch for this house member', - 'HOUSEHOLDS_COUNT_QUESTION': "How many households have you listed in your Enumeration Area?", - 'HOUSEHOLD_SELECTION_SMS_MESSAGE': "Thank you. You will receive the household numbers selected for your Enumeration Area", - 'HOUSEHOLD_CONFIRMATION_MESSAGE': "Thank you. Houselist for Enumeration Area is available for member registration.", - # % NUMBER_OF_HOUSEHOLD_PER_INTERVIEWER, - 'HOUSEHOLDS_COUNT_QUESTION_WITH_VALIDATION_MESSAGE': "Count must be greater than %s. How many households have you listed in your Enumeration Area?", - 'MEMBER_SUCCESS_MESSAGE': "Thank you. Would you like to proceed to the next Household Member?\n1: Yes\n2: No", - 'HOUSEHOLD_COMPLETION_MESSAGE': "Thank you. You have completed this household. Would you like to retake this household?\n1: Yes\n2: No", - 'RESUME_MESSAGE': "Would you like to to resume with {{batch}} questions for {{house_member}}?\n1: Yes\n2: No", - 'SELECT_HEAD_OR_MEMBER': 'Household {{household}}, please select household member to register:\n1: Respondent\n2: Member', - 'END_REGISTRATION': 'Thank you for registering household member. Would you like to register another member for household %s?\n1: Yes\n2: No', - 'INTERVIEWER_BLOCKED_MESSAGE': 'Sorry. You are not registered for any surveys.', - 'HEAD_REGISTERED': "Head already registered for this household. Registering members now:\n", - 'NON_RESPONSE_MENU': "\n3: Report non-response", - 'NON_RESPONSE_COMPLETION': "Thank you. You have completed reporting non-responses. Would you like to start again?\n1: Yes\n2: No", - 'NON_RESPONSE_MSG': 'Enter reason for non response' -} - -USSD_PROVIDER = "YO" -NUMBER_OF_HOUSEHOLD_PER_INTERVIEWER = 10 -PRIME_LOCATION_TYPE = 'District' -HAS_APPLICATION_CODE = True -APPLICATION_CODE = '10' -# all upload error logs older than this (in days) will be automatically cleared -UPLOAD_ERROR_LOG_EXPIRY = 30 -INTERVIEWER_MIN_AGE = 18 -INTERVIEWER_MAX_AGE = 60 diff --git a/survey/interviewer_configs.py.example b/survey/interviewer_configs.py.example deleted file mode 100644 index 6774a996..00000000 --- a/survey/interviewer_configs.py.example +++ /dev/null @@ -1,98 +0,0 @@ -LEVEL_OF_EDUCATION = ( ("Did not attend school", "Did not attend school"), - ("Nursery", "Nursery"), - ("Primary", "Primary"), - ("'O' Level", "'O' Level"), - ("'A' Level", "'A' Level"), - ("Tertiary", "Tertiary"), - ("University", "University"), - ) - -LANGUAGES = ( ("English", "English"), - ("Luganda", "Luganda"), - ("Runyankore-Rukiga", "Runyankore-Rukiga"), - ("Runyoro-Rutoro", "Runyoro-Rutoro"), - ("Swahili", "Swahili"), - ("Ateso-Karimojong", "Ateso-Karimojong"), - ("Luo", "Luo"), - ("Lugbara", "Lugbara"), - ) -MONTHS =( (1, 'January'), - (2, 'February'), - (3, 'March'), - (4, 'April'), - (5, 'May'), - (6, 'June'), - (7, 'July'), - (8, 'August'), - (9, 'September'), - (10,'October'), - (11,'November'), - (12,'December'), - ) -OCCUPATION = ( - ("Agricultural labor", "Agricultural labor"), - ("Livestock herding", "Livestock herding"), - ("Own farm labor", "Own farm labor"), - ("Employed(salaried)", "Employed(salaried)"), - ("Waged labor (Casual)", "Waged labor (Casual)"), - ("Petty trade", "Petty trade"), - ("Unemployed", "Unemployed"), - ("Student", "Student"), - ("Business person", "Business person"), - ("Retired pensioner", "Retired pensioner"), - ("Retired (no pension)", "Retired (no pension)"), - ("Housewife", "Housewife"), - ("Domestic help", "Domestic help"), - ("Hunting, gathering", "Hunting, gathering"), - ("Firewood/charcoal", "Firewood/charcoal"), - ("Brewing", "Brewing"), - ("Weaving/basketry", "Weaving/basketry"), - ("Fishing", "Fishing"), - ("Other: ", "Other"), - ) - -MESSAGES = { - 'UNKNOWN_RESPONSE' : "Pls enter a valid reply", - 'SUCCESS_MESSAGE': "This survey has come to an end. Your responses have been received. Thank you.", - 'BATCH_5_MIN_TIMEDOUT_MESSAGE': "This batch is already completed and 5 minutes have passed. You may no longer retake it.", - 'USER_NOT_REGISTERED': "Sorry, your mobile number is not registered for any surveys.", - 'START': "Welcome {{interviewer}} to the survey. Current Survey is: {{survey}}", - 'HOUSE_REGISTRATION' : 'House Registration', - 'REGISTER_MEMBER' : 'Member Registration', - 'START_LISTING': 'Start Listing', - 'START_SURVEY': 'Start Survey', - 'SELECT_HOUSEHOLD' : 'To Select Household', - 'NEW_HOUSEHOLD' : 'New Household', - 'HOUSEHOLD_LIST': "Household Listing.", - 'HOUSEHOLD_LIST_PRE': 'Please enter details for this household', - 'MEMBERS_LIST': "Please select a member from the list", - 'MEMBERS_LIST_EMPTY': "Sorry no pending member in this household", - 'SUCCESS_MESSAGE_FOR_COMPLETING_ALL_HOUSEHOLDS': "Survey Completed. Thank you.", - 'RETAKE_SURVEY': "You have already completed this household. Would you like to start again?\n1: Yes\n2: No", - 'NO_HOUSEHOLDS': "Sorry, you have no households registered.", - 'NO_OPEN_BATCH': "Sorry, currently there are no open surveys in your Enumeration Area.", - 'NO_MORE_OPEN_BATCH' : 'Sorry, no pending batch for this house member', - 'HOUSEHOLDS_COUNT_QUESTION': "How many households have you listed in your Enumeration Area?", - 'HOUSEHOLD_SELECTION_SMS_MESSAGE': "Thank you. You will receive the household numbers selected for your Enumeration Area", - 'HOUSEHOLD_CONFIRMATION_MESSAGE': "Thank you. Houselist for Enumeration Area is available for member registration.", - 'HOUSEHOLDS_COUNT_QUESTION_WITH_VALIDATION_MESSAGE': "Count must be greater than %s. How many households have you listed in your Enumeration Area?", #% NUMBER_OF_HOUSEHOLD_PER_INTERVIEWER, - 'MEMBER_SUCCESS_MESSAGE': "Thank you. Would you like to proceed to the next Household Member?\n1: Yes\n2: No", - 'HOUSEHOLD_COMPLETION_MESSAGE': "Thank you. You have completed this household. Would you like to retake this household?\n1: Yes\n2: No", - 'RESUME_MESSAGE': "Would you like to to resume with {{batch}} questions for {{house_member}}?\n1: Yes\n2: No", - 'SELECT_HEAD_OR_MEMBER': 'Household {{household}}, please select household member to register:\n1: Respondent\n2: Member', - 'END_REGISTRATION': 'Thank you for registering household member. Would you like to register another member for household %s?\n1: Yes\n2: No', - 'INTERVIEWER_BLOCKED_MESSAGE': 'Sorry. You are not registered for any surveys.', - 'HEAD_REGISTERED': "Head already registered for this household. Registering members now:\n", - 'NON_RESPONSE_MENU': "\n3: Report non-response", - 'NON_RESPONSE_COMPLETION': "Thank you. You have completed reporting non-responses. Would you like to start again?\n1: Yes\n2: No", - 'NON_RESPONSE_MSG' : 'Enter reason for non response' -} - -USSD_PROVIDER = "YO" -NUMBER_OF_HOUSEHOLD_PER_INTERVIEWER = 10 -PRIME_LOCATION_TYPE = 'District' -HAS_APPLICATION_CODE = True -APPLICATION_CODE = '10' -UPLOAD_ERROR_LOG_EXPIRY = 30 # all upload error logs older than this (in days) will be automatically cleared -INTERVIEWER_MIN_AGE = 18 -INTERVIEWER_MAX_AGE = 60 diff --git a/survey/management/__init__.py b/survey/management/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/survey/management/commands/__init__.py b/survey/management/commands/__init__.py deleted file mode 100644 index b2bea875..00000000 --- a/survey/management/commands/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from survey.management.commands.import_location import Command -__all__ = [''] diff --git a/survey/management/commands/import_countries.py b/survey/management/commands/import_countries.py deleted file mode 100644 index 62b6fd7a..00000000 --- a/survey/management/commands/import_countries.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.core.management.base import BaseCommand, CommandError -from survey.models.locations import * -from django.template.defaultfilters import slugify -import csv - - -class Command(BaseCommand): - args = 'name_of_the_csv.file' - help = 'Populates locations from a csv file' - - def handle(self, *args, **kwargs): - csv_file = csv.reader(open(args[0], "rb")) - country, created = LocationType.objects.get_or_create( - name='Country', slug=slugify('country')) - for items in csv_file: - tree_parent = None - for index, item in enumerate(items): - Location.objects.get_or_create( - name=item.strip(), type=country, parent=tree_parent) - self.stdout.write('Successfully imported!') diff --git a/survey/management/commands/import_location.py b/survey/management/commands/import_location.py deleted file mode 100644 index 6afdfac9..00000000 --- a/survey/management/commands/import_location.py +++ /dev/null @@ -1,71 +0,0 @@ -from django.core.management.base import BaseCommand, CommandError -from survey.models import Location, LocationType, EnumerationArea -from django.template.defaultfilters import slugify -import csv -import string -from django.db import transaction -from mptt.models import MPTTModel, TreeForeignKey -import datetime - - -class Command(BaseCommand): - args = 'name_of_the_csv.file' - help = 'Populates locations from a csv file' - - # @transaction.commit_on_success - def handle(self, *args, **kwargs): - print datetime.datetime.now(), "start" - csv_file = csv.reader(open(args[0], "rb")) - headers = csv_file.next() - location_types = [] - last_entry_empty = False - if not headers[-1].strip(): - headers.pop(-1) - last_entry_empty = True - has_ea = False - if headers[-1].lower().replace('name', '') == 'ea': - has_ea = True - headers.pop(-1) - location_type = None - for idx, header in enumerate(headers): - header = string.capwords(header.strip().replace("Name", "")) - #self.stdout.write('loading %s' % header) - location_type, created = LocationType.objects.get_or_create(name=header, slug=slugify(header), - parent=location_type) - location_types.append(location_type) - total_divisions = len(location_types) - count = 0 - country = None - for row in csv_file: - parent = None - if len(row) < total_divisions: - print 'skiping entry... ', row - continue - else: - if len(row) > total_divisions: - ea_name = row.pop(total_divisions) - row = row[:total_divisions] - - with transaction.atomic(): - with Location.tree.disable_mptt_updates(): - for index, col in enumerate(row): - loc_name = string.capwords(col.strip()) - loc_type = location_types[index] - parent, _ = Location.objects.get_or_create( - name=loc_name, type=loc_type, parent=parent) - if has_ea: - if parent: - ea, created = EnumerationArea.objects.get_or_create( - name=ea_name, code='%s-%s' % (parent.pk, ea_name)) - if created: - ea.locations.add(parent) - count = count + 1 - if count % 1000 == 0: - print 'loaded up to entry...', row - # clean up... delete locations and types having no name - Location.tree.rebuild() - LocationType.objects.filter(name='').delete() - Location.objects.filter(name='').delete() - EnumerationArea.objects.filter(name='').delete() - print datetime.datetime.now(), "completed" - self.stdout.write('Successfully imported!') diff --git a/survey/management/commands/load_parameters.py b/survey/management/commands/load_parameters.py deleted file mode 100644 index cbe55786..00000000 --- a/survey/management/commands/load_parameters.py +++ /dev/null @@ -1,84 +0,0 @@ -__author__ = 'anthony' -from django.core.management.base import BaseCommand, CommandError -from django.contrib.auth.models import User, Permission -from django.contrib.contenttypes.models import ContentType -from survey.models import AnswerAccessDefinition, NumericalAnswer, TextAnswer, \ - MultiChoiceAnswer, MultiSelectAnswer, ImageAnswer, GeopointAnswer, DateAnswer, AudioAnswer, VideoAnswer, \ - USSDAccess, ODKAccess, WebAccess - - -class Command(BaseCommand): - help = 'Creates default parameters' - - def handle(self, *args, **kwargs): - self.stdout.write('Creating permissions....') - content_type = ContentType.objects.get_for_model(User) - Permission.objects.get_or_create( - codename='can_enter_data', name='Can enter data', content_type=content_type) - Permission.objects.get_or_create( - codename='can_view_batches', name='Can view Batches', content_type=content_type) - Permission.objects.get_or_create( - codename='can_view_interviewers', name='Can view Interviewers', content_type=content_type) - Permission.objects.get_or_create( - codename='can_view_aggregates', name='Can view Aggregates', content_type=content_type) - Permission.objects.get_or_create( - codename='view_completed_survey', name='Can view Completed Surveys', content_type=content_type) - Permission.objects.get_or_create( - codename='can_view_households', name='Can view Households', content_type=content_type) - Permission.objects.get_or_create( - codename='can_view_locations', name='Can view Locations', content_type=content_type) - Permission.objects.get_or_create( - codename='can_view_users', name='Can view Users', content_type=content_type) - Permission.objects.get_or_create( - codename='can_view_household_groups', name='Can view Household Groups', content_type=content_type) - - self.stdout.write('Permissions.') - self.stdout.write('Creating answer definition... ') - # ussd definition - AnswerAccessDefinition.objects.get_or_create(channel=USSDAccess.choice_name(), - answer_type=NumericalAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=USSDAccess.choice_name(), - answer_type=TextAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=USSDAccess.choice_name(), - answer_type=MultiChoiceAnswer.choice_name()) - - # ODK definition - AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), - answer_type=NumericalAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), - answer_type=TextAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), - answer_type=MultiChoiceAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), - answer_type=MultiSelectAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), - answer_type=ImageAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), - answer_type=GeopointAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), - answer_type=DateAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), - answer_type=AudioAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), - answer_type=VideoAnswer.choice_name()) - - # web form definition - AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), - answer_type=NumericalAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), - answer_type=TextAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), - answer_type=MultiChoiceAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), - answer_type=MultiSelectAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), - answer_type=ImageAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), - answer_type=GeopointAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), - answer_type=DateAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), - answer_type=AudioAnswer.choice_name()) - AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), - answer_type=VideoAnswer.choice_name()) - self.stdout.write('Successfully imported!') diff --git a/survey/migrations/0001_initial.py b/survey/migrations/0001_initial.py deleted file mode 100644 index cef11e98..00000000 --- a/survey/migrations/0001_initial.py +++ /dev/null @@ -1,1391 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import survey.models.interviewer -import django_extensions.db.fields -import mptt.fields -import django.db.models.deletion -from django.conf import settings -import django.utils.timezone -import survey.models.odk_submission -import django.core.validators - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='AboutUs', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('content', models.TextField()), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='AnswerAccessDefinition', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('answer_type', models.CharField(max_length=100, choices=[('Numerical Answer', 'Numerical Answer'), ('Text Answer', 'Text Answer'), ('Multi Choice Answer', 'Multi Choice Answer'), ( - 'Multi Select Answer', 'Multi Select Answer'), ('Date Answer', 'Date Answer'), ('Audio Answer', 'Audio Answer'), ('Video Answer', 'Video Answer'), ('Image Answer', 'Image Answer'), ('Geopoint Answer', 'Geopoint Answer')])), - ('channel', models.CharField(max_length=100, choices=[ - ('Ussd Access', 'Ussd Access'), ('Odk Access', 'Odk Access'), ('Web Access', 'Web Access')])), - ], - ), - migrations.CreateModel( - name='Attachment', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('media_file', models.FileField( - upload_to=survey.models.odk_submission.upload_to)), - ('mimetype', models.CharField( - default=b'', max_length=50, blank=True)), - ], - ), - migrations.CreateModel( - name='AudioAnswer', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('value', models.FileField( - null=True, upload_to=b'/home/anthony/workspace/uSurvey/mics/answerFiles')), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='Backend', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(unique=True, max_length=20)), - ], - ), - migrations.CreateModel( - name='Batch', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('order', models.PositiveIntegerField(null=True)), - ('name', models.CharField(max_length=100, null=True, db_index=True)), - ('description', models.CharField( - max_length=300, null=True, blank=True)), - ], - ), - migrations.CreateModel( - name='BatchChannel', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('channel', models.CharField(max_length=100, choices=[ - ('Ussd Access', 'Ussd Access'), ('Odk Access', 'Odk Access'), ('Web Access', 'Web Access')])), - ('batch', models.ForeignKey( - related_name='access_channels', to='survey.Batch')), - ], - ), - migrations.CreateModel( - name='BatchCommencement', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='BatchLocationStatus', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('non_response', models.BooleanField(default=False)), - ('batch', models.ForeignKey( - related_name='open_locations', to='survey.Batch', null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='DateAnswer', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('value', models.DateField(null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='EnumerationArea', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('name', models.CharField(max_length=100, null=True)), - ('code', models.CharField(max_length=200, - unique=True, null=True, editable=False, blank=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='Formula', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ], - ), - migrations.CreateModel( - name='GeopointAnswer', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='GroupCondition', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('value', models.CharField(max_length=50)), - ('attribute', models.CharField(default=b'AGE', max_length=20, choices=[ - (b'GENDER', b'GENDER'), (b'AGE', b'AGE'), (b'GENERAL', b'ROLE')])), - ('condition', models.CharField(default=b'EQUALS', max_length=20, choices=[ - (b'GREATER_THAN', b'GREATER_THAN'), (b'LESS_THAN', b'LESS_THAN'), (b'EQUALS', b'EQUALS')])), - ], - ), - migrations.CreateModel( - name='Household', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('house_number', models.PositiveIntegerField( - verbose_name=b'Household Number')), - ('physical_address', models.CharField(max_length=200, - null=True, verbose_name=b'Structure Address', blank=True)), - ('registration_channel', models.CharField(max_length=100, choices=[ - ('Ussd Access', 'Ussd Access'), ('Odk Access', 'Odk Access'), ('Web Access', 'Web Access')])), - ('head_desc', models.CharField(max_length=200)), - ('head_sex', models.BooleanField(default=False)), - ], - ), - migrations.CreateModel( - name='HouseholdBatchCompletion', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('batch', models.ForeignKey( - related_name='batch_completion_households', to='survey.Batch', null=True)), - ('household', models.ForeignKey( - related_name='batch_completion_batches', to='survey.Household', null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='HouseholdListing', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('ea', models.ForeignKey(related_name='household_enumeration_area', - to='survey.EnumerationArea', null=True)), - ], - ), - migrations.CreateModel( - name='HouseholdMember', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('surname', models.CharField( - max_length=25, verbose_name=b'Family Name')), - ('first_name', models.CharField(max_length=25, - null=True, verbose_name=b'First Name', blank=True)), - ('gender', models.BooleanField(default=True, - verbose_name=b'Sex', choices=[(1, b'M'), (0, b'F')])), - ('date_of_birth', models.DateField()), - ('registration_channel', models.CharField(max_length=100, choices=[ - ('Ussd Access', 'Ussd Access'), ('Odk Access', 'Odk Access'), ('Web Access', 'Web Access')])), - ], - options={ - 'get_latest_by': 'created', - }, - ), - migrations.CreateModel( - name='HouseholdMemberBatchCompletion', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('batch', models.ForeignKey( - related_name='completed_households', to='survey.Batch', null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='HouseholdMemberGroup', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('name', models.CharField(max_length=50)), - ('order', models.PositiveIntegerField(default=0, unique=True)), - ], - ), - migrations.CreateModel( - name='HouseMemberSurveyCompletion', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='HouseSurveyCompletion', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('household', models.ForeignKey( - related_name='completion_registry', to='survey.Household', null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='ImageAnswer', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('value', models.FileField( - null=True, upload_to=b'/home/anthony/workspace/uSurvey/mics/answerFiles')), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='Indicator', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('name', models.CharField(max_length=255)), - ('description', models.TextField(null=True)), - ('measure', models.CharField(default=b'Percentage', max_length=255, choices=[ - (b'%', b'Percentage'), (b'Number', b'Count')])), - ('batch', models.ForeignKey( - related_name='indicators', to='survey.Batch', null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='Interview', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('closure_date', models.DateTimeField( - null=True, editable=False, blank=True)), - ('batch', models.ForeignKey( - related_name='interviews', to='survey.Batch')), - ('ea', models.ForeignKey( - related_name='interviews', to='survey.EnumerationArea')), - ], - ), - migrations.CreateModel( - name='Interviewer', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('name', models.CharField(max_length=100)), - ('gender', models.CharField(default=b'1', max_length=10, - verbose_name=b'Sex', choices=[(b'1', b'M'), (b'0', b'F')])), - ('date_of_birth', models.DateField(null=True, validators=[ - survey.models.interviewer.validate_min_date_of_birth, survey.models.interviewer.validate_max_date_of_birth])), - ('level_of_education', models.CharField(default=b'Primary', max_length=100, null=True, verbose_name=b'Highest level of education completed', choices=[(b'Did not attend school', b'Did not attend school'), ( - b'Nursery', b'Nursery'), (b'Primary', b'Primary'), (b"'O' Level", b"'O' Level"), (b"'A' Level", b"'A' Level"), (b'Tertiary', b'Tertiary'), (b'University', b'University')])), - ('is_blocked', models.BooleanField(default=False)), - ('language', models.CharField(default=b'English', max_length=100, null=True, verbose_name=b'Preferred language of communication', choices=[(b'English', b'English'), (b'Luganda', b'Luganda'), ( - b'Runyankore-Rukiga', b'Runyankore-Rukiga'), (b'Runyoro-Rutoro', b'Runyoro-Rutoro'), (b'Swahili', b'Swahili'), (b'Ateso-Karimojong', b'Ateso-Karimojong'), (b'Luo', b'Luo'), (b'Lugbara', b'Lugbara')])), - ('weights', models.FloatField(default=0)), - ('ea', models.ForeignKey(related_name='interviewers', - verbose_name=b'Enumeration Area', to='survey.EnumerationArea', null=True)), - ], - ), - migrations.CreateModel( - name='InterviewerAccess', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('user_identifier', models.CharField(max_length=100)), - ('is_active', models.BooleanField( - default=True, verbose_name=b'Activated')), - ('reponse_timeout', models.PositiveIntegerField( - default=1000, help_text=b'Max time to wait for response before ending interview', null=True, blank=True)), - ('duration', models.CharField(default=b'H', max_length=100, null=True, blank=True, choices=[ - (b'D', b'Days'), (b'H', b'Hours'), (b'M', b'Minutes'), (b'S', b'Seconds')])), - ], - ), - migrations.CreateModel( - name='Location', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('name', models.CharField(max_length=50)), - ('code', models.CharField(max_length=100, null=True, blank=True)), - ('lft', models.PositiveIntegerField( - editable=False, db_index=True)), - ('rght', models.PositiveIntegerField( - editable=False, db_index=True)), - ('tree_id', models.PositiveIntegerField( - editable=False, db_index=True)), - ('level', models.PositiveIntegerField( - editable=False, db_index=True)), - ], - ), - migrations.CreateModel( - name='LocationType', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('name', models.CharField(unique=True, max_length=200)), - ('location_code', models.PositiveIntegerField(null=True, blank=True)), - ('slug', models.SlugField()), - ('lft', models.PositiveIntegerField( - editable=False, db_index=True)), - ('rght', models.PositiveIntegerField( - editable=False, db_index=True)), - ('tree_id', models.PositiveIntegerField( - editable=False, db_index=True)), - ('level', models.PositiveIntegerField( - editable=False, db_index=True)), - ('parent', mptt.fields.TreeForeignKey(related_name='sub_types', - blank=True, to='survey.LocationType', null=True)), - ], - ), - migrations.CreateModel( - name='LocationTypeDetails', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('required', models.BooleanField( - default=False, verbose_name=b'required')), - ('has_code', models.BooleanField( - default=False, verbose_name=b'has code')), - ('length_of_code', models.PositiveIntegerField(blank=True, null=True, validators=[ - django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10)])), - ('order', models.PositiveIntegerField( - unique=True, null=True, blank=True)), - ('country', models.ForeignKey( - related_name='details', to='survey.Location', null=True)), - ('location_type', models.ForeignKey( - related_name='details', to='survey.LocationType')), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='LocationWeight', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('selection_probability', models.FloatField(default=1.0)), - ('location', models.ForeignKey( - related_name='weight', to='survey.Location')), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='MultiChoiceAnswer', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('interview', models.ForeignKey( - related_name='multichoiceanswer', to='survey.Interview')), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='MultiSelectAnswer', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('interview', models.ForeignKey( - related_name='multiselectanswer', to='survey.Interview')), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='NonResponseAnswer', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('value', models.CharField(max_length=100)), - ('household', models.ForeignKey( - related_name='non_response_answers', to='survey.Household')), - ('interviewer', models.ForeignKey( - related_name='non_response_answers', to='survey.Interviewer', null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='NumericalAnswer', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('value', models.PositiveIntegerField(null=True)), - ('interview', models.ForeignKey( - related_name='numericalanswer', to='survey.Interview')), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='ODKSubmission', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('form_id', models.CharField(max_length=256)), - ('description', models.CharField( - max_length=256, null=True, blank=True)), - ('instance_id', models.CharField(max_length=256)), - ('xml', models.TextField()), - ('household', models.ForeignKey(related_name='odk_submissions', - blank=True, to='survey.Household', null=True)), - ], - ), - migrations.CreateModel( - name='Point', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('latitude', models.DecimalField(max_digits=13, decimal_places=10)), - ('longitude', models.DecimalField( - max_digits=13, decimal_places=10)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='Question', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('identifier', models.CharField(max_length=100, - null=True, verbose_name=b'Variable Name')), - ('text', models.CharField(max_length=150)), - ('answer_type', models.CharField(max_length=100, choices=[('Numerical Answer', 'Numerical Answer'), ('Text Answer', 'Text Answer'), ('Multi Choice Answer', 'Multi Choice Answer'), ( - 'Multi Select Answer', 'Multi Select Answer'), ('Date Answer', 'Date Answer'), ('Audio Answer', 'Audio Answer'), ('Video Answer', 'Video Answer'), ('Image Answer', 'Image Answer'), ('Geopoint Answer', 'Geopoint Answer')])), - ('batch', models.ForeignKey( - related_name='batch_questions', to='survey.Batch')), - ('group', models.ForeignKey( - related_name='questions', to='survey.HouseholdMemberGroup')), - ], - ), - migrations.CreateModel( - name='QuestionFlow', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('validation_test', models.CharField(blank=True, max_length=200, null=True, choices=[(b'starts_with', b'starts_with'), (b'ends_with', b'ends_with'), ( - b'equals', b'equals'), (b'between', b'between'), (b'less_than', b'less_than'), (b'greater_than', b'greater_than'), (b'contains', b'contains')])), - ('name', models.CharField(max_length=200, null=True, blank=True)), - ('desc', models.CharField(max_length=200, null=True, blank=True)), - ('next_question', models.ForeignKey(related_name='connecting_flows', - on_delete=django.db.models.deletion.SET_NULL, blank=True, to='survey.Question', null=True)), - ('question', models.ForeignKey( - related_name='flows', to='survey.Question')), - ], - ), - migrations.CreateModel( - name='QuestionModule', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('name', models.CharField(max_length=255)), - ('description', models.TextField(null=True, blank=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='QuestionOption', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('text', models.CharField(max_length=150)), - ('order', models.PositiveIntegerField()), - ('question', models.ForeignKey( - related_name='options', to='survey.Question', null=True)), - ], - options={ - 'ordering': ['order'], - }, - ), - migrations.CreateModel( - name='QuestionTemplate', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('identifier', models.CharField( - max_length=100, unique=True, null=True)), - ('text', models.CharField(max_length=150)), - ('answer_type', models.CharField(max_length=100, choices=[('Numerical Answer', 'Numerical Answer'), ('Text Answer', 'Text Answer'), ('Multi Choice Answer', 'Multi Choice Answer'), ( - 'Multi Select Answer', 'Multi Select Answer'), ('Date Answer', 'Date Answer'), ('Audio Answer', 'Audio Answer'), ('Video Answer', 'Video Answer'), ('Image Answer', 'Image Answer'), ('Geopoint Answer', 'Geopoint Answer')])), - ('group', models.ForeignKey( - related_name='question_templates', to='survey.HouseholdMemberGroup')), - ('module', models.ForeignKey( - related_name='question_templates', to='survey.QuestionModule')), - ], - ), - migrations.CreateModel( - name='RandomSelection', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('household', models.OneToOneField( - related_name='random_selection', to='survey.Household')), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='Survey', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('name', models.CharField(max_length=100, unique=True, null=True)), - ('description', models.CharField( - max_length=300, null=True, blank=True)), - ('sample_size', models.PositiveIntegerField(default=10)), - ('has_sampling', models.BooleanField( - default=True, verbose_name=b'Survey Type')), - ('preferred_listing', models.ForeignKey(related_name='householdlist_users', blank=True, to='survey.Survey', - help_text=b'Select which survey household listing to reuse. Leave empty for fresh listing', null=True)), - ], - options={ - 'ordering': ['name'], - 'permissions': (('view_completed_survey', 'Can view Completed interviewers'),), - }, - ), - migrations.CreateModel( - name='SurveyAllocation', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('stage', models.CharField(blank=True, max_length=20, - null=True, choices=[(1, b'LISTING'), (2, b'SURVEY')])), - ('status', models.IntegerField(default=0, choices=[ - (0, b'PENDING'), (2, b'DEALLOCATED'), (1, b'COMPLETED')])), - ('allocation_ea', models.ForeignKey( - related_name='survey_allocations', to='survey.EnumerationArea')), - ('interviewer', models.ForeignKey( - related_name='assignments', to='survey.Interviewer')), - ('survey', models.ForeignKey( - related_name='work_allocation', to='survey.Survey')), - ], - ), - migrations.CreateModel( - name='SurveyHouseholdListing', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('listing', models.ForeignKey( - related_name='survey_houselistings', to='survey.HouseholdListing')), - ('survey', models.ForeignKey( - related_name='survey_house_listings', to='survey.Survey')), - ], - ), - migrations.CreateModel( - name='TemplateOption', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('text', models.CharField(max_length=150)), - ('order', models.PositiveIntegerField()), - ('question', models.ForeignKey(related_name='options', - to='survey.QuestionTemplate', null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='TestArgument', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('position', models.PositiveIntegerField()), - ], - options={ - 'get_latest_by': 'position', - }, - ), - migrations.CreateModel( - name='TextAnswer', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('value', models.CharField(max_length=100)), - ('interview', models.ForeignKey( - related_name='textanswer', to='survey.Interview')), - ('question', models.ForeignKey(related_name='textanswer', - on_delete=django.db.models.deletion.PROTECT, to='survey.Question', null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='UploadErrorLog', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('model', models.CharField(max_length=20, null=True)), - ('filename', models.CharField(max_length=20, null=True, blank=True)), - ('row_number', models.PositiveIntegerField(null=True, blank=True)), - ('error', models.CharField(max_length=200, null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='UserProfile', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('mobile_number', models.CharField(unique=True, max_length=10, validators=[ - django.core.validators.MinLengthValidator(9), django.core.validators.MaxLengthValidator(9)])), - ('user', models.OneToOneField( - related_name='userprofile', to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.CreateModel( - name='VideoAnswer', - fields=[ - ('id', models.AutoField(verbose_name='ID', - serialize=False, auto_created=True, primary_key=True)), - ('created', django_extensions.db.fields.CreationDateTimeField( - default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), - ('modified', django_extensions.db.fields.ModificationDateTimeField( - default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), - ('value', models.FileField( - null=True, upload_to=b'/home/anthony/workspace/uSurvey/mics/answerFiles')), - ('interview', models.ForeignKey( - related_name='videoanswer', to='survey.Interview')), - ('question', models.ForeignKey(related_name='videoanswer', - on_delete=django.db.models.deletion.PROTECT, to='survey.Question', null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='DateArgument', - fields=[ - ('testargument_ptr', models.OneToOneField(parent_link=True, auto_created=True, - primary_key=True, serialize=False, to='survey.TestArgument')), - ('param', models.DateField()), - ], - bases=('survey.testargument',), - ), - migrations.CreateModel( - name='HouseholdHead', - fields=[ - ('householdmember_ptr', models.OneToOneField(parent_link=True, auto_created=True, - primary_key=True, serialize=False, to='survey.HouseholdMember')), - ('occupation', models.CharField(default=b'16', max_length=100, - verbose_name=b'Occupation / Main Livelihood')), - ('level_of_education', models.CharField(default=b'Primary', max_length=100, null=True, verbose_name=b'Highest level of education completed', choices=[(b'Did not attend school', b'Did not attend school'), ( - b'Nursery', b'Nursery'), (b'Primary', b'Primary'), (b"'O' Level", b"'O' Level"), (b"'A' Level", b"'A' Level"), (b'Tertiary', b'Tertiary'), (b'University', b'University')])), - ('resident_since', models.DateField(null=True, - verbose_name=b'Since when have you lived here', blank=True)), - ], - options={ - 'verbose_name': 'Main Respondent', - }, - bases=('survey.householdmember',), - ), - migrations.CreateModel( - name='NumberArgument', - fields=[ - ('testargument_ptr', models.OneToOneField(parent_link=True, auto_created=True, - primary_key=True, serialize=False, to='survey.TestArgument')), - ('param', models.IntegerField()), - ], - bases=('survey.testargument',), - ), - migrations.CreateModel( - name='ODKAccess', - fields=[ - ('intervieweraccess_ptr', models.OneToOneField(parent_link=True, auto_created=True, - primary_key=True, serialize=False, to='survey.InterviewerAccess')), - ('odk_token', models.CharField(default=b'12345', max_length=10)), - ], - bases=('survey.intervieweraccess',), - ), - migrations.CreateModel( - name='ODKGeoPoint', - fields=[ - ('point_ptr', models.OneToOneField(parent_link=True, auto_created=True, - primary_key=True, serialize=False, to='survey.Point')), - ('altitude', models.DecimalField(max_digits=10, decimal_places=3)), - ('precision', models.DecimalField(max_digits=10, decimal_places=3)), - ], - options={ - 'abstract': False, - }, - bases=('survey.point',), - ), - migrations.CreateModel( - name='TextArgument', - fields=[ - ('testargument_ptr', models.OneToOneField(parent_link=True, auto_created=True, - primary_key=True, serialize=False, to='survey.TestArgument')), - ('param', models.TextField()), - ], - bases=('survey.testargument',), - ), - migrations.CreateModel( - name='USSDAccess', - fields=[ - ('intervieweraccess_ptr', models.OneToOneField(parent_link=True, auto_created=True, - primary_key=True, serialize=False, to='survey.InterviewerAccess')), - ('aggregator', models.CharField(default=b'testAggregator', max_length=100, - null=True, blank=True, choices=[(b'testAggregator', b'testAggregator')])), - ], - bases=('survey.intervieweraccess',), - ), - migrations.CreateModel( - name='WebAccess', - fields=[ - ('intervieweraccess_ptr', models.OneToOneField(parent_link=True, auto_created=True, - primary_key=True, serialize=False, to='survey.InterviewerAccess')), - ], - bases=('survey.intervieweraccess',), - ), - migrations.AddField( - model_name='testargument', - name='flow', - field=models.ForeignKey( - related_name='testargument', to='survey.QuestionFlow'), - ), - migrations.AddField( - model_name='randomselection', - name='survey', - field=models.ForeignKey( - related_name='random_selections', to='survey.Survey'), - ), - migrations.AddField( - model_name='question', - name='module', - field=models.ForeignKey( - related_name='questions', default=b'', to='survey.QuestionModule'), - ), - migrations.AddField( - model_name='odksubmission', - name='household_member', - field=models.ForeignKey( - related_name='odk_submissions', blank=True, to='survey.HouseholdMember', null=True), - ), - migrations.AddField( - model_name='odksubmission', - name='interviewer', - field=models.ForeignKey( - related_name='odk_submissions', to='survey.Interviewer'), - ), - migrations.AddField( - model_name='odksubmission', - name='survey', - field=models.ForeignKey( - related_name='odk_submissions', to='survey.Survey'), - ), - migrations.AddField( - model_name='numericalanswer', - name='question', - field=models.ForeignKey(related_name='numericalanswer', - on_delete=django.db.models.deletion.PROTECT, to='survey.Question', null=True), - ), - migrations.AddField( - model_name='nonresponseanswer', - name='survey_listing', - field=models.ForeignKey( - related_name='non_response_answers', to='survey.SurveyHouseholdListing'), - ), - migrations.AddField( - model_name='multiselectanswer', - name='question', - field=models.ForeignKey(related_name='multiselectanswer', - on_delete=django.db.models.deletion.PROTECT, to='survey.Question', null=True), - ), - migrations.AddField( - model_name='multiselectanswer', - name='value', - field=models.ManyToManyField(to='survey.QuestionOption'), - ), - migrations.AddField( - model_name='multichoiceanswer', - name='question', - field=models.ForeignKey(related_name='multichoiceanswer', - on_delete=django.db.models.deletion.PROTECT, to='survey.Question', null=True), - ), - migrations.AddField( - model_name='multichoiceanswer', - name='value', - field=models.ForeignKey(to='survey.QuestionOption', null=True), - ), - migrations.AddField( - model_name='locationweight', - name='survey', - field=models.ForeignKey( - related_name='location_weight', to='survey.Survey'), - ), - migrations.AddField( - model_name='location', - name='coordinates', - field=models.ManyToManyField( - related_name='admin_div_locations', to='survey.Point'), - ), - migrations.AddField( - model_name='location', - name='parent', - field=mptt.fields.TreeForeignKey( - related_name='sub_locations', blank=True, to='survey.Location', null=True), - ), - migrations.AddField( - model_name='location', - name='type', - field=models.ForeignKey( - related_name='locations', to='survey.LocationType'), - ), - migrations.AddField( - model_name='intervieweraccess', - name='interviewer', - field=models.ForeignKey( - related_name='intervieweraccess', to='survey.Interviewer'), - ), - migrations.AddField( - model_name='interview', - name='householdmember', - field=models.ForeignKey( - related_name='interviews', to='survey.HouseholdMember', null=True), - ), - migrations.AddField( - model_name='interview', - name='interview_channel', - field=models.ForeignKey( - related_name='interviews', to='survey.InterviewerAccess'), - ), - migrations.AddField( - model_name='interview', - name='interviewer', - field=models.ForeignKey( - related_name='interviews', to='survey.Interviewer', null=True), - ), - migrations.AddField( - model_name='interview', - name='last_question', - field=models.ForeignKey( - related_name='ongoing', blank=True, to='survey.Question', null=True), - ), - migrations.AddField( - model_name='indicator', - name='module', - field=models.ForeignKey( - related_name='indicator', to='survey.QuestionModule'), - ), - migrations.AddField( - model_name='imageanswer', - name='interview', - field=models.ForeignKey( - related_name='imageanswer', to='survey.Interview'), - ), - migrations.AddField( - model_name='imageanswer', - name='question', - field=models.ForeignKey( - related_name='imageanswer', on_delete=django.db.models.deletion.PROTECT, to='survey.Question', null=True), - ), - migrations.AddField( - model_name='housesurveycompletion', - name='interviewer', - field=models.ForeignKey( - related_name='house_completion', to='survey.Interviewer', null=True), - ), - migrations.AddField( - model_name='housesurveycompletion', - name='survey', - field=models.ForeignKey( - related_name='house_completion', to='survey.Survey'), - ), - migrations.AddField( - model_name='housemembersurveycompletion', - name='householdmember', - field=models.ForeignKey( - related_name='completion_register', to='survey.HouseholdMember', null=True), - ), - migrations.AddField( - model_name='housemembersurveycompletion', - name='interviewer', - field=models.ForeignKey( - related_name='house_member_completion', to='survey.Interviewer', null=True), - ), - migrations.AddField( - model_name='housemembersurveycompletion', - name='survey', - field=models.ForeignKey( - related_name='completion_register', to='survey.Survey'), - ), - migrations.AddField( - model_name='householdmemberbatchcompletion', - name='householdmember', - field=models.ForeignKey( - related_name='completed_member_batches', to='survey.HouseholdMember', null=True), - ), - migrations.AddField( - model_name='householdmemberbatchcompletion', - name='interviewer', - field=models.ForeignKey( - related_name='completed_batches', to='survey.Interviewer', null=True), - ), - migrations.AddField( - model_name='householdmember', - name='household', - field=models.ForeignKey( - related_name='household_members', to='survey.Household'), - ), - migrations.AddField( - model_name='householdmember', - name='registrar', - field=models.ForeignKey( - related_name='registered_household_members', to='survey.Interviewer'), - ), - migrations.AddField( - model_name='householdmember', - name='survey_listing', - field=models.ForeignKey( - related_name='house_members', to='survey.SurveyHouseholdListing'), - ), - migrations.AddField( - model_name='householdlisting', - name='initial_survey', - field=models.ForeignKey( - related_name='listings', to='survey.Survey'), - ), - migrations.AddField( - model_name='householdlisting', - name='list_registrar', - field=models.ForeignKey( - related_name='listings', verbose_name=b'Interviewer', to='survey.Interviewer'), - ), - migrations.AddField( - model_name='householdbatchcompletion', - name='interviewer', - field=models.ForeignKey( - related_name='batch_completed_households', to='survey.Interviewer', null=True), - ), - migrations.AddField( - model_name='household', - name='last_registrar', - field=models.ForeignKey(related_name='registered_households', - verbose_name=b'Interviewer', to='survey.Interviewer'), - ), - migrations.AddField( - model_name='household', - name='listing', - field=models.ForeignKey( - related_name='households', to='survey.HouseholdListing'), - ), - migrations.AddField( - model_name='groupcondition', - name='groups', - field=models.ManyToManyField( - related_name='conditions', to='survey.HouseholdMemberGroup'), - ), - migrations.AddField( - model_name='geopointanswer', - name='interview', - field=models.ForeignKey( - related_name='geopointanswer', to='survey.Interview'), - ), - migrations.AddField( - model_name='geopointanswer', - name='question', - field=models.ForeignKey(related_name='geopointanswer', - on_delete=django.db.models.deletion.PROTECT, to='survey.Question', null=True), - ), - migrations.AddField( - model_name='formula', - name='count', - field=models.ForeignKey( - related_name='as_count', blank=True, to='survey.Question', null=True), - ), - migrations.AddField( - model_name='formula', - name='denominator', - field=models.ForeignKey( - related_name='as_denominator', blank=True, to='survey.Question', null=True), - ), - migrations.AddField( - model_name='formula', - name='denominator_options', - field=models.ManyToManyField( - related_name='denominator_options', to='survey.QuestionOption'), - ), - migrations.AddField( - model_name='formula', - name='groups', - field=models.ForeignKey( - related_name='as_group', blank=True, to='survey.HouseholdMemberGroup', null=True), - ), - migrations.AddField( - model_name='formula', - name='indicator', - field=models.ForeignKey( - related_name='formula', to='survey.Indicator', null=True), - ), - migrations.AddField( - model_name='formula', - name='numerator', - field=models.ForeignKey( - related_name='as_numerator', to='survey.Question', null=True), - ), - migrations.AddField( - model_name='formula', - name='numerator_options', - field=models.ManyToManyField( - related_name='numerator_options', to='survey.QuestionOption'), - ), - migrations.AddField( - model_name='enumerationarea', - name='locations', - field=models.ManyToManyField( - related_name='enumeration_areas', to='survey.Location'), - ), - migrations.AddField( - model_name='dateanswer', - name='interview', - field=models.ForeignKey( - related_name='dateanswer', to='survey.Interview'), - ), - migrations.AddField( - model_name='dateanswer', - name='question', - field=models.ForeignKey( - related_name='dateanswer', on_delete=django.db.models.deletion.PROTECT, to='survey.Question', null=True), - ), - migrations.AddField( - model_name='batchlocationstatus', - name='location', - field=models.ForeignKey( - related_name='open_batches', to='survey.Location', null=True), - ), - migrations.AddField( - model_name='batchcommencement', - name='ea', - field=models.ForeignKey( - related_name='commencement_registry', to='survey.EnumerationArea', null=True), - ), - migrations.AddField( - model_name='batchcommencement', - name='survey', - field=models.ForeignKey( - related_name='commencement_registry', to='survey.Survey', null=True), - ), - migrations.AddField( - model_name='batch', - name='start_question', - field=models.OneToOneField(related_name='starter_batch', null=True, - on_delete=django.db.models.deletion.SET_NULL, blank=True, to='survey.Question'), - ), - migrations.AddField( - model_name='batch', - name='survey', - field=models.ForeignKey( - related_name='batches', to='survey.Survey', null=True), - ), - migrations.AddField( - model_name='audioanswer', - name='interview', - field=models.ForeignKey( - related_name='audioanswer', to='survey.Interview'), - ), - migrations.AddField( - model_name='audioanswer', - name='question', - field=models.ForeignKey( - related_name='audioanswer', on_delete=django.db.models.deletion.PROTECT, to='survey.Question', null=True), - ), - migrations.AddField( - model_name='attachment', - name='submission', - field=models.ForeignKey( - related_name='attachments', to='survey.ODKSubmission'), - ), - migrations.AlterUniqueTogether( - name='answeraccessdefinition', - unique_together=set([('answer_type', 'channel')]), - ), - migrations.AlterUniqueTogether( - name='question', - unique_together=set([('identifier', 'batch')]), - ), - migrations.AlterUniqueTogether( - name='interview', - unique_together=set([('householdmember', 'batch')]), - ), - migrations.AlterUniqueTogether( - name='householdlisting', - unique_together=set([('initial_survey', 'ea')]), - ), - migrations.AlterUniqueTogether( - name='household', - unique_together=set([('house_number', 'listing')]), - ), - migrations.AlterUniqueTogether( - name='groupcondition', - unique_together=set([('value', 'attribute', 'condition')]), - ), - migrations.AddField( - model_name='geopointanswer', - name='value', - field=models.ForeignKey(to='survey.ODKGeoPoint', null=True), - ), - migrations.AlterUniqueTogether( - name='batchchannel', - unique_together=set([('batch', 'channel')]), - ), - migrations.AlterUniqueTogether( - name='batch', - unique_together=set([('survey', 'name')]), - ), - ] diff --git a/survey/migrations/0002_auto_20160621_1244.py b/survey/migrations/0002_auto_20160621_1244.py deleted file mode 100644 index 04c21eba..00000000 --- a/survey/migrations/0002_auto_20160621_1244.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('survey', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='audioanswer', - name='loop_id', - field=models.IntegerField(default=-1), - ), - migrations.AddField( - model_name='dateanswer', - name='loop_id', - field=models.IntegerField(default=-1), - ), - migrations.AddField( - model_name='geopointanswer', - name='loop_id', - field=models.IntegerField(default=-1), - ), - migrations.AddField( - model_name='imageanswer', - name='loop_id', - field=models.IntegerField(default=-1), - ), - migrations.AddField( - model_name='multichoiceanswer', - name='loop_id', - field=models.IntegerField(default=-1), - ), - migrations.AddField( - model_name='multiselectanswer', - name='loop_id', - field=models.IntegerField(default=-1), - ), - migrations.AddField( - model_name='numericalanswer', - name='loop_id', - field=models.IntegerField(default=-1), - ), - migrations.AddField( - model_name='textanswer', - name='loop_id', - field=models.IntegerField(default=-1), - ), - migrations.AddField( - model_name='videoanswer', - name='loop_id', - field=models.IntegerField(default=-1), - ), - ] diff --git a/survey/migrations/__init__.py b/survey/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/survey/models/__init__.py b/survey/models/__init__.py deleted file mode 100644 index dd18f974..00000000 --- a/survey/models/__init__.py +++ /dev/null @@ -1,94 +0,0 @@ -from survey.models.interviewer import Interviewer, SurveyAllocation -from survey.models.interviews import NumericalAnswer, Interview, TextAnswer, MultiChoiceAnswer, \ - MultiSelectAnswer, Answer, ODKGeoPoint, DateAnswer, VideoAnswer, \ - ImageAnswer, AudioAnswer, GeopointAnswer, NonResponseAnswer -from survey.models.backend import Backend -from survey.models.questions import Question, QuestionOption, QuestionFlow, TextArgument, TestArgument -from survey.models.question_templates import QuestionTemplate, TemplateOption -from survey.models.base import BaseModel -from survey.models.batch import Batch, BatchLocationStatus, BatchChannel -from survey.models.enumeration_area import EnumerationArea -from survey.models.household_batch_completion import HouseholdMemberBatchCompletion, HouseholdMemberBatchCompletion, \ - HouseholdBatchCompletion, HouseMemberSurveyCompletion, HouseSurveyCompletion -from survey.models.householdgroups import HouseholdMemberGroup, GroupCondition -from survey.models.households import Household, HouseholdHead, HouseholdMember, HouseholdListing, SurveyHouseholdListing, \ - RandomSelection -from survey.models.access_channels import InterviewerAccess, ODKAccess, USSDAccess, WebAccess -from survey.models.location_weight import LocationWeight -# from survey.models.locations import LocationAutoComplete, LocationCode -from survey.models.surveys import Survey, BatchCommencement -from survey.models.upload_error_logs import UploadErrorLog -from survey.models.users import UserProfile -from survey.models.question_module import QuestionModule -from survey.models.location_type_details import LocationTypeDetails -from survey.models.indicators import Indicator -from survey.models.about_us_content import AboutUs -from survey.models.odk_submission import ODKSubmission, Attachment -from survey.models.formula import Formula -from survey.models.interviews import AnswerAccessDefinition -from survey.models.locations import Location, LocationType - -__all__ = [ - # 'ULocation' - 'InterviewerAccess', - 'TemplateOption', - 'WebAccess', - 'SurveyAllocation', - 'Location', - 'LocationType', - 'AnswerAccessDefinition', - 'ODKAccess', - 'USSDAccess', - 'Answer', - 'TextAnswer', - 'NumericalAnswer', - 'MultiChoiceAnswer', - 'MultiSelectAnswer', - 'ODKGeoPoint', - 'DateAnswer', - 'GeopointAnswer', - 'VideoAnswer', - 'ImageAnswer', - 'AudioAnswer', - 'NonResponseAnswer', - 'Interview', - 'Question', - 'QuestionOption', - 'QuestionTemplate', - 'BaseModel', - 'Backend', - 'Batch', - 'BatchChannel', - 'Formula', - 'HouseholdMemberGroup', - 'RandomSelection', - 'Household', - 'HouseholdHead', - 'HouseholdMemberBatchCompletion', - 'HouseMemberSurveyCompletion', - 'HouseSurveyCompletion', - 'GroupCondition', - 'Interviewer', - 'BatchCommencement', - 'SurveyHouseholdListing', - # 'LocationAutoComplete', - 'Survey', - 'UserProfile', - 'QuestionModule', - 'QuestionFlow', - 'TextArgument', - 'TestArgument', - 'LocationTypeDetails', - # 'BatchQuestionOrder', - # 'LocationCode', - 'Indicator', - 'LocationWeight', - 'UploadErrorLog', - 'HouseholdMemberBatchCompletion', - 'BatchLocationStatus', - 'HouseholdMember', - 'HouseholdBatchCompletion', - 'HouseholdListing', - 'AboutUs', - 'EnumerationArea' -] diff --git a/survey/models/about_us_content.py b/survey/models/about_us_content.py deleted file mode 100644 index f5d4df62..00000000 --- a/survey/models/about_us_content.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.db import models -from survey.models.base import BaseModel - - -class AboutUs(BaseModel): - content = models.TextField(null=False, blank=False) diff --git a/survey/models/access_channels.py b/survey/models/access_channels.py deleted file mode 100644 index 205e088d..00000000 --- a/survey/models/access_channels.py +++ /dev/null @@ -1,65 +0,0 @@ -import datetime -from django.conf import settings -from django.db import models -from survey.models.base import BaseModel -from model_utils.managers import InheritanceManager -from django.utils.safestring import mark_safe -from django.contrib.auth.models import User - -DEFAULT_TOKEN = getattr(settings, 'DEFAULT_TOKEN', "12345") - - -class InterviewerAccess(BaseModel): - DAYS = 'D' - HOURS = 'H' - MINUTES = 'M' - SECONDS = 'S' - objects = InheritanceManager() - REPONSE_TIMEOUT_DURATIONS = [ - (DAYS, 'Days'), (HOURS, 'Hours'), (MINUTES, 'Minutes'), (SECONDS, 'Seconds')] - interviewer = models.ForeignKey('Interviewer', related_name='%(class)s') - user_identifier = models.CharField(max_length=100) - is_active = models.BooleanField(default=True, verbose_name='Activated') - reponse_timeout = models.PositiveIntegerField(default=1000, - help_text='Max time to wait for response before ending interview', - null=True, blank=True) - duration = models.CharField(default=HOURS, choices=REPONSE_TIMEOUT_DURATIONS, max_length=100, - null=True, blank=True) - - class Meta: - app_label = 'survey' - - @classmethod - def choice_name(cls): - return cls._meta.verbose_name.title() - - @classmethod - def access_channels(cls): - return [cl.choice_name() for cl in cls.__subclasses__()] - - def __unicode__(self): - name = '%s' % ( - 'green' if self.is_active else 'red', self.user_identifier) - return mark_safe(name) - - -class USSDAccess(InterviewerAccess): - aggregator = models.CharField(choices=settings.AGGREGATORS, max_length=100, null=True, blank=True, - default=settings.DEFAULT_AGGREGATOR) - - class Meta: - app_label = 'survey' - - -class ODKAccess(InterviewerAccess): - odk_token = models.CharField(max_length=10, default=DEFAULT_TOKEN) - - class Meta: - app_label = 'survey' - - -class WebAccess(InterviewerAccess): - pass - - class Meta: - app_label = 'survey' diff --git a/survey/models/backend.py b/survey/models/backend.py deleted file mode 100644 index af70621c..00000000 --- a/survey/models/backend.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.db import models - - -class Backend(models.Model): - name = models.CharField(max_length=20, unique=True) - - class Meta: - app_label = 'survey' - - def __unicode__(self): - return self.name diff --git a/survey/models/base.py b/survey/models/base.py deleted file mode 100644 index d61702e1..00000000 --- a/survey/models/base.py +++ /dev/null @@ -1,17 +0,0 @@ -from django_extensions.db.models import TimeStampedModel - - -class BaseModel(TimeStampedModel): - - class Meta: - app_label = 'survey' - abstract = True - - def __repr__(self): - if hasattr(self, 'id'): - return '%s-%s' % (self.__class__.__name__, self.id) - else: - return super(BaseModel, self).__repr__() - - # def __int__(self): - # return self.pk diff --git a/survey/models/batch.py b/survey/models/batch.py deleted file mode 100644 index e6e33039..00000000 --- a/survey/models/batch.py +++ /dev/null @@ -1,331 +0,0 @@ -from django.db import models -from django.db.models import Max -from survey.models.householdgroups import HouseholdMemberGroup -from survey.models.locations import Location -from survey.models.surveys import Survey -from survey.models.base import BaseModel -from survey.utils.views_helper import get_descendants -from survey.models.questions import Question, QuestionFlow -from survey.forms.logic import LogicForm -from survey.models.access_channels import InterviewerAccess -# from survey.models.enumeration_area import EnumerationArea -from survey.models.interviews import AnswerAccessDefinition, Answer -from survey.models.access_channels import ODKAccess -from django.core.exceptions import ValidationError -from ordered_set import OrderedSet -from collections import OrderedDict -from cacheops import cached_as -from django.conf import settings - -ALL_GROUPS = HouseholdMemberGroup.objects.all() -ALL_ANSWERS = Answer.answer_types() - - -class Batch(BaseModel): - order = models.PositiveIntegerField(null=True) - name = models.CharField(max_length=100, blank=False, - null=True, db_index=True) - description = models.CharField(max_length=300, blank=True, null=True) - survey = models.ForeignKey(Survey, null=True, related_name="batches") -# eas = models.ManyToManyField(EnumerationArea, related_name='batches', null=True) #enumeration areas for which this Batch is open -# group = models.ForeignKey("HouseholdMemberGroup", null=True, related_name="question_group") - start_question = models.OneToOneField( - Question, related_name='starter_batch', null=True, blank=True, on_delete=models.SET_NULL) - BATCH_IS_OPEN_MESSAGE = "Batch cannot be deleted because it is open in %s." - BATCH_HAS_ANSWERS_MESSAGE = "Batch cannot be deleted because it has responses." - - class Meta: - app_label = 'survey' - unique_together = [('survey', 'name',)] - - def save(self, *args, **kwargs): - last_order = Batch.objects.aggregate(Max('order'))['order__max'] - self.order = last_order + 1 if last_order else 1 - super(Batch, self).save(*args, **kwargs) - - def __unicode__(self): - return "%s" % self.name - - def can_be_deleted(self): - return True, '' - - def get_looper_flow(self, question): - try: - return question.connecting_flows.get(desc=LogicForm.BACK_TO_ACTION) - except QuestionFlow.DoesNotExist: - return first_inline_flow_with_desc(question, LogicForm.BACK_TO_ACTION) - - def loop_starters(self): - from survey.forms.logic import LogicForm - flows = QuestionFlow.objects.filter( - next_question__batch=self, desc=LogicForm.BACK_TO_ACTION) - return set([f.next_question for f in flows]) - - def loop_enders(self): - from survey.forms.logic import LogicForm - flows = QuestionFlow.objects.filter( - question__batch=self, desc=LogicForm.BACK_TO_ACTION) - return set([f.question for f in flows]) - - def non_response_enabled(self, ea): - locations = set() - ea_locations = ea.locations.all() - if ea_locations: - map(lambda loc: locations.update( - loc.get_ancestors(include_self=True)), ea_locations) - return self.open_locations.filter(non_response=True, location__pk__in=[location.pk for location in locations]).exists() - - @property - def answer_types(self): - access_channels = self.access_channels.values_list( - 'channel', flat=True) - return set(AnswerAccessDefinition.objects.filter(channel__in=access_channels).values_list('answer_type', flat=True)) - - def get_non_response_active_locations(self): - locations = set() - locations_register = self.open_locations.filter(non_response=True) - if locations_register: - map(lambda reg: locations.update(reg.location.get_descendants( - include_self=True)), locations_register) - return locations - - @property - def non_response_eas(self): - eas = set() - locations = self.get_non_response_active_locations() - map(lambda loc: eas.update(loc.enumeration_areas.all()), locations) - return eas - - def other_surveys_with_open_batches_in(self, location): - batch_ids = location.open_batches.all().exclude( - batch__survey=self.survey).values_list('batch', flat=True) - return Survey.objects.filter(batch__id__in=batch_ids) - - def open_for_location(self, location): - return self.open_locations.get_or_create(batch=self, location=location) - - def close_for_location(self, location): - self.open_locations.filter(batch=self, location=location).delete() - - def is_closed_for(self, location): - # its closed if its closed in any parent loc or self - locations = location.get_ancestors(include_self=True) - return self.open_locations.filter(location__pk__in=locations).count() == 0 - - def is_open_for(self, location): - return not self.is_closed_for(location) - - def is_applicable(self, house_member): - return True # probably might be used later - - def next_inline(self, question, groups=ALL_GROUPS, channel=ODKAccess.choice_name(), exclude_groups=[]): - qflows = QuestionFlow.objects.filter( - question__batch=self, validation_test__isnull=True) - if qflows.exists(): - return next_inline_question(question, qflows, groups, - AnswerAccessDefinition.answer_types(channel), exclude_groups) - - def is_open(self): - return self.open_locations.all().exists() - - def last_question_inline(self): - qflows = QuestionFlow.objects.filter( - question__batch=self, validation_test__isnull=True) - if qflows.exists(): - return last_inline(self.start_question, qflows) - else: - return self.start_question - - def questions_inline(self): - qflows = QuestionFlow.objects.filter( - question__batch=self, validation_test__isnull=True) - if self.start_question: - inlines = inline_questions(self.start_question, qflows) - if inlines and inlines[-1] is None: - inlines.pop(-1) - return inlines - else: - return [] - - def loop_backs_questions(self): - ''' - :return: loop question structure for this batch (q_start_pk, q_end_pk) = [] - ''' - cached_as(Batch.objects.filter(id=self.id)) - - def _loop_backs_questions(): - survey_questions = self.survey_questions - loop_starters = self.loop_starters() - loop_enders = self.loop_enders() - start = None - loop_desc = OrderedDict() - present_loop = [] - for q in survey_questions: - if q in loop_starters: - start = q - present_loop = [] - if q in loop_enders: - present_loop.append(q) - loop_desc[(start.pk, q.pk)] = present_loop - start = None - if start: - present_loop.append(q) - # just transpose - return loop_desc - return _loop_backs_questions() - - def loop_back_boundaries(self): - cached_as(Batch.objects.filter(id=self.id)) - - def _loop_back_boundaries(): - loop_desc = self.loop_backs_questions() - quest_map = OrderedDict() - for boundary, loop_questions in loop_desc.items(): - map(lambda q: quest_map.update( - {q.pk: boundary}), loop_questions) - return quest_map - return _loop_back_boundaries() - - def previous_inlines(self, question): - inlines = self.questions_inline() - if question not in inlines: - raise ValidationError('%s not inline' % question.identifier) - previous = [] - for q in inlines: - if q.identifier == question.identifier: - break - else: - previous.append(q) - return set(previous) - - def zombie_questions(self): - return Question.zombies(self) - - @property - def survey_questions(self): - cached_as(Batch.objects.filter(id=self.id)) - - def _survey_questions(): - inline_ques = self.questions_inline() - questions = OrderedSet(inline_ques) - survey_questions = OrderedSet() - for ques in inline_ques: - survey_questions.append(ques) - # boldly assuming subquests dont go - map(lambda q: survey_questions.add( - q), ques.direct_sub_questions()) - # more than quest subquestion deep for present implemnt - return survey_questions - return _survey_questions() - - def activate_non_response_for(self, location): - self.open_locations.filter(location=location).update(non_response=True) - - def deactivate_non_response_for(self, location): - self.open_locations.filter( - location=location).update(non_response=False) - -# def sub_questions(question, flows): -# questions = OrderedSet() -# try: -# qflows = flows.filter(question=question).exclude(next_question=question) -# if qflows: -# for flow in qflows: -# if flow.next_question: -# questions.add(flow.next_question) -# subsequent = sub_questions(flow.next_question, flows) -# map(lambda q: questions.add(q), subsequent) -# except QuestionFlow.DoesNotExist: -# return OrderedSet() -# return questions - - -def next_inline_question(question, flows, groups=ALL_GROUPS, answer_types=ALL_ANSWERS, exclude_groups=[]): - if groups is None: - groups = ALL_GROUPS - if answer_types is None: - answer_types = ALL_ANSWERS - try: - qflow = flows.get(question=question, validation_test__isnull=True) - next_question = qflow.next_question - if next_question and next_question.group in groups and next_question.group not in exclude_groups and \ - next_question.answer_type in answer_types: - return next_question - else: - return next_inline_question(next_question, flows, groups=groups, answer_types=answer_types, - exclude_groups=exclude_groups) - except QuestionFlow.DoesNotExist: - return None - - -def last_inline(question, flows): - try: - qflow = flows.get( - question=question, validation_test__isnull=True, next_question__isnull=False) - return last_inline(qflow.next_question, flows) - except QuestionFlow.DoesNotExist: - return question - - -def first_inline_flow_with_desc(question, desc): - try: - if question.flows.filter(desc=desc).exists(): - return question.flows.get(desc=desc) - if question.connecting_flows.filter(desc=desc).exists(): - return None - iflow = question.flows.get( - validation_test__isnull=True, next_question__isnull=False) - return first_inline_flow_with_desc(iflow.next_question, desc) - except QuestionFlow.DoesNotExist: - return None - - -def inline_questions(question, flows): - questions = [question, ] - try: - qflow = flows.get(question=question, validation_test__isnull=True) - questions.extend(inline_questions(qflow.next_question, flows)) - except QuestionFlow.DoesNotExist: - pass - return questions - - -def inline_flows(question, flows): - flows = [] - try: - qflow = flows.get(question=question, validation_test__isnull=True) - flows.append(qflow) - flows.extend(inline_questions(qflow.next_question, flows)) - except QuestionFlow.DoesNotExist: - pass - return flows - - -# @property -# def batch_questions(self): -# flows = self.flows.all() -# questions = set([self.start_question, ]) -# for flow in flows: -# questions.add(flow.next_question) -# return questions - -# map(lambda flow: question_map.update({flow.next_question.identifier: flow.next_question}), flows) -# compute() - - -class BatchLocationStatus(BaseModel): - batch = models.ForeignKey(Batch, null=True, related_name="open_locations") - location = models.ForeignKey( - Location, null=True, related_name="open_batches") - non_response = models.BooleanField(null=False, default=False) - - -class BatchChannel(BaseModel): - ACCESS_CHANNELS = [(name, name) - for name in InterviewerAccess.access_channels()] - batch = models.ForeignKey(Batch, related_name='access_channels') - channel = models.CharField(max_length=100, choices=ACCESS_CHANNELS) - - class Meta: - app_label = 'survey' - unique_together = ('batch', 'channel') diff --git a/survey/models/enumeration_area.py b/survey/models/enumeration_area.py deleted file mode 100644 index 27742a5d..00000000 --- a/survey/models/enumeration_area.py +++ /dev/null @@ -1,66 +0,0 @@ -# from rapidsms.contrib.locations.models import Location -from survey.models.locations import Location -from survey.models import BaseModel -from django.db import models -from survey.models.surveys import Survey -from survey.models.batch import Batch, BatchLocationStatus - - -class EnumerationArea(BaseModel): - name = models.CharField(max_length=100, blank=False, null=True) - code = models.CharField(max_length=200, editable=False, - blank=True, null=True, unique=True) - # total_households = models.PositiveIntegerField(null=True, blank=True) - locations = models.ManyToManyField( - Location, related_name="enumeration_areas") - - def __unicode__(self): - return self.name - - def parent_locations(self): - # now this assumes ea locations are made from smallest units belonging - # to same group - if self.locations.exists(): - location = self.locations.all()[0] - return location.get_ancestors().exclude(parent__isnull=True) - - def get_survey_openings(self, survey=None): - parent_locations = set() - ea_locations = self.locations.all() - if ea_locations: - map(lambda loc: parent_locations.update( - loc.get_ancestors(include_self=True)), ea_locations) - kwargs = {'location__pk__in': [loc.pk for loc in parent_locations]} - if survey: - kwargs['batch__survey'] = survey - location_registry = BatchLocationStatus.objects.filter(**kwargs) - return location_registry - - def open_surveys(self): - location_registry = self.get_survey_openings() - return list(Survey.objects.filter(pk__in=[reg.batch.survey.pk for reg in location_registry])) - - def open_batches(self, survey, house_member=None, access_channel=None): - location_registry = self.get_survey_openings(survey) - batches = list([reg.batch for reg in location_registry]) - if house_member is not None: - applicable = [] - for batch in batches: - if batch.is_applicable(house_member): - # if access_channel and not batch.access_channels.filter(channel=access_channel): - # pass - # else: - applicable.append(batch) - batches = applicable - return sorted(batches, key=lambda batch: batch.order) - - def get_siblings(self): - return self.under_(self.parent_location()) - - @classmethod - def under_(cls, selected_location): - return cls.objects.filter(locations__in=selected_location.get_leafnodes()).distinct('name') - -# def validate_unique(self, *args, **kwargs): -# super(EnumerationArea, self).validate_unique(*args, **kwargs) -# diff --git a/survey/models/formula.py b/survey/models/formula.py deleted file mode 100644 index ca7d8eb6..00000000 --- a/survey/models/formula.py +++ /dev/null @@ -1,153 +0,0 @@ -from django.db import models -from django.db.models import Sum -from survey.models.base import BaseModel -from survey.models.interviewer import Interviewer -from survey.models.questions import Question, QuestionOption - - -class Formula(BaseModel): - numerator = models.ForeignKey( - Question, null=True, related_name="as_numerator") - groups = models.ForeignKey( - "HouseholdMemberGroup", null=True, blank=True, related_name="as_group") - denominator = models.ForeignKey( - Question, null=True, blank=True, related_name="as_denominator") - numerator_options = models.ManyToManyField( - QuestionOption, related_name='numerator_options') - denominator_options = models.ManyToManyField( - QuestionOption, related_name='denominator_options') - count = models.ForeignKey( - Question, null=True, blank=True, related_name="as_count") - indicator = models.ForeignKey( - "Indicator", null=True, related_name="formula") - - class Meta: - app_label = 'survey' - - def get_count_type(self): - return self.count or self.groups - - def compute_for_location(self, location): - interviewers = Interviewer.lives_under_location(location) - if self.numerator.is_multichoice(): - return self.compute_multichoice_question_for_interviewers(interviewers) - else: - return self.compute_numerical_question_for_interviewers(interviewers) - - def compute_for_next_location_type_in_the_hierarchy(self, current_location): - locations = current_location.get_children() - data = {} - for location in locations: - data[location] = self.compute_for_location(location) - return data - - def compute_numerical_question_for_interviewers(self, interviewers): - values = [] - for interviewer in interviewers: - values.append( - self.compute_numerical_question_for_interviewer(interviewer)) - return sum(values) / len(values) - - def compute_multichoice_question_for_interviewers(self, interviewers): - values = [] - computed_dict = {} - for interviewer in interviewers: - values.append( - self.compute_multichoice_question_for_interviewer(interviewer)) - - denominator = len(values) - for option in self.numerator.options.all(): - numerator = sum([value[option.text] for value in values]) - computed_dict[option.text] = numerator / denominator - return computed_dict - - def process_formula(self, numerator, denominator, interviewer): - return ((numerator * interviewer.weights) / denominator) * 100 - - def compute_multichoice_question_for_interviewer(self, interviewer): - values = {} - denominator = self.answer_sum_for_interviewer( - self.denominator, interviewer) - for option in self.numerator.options.all(): - numerator = self.compute_numerator_for_option(option, interviewer) - values[option.text] = self.process_formula( - numerator, denominator, interviewer) - return values - - def compute_numerator_for_option(self, option, interviewer): - households = self.numerator.answer_class().objects.filter(interviewer=interviewer, answer=option).values_list( - 'household', flat=True) - return \ - self.denominator.answer_class().objects.filter(household__in=households, - question=self.denominator).aggregate( - Sum('answer'))['answer__sum'] - - def answer_sum_for_interviewer(self, question, interviewer): - return question.answer_class().objects.filter(interviewer=interviewer, question=question).\ - aggregate(Sum('answer'))['answer__sum'] - - def compute_numerical_question_for_interviewer(self, interviewer): - denominator = self.answer_sum_for_interviewer( - self.denominator, interviewer) - numerator = self.answer_sum_for_interviewer( - self.numerator, interviewer) - return self.process_formula(numerator, denominator, interviewer) - - def get_denominator_sum_based_on_question_type(self, interviewer, question): - if question.is_multichoice(): - option_ids = [] - for option in self.denominator_options.all(): - option_ids.append(option.id) - denominator_number = len(question.answer_class().objects.filter(interviewer=interviewer, - answer__id__in=option_ids)) - else: - denominator_number = self.answer_sum_for_interviewer( - question, interviewer) - return denominator_number - - def numerator_computation(self, interviewer, question): - numerator_number = 1 - if question.is_multichoice(): - option_ids = [] - for option in self.numerator_options.all(): - option_ids.append(option.id) - numerator_number = len(question.answer_class().objects.filter(interviewer=interviewer, - answer__id__in=option_ids)) - else: - numerator_number = self.answer_sum_for_interviewer( - question, interviewer) - return numerator_number - - def denominator_computation(self, interviewer, survey): - denominator_number = 1 - - if self.groups: - all_members_in_group = [] - for household in survey.survey_household.all().filter(interviewer=interviewer): - all_members_in_group.append( - household.members_belonging_to_group(self.groups)) - - denominator_number = len(all_members_in_group) - elif self.count: - denominator_number = self.get_denominator_sum_based_on_question_type( - interviewer, self.count) - - elif self.denominator: - denominator_number = self.get_denominator_sum_based_on_question_type( - interviewer, self.denominator) - - return denominator_number - - def compute_for_household_with_sub_options(self, survey, interviewer): - numerator_number = self.numerator_computation( - interviewer, self.numerator) - denominator_number = self.denominator_computation(interviewer, survey) - return numerator_number / denominator_number - - def save_denominator_options(self, question_options): - for option in question_options: - self.denominator_options.add(option) - - def save_numerator_options(self, question_options): - for option in question_options: - self.numerator_options.add(option) diff --git a/survey/models/helper_constants.py b/survey/models/helper_constants.py deleted file mode 100644 index 7d912a83..00000000 --- a/survey/models/helper_constants.py +++ /dev/null @@ -1,10 +0,0 @@ - -CONDITIONS = { - 'EQUALS': 'EQUALS', - 'EQUALS_OPTION': 'EQUALS OPTION', - 'GREATER_THAN_QUESTION': '> QUESTION RESPONSE', - 'GREATER_THAN_VALUE': '> VALUE', - 'LESS_THAN_QUESTION': '< QUESTION RESPONSE', - 'LESS_THAN_VALUE': '< VALUE', - 'BETWEEN': 'BETWEEN', -} diff --git a/survey/models/household_batch_completion.py b/survey/models/household_batch_completion.py deleted file mode 100644 index b17ff9a2..00000000 --- a/survey/models/household_batch_completion.py +++ /dev/null @@ -1,36 +0,0 @@ -from django.db import models -from survey.models.base import BaseModel - - -class HouseholdMemberBatchCompletion(BaseModel): - householdmember = models.ForeignKey( - 'HouseholdMember', null=True, related_name="completed_member_batches") - batch = models.ForeignKey( - 'Batch', null=True, related_name="completed_households") - interviewer = models.ForeignKey( - 'Interviewer', null=True, related_name="completed_batches") - - -class HouseholdBatchCompletion(BaseModel): - household = models.ForeignKey( - 'Household', null=True, related_name="batch_completion_batches") - batch = models.ForeignKey( - 'Batch', null=True, related_name="batch_completion_households") - interviewer = models.ForeignKey( - 'Interviewer', null=True, related_name="batch_completed_households") - - -class HouseMemberSurveyCompletion(BaseModel): - householdmember = models.ForeignKey( - 'HouseholdMember', null=True, related_name="completion_register") - survey = models.ForeignKey('Survey', related_name='completion_register') - interviewer = models.ForeignKey( - 'Interviewer', null=True, related_name="house_member_completion") - - -class HouseSurveyCompletion(BaseModel): - household = models.ForeignKey( - 'Household', null=True, related_name="completion_registry") - survey = models.ForeignKey('Survey', related_name='house_completion') - interviewer = models.ForeignKey( - 'Interviewer', null=True, related_name="house_completion") diff --git a/survey/models/householdgroups.py b/survey/models/householdgroups.py deleted file mode 100644 index 318aaf87..00000000 --- a/survey/models/householdgroups.py +++ /dev/null @@ -1,146 +0,0 @@ -from django.db import models -from django.utils.datastructures import SortedDict -from survey.models.base import BaseModel -import ast - - -class HouseholdMemberGroup(BaseModel): - name = models.CharField(max_length=50) - order = models.PositiveIntegerField( - null=False, blank=False, unique=True, default=0) - - def __unicode__(self): - return self.name - - def all_questions(self): - return self.question_group.all() - - def get_all_conditions(self): - return self.conditions.all() - - def maximum_question_order(self): - all_questions = self.all_questions() - return all_questions.order_by('order').reverse()[0].order if all_questions else 0 - - def remove_related_questions(self): - self.question_templates.all().delete() - - def household_members_count_per_location_in(self, locations, survey): - data = SortedDict() - all_households = survey.registered_households.all() - from survey.models import HouseholdMember - for location in locations: - location_descendants = location.get_leafnodes( - include_self=True).values_list('id', flat=True) - households = all_households.filter( - listing__ea__locations__in=location_descendants).values_list('id', flat=True) - all_members = HouseholdMember.objects.filter( - household__id__in=households) - qualified_members = filter( - lambda member: member.belongs_to(self), all_members) - data[location] = {self.name: len(qualified_members)} - return data - - def hierarchical_result_for(self, location_parent, survey): - locations = location_parent.get_children().order_by('name')[:10] - answers = self.household_members_count_per_location_in( - locations, survey) - return answers - - @classmethod - def max_order(cls): - all_groups = cls.objects.all() - return all_groups.order_by('-order')[0].order if all_groups else 0 - - class Meta: - app_label = 'survey' - - -def household_member_test(func): - func.is_reply_test = True - return func - - -class GroupCondition(BaseModel): - CONDITIONS = { - 'EQUALS': 'EQUALS', - 'GREATER_THAN': 'GREATER_THAN', - 'LESS_THAN': 'LESS_THAN', - } - - MATCHING_METHODS = { - 'EQUALS': 'is_equal', - 'GREATER_THAN': 'is_greater_than', - 'LESS_THAN': 'is_less_than', - } - - GROUP_TYPES = { - 'AGE': 'AGE', - 'GENDER': 'GENDER', - 'GENERAL': 'ROLE' - } - - value = models.CharField(max_length=50) - attribute = models.CharField( - max_length=20, default='AGE', choices=GROUP_TYPES.items()) - condition = models.CharField( - max_length=20, default='EQUALS', choices=CONDITIONS.items()) - groups = models.ManyToManyField( - HouseholdMemberGroup, related_name='conditions') - - def confirm_head(self, value): - return bool(value) - - def odk_confirm_head(self, value): - return 'true()' if bool(value) else 'false()' - - def matches(self, attributes): - value = attributes.get(self.attribute.upper()) - return self.matches_condition(value) - - def odk_matches(self, odk_attributes): - value_path = odk_attributes.get(self.attribute.upper()) - method = getattr(self, 'odk_%s' % - self.MATCHING_METHODS[self.condition]) - return method(value_path) - - def matches_condition(self, value): - method = getattr(self, self.MATCHING_METHODS[self.condition]) - return method(value) - - def is_equal(self, value): - return str(self.value) == str(value) or value == self.confirm_head(self.value) - - def odk_is_equal(self, value_path): - return "%s = '%s'" % (value_path, self.value) - - def is_greater_than(self, value): - return int(value) >= int(self.value) - - def odk_is_greater_than(self, value_path): - return "%s > %s" % (value_path, self.value) - - def is_less_than(self, value): - return int(value) <= int(self.value) - - def odk_is_less_than(self, value_path): - return "%s < %s" % (value_path, self.value) - - @property - def display_value(self): - if self.attribute == GroupCondition.GROUP_TYPES['GENDER']: - return 'Male' if self.value == '1' else 'Female' - if self.attribute == 'GENERAL': - return 'HEAD' if self.value == '1' else 'NOT HEAD' - return self.value - - @property - def display_attribute(self): - return GroupCondition.GROUP_TYPES[self.attribute] - - class Meta: - app_label = 'survey' - unique_together = ('value', 'attribute', 'condition') - - def __unicode__(self): - return "%s %s %s" % (GroupCondition.GROUP_TYPES[self.attribute], self.condition, self.display_value) diff --git a/survey/models/households.py b/survey/models/households.py deleted file mode 100644 index 02e5ce45..00000000 --- a/survey/models/households.py +++ /dev/null @@ -1,284 +0,0 @@ -from datetime import date, datetime -from django.utils.datastructures import SortedDict -import dateutils -import string -from django.core.exceptions import ObjectDoesNotExist -from django.core.validators import MinValueValidator, MaxValueValidator -from django.db import models -from django.conf import settings -from survey.interviewer_configs import LEVEL_OF_EDUCATION, MONTHS -from survey.models.base import BaseModel -from survey.models.interviews import Answer -from survey.models.access_channels import InterviewerAccess -from survey.models.householdgroups import HouseholdMemberGroup -from survey.models.household_batch_completion import HouseSurveyCompletion, HouseholdBatchCompletion, \ - HouseholdMemberBatchCompletion, HouseMemberSurveyCompletion -from django.core.exceptions import ValidationError -from django import template -from django.db.models import Max - - -class HouseholdListing(BaseModel): - ea = models.ForeignKey("EnumerationArea", null=True, - related_name="household_enumeration_area") - list_registrar = models.ForeignKey( - 'Interviewer', related_name='listings', verbose_name='Interviewer') - initial_survey = models.ForeignKey('Survey', related_name='listings') - # total_households = models.IntegerField(null=True, blank=True) - - def __unicode__(self): - return '%s-%s' % (self.ea, self.initial_survey) - - class Meta: - app_label = 'survey' - unique_together = [('initial_survey', 'ea'), ] - - -class SurveyHouseholdListing(BaseModel): - listing = models.ForeignKey( - HouseholdListing, related_name='survey_houselistings') - survey = models.ForeignKey('Survey', related_name='survey_house_listings') - - class Meta: - app_label = 'survey' - - @classmethod - def get_or_create_survey_listing(cls, interviewer, survey): - survey_listing = None - try: - return cls.objects.get(survey=survey, listing__ea=interviewer.ea) - except cls.DoesNotExist: - try: - listing = HouseholdListing.objects.get( - ea=interviewer.ea, initial_survey=survey.preferred_listing) - except HouseholdListing.DoesNotExist: - listing = HouseholdListing.objects.create( - ea=interviewer.ea, initial_survey=survey, list_registrar=interviewer) - survey_listing = cls.objects.create(survey=survey, listing=listing) - return survey_listing - - -class Household(BaseModel): - MALE = True - FEMALE = False - REGISTRATION_CHANNELS = [(name, name) - for name in InterviewerAccess.access_channels()] - house_number = models.PositiveIntegerField(verbose_name="Household Number") - listing = models.ForeignKey(HouseholdListing, related_name='households') - physical_address = models.CharField( - max_length=200, null=True, blank=True, verbose_name="Structure Address") - last_registrar = models.ForeignKey( - 'Interviewer', related_name='registered_households', verbose_name='Interviewer') - registration_channel = models.CharField( - max_length=100, choices=REGISTRATION_CHANNELS) - head_desc = models.CharField(max_length=200) - head_sex = models.BooleanField(default=FEMALE) - - class Meta: - app_label = 'survey' - unique_together = [('house_number', 'listing'), ] - - def __unicode__(self): - return 'HH-%s' % self.house_number - - @classmethod - def next_new_house(cls, listing): - max = cls.objects.filter( - listing=listing).aggregate(Max('house_number')) - if not max: - max = {'house_number': 0} - return max['house_number'] + 1 - - # def clean(self): - # super(Household, self).clean() - # if self.house_number > self.ea.total_households: - # raise ValidationError('Household number has exceeded total households in the Enumeration area') - - def get_head(self): - try: - return HouseholdHead.objects.filter(household=self)[0] - except IndexError: - return None - - @property - def head_name(self): - head = self.get_head() - if head: - return '%s %s' % (head.first_name or '', head.surname or '') - - @property - def members(self): - return HouseholdMember.objects.filter(household=self) - - def _get_related_location_name(self, key, location_hierarchy): - location_object = location_hierarchy.get(key, None) - location_name = "" - if location_object: - location_name = location_object.name - return location_name - - def get_related_location(self): - location_hierarchy = self.location_hierarchy() - related_location = SortedDict() - return related_location - - @classmethod - def set_related_locations(cls, households): - for household in households: - household.related_locations = household.get_related_location() - return households - - @classmethod - def all_households_in(cls, location, survey, ea=None): - all_households = Household.objects.filter(listing__survey_houselistings__survey=survey, - listing__ea__locations__in=location.get_leafnodes(include_self=True)).distinct() - if ea: - return all_households.filter(listing__ea=ea) - return all_households - - def has_completed(self, survey): - completion_recs = HouseMemberSurveyCompletion.objects.filter( - householdmember__household=self, survey=survey).distinct() - return completion_recs.count() >= self.members.count() - - def has_completed_batch(self, batch): - completion_recs = HouseholdMemberBatchCompletion.objects.filter( - householdmember__household=self, batch=batch).distinct() - return completion_recs.count() >= self.members.count() - - def survey_completed(self, survey, registrar): - return HouseSurveyCompletion.objects.create(household=self, survey=survey, interviewer=registrar) - - def batch_completed(self, batch, registrar): - return HouseholdBatchCompletion.objects.create(household=self, batch=batch, interviewer=registrar) - - def date_interviewed_for(self, batch): - hmcs = HouseholdMemberBatchCompletion.objects.filter( - householdmember__household=self, batch=batch) - if hmcs.exists(): - return hmcs[0].created - - def total_members_interviewed(self, batch): - return HouseholdMemberBatchCompletion.objects.\ - filter(householdmember__household=self, batch=batch).values( - 'householdmember').distinct().count() - - -class HouseholdMember(BaseModel): - MALE = 1 - FEMALE = 0 - surname = models.CharField(max_length=25, verbose_name="Family Name") - first_name = models.CharField( - max_length=25, blank=True, null=True, verbose_name="First Name") - gender = models.BooleanField(default=True, verbose_name="Sex", choices=[ - (MALE, 'M'), (FEMALE, 'F')]) - date_of_birth = models.DateField(auto_now=False) - household = models.ForeignKey(Household, related_name='household_members') - survey_listing = models.ForeignKey( - SurveyHouseholdListing, related_name='house_members') - registrar = models.ForeignKey( - 'Interviewer', related_name='registered_household_members') - registration_channel = models.CharField( - max_length=100, choices=Household.REGISTRATION_CHANNELS) - - def is_head(self): - try: - HouseholdHead.objects.get(pk=self) - return True - except HouseholdHead.DoesNotExist: - return False - - @property - def ea(self): - return self.household.listing.ea - - def __unicode__(self): - return '%s, %s' % (self.surname, self.first_name) - - def answers(self, batch): - answers = [] - map(lambda answer_class: - answers.extend(answer_class.objects.filter( - householdmember=self, batch=batch)), - Answer.answer_types) - return answers - - @property - def age(self): - return dateutils.relativedelta(datetime.utcnow().date(), self.date_of_birth).years - - def belongs_to(self, member_group): - attributes = {'AGE': self.age, - 'GENDER': str(int(self.gender)), - 'GENERAL': str(int(self.is_head())) - } - for condition in member_group.get_all_conditions(): - if not condition.matches(attributes): - return False - return True - - @property - def groups(self): - groups = HouseholdMemberGroup.objects.all() - groups = filter(lambda group: self.belongs_to(group), groups) - return groups - - def batch_completed(self, batch): - return HouseholdMemberBatchCompletion.objects.create(householdmember=self, - batch=batch, - interviewer=self.registrar) - - def survey_completed(self): - return HouseMemberSurveyCompletion.objects.create(householdmember=self, - interviewer=self.registrar, - survey=self.survey_listing.survey) - - def get_composed(self, raw_text): - question_context = template.Context(dict([(field.verbose_name.upper().replace(' ', '_'), - getattr(self, field.name)) - for field in self._meta.fields if hasattr(self, field.name)])) - return template.Template(raw_text).render(question_context) - - def reply(self, question): - if self.belongs_to(question.group): - answer_class = Answer.get_class(question.answer_type) - answers = answer_class.objects.filter( - interview__householdmember=self, question=question) - if answers.exists(): - reply = unicode(answers[0].to_text()) - return string.capwords(reply) - return '' - - class Meta: - app_label = 'survey' - get_latest_by = 'created' - - -class HouseholdHead(HouseholdMember): - occupation = models.CharField(max_length=100, blank=False, null=False, - verbose_name="Occupation / Main Livelihood", default="16") - level_of_education = models.CharField(max_length=100, null=True, choices=LEVEL_OF_EDUCATION, - blank=False, default='Primary', - verbose_name="Highest level of education completed") - resident_since = models.DateField(auto_now=False, verbose_name='Since when have you lived here', null=True, - blank=True) # typically only month and year would be filled - - def is_head(self): - return True - - def save(self, *args, **kwargs): - if HouseholdHead.objects.filter(household=self.household, - survey_listing=self.survey_listing).exclude(pk=self.pk).exists(): - raise ValidationError('Head already exists') - else: - return super(HouseholdHead, self).save(*args, **kwargs) - - class Meta: - app_label = 'survey' - verbose_name = 'Main Respondent' - - -class RandomSelection(BaseModel): - survey = models.ForeignKey('Survey', related_name='random_selections') - household = models.OneToOneField( - Household, related_name='random_selection') diff --git a/survey/models/indicators.py b/survey/models/indicators.py deleted file mode 100644 index 9b916833..00000000 --- a/survey/models/indicators.py +++ /dev/null @@ -1,20 +0,0 @@ -from survey.models.base import BaseModel -from django.db import models -from survey.models.question_module import QuestionModule - - -class Indicator(BaseModel): - MEASURE_CHOICES = (('%', 'Percentage'), - ('Number', 'Count')) - module = models.ForeignKey( - QuestionModule, null=False, related_name='indicator') - name = models.CharField(max_length=255, null=False) - description = models.TextField(null=True) - measure = models.CharField( - max_length=255, null=False, choices=MEASURE_CHOICES, default=MEASURE_CHOICES[0][1]) - batch = models.ForeignKey("Batch", null=True, related_name='indicators') - - def is_percentage_indicator(self): - percentage_measure = [Indicator.MEASURE_CHOICES[ - 0][1], Indicator.MEASURE_CHOICES[0][0]] - return self.measure in percentage_measure diff --git a/survey/models/interviewer.py b/survey/models/interviewer.py deleted file mode 100644 index 04485ed8..00000000 --- a/survey/models/interviewer.py +++ /dev/null @@ -1,226 +0,0 @@ -from datetime import date, timedelta, datetime -from django.conf import settings -from django.core.validators import MinLengthValidator, MaxLengthValidator, MinValueValidator, MaxValueValidator -from django.db import models -from survey.interviewer_configs import LEVEL_OF_EDUCATION, LANGUAGES, INTERVIEWER_MIN_AGE, INTERVIEWER_MAX_AGE -from survey.models.base import BaseModel -from survey.models.access_channels import USSDAccess, ODKAccess, InterviewerAccess -from survey.models.locations import Location -import random -from django.core.exceptions import ValidationError -from dateutil.relativedelta import relativedelta -from survey.models.household_batch_completion import HouseSurveyCompletion, HouseholdBatchCompletion -from survey.models.households import Household, SurveyHouseholdListing, HouseholdListing, RandomSelection -from survey.utils.sms import send_sms - - -def validate_min_date_of_birth(value): - if relativedelta(datetime.utcnow().date(), value).years < INTERVIEWER_MIN_AGE: - raise ValidationError( - 'interviewers must be at most %s years' % INTERVIEWER_MIN_AGE) - - -def validate_max_date_of_birth(value): - if relativedelta(datetime.utcnow().date(), value).years > INTERVIEWER_MAX_AGE: - raise ValidationError( - 'interviewers must not be at more than %s years' % INTERVIEWER_MAX_AGE) - - -class Interviewer(BaseModel): - MALE = '1' - FEMALE = '0' - name = models.CharField(max_length=100, blank=False, null=False) - gender = models.CharField(default=MALE, verbose_name="Sex", choices=[ - (MALE, "M"), (FEMALE, "F")], max_length=10) -# age = models.PositiveIntegerField(validators=[MinValueValidator(18), MaxValueValidator(50)], null=True) - date_of_birth = models.DateField( - null=True, validators=[validate_min_date_of_birth, validate_max_date_of_birth]) - level_of_education = models.CharField(max_length=100, null=True, choices=LEVEL_OF_EDUCATION, - blank=False, default='Primary', - verbose_name="Highest level of education completed") - is_blocked = models.BooleanField(default=False) - ea = models.ForeignKey('EnumerationArea', null=True, - related_name="interviewers", verbose_name='Enumeration Area') - language = models.CharField(max_length=100, null=True, choices=LANGUAGES, - blank=False, default='English', verbose_name="Preferred language of communication") - weights = models.FloatField(default=0, blank=False) - - class Meta: - app_label = 'survey' - - def __unicode__(self): - return self.name - - @property - def age(self): - return relativedelta(datetime.utcnow().date(), self.date_of_birth).years - - def total_households_completed(self, survey=None): - try: - if survey is None: - survey = self.assignments.get( - status=SurveyAllocation.PENDING).survey - return HouseSurveyCompletion.objects.filter(survey=survey, interviewer=self).distinct().count() - except: - return 0 - - def total_households_batch_completed(self, batch): - return HouseholdBatchCompletion.objects.filter(batch=batch, interviewer=self).distinct().count() - - def completed_batch_or_survey(self, survey, batch): - if survey and not batch: - return self.total_households_completed(survey) > 0 - return self.total_households_batch_completed(batch) > 0 - - def locations_in_hierarchy(self): - locs = self.ea.locations.all() # this should evaluate to country - if locs: - return locs[0].get_ancestors(include_self=True).exclude(parent__isnull=True) - else: - Location.objects.none() - - @property - def ussd_access(self): - return USSDAccess.objects.filter(interviewer=self) - - @property - def access_ids(self): - accesses = self.intervieweraccess.all() - ids = [] - if accesses.exists(): - ids = [acc.user_identifier for acc in accesses] - return ids - - @property - def odk_access(self): - return ODKAccess.objects.filter(interviewer=self) - - def get_ussd_access(self, mobile_number): - return USSDAccess.objects.get(interviewer=self, user_identifier=mobile_number) - - def get_odk_access(self, identifier): - return ODKAccess.objects.get(interviewer=self, user_identifier=identifier) - - def present_households(self, survey=None): - if survey is None: - return Household.objects.filter(listing__ea=self.ea) - else: - return Household.objects.filter(listing__ea=self.ea, listing__survey_houselistings__survey=survey) - - def generate_survey_households(self, survey): - survey_households = self.present_households(survey) - if survey.has_sampling: - selections = RandomSelection.objects.filter( - survey=survey, household__listing__ea=self.ea).distinct() - households = [s.household for s in selections] - if survey.sample_size > selections.count(): - # random select households as per sample size - # first shuffle registered households and select up to sample - # number - sample_size = survey.sample_size - selections.count() - if households: - survey_households = survey_households.exclude( - pk__in=[h.pk for h in households]) - # to do bulk create - survey_households = list(survey_households) - random.shuffle(survey_households) - survey_households = survey_households[:sample_size] - random_selections = [] - for household in survey_households: - random_selections.append(RandomSelection( - household=household, survey=survey)) - RandomSelection.objects.bulk_create(random_selections) - survey_households.extend(households) - else: - # import pdb; pdb.set_trace() - survey_households = households - msg = '\n'.join(['Survey: %s' % survey, 'Households: %s' % - ','.join([str(h) for h in survey_households])]) - for access in self.ussd_access: - send_sms(access.user_identifier, msg) - else: - survey_households = list(survey_households) - return sorted(survey_households, key=lambda household: household.house_number) - - def survey_households(self, survey): - if survey.has_sampling: - selections = RandomSelection.objects.filter( - survey=survey, household__listing__ea=self.ea).distinct() - households = [s.household for s in selections] - else: - households = self.present_households(survey) - return sorted(households, key=lambda household: household.house_number) - - def has_survey(self): - return self.assignments.filter(status=SurveyAllocation.PENDING).count() > 0 - - @property - def has_access(self): - return self.intervieweraccess.filter(is_active=True).exists() - - @classmethod - def sms_interviewers_in_locations(cls, locations, text): - interviewers = [] - for location in locations: - interviewers.extend(Interviewer.objects.filter( - ea__locations__in=location.get_leafnodes(True))) - # send(text, interviewers) - - def allocated_surveys(self): - return self.assignments.filter(status=SurveyAllocation.PENDING, allocation_ea=self.ea) - - -class SurveyAllocation(BaseModel): - LISTING = 1 - SURVEY = 2 - PENDING = 0 - COMPLETED = 1 - DEALLOCATED = 2 - interviewer = models.ForeignKey(Interviewer, related_name='assignments') - survey = models.ForeignKey('Survey', related_name='work_allocation') - allocation_ea = models.ForeignKey( - 'EnumerationArea', related_name='survey_allocations') - stage = models.CharField(max_length=20, choices=[(LISTING, 'LISTING'), - (SURVEY, 'SURVEY'), ], - null=True, blank=True) - status = models.IntegerField(default=PENDING, choices=[(PENDING, 'PENDING'), (DEALLOCATED, 'DEALLOCATED'), - (COMPLETED, 'COMPLETED')]) - - class Meta: - app_label = 'survey' - - def __unicode__(self): - return self.survey.name - - @classmethod - def get_allocation(cls, interviewer): - try: - allocation = cls.get_allocation_details(interviewer) - if allocation: - return allocation.survey - except cls.DoesNotExist: - return None - - @classmethod - def get_allocation_details(cls, interviewer): - try: - return cls.objects.get(interviewer=interviewer, allocation_ea=interviewer.ea, status=cls.PENDING) - except cls.DoesNotExist: - return None - - def is_valid(self): - ''' - It's up to users of this class to ensure that interviewer as not been assigned to new ea from allocated one - :return: - ''' - return self.status == self.PENDING and self.interviewer.ea == self.allocation_ea - - def batches_enabled(self): - if self.is_valid(): - if self.survey.has_sampling: - if self.interviewer.present_households(self.survey).count() < self.survey.sample_size: - return False - return True - else: - raise ValidationError( - 'Interviewer not assigned to EA: ' % self.allocation_ea) diff --git a/survey/models/interviews.py b/survey/models/interviews.py deleted file mode 100644 index 94e0aeec..00000000 --- a/survey/models/interviews.py +++ /dev/null @@ -1,597 +0,0 @@ -import os -import string -from datetime import datetime -from django.conf import settings -from django.core.exceptions import ObjectDoesNotExist, ValidationError -from django.db import models -from survey.models.access_channels import InterviewerAccess, ODKAccess, USSDAccess -from survey.models.base import BaseModel -from dateutil.parser import parse as extract_date -from survey.models.locations import Point -from django import template -from survey.interviewer_configs import MESSAGES -from survey.utils.decorators import static_var -from django.db.models.signals import post_save -from django.dispatch import receiver -from django.db.models import Max - - -def reply_test(cls, func): - func.is_reply_test = True - return func - - -class Interview(BaseModel): - interviewer = models.ForeignKey( - "Interviewer", null=True, related_name="interviews") - householdmember = models.ForeignKey( - "HouseholdMember", null=True, related_name="interviews", db_index=True) - batch = models.ForeignKey( - "Batch", related_name='interviews', db_index=True) - interview_channel = models.ForeignKey( - InterviewerAccess, related_name='interviews') - closure_date = models.DateTimeField(null=True, blank=True, editable=False) - ea = models.ForeignKey( - 'EnumerationArea', related_name='interviews', db_index=True) - last_question = models.ForeignKey( - "Question", related_name='ongoing', null=True, blank=True) - - def __unicode__(self): - return '%s-%s: %s' % (self.interviewer.name, self.householdmember.first_name, self.batch.name) - - def is_closed(self): - return self.closure_date is not None - - @property - def has_started(self): - return self.last_question is not None - - def get_anwser(self, question, capwords=True): - if self.householdmember.belongs_to(question.group): - answer_class = Answer.get_class(question.answer_type) - answers = answer_class.objects.filter( - interview=self, question=question) - if answers.exists(): - reply = unicode(answers[0].to_text()) - if capwords: - reply = string.capwords(reply) - return reply - return '' - - def respond(self, reply=None, channel=ODKAccess.choice_name()): - ''' - Respond to given reply for specified channel. - This method is volatile.Raises exception if some error happens in the process - :param reply: - :param channel: - :return: Returns next question if any - ''' - if self.is_closed(): - return - if self.last_question and reply is None: - return self.householdmember.get_composed(self.last_question.display_text(USSDAccess.choice_name())) - next_question = None - if self.has_started: - # save reply - answer_class = Answer.get_class(self.last_question.answer_type) - answer_class.create(self, self.last_question, reply) - # compute nnext - next_question = self.last_question.next_question(reply) - else: - next_question = self.batch.start_question - # now confirm the question is applicable - if next_question and (self.householdmember.belongs_to(next_question.group) - and AnswerAccessDefinition.is_valid(channel, next_question.answer_type)) is False: - next_question = self.batch.next_inline(self.last_question, groups=self.householdmember.groups, - channel=channel) # if not get next line question - response_text = None - if next_question: - self.last_question = next_question - response_text = self.householdmember.get_composed( - next_question.display_text(USSDAccess.choice_name())) - else: - print 'interview batch ', self.batch.name - # if self.batch.name == 'Test5': - # import pdb; pdb.set_trace() - # no more questions to ask. capture this time as closure - self.closure_date = datetime.now() - self.save() - return response_text - - # @property - # def has_pending_questions(self): - # return self.last_question is not None and - # self.last_question.flows.filter(next_question__isnull=False).exists() - - @classmethod - def pending_batches(cls, house_member, ea, survey, available_batches): - if available_batches is None: - available_batches = ea.open_batches(survey) - print 'available batches: ', available_batches - completed_interviews = Interview.objects.filter(householdmember=house_member, batch__in=available_batches, - closure_date__isnull=False) - completed_batches = [ - interview.batch for interview in completed_interviews] - return [batch for batch in available_batches if batch.start_question and batch not in completed_batches] - - @classmethod - def interviews(cls, house_member, interviewer, survey): - available_batches = interviewer.ea.open_batches(survey, house_member) - interviews = Interview.objects.filter( - householdmember=house_member, batch__in=available_batches) - # return (completed_interviews, pending_interviews) - return (interviews.filter(closure_date__isnull=False), interviews.filter(closure_date__isnull=True)) -# completed_batches = [interview.batch for interview in completed_interviews] -# return [batch for batch in available_batches if batch not in -# completed_batches] - - def members_with_open_batches(self): - pass - - -# @property -# def first_question(self): -# question = self.batch.start_question - - class Meta: - app_label = 'survey' - unique_together = [('householdmember', 'batch'), ] - - -class Answer(BaseModel): - NO_LOOP = -1 - interview = models.ForeignKey( - Interview, related_name='%(class)s', db_index=True) -# interviewer_response = models.CharField(max_length=200) #This shall hold the actual response from interviewer -# #value shall hold the exact worth of the response - question = models.ForeignKey("Question", null=True, related_name="%(class)s", - on_delete=models.PROTECT, db_index=True) - # used to identify answers belonging to - loop_id = models.IntegerField(default=-1) - # same question loop for looping flows - - @classmethod - def create(cls, interview, question, answer, loop_id=NO_LOOP): - return cls.objects.create(question=question, value=answer, interview=interview, loop_id=loop_id) - - @classmethod - def supported_answers(cls): - return Answer.__subclasses__() - - @classmethod - def answer_types(cls): - return [cl.choice_name() for cl in Answer.__subclasses__() if cl is not NonResponseAnswer] - - @classmethod - def get_class(cls, verbose_name): - verbose_name = verbose_name.strip() - for cl in Answer.__subclasses__(): - if cl.choice_name() == verbose_name: - return cl - ValueError('unknown class') - - def to_text(self): - return self.value - - def pretty_print(self, as_label=False): - return self.to_text() - - @classmethod - def choice_name(cls): - return cls._meta.verbose_name.title() - - @classmethod - def contains(cls, answer, txt): - answer = answer.lower() - txt = txt.lower() - try: - return answer.index(txt) >= 0 - except ValueError: - return False - - @classmethod - def odk_contains(cls, node_path, value): - return "regex(%s, '.*(%s).*')" % (node_path, value) - - @classmethod - def equals(cls, answer, value): - return answer == value - - @classmethod - def odk_equals(cls, node_path, value): - return "%s = '%s'" % (node_path, value) - - @classmethod - def starts_with(cls, answer, txt): - answer = answer.lower() - txt = txt.lower() - return answer.startswith(txt) - - @classmethod - def odk_starts_with(cls, node_path, value): - return "regex(%s, '^(%s).*')" % (node_path, value) - - @classmethod - def ends_with(cls, answer, txt): - answer = answer.lower() - txt = txt.lower() - return answer.endswith(txt) - - @classmethod - def odk_ends_with(cls, node_path, value): - return "regex(%s, '.*(%s)$')" % (node_path, value) - - @classmethod - def greater_than(cls, answer, value): - return answer > value - - @classmethod - def odk_greater_than(cls, node_path, value): - return "%s > '%s'" % (node_path, value) - - @classmethod - def less_than(cls, answer, value): - return answer < value - - @classmethod - def odk_less_than(cls, node_path, value): - return "%s < '%s'" % (node_path, value) - - @classmethod - def between(cls, answer, lowerlmt, upperlmt): - return upperlmt > answer >= lowerlmt - - @classmethod - def odk_between(cls, node_path, lowerlmt, upperlmt): - return "(%s > '%s') and (%s < '%s')" % (node_path, lowerlmt, node_path, upperlmt) - - @classmethod - def print_odk_validation(cls, node_path, validator_name, *args): - printer = getattr(cls, 'odk_%s' % validator_name) - return printer(node_path, *args) - - @classmethod - def passes_test(cls, text_exp): - return bool(eval(text_exp)) - - @classmethod - def validators(cls): - return [cls.starts_with, cls.ends_with, cls.equals, cls.between, cls.less_than, - cls.greater_than, cls.contains, ] - - @classmethod - def validate_test_value(cls, value): - '''Shall be used to validate that a given value is appropriate for this answer - :return: - ''' - for validator in cls._meta.get_field_by_name('value')[0].validators: - validator(value) - - class Meta: - app_label = 'survey' - abstract = True - get_latest_by = 'created' - - -class NumericalAnswer(Answer): - value = models.PositiveIntegerField(null=True) - - @classmethod - def validators(cls): - return [cls.greater_than, cls.equals, cls.less_than, cls.between] - - @classmethod - def create(cls, interview, question, answer, loop_id=Answer.NO_LOOP): - try: - value = int(answer) - except Exception: - raise - return cls.objects.create(question=question, value=answer, interview=interview, loop_id=loop_id) - - @classmethod - def greater_than(cls, answer, value): - answer = int(answer) - value = int(value) - return answer > value - - @classmethod - def odk_greater_than(cls, node_path, value): - return "%s > %s" % (node_path, value) - - @classmethod - def less_than(cls, answer, value): - answer = int(answer) - value = int(value) - return answer < value - - @classmethod - def odk_less_than(cls, node_path, value): - return "%s < %s" % (node_path, value) - - @classmethod - def between(cls, answer, lowerlmt, upperlmt): - answer = int(answer) - upperlmt = int(upperlmt) - lowerlmt = int(lowerlmt) - return upperlmt > answer >= lowerlmt - - @classmethod - def odk_between(cls, node_path, lowerlmt, upperlmt): - return "(%s > %s) and (%s < %s)" % (node_path, lowerlmt, node_path, upperlmt) - - @classmethod - def validate_test_value(cls, value): - try: - int(value) - except ValueError, ex: - raise ValidationError([unicode(ex), ]) - - class Meta: - app_label = 'survey' - abstract = False - - -class ODKGeoPoint(Point): - altitude = models.DecimalField(decimal_places=3, max_digits=10) - precision = models.DecimalField(decimal_places=3, max_digits=10) - - class Meta: - app_label = 'survey' - abstract = False - - -class TextAnswer(Answer): - value = models.CharField(max_length=100, blank=False, null=False) - - class Meta: - app_label = 'survey' - abstract = False - - @classmethod - def validators(cls): - return [cls.starts_with, cls.equals, cls.ends_with, cls.contains] - - @classmethod - def equals(cls, answer, value): - answer = answer.lower() - value = value.lower() - return answer == value - - -class MultiChoiceAnswer(Answer): - value = models.ForeignKey("QuestionOption", null=True) - - @classmethod - def create(cls, interview, question, answer, loop_id=Answer.NO_LOOP): - try: - answer = int(answer) - answer = question.options.get(order=answer) - except: - pass - return cls.objects.create(question=question, value=answer, interview=interview, loop_id=loop_id) - - class Meta: - app_label = 'survey' - abstract = False - - @classmethod - def validators(cls): - return [cls.equals, ] - - def to_text(self): - return self.value.text - - def pretty_print(self, as_label=False): - if as_label: - return self.value.order - else: - return self.value.text - - @classmethod - @static_var('label', 'Equals Option') - def equals(cls, answer, txt): - return super(MultiChoiceAnswer, cls).equals(answer, txt) - - -class MultiSelectAnswer(Answer): - value = models.ManyToManyField("QuestionOption", ) - - @classmethod - def create(cls, interview, question, answer, loop_id=Answer.NO_LOOP): - if isinstance(answer, basestring): - answer = answer.split(' ') - if isinstance(answer, list): - selected = [a.lower() for a in answer] - options = question.options.all() - chosen = [op.pk for op in options if op.text.lower() in selected] - selected = question.options.filter(pk__in=chosen) - else: - selected = answer - ans = cls.objects.create( - question=question, interview=interview, loop_id=loop_id) - for opt in selected: - ans.value.add(opt) - return ans - - # def __init__(self, question, answer, *args, **kwargs): - # super(MultiSelectAnswer, self).__init__(*args, **kwargs) - # if isinstance(answer, basestring): - # answer = answer.split(' ') - # if isinstance(answer, list): - # selected = [a.lower() for a in answer] - # options = question.options.all() - # chosen = [op.pk for op in options if op.text.lower() in selected] - # self.selected = question.options.filter(pk__in=chosen) - # else: - # self.selected = answer - # self.question = question - - class Meta: - app_label = 'survey' - abstract = False - - def to_text(self): - texts = [] - map(lambda opt: texts.append(opt.text), self.value.all()) - return ' and '.join(texts) - - def to_label(self): - texts = [] - map(lambda opt: texts.append(str(opt.order)), self.value.all()) - return ' and '.join(texts) - - def pretty_print(self, as_label=False): - if as_label: - return self.to_label() - else: - return self.to_text() - - @classmethod - def validators(cls): - return [cls.equals, cls.contains] - - @classmethod - def equals(cls, answer, txt): - return answer.text.lower() == txt.lower() - - -class DateAnswer(Answer): - value = models.DateField(null=True) - - @classmethod - def create(cls, interview, question, answer, loop_id=Answer.NO_LOOP): - if isinstance(answer, basestring): - answer = extract_date(answer, fuzzy=True) - question = question - return cls.objects.create(question=question, value=answer, interview=interview, loop_id=loop_id) - - class Meta: - app_label = 'survey' - abstract = False - - @classmethod - def validators(cls): - return [cls.greater_than, cls.equals, cls.less_than, cls.between] - - @classmethod - def validate_test_value(cls, value): - '''Shall be used to validate that a given value is appropriate for this answer - :return: - ''' - try: - extract_date(value, fuzzy=True) - except ValueError, ex: - raise ValidationError([unicode(ex), ]) - - -class AudioAnswer(Answer): - value = models.FileField(upload_to=settings.ANSWER_UPLOADS, null=True) - - class Meta: - app_label = 'survey' - abstract = False - - @classmethod - def validators(cls): - return [] - - def to_text(self): - return '' - - -class VideoAnswer(Answer): - value = models.FileField(upload_to=settings.ANSWER_UPLOADS, null=True) - - class Meta: - app_label = 'survey' - abstract = False - - @classmethod - def validators(cls): - return [] - - def to_text(self): - return '' - - -class ImageAnswer(Answer): - value = models.FileField(upload_to=settings.ANSWER_UPLOADS, null=True) - - class Meta: - app_label = 'survey' - abstract = False - - @classmethod - def validators(cls): - return [] - - def to_text(self): - return '' - - -class GeopointAnswer(Answer): - value = models.ForeignKey(ODKGeoPoint, null=True) - - @classmethod - def create(cls, interview, question, answer, loop_id=Answer.NO_LOOP): - if isinstance(answer, basestring): - answer = answer.split(' ') - answer = ODKGeoPoint(latitude=answer[0], longitude=answer[ - 1], altitude=[2], precision=answer[3]) - return cls.objects.create(question=question, value=answer, interview=interview, loop_id=loop_id) - - class Meta: - app_label = 'survey' - abstract = False - - @classmethod - def validators(cls): - return [cls.equals] - - def to_text(self): - return '' - - -class NonResponseAnswer(BaseModel): - household = models.ForeignKey( - 'Household', related_name='non_response_answers') - survey_listing = models.ForeignKey( - 'SurveyHouseholdListing', related_name='non_response_answers') - value = models.CharField(max_length=100, blank=False, null=False) - interviewer = models.ForeignKey( - "Interviewer", null=True, related_name='non_response_answers') - - class Meta: - app_label = 'survey' - abstract = False - - @classmethod - def validators(cls): - return [] - - -class AnswerAccessDefinition(BaseModel): - ACCESS_CHANNELS = [(name, name) - for name in InterviewerAccess.access_channels()] - ANSWER_TYPES = [(name, name) for name in Answer.answer_types()] - answer_type = models.CharField(max_length=100, choices=ANSWER_TYPES) - channel = models.CharField(max_length=100, choices=ACCESS_CHANNELS) - - class Meta: - app_label = 'survey' - unique_together = [('answer_type', 'channel'), ] - - @classmethod - def is_valid(cls, channel, answer_type): - try: - return cls.objects.get(answer_type=answer_type, channel=channel) is not None - except cls.DoesNotExist: - return False - - @classmethod - def access_channels(cls, answer_type): - return set(AnswerAccessDefinition.objects.filter(answer_type=answer_type).values_list('channel', flat=True)) - - @classmethod - def answer_types(cls, channel): - return set(AnswerAccessDefinition.objects.filter(channel=channel).values_list('answer_type', flat=True)) diff --git a/survey/models/location_type_details.py b/survey/models/location_type_details.py deleted file mode 100644 index 76bbff2d..00000000 --- a/survey/models/location_type_details.py +++ /dev/null @@ -1,41 +0,0 @@ -from django.core.validators import MinValueValidator, MaxValueValidator -from django.db import models -from django.db.models import Max -from survey.models.locations import LocationType, Location -from survey.models import BaseModel - - -class LocationTypeDetails(BaseModel): - INITIAL_ORDER = 0 - ONE = 1 - - required = models.BooleanField(default=False, verbose_name='required') - has_code = models.BooleanField(default=False, verbose_name='has code') - length_of_code = models.PositiveIntegerField(validators=[MinValueValidator(0), MaxValueValidator(10)], blank=True, - null=True) - location_type = models.ForeignKey( - LocationType, null=False, related_name="details") - country = models.ForeignKey(Location, null=True, related_name="details") - order = models.PositiveIntegerField(unique=True, blank=True, null=True) - - @classmethod - def get_ordered_types(cls): - return LocationType.objects.all().order_by('details__order') - - def save(self, *args, **kwargs): - last_order = LocationTypeDetails.objects.aggregate( - Max('order'))['order__max'] or self.INITIAL_ORDER - if not self.order: - self.order = last_order + self.ONE - super(LocationTypeDetails, self).save(*args, **kwargs) - - @classmethod - def the_country(cls): - all_types_details = cls.objects.all().exclude(country=None) - if all_types_details.exists(): - return all_types_details[0].country - return None - - @classmethod - def get_second_lowest_level_type(cls): - return cls.objects.all().order_by('-order')[1].location_type diff --git a/survey/models/location_weight.py b/survey/models/location_weight.py deleted file mode 100644 index c80bd135..00000000 --- a/survey/models/location_weight.py +++ /dev/null @@ -1,10 +0,0 @@ -from survey.models.locations import Location -from django.db import models -from survey.models.base import BaseModel - - -class LocationWeight(BaseModel): - location = models.ForeignKey(Location, null=False, related_name='weight') - survey = models.ForeignKey( - 'Survey', null=False, related_name='location_weight') - selection_probability = models.FloatField(null=False, default=1.0) diff --git a/survey/models/locations.py b/survey/models/locations.py deleted file mode 100644 index 51510605..00000000 --- a/survey/models/locations.py +++ /dev/null @@ -1,144 +0,0 @@ -from django.db import models -# from django.db.models.signals import post_save -from mptt.models import MPTTModel, TreeForeignKey -from mptt.managers import TreeManager -from survey.models.base import BaseModel - - -class Point(BaseModel): - """ - This model represents an anonymous point on the globe. It should be - replaced with something from GeoDjango soon, but I can't seem to get - Spatialite to build right now... - """ - - latitude = models.DecimalField(max_digits=13, decimal_places=10) - longitude = models.DecimalField(max_digits=13, decimal_places=10) - - def __unicode__(self): - return "%s, %s" % (self.latitude, self.longitude) - - def __repr__(self): - return '<%s: %s>' %\ - (type(self).__name__, self) - - -class LocationType(MPTTModel, BaseModel): - name = models.CharField(max_length=200, unique=True) - parent = TreeForeignKey('self', null=True, blank=True, - related_name='sub_types', db_index=True) - location_code = models.PositiveIntegerField(null=True, blank=True) - slug = models.SlugField() - - def __unicode__(self): - return self.name - - class Meta: - app_label = 'survey' - - @classmethod - def all(cls): - return cls.objects.all().order_by('level') - - @classmethod - def smallest_unit(cls): - try: - root_node = cls.objects.get(parent=None) - return root_node.get_leafnodes(False)[0] - except cls.DoesNotExist, IndexError: - return None - - @classmethod - def largest_unit(cls): - try: - root_node = cls.objects.get(parent=None) - return root_node.get_children()[0] - except cls.DoesNotExist, IndexError: - return None - - @classmethod - def in_between(cls): - if cls.objects.exists(): - return cls.objects.exclude(pk=LocationType.smallest_unit().pk).exclude(parent__isnull=True) - else: - return cls.objects.none() - - -class Location(MPTTModel, BaseModel): - name = models.CharField(max_length=50) - type = models.ForeignKey(LocationType, related_name='locations') - code = models.CharField(max_length=100, null=True, blank=True) - parent = TreeForeignKey('self', null=True, blank=True, - related_name='sub_locations', db_index=True) - # would use this in the future. But ignore for now - coordinates = models.ManyToManyField( - Point, related_name='admin_div_locations') - tree = TreeManager() - objects = models.Manager() - - class MPTTMeta: - order_insertion_by = ['name'] - ordering = ['name', ] - - def __unicode__(self): - return self.name - - @property - def tree_parent(self): - return self.parent - - def is_sub_location(self, location): - return location.is_ancestor_of(self) - - class Meta: - app_label = 'survey' - # unique_together = [('code', 'type'), ] - - -# -# class LocationCode(BaseModel): -# location = models.ForeignKey(Location, null=False, related_name="code") -# code = models.CharField(max_length=10, null=False, default=0) -# -# @classmethod -# def get_household_code(cls, interviewer): -# location_hierarchy = interviewer.locations_in_hierarchy() -# codes = cls.objects.filter(location__in=location_hierarchy).order_by('location').values_list('code', flat=True) -# return ''.join(codes) -# -# -# class LocationAutoComplete(models.Model): -# location = models.ForeignKey(Location, null=True) -# text = models.CharField(max_length=500) -# -# class Meta: -# app_label = 'survey' -# -# -# def generate_auto_complete_text_for_location(location): -# auto_complete = LocationAutoComplete.objects.filter(location=location) -# if not auto_complete: -# auto_complete = LocationAutoComplete(location=location) -# else: -# auto_complete = auto_complete[0] -# parents = [location.name] -# while location.tree_parent: -# location = location.tree_parent -# parents.append(location.name) -# parents.reverse() -# auto_complete.text = " > ".join(parents) -# auto_complete.save() -# -# -# @receiver(post_save, sender=Location) -# def create_location_auto_complete_text(sender, instance, **kwargs): -# generate_auto_complete_text_for_location(instance) -# for location in instance.get_descendants(): -# generate_auto_complete_text_for_location(location) -# -# -# def auto_complete_text(self): -# return LocationAutoComplete.objects.get(location=self).text -# -# -# Location.auto_complete_text = auto_complete_text diff --git a/survey/models/odk_submission.py b/survey/models/odk_submission.py deleted file mode 100644 index b7e58591..00000000 --- a/survey/models/odk_submission.py +++ /dev/null @@ -1,64 +0,0 @@ -import os -import mimetypes -from django.conf import settings -from tempfile import NamedTemporaryFile -from hashlib import md5 -from django.core.files.storage import get_storage_class -from django.db import models -from survey.models.base import BaseModel -from survey.models.interviewer import Interviewer -from survey.models.surveys import Survey -from survey.models.households import HouseholdMember, Household - - -class ODKSubmission(BaseModel): - interviewer = models.ForeignKey( - Interviewer, related_name="odk_submissions") - survey = models.ForeignKey(Survey, related_name="odk_submissions") - household_member = models.ForeignKey( - HouseholdMember, related_name="odk_submissions", null=True, blank=True) - household = models.ForeignKey( - Household, related_name="odk_submissions", null=True, blank=True) - form_id = models.CharField(max_length=256) - description = models.CharField(max_length=256, null=True, blank=True) - instance_id = models.CharField(max_length=256) - xml = models.TextField() - - class Meta: - app_label = 'survey' - - def has_attachments(self): - return self.attachments.count() > 0 - - def save_attachments(self, media_files): - for f in media_files: - content_type = f.content_type if hasattr(f, 'content_type') else '' - attach, created = Attachment.objects.get_or_create(submission=self, - media_file=f, - mimetype=content_type) - - -def upload_to(attachment, filename): - return os.path.join( - settings.SUBMISSION_UPLOAD_BASE, - attachment.submission.pk, - 'attachments', - os.path.basename(filename)) - - -class Attachment(BaseModel): - submission = models.ForeignKey(ODKSubmission, related_name="attachments") - media_file = models.FileField(upload_to=upload_to) - mimetype = models.CharField( - max_length=50, null=False, blank=True, default='') - - class Meta: - app_label = 'survey' - - def save(self, *args, **kwargs): - if self.media_file and self.mimetype == '': - # guess mimetype - mimetype, encoding = mimetypes.guess_type(self.media_file.name) - if mimetype: - self.mimetype = mimetype - super(Attachment, self).save(*args, **kwargs) diff --git a/survey/models/question_module.py b/survey/models/question_module.py deleted file mode 100644 index 3722f219..00000000 --- a/survey/models/question_module.py +++ /dev/null @@ -1,13 +0,0 @@ -from survey.models import BaseModel -from django.db import models - - -class QuestionModule(BaseModel): - name = models.CharField(max_length=255) - description = models.TextField(null=True, blank=True) - - def remove_related_questions(self): - self.question_templates.all().delete() - - def __unicode__(self): - return self.name diff --git a/survey/models/question_templates.py b/survey/models/question_templates.py deleted file mode 100644 index a32a896b..00000000 --- a/survey/models/question_templates.py +++ /dev/null @@ -1,58 +0,0 @@ -import os -from django.conf import settings -from django.core.exceptions import ObjectDoesNotExist, ValidationError -from django.db import models -from survey.models.interviewer import Interviewer -from survey.models.interviews import Answer, MultiChoiceAnswer, MultiSelectAnswer -from survey.models.base import BaseModel -from survey.models.access_channels import InterviewerAccess -from model_utils.managers import InheritanceManager - - -class QuestionTemplate(BaseModel): - ANSWER_TYPES = [(name, name) for name in Answer.answer_types()] - identifier = models.CharField( - max_length=100, blank=False, null=True, unique=True) - group = models.ForeignKey("HouseholdMemberGroup", - related_name="question_templates") - text = models.CharField(max_length=150, blank=False, null=False, - # help_text="To replace the household member's name \ - # in the question, please include the variable - # FAMILY_NAME in curly brackets, e.g. {{ - # FAMILY_NAME }}. " - ) - answer_type = models.CharField( - max_length=100, blank=False, null=False, choices=ANSWER_TYPES) - module = models.ForeignKey( - "QuestionModule", related_name="question_templates") - - class Meta: - app_label = 'survey' - - def __unicode__(self): - return "%s - %s: (%s)" % (self.identifier, self.text, self.answer_type.upper()) - - def save(self, *args, **kwargs): - if self.answer_type not in [MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name()]: - self.options.all().delete() - return super(QuestionTemplate, self).save(*args, **kwargs) - - -class TemplateOption(BaseModel): - question = models.ForeignKey( - QuestionTemplate, null=True, related_name="options") - text = models.CharField(max_length=150, blank=False, null=False) - order = models.PositiveIntegerField() - - @property - def to_text(self): - return "%d: %s" % (self.order, self.text) - -# class QuestionTemplateChannel(BaseModel): -# ACCESS_CHANNELS = [(name, name) for name in InterviewerAccess.access_channels()] -# question_template = models.ForeignKey(QuestionTemplate, related_name='access_channels') -# channel = models.CharField(max_length=100, choices=ACCESS_CHANNELS) -# -# class Meta: -# app_label = 'survey' -# unique_together = ('question_template', 'channel') diff --git a/survey/models/questions.py b/survey/models/questions.py deleted file mode 100644 index 6375f9ad..00000000 --- a/survey/models/questions.py +++ /dev/null @@ -1,263 +0,0 @@ -import os -from django.conf import settings -from django.core.exceptions import ObjectDoesNotExist, ValidationError -from django.db import models -from survey.models.interviews import Answer, MultiChoiceAnswer, MultiSelectAnswer -from survey.models.householdgroups import HouseholdMemberGroup -from survey.models.access_channels import USSDAccess -from survey.models.base import BaseModel -from survey.models.households import Household -from mptt.models import MPTTModel, TreeForeignKey -from model_utils.managers import InheritanceManager -from collections import OrderedDict -from ordered_set import OrderedSet - - -class Question(BaseModel): - ANSWER_TYPES = [(name, name) for name in Answer.answer_types()] - identifier = models.CharField( - max_length=100, blank=False, null=True, verbose_name='Variable Name') - text = models.CharField(max_length=150, blank=False, null=False, - # help_text="To replace the household member's name \ - # in the question, please include the variable - # FAMILY_NAME in curly brackets, e.g. {{ - # FAMILY_NAME }}. " - ) - answer_type = models.CharField( - max_length=100, blank=False, null=False, choices=ANSWER_TYPES) - group = models.ForeignKey(HouseholdMemberGroup, related_name='questions') - batch = models.ForeignKey('Batch', related_name='batch_questions') - module = models.ForeignKey( - "QuestionModule", related_name="questions", default='') - - class Meta: - app_label = 'survey' - unique_together = [('identifier', 'batch'), ] - - def answers(self): - return Answer.get_class(self.answer_type).objects.filter(question=self) - - # just utility to get number of times this question has been answered - def total_answers(self): - return Answer.get_class(self.answer_type).objects.filter(question=self).count() - - def is_loop_start(self): - from survey.forms.logic import LogicForm - # actually the more correct way is to - return self.connecting_flows.filter(desc=LogicForm.BACK_TO_ACTION).exists() - # check if the next is previous - - def is_loop_end(self): - from survey.forms.logic import LogicForm - # actually the more correct way is - return self.flows.filter(desc=LogicForm.BACK_TO_ACTION).exists() - # to check if connecting quest is asked after - - @property - def loop_ender(self): - try: - from survey.forms.logic import LogicForm - return self.connecting_flows.get(desc=LogicForm.BACK_TO_ACTION).question - except QuestionFlow.DoesNotExist: - inlines = self.batch.questions_inline() - - @property - def looper_flow(self): - # if self.is_loop_start() or self.is_loop_end(): - return self.batch.get_looper_flow(self) - - def loop_boundary(self): - return self.batch.loop_back_boundaries().get(self.pk, None) - - # def loop_inlines(self): - - def delete(self, using=None): - ''' - Delete related answers before deleting this object - :param using: - :return: - ''' - answer_class = Answer.get_class(self.answer_type) - answer_class.objects.filter(question=self).delete() - return super(Question, self).delete(using=using) - - def display_text(self, channel=None): - text = self.text - if channel and channel == USSDAccess.choice_name() and self.answer_type == MultiChoiceAnswer.choice_name(): - extras = [] - # append question options - for option in self.options.all().order_by('order'): - extras.append(option.to_text) - text = '%s\n%s' % (text, '\n'.join(extras)) - return text - - def next_question(self, reply): - flows = self.flows.all() - answer_class = Answer.get_class(self.answer_type) - resulting_flow = None - for flow in flows: - if flow.validation_test: - test_values = [arg.param for arg in flow.text_arguments] - if getattr(answer_class, flow.validation_test)(reply, *test_values) == True: - resulting_flow = flow - break - else: - resulting_flow = flow - if resulting_flow: - return resulting_flow.next_question - - def previous_inlines(self): - inlines = self.batch.questions_inline() - if self not in inlines: - raise ValidationError('%s not inline' % self.identifier) - previous = [] - for q in inlines: - if q.identifier == self.identifier: - break - else: - previous.append(q) - return set(previous) - - def direct_sub_questions(self): - from survey.forms.logic import LogicForm - sub_flows = self.flows.filter( - desc=LogicForm.SUBQUESTION_ACTION, validation_test__isnull=False) - return OrderedSet([flow.next_question for flow in sub_flows]) - - def conditional_flows(self): - return self.flows.filter(validation_test__isnull=False) - - def preceeding_conditional_flows(self): - return self.connecting_flows.filter(validation_test__isnull=False) - - def __unicode__(self): - return "%s - %s: (%s)" % (self.identifier, self.text, self.answer_type.upper()) - - def save(self, *args, **kwargs): - if self.answer_type not in [MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name()]: - self.options.all().delete() - return super(Question, self).save(*args, **kwargs) - - @classmethod - def zombies(cls, batch): - # these are the batch questions that do not belong to any flow in any - # way - survey_questions = batch.survey_questions - return batch.batch_questions.exclude(pk__in=[q.pk for q in survey_questions]) - - def hierarchical_result_for(self, location_parent, survey): - locations = location_parent.get_children().order_by('name')[:10] - answers = self.multichoiceanswer.all() - return self._format_answer(locations, answers, survey) - - def _format_answer(self, locations, answers, survey): - question_options = self.options.all() - data = OrderedDict() - for location in locations: - households = Household.all_households_in(location, survey) - data[location] = {option.text: answers.filter(value=option, interview__householdmember__household__in=households).count() for option in - question_options} - return data - - -class QuestionFlow(BaseModel): - VALIDATION_TESTS = [(validator.__name__, validator.__name__) - for validator in Answer.validators()] - question = models.ForeignKey(Question, related_name='flows') - validation_test = models.CharField( - max_length=200, null=True, blank=True, choices=VALIDATION_TESTS) - # if validation passes, classify this flow response as having this value - name = models.CharField(max_length=200, null=True, blank=True) - # this would provide a brief description of this flow - desc = models.CharField(max_length=200, null=True, blank=True) - next_question = models.ForeignKey( - Question, related_name='connecting_flows', null=True, blank=True, on_delete=models.SET_NULL) - - class Meta: - app_label = 'survey' - # unique_together = [('question', 'next_question', 'desc', ),] - - @property - def test_params(self): - return [t.param for t in self.text_arguments] - - def params_display(self): - params = [] - for arg in self.text_arguments: - if self.question.answer_type == MultiChoiceAnswer.choice_name(): - params.append(self.question.options.get(order=arg.param).text) - else: - params.append(arg.param) - - return params - - @property - def text_arguments(self): - return TextArgument.objects.filter(flow=self).order_by('position') - - @property - def test_arguments(self): - return TestArgument.objects.filter(flow=self).select_subclasses().order_by('position') - - def save(self, *args, **kwargs): - # if self.name is None: - # if self.next_question: - # identifier = self.next_question.identifier - # else: identifier = '' - # self.name = "%s %s %s" % (self.question.identifier, self.validation_test or "", identifier) - return super(QuestionFlow, self).save(*args, **kwargs) - - -class TestArgument(models.Model): - objects = InheritanceManager() - flow = models.ForeignKey(QuestionFlow, related_name='%(class)s') - position = models.PositiveIntegerField() - - @classmethod - def argument_types(cls): - return [cl.__name__ for cl in cls.__subclasses__()] - - def __unicode__(self): - return self.param - - class Meta: - app_label = 'survey' - get_latest_by = 'position' - - -class TextArgument(TestArgument): - param = models.TextField() - - class Meta: - app_label = 'survey' - - -class NumberArgument(TestArgument): - param = models.IntegerField() - - class Meta: - app_label = 'survey' - - -class DateArgument(TestArgument): - param = models.DateField() - - class Meta: - app_label = 'survey' - - -class QuestionOption(BaseModel): - question = models.ForeignKey(Question, null=True, related_name="options") - text = models.CharField(max_length=150, blank=False, null=False) - order = models.PositiveIntegerField() - - class Meta: - app_label = 'survey' - ordering = ['order', ] - - @property - def to_text(self): - return "%d: %s" % (self.order, self.text) - - def __unicode__(self): - return self.text diff --git a/survey/models/surveys.py b/survey/models/surveys.py deleted file mode 100644 index f6c1a637..00000000 --- a/survey/models/surveys.py +++ /dev/null @@ -1,97 +0,0 @@ -from django.db import models -from survey.models.base import BaseModel -from django.core.validators import MinValueValidator, MaxValueValidator -from survey.models.locations import Location, LocationType -from survey.models.interviewer import Interviewer -from survey.models.households import Household - - -class Survey(BaseModel): - name = models.CharField(max_length=100, blank=False, - null=True, unique=True) - description = models.CharField(max_length=300, blank=True, null=True) - sample_size = models.PositiveIntegerField( - null=False, blank=False, default=10) - has_sampling = models.BooleanField( - default=True, verbose_name='Survey Type') - preferred_listing = models.ForeignKey('Survey', related_name='householdlist_users', - help_text='Select which survey household listing to reuse. Leave empty for fresh listing', - null=True, blank=True) - - # min_percent_reg_houses = models.IntegerField(verbose_name='Min % Of Registered Households', default=80, validators=[MinValueValidator(0), MaxValueValidator(100)], - # help_text='Enter minimum percentage of total household to be registered - # before survey can start on ODK channel') - - class Meta: - app_label = 'survey' - ordering = ['name', ] - permissions = ( - ("view_completed_survey", "Can view Completed interviewers"), - ) - - def __unicode__(self): - return self.name - - @classmethod - def save_sample_size(cls, survey_form): - survey = survey_form.save(commit=False) - if not survey.has_sampling: - survey.sample_size = 0 - survey.save() - - def is_open_for(self, location): - all_batches = self.batches.all() - for batch in all_batches: - if batch.is_open_for(location): - return True - return False - - def is_open(self): - return any([batch.is_open() for batch in self.batches.all()]) - - def generate_completion_report(self, batch=None): - data = [] - all_interviewers = Interviewer.objects.all() - for interviewer in all_interviewers: - if interviewer.completed_batch_or_survey(self, batch=batch): - row = [interviewer.name, ','.join(interviewer.access_ids)] - if interviewer.ea: - row.extend(interviewer.locations_in_hierarchy( - ).values_list('name', flat=True)) - data.append(row) - return data - - @property - def registered_households(self): - return Household.objects.filter(listing__survey_houselistings__survey=self).distinct() - # def batches_enabled(self, interviewer): - # if self.has_sampling: - # if interviewer.present_households(self).count() < self.sample_size: - # return False - # return True - - def bulk_enable_batches(self, eas): - register = [] - for ea in eas: - register.append(BatchCommencement(survey=self, ea=ea)) - BatchCommencement.objects.bulk_create(register) - - def bulk_disable_batches(self, eas): - return self.commencement_registry.filter(ea__in=eas).delete() - - def enable_batches(self, ea): - return BatchCommencement.objects.create(survey=self, ea=ea) - - def disable_batches(self, ea): - return self.commencement_registry.filter(ea=ea).delete() - - -class SurveySampleSizeReached(Exception): - pass - - -class BatchCommencement(BaseModel): - survey = models.ForeignKey( - Survey, null=True, related_name='commencement_registry') - ea = models.ForeignKey('EnumerationArea', null=True, - related_name='commencement_registry') diff --git a/survey/models/upload_error_logs.py b/survey/models/upload_error_logs.py deleted file mode 100644 index 0f9bc7e5..00000000 --- a/survey/models/upload_error_logs.py +++ /dev/null @@ -1,9 +0,0 @@ -from survey.models import BaseModel -from django.db import models - - -class UploadErrorLog(BaseModel): - model = models.CharField(max_length=20, blank=False, null=True) - filename = models.CharField(max_length=20, blank=True, null=True) - row_number = models.PositiveIntegerField(blank=True, null=True) - error = models.CharField(max_length=200, blank=False, null=True) diff --git a/survey/models/users.py b/survey/models/users.py deleted file mode 100644 index 89c7748c..00000000 --- a/survey/models/users.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.contrib.auth.models import User -from django.core.validators import MinLengthValidator, MaxLengthValidator -from django.db import models -from survey.models.base import BaseModel - - -class UserProfile(BaseModel): - user = models.OneToOneField(User, related_name="userprofile") - mobile_number = models.CharField(validators=[MinLengthValidator(9), MaxLengthValidator(9)], - max_length=10, unique=True, null=False, blank=False) - - class Meta: - app_label = 'survey' diff --git a/survey/odk/__init__.py b/survey/odk/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/survey/odk/utils/__init__.py b/survey/odk/utils/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/survey/odk/utils/log.py b/survey/odk/utils/log.py deleted file mode 100644 index 2ff76fbc..00000000 --- a/survey/odk/utils/log.py +++ /dev/null @@ -1,101 +0,0 @@ -import logging -import os -from datetime import datetime -from django.conf import settings - -APP_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -LOG_FILE = os.path.join(APP_DIR, 'mics_odk.log') -logger = logging.getLogger('audit_logger') -handler = logging.FileHandler(LOG_FILE) -formatter = logging.Formatter('[%(asctime)s] %(levelname)s %(message)s') -handler.setFormatter(formatter) -logger.addHandler(handler) -if settings.DEBUG: - logger.setLevel(logging.DEBUG) -else: - logger.setLevel(logging.INFO) - - -class Enum(object): - __name__ = "Enum" - - def __init__(self, **enums): - self.enums = enums - - def __getattr__(self, item): - return self.enums[item] - - def __getitem__(self, item): - return self.__getattr__(item) - - def __iter__(self): - return self.enums.itervalues() - -Actions = Enum( - PROFILE_ACCESSED="profile-accessed", - PUBLIC_PROFILE_ACCESSED="public-profile-accessed", - PROFILE_SETTINGS_UPDATED="profile-settings-updated", - USER_LOGIN="user-login", - USER_LOGOUT="user-logout", - USER_BULK_SUBMISSION="bulk-submissions-made", - USER_FORMLIST_REQUESTED="formlist-requested", - FORM_ACCESSED="form-accessed", - FORM_PUBLISHED="form-published", - FORM_UPDATED="form-updated", - FORM_XLS_DOWNLOADED="form-xls-downloaded", - FORM_XLS_UPDATED="form-xls-updated", - FORM_DELETED="form-deleted", - FORM_CLONED="form-cloned", - FORM_XML_DOWNLOADED="form-xml-downloaded", - FORM_JSON_DOWNLOADED="form-json-downloaded", - FORM_PERMISSIONS_UPDATED="form-permissions-updated", - FORM_ENTER_DATA_REQUESTED="form-enter-data-requested", - FORM_MAP_VIEWED="form-map-viewed", - FORM_DATA_VIEWED="form-data-viewed", - EXPORT_CREATED="export-created", - EXPORT_DOWNLOADED="export-downloaded", - EXPORT_DELETED="export-deleted", - EXPORT_LIST_REQUESTED="export-list-requested", - SUBMISSION_CREATED="submission-created", - SUBMISSION_UPDATED="submission-updated", - SUBMISSION_DELETED="submission-deleted", - SUBMISSION_ACCESSED="submission-accessed", - SUBMISSION_EDIT_REQUESTED="submission-edit-requested", - SUBMISSION_REQUESTED="submission-requested", - BAMBOO_LINK_CREATED="bamboo-link-created", - BAMBOO_LINK_DELETED="bamboo-link-deleted", - SMS_SUPPORT_ACTIVATED="sms-support-activated", - SMS_SUPPORT_DEACTIVATED="sms-support-deactivated", -) - - -def get_client_ip(request): - x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') - if x_forwarded_for: - ip = x_forwarded_for.split(',')[0] - else: - ip = request.META.get('REMOTE_ADDR') - return ip - - -def audit_log(action, request_user, investigator, message, audit, request, level=logging.DEBUG): - """ - Create a log message based on these params - - @param action: Action performed e.g. form-deleted - @param request_username: User performing the action - @param account_username: The investigator name the action was performed on - @param message: The message to be displayed on the log - @param level: log level - @param audit: a dict of key/values of other info pertaining to the action e.g. form's id_string, submission uuid - @return: None - """ - extra = { - 'action': action, - 'request_username': str(request_user), - 'account_username': investigator.name if investigator.name - else str(investigator), - 'client_ip': get_client_ip(request), - 'audit': audit - } - logger.log(level, message, extra=extra) diff --git a/survey/odk/utils/odk_helper.py b/survey/odk/utils/odk_helper.py deleted file mode 100644 index 5046cec1..00000000 --- a/survey/odk/utils/odk_helper.py +++ /dev/null @@ -1,619 +0,0 @@ -from datetime import date, datetime -import decimal -import os -import zipfile -import pytz -from lxml import etree -from django.http import HttpResponse, HttpResponseNotFound, StreamingHttpResponse -from django.core.servers.basehttp import FileWrapper -from django.core.files.storage import get_storage_class -from django.conf import settings -from django.shortcuts import render -from django import template -from django.db import transaction -from django.shortcuts import get_object_or_404 -from djangohttpdigest.digest import Digestor, parse_authorization_header -from djangohttpdigest.authentication import SimpleHardcodedAuthenticator -from django.utils.translation import ugettext as _ -from survey.models import Survey, Interviewer, Interview, SurveyAllocation, ODKAccess, Household, HouseholdMember, HouseholdHead, \ - Question, Batch, ODKSubmission, ODKGeoPoint, TextAnswer, Answer, NonResponseAnswer, \ - VideoAnswer, AudioAnswer, ImageAnswer, MultiSelectAnswer, MultiChoiceAnswer, DateAnswer, GeopointAnswer, \ - SurveyHouseholdListing, HouseholdListing -from survey.odk.utils.log import logger -from functools import wraps -from survey.utils.zip import InMemoryZip -from django.contrib.sites.models import Site -from dateutils import relativedelta -from django.db.utils import IntegrityError -from django_rq import job - - -OPEN_ROSA_VERSION_HEADER = 'X-OpenRosa-Version' -HTTP_OPEN_ROSA_VERSION_HEADER = 'HTTP_X_OPENROSA_VERSION' -OPEN_ROSA_VERSION = '1.0' -DEFAULT_CONTENT_TYPE = 'text/xml; charset=utf-8' -NEW_OR_OLD_HOUSEHOLD_CHOICE_PATH = '//survey/chooseExistingHousehold' -HOUSEHOLD_SELECT_PATH = '//survey/registeredHousehold/household' -HOUSEHOLD_MEMBER_SELECT_PATH = '//survey/registeredHousehold/householdMember/h{{household_id}}' -HOUSEHOLD_MEMBER_ID_DELIMITER = '_' -MANUAL_HOUSEHOLD_SAMPLE_PATH = '//survey/household/houseNumber' -MANUAL_HOUSEHOLD_MEMBER_PATH = '//survey/household/householdMember' -ANSWER_PATH = '//survey/b{{batch_id}}/q{{question_id}}' -ANSWER_BASE_PATH = '//survey/b{{batch_id}}' -LOOP_ANSWER_BASE_PATH = '//survey/b{{batch_id}}/q{{start_id}}q{{end_id}}' -INSTANCE_ID_PATH = '//survey/meta/instanceID' -FORM_ID_PATH = '//survey/@id' -# ONLY_HOUSEHOLD_PATH = '//survey/onlyHousehold' -HOUSE_LISTING_FORM_ID_PREFIX = 'hreg_' -FORM_TYPE_PATH = '//survey/type' -HOUSE_NUMBER_PATH = '//survey/household/houseNumber' -HOUSEHOLD_PATH = '//survey/household' -PHYSICAL_ADDR_PATH = '//survey/household/physicalAddress' -HEAD_DESC_PATH = '//survey/household/headDesc' -HEAD_SEX_PATH = '//survey/household/headSex' -LISTING_COMPLETED = '//survey/listingCompleted' -MULTI_SELECT_XFORM_SEP = ' ' -GEOPOINT_XFORM_SEP = ' ' -# default content length for submission requests -DEFAULT_CONTENT_LENGTH = 10000000 -MAX_DISPLAY_PER_COLLECTOR = 1000 -LISTING = 'L' -SURVEY = 'S' -NON_RESPONSE = 'NR' -MALE = 'M' -FEMALE = 'F' -COULD_NOT_COMPLETE_SURVEY = '0' - -LISTING_DESC = 'LISTING' -SURVEY_DESC = 'HOUSE MEMBER SURVEY' -NON_RESPONSE_DESC = 'NONE RESPONSE' - - -class NotEnoughHouseholds(ValueError): - pass - - -class HouseholdNumberAlreadyExists(IntegrityError): - pass - - -def _get_tree_from_blob(blob_contents): - return etree.fromstring(blob_contents) - - -def _get_tree(xml_file): - return etree.fromstring() - - -# either tree or xml_string must be defined -def _get_nodes(search_path, tree=None, xml_string=None): - if tree is None: - tree = etree.fromstring(xml_string) - try: - return tree.xpath(search_path) - except Exception, ex: - logger.error('Error retrieving path: %s, Desc: %s' % - (search_path, str(ex))) - -# def only_household_reg(survey_tree): -# flag_nodes = _get_nodes(ONLY_HOUSEHOLD_PATH, tree=survey_tree) -# return (flag_nodes is not None and len(flag_nodes) > 0 and flag_nodes[0].text == '1') -# -# def batches_included(survey_tree): -# return only_household_reg(survey_tree) is False - - -def _get_household_members(survey_tree): - member_nodes = _get_nodes(MANUAL_HOUSEHOLD_MEMBER_PATH, tree=survey_tree) - return HouseholdMember.objects.filter(pk__in=[member_node.text for member_node in member_nodes]).all() - - -def _get_member_attrs(survey_tree): - member_nodes = _get_nodes( - '%s/*' % MANUAL_HOUSEHOLD_MEMBER_PATH, tree=survey_tree) - return dict([(member_node.tag, member_node.text) for member_node in member_nodes]) - - -def _get_household_house_number(survey_tree): - return _get_nodes(MANUAL_HOUSEHOLD_SAMPLE_PATH, tree=survey_tree)[0].text - - -def _get_household_physical_addr(survey_tree): - return _get_nodes(PHYSICAL_ADDR_PATH, tree=survey_tree)[0].text - - -def _get_household_head_desc(survey_tree): - return _get_nodes(HEAD_DESC_PATH, tree=survey_tree)[0].text - - -def _get_listing_completed(survey_tree): - status = _get_nodes(LISTING_COMPLETED, tree=survey_tree)[0].text - if status == '1': - return True - else: - return False - - -def _get_household_head_sex(survey_tree): - sex = _get_nodes(HEAD_SEX_PATH, tree=survey_tree)[0].text - if sex == MALE: - return True - else: - return False - - -def _choosed_existing_household(survey_tree): - return _get_nodes(NEW_OR_OLD_HOUSEHOLD_CHOICE_PATH, tree=survey_tree)[0].text == '1' - - -def _get_selected_household_member(survey_tree): - household_id = _get_nodes(HOUSEHOLD_SELECT_PATH, tree=survey_tree)[0].text - context = template.Context({'household_id': household_id}) - hm_path = template.Template(HOUSEHOLD_MEMBER_SELECT_PATH).render(context) - member_id = (_get_nodes(hm_path, tree=survey_tree)[0].text).split( - HOUSEHOLD_MEMBER_ID_DELIMITER)[1] - return HouseholdMember.objects.get(pk=member_id) - - -def _get_or_create_household_member(survey_allocation, survey_tree): - interviewer, survey = survey_allocation.interviewer, survey_allocation.survey - house_number = _get_household_house_number(survey_tree) - survey_listing = SurveyHouseholdListing.get_or_create_survey_listing( - interviewer, survey) - house_listing = survey_listing.listing - try: - household = Household.objects.get(listing=house_listing, - house_number=house_number) - except Household.DoesNotExist: - physical_addr = '' - try: - physical_addr = _get_household_physical_addr(survey_tree) - except IndexError: - pass - household = Household.objects.create(last_registrar=interviewer, - listing=house_listing, - registration_channel=ODKAccess.choice_name(), - house_number=house_number, physical_address=physical_addr) - # now time for member details - MALE = '1' - IS_HEAD = '1' - mem_attrs = _get_member_attrs(survey_tree) - kwargs = {} - kwargs['surname'] = mem_attrs.get('surname') - kwargs['first_name'] = mem_attrs['firstName'] - kwargs['gender'] = (mem_attrs['sex'] == MALE) - date_of_birth = current_val = datetime.now( - ) - relativedelta(years=int(mem_attrs['age'])) - kwargs['date_of_birth'] = date_of_birth - #datetime.strptime(mem_attrs['dateOfBirth'], '%Y-%m-%d') - kwargs['household'] = household - kwargs['registrar'] = interviewer - kwargs['registration_channel'] = ODKAccess.choice_name() - kwargs['survey_listing'] = survey_listing - if not household.get_head() and mem_attrs['isHead'] == IS_HEAD: - # kwargs['occupation'] = mem_attrs.get('occupation', '') - # kwargs['level_of_education'] = mem_attrs.get('levelOfEducation', '') - # resident_since = datetime.strptime(mem_attrs.get('residentSince', '1900-01-01'), '%Y-%m-%d') - # kwargs['resident_since']=resident_since - head = HouseholdHead.objects.create(**kwargs) - if household.head_desc is not head.surname: - household.head_desc = head.surname - household.save() - return head - else: - return HouseholdMember.objects.create(**kwargs) - - -def record_interview_answer(interview, question, answer, loop_id=Answer.NO_LOOP): - if not isinstance(answer, NonResponseAnswer): - answer_class = Answer.get_class(question.answer_type) - print 'answer type ', answer_class.__name__ - print 'question is ', question - print 'question pk is ', question.pk - print 'interview is ', interview - print 'answer text is ', answer - answer = answer_class.create( - interview, question, answer, loop_id=loop_id) - return answer - else: - answer.interview = interview - answer.save() - return answer - - -def get_answer_path(batch, question): - loop_boundaries = batch.loop_back_boundaries() - batch = question.batch - boundary = loop_boundaries.get(question.pk, None) - if boundary: - start_id, end_id = boundary - return '//survey/b%s/q%sq%s/q%s' % (batch.pk, - start_id, end_id, - question.pk) - # should take account with looping question - return '//survey/b%s/q%s' % (batch.pk, question.pk) - - -def _get_responses(interviewer, survey_tree, survey): - response_dict = {} - batches = interviewer.ea.open_batches(survey) - for batch in batches: - context = template.Context({'batch_id': batch.pk, }) - # non_response_path = template.Template(IS_NON_RESPONSE_PATH).render(context) - # non_response_node = _get_nodes(non_response_path, tree=survey_tree) - # if non_response_node and non_response_node[0].text.strip() == NON_RESPONSE: - # nrsp_answer_path = template.Template(NON_RESP_ANSWER_PATH).render(context) - # resp = NonResponseAnswer(batch.start_question, _get_nodes(nrsp_answer_path, tree=survey_tree)[0].text) - # response_dict[(batch.pk, batch.start_question.pk)] = resp - # else: - loop_boundaries = batch.loop_back_boundaries() - for question in batch.survey_questions: - boundary = loop_boundaries.get(question.pk, None) - if boundary: - start_id, end_id = boundary - base_path = template.Template(LOOP_ANSWER_BASE_PATH).render(template.Context({'start_id': start_id, - 'batch_id': batch.pk, 'end_id': end_id})) - else: - base_path = template.Template(ANSWER_BASE_PATH).render( - template.Context({'batch_id': batch.pk})) - base_nodes = _get_nodes(base_path, tree=survey_tree) - for node in base_nodes: - responses = response_dict.get((batch.pk, question.pk), []) - resp_text = _get_nodes('./q%s' % question.pk, node) - if len(resp_text) > 0 and resp_text[0] is not None: - responses.append(resp_text[0].text) - response_dict[(batch.pk, question.pk)] = responses - return response_dict - - -def _get_instance_id(survey_tree): - return _get_nodes(INSTANCE_ID_PATH, tree=survey_tree)[0].text - - -def _get_form_id(survey_tree): - return _get_nodes(FORM_ID_PATH, tree=survey_tree)[0] - - -def _get_survey(survey_tree): - pk = _get_nodes(FORM_ID_PATH, tree=survey_tree)[0] - return Survey.objects.get(pk=pk) - - -def _get_allocation(survey_tree): - pk = _get_nodes(FORM_ID_PATH, tree=survey_tree)[0].strip('alloc-') - return SurveyAllocation.objects.get(pk=pk) - - -def _get_form_type(survey_tree): - return _get_nodes(FORM_TYPE_PATH, tree=survey_tree)[0].text - - -def save_household_list(interviewer, survey, survey_tree, survey_listing): - house_nodes = _get_nodes(HOUSEHOLD_PATH, tree=survey_tree) - if len(house_nodes) < survey.sample_size: - raise NotEnoughHouseholds('Not enough households') - house_number = 1 - households = [] - try: - for node in house_nodes: - household, _ = Household.objects.get_or_create( - house_number=house_number, - listing=survey_listing.listing, - last_registrar=interviewer, - registration_channel=ODKAccess.choice_name(), - physical_address=_get_nodes( - './physicalAddress', tree=node)[0].text, - head_desc=_get_nodes('./headDesc', tree=node)[0].text, - head_sex=_get_nodes('./headSex', tree=node)[0].text, - ) - house_number = house_number + 1 - households.append(household) - except IntegrityError: - raise HouseholdNumberAlreadyExists('Household number already exists') - return households - - -def save_nonresponse_answers(interviewer, survey, survey_tree, survey_listing): - house_nodes = _get_nodes(HOUSEHOLD_PATH, tree=survey_tree) - house_number = 1 - non_responses = [] - for node in house_nodes: - if _get_nodes('./nr', tree=node)[0].text == COULD_NOT_COMPLETE_SURVEY: - nr_msg = _get_nodes('./qnsr', tree=node)[0].text if len(_get_nodes('./qnsr', tree=node)) \ - else _get_nodes('./qnr', tree=node)[0].text - non_responses.append( - NonResponseAnswer.objects.create( - household=Household.objects.get(listing=survey_listing.listing, - house_number=_get_nodes('./houseNumber', tree=node)[0].text), - survey_listing=survey_listing, - interviewer=interviewer, - value=nr_msg - ) - ) - return non_responses - - -@job('odk') -def save_survey_questions(survey_allocation, xml_blob, media_files): - survey_tree = _get_tree_from_blob(xml_blob) - interviewer = survey_allocation.interviewer - survey = survey_allocation.survey - form_id = _get_form_id(survey_tree) - member = _get_or_create_household_member(survey_allocation, survey_tree) - response_dict = _get_responses(interviewer, survey_tree, survey) - treated_batches = {} - interviews = {} - if response_dict: - for (b_id, q_id), answers in response_dict.items(): - question = Question.objects.get(pk=q_id) - batch = treated_batches.get(b_id, Batch.objects.get(pk=b_id)) - loop_id = Answer.NO_LOOP - for idx, answer in enumerate(answers): - if len(answers) > 0: - loop_id = idx - if answer is not None: - if question.answer_type in [AudioAnswer.choice_name(), ImageAnswer.choice_name(), - VideoAnswer.choice_name()]: - answer = media_files.get(answer, None) - interview = interviews.get(b_id, None) - if interview is None: - interview, _ = Interview.objects.get_or_create( - interviewer=interviewer, - householdmember=member, - batch=batch, - interview_channel=interviewer.odk_access[0], - ea=survey_allocation.allocation_ea - ) - interviews[b_id] = interview - created = record_interview_answer( - interview, question, answer, loop_id) - if b_id not in treated_batches.keys(): - treated_batches[b_id] = batch - # create batch completion for question batches - map(lambda batch: member.batch_completed( - batch), treated_batches.values()) - member.survey_completed() - if member.household.has_completed(survey): - map(lambda batch: member.household.batch_completed( - batch, interviewer), treated_batches.values()) - member.household.survey_completed(survey, interviewer) - household = member.household - submission = ODKSubmission.objects.create(interviewer=interviewer, - survey=survey, form_id=form_id, description=SURVEY_DESC, - instance_id=_get_instance_id(survey_tree), household_member=member, household=household, - xml=etree.tostring(survey_tree, pretty_print=True)) - # execute.delay(submission.save_attachments, media_files) - submission.save_attachments(media_files.values()) - return submission - -#@transaction.autocommit - - -def process_submission(interviewer, xml_file, media_files=[], request=None): - """ - extract surveys and for this xml file and for specified household member - """ - media_files = dict([(os.path.basename(f.name), f) for f in media_files]) - reports = [] - xml_blob = xml_file.read() - survey_tree = _get_tree_from_blob(xml_blob) - form_id = _get_form_id(survey_tree) - instance_id = _get_instance_id(survey_tree) - description = '' - if form_id.startswith('alloc-'): - survey_allocation = _get_allocation(survey_tree) - survey = survey_allocation.survey - else: - survey = _get_survey(survey_tree) - survey_allocation = get_survey_allocation(interviewer) - household = None - member = None - survey_listing = SurveyHouseholdListing.get_or_create_survey_listing( - interviewer, survey) - if _get_form_type(survey_tree) == LISTING: - description = LISTING_DESC - households = save_household_list( - interviewer, survey, survey_tree, survey_listing) - survey_allocation.stage = SurveyAllocation.SURVEY - survey_allocation.save() - for household in households: - submission = ODKSubmission.objects.create(interviewer=interviewer, - survey=survey, form_id=form_id, description=LISTING_DESC, - instance_id=instance_id, household_member=member, household=household, - xml=etree.tostring(survey_tree, pretty_print=True)) - elif _get_form_type(survey_tree) == NON_RESPONSE: - description = NON_RESPONSE_DESC - non_responses = save_nonresponse_answers( - interviewer, survey, survey_tree, survey_listing) - for non_response in non_responses: - submission = ODKSubmission.objects.create(interviewer=interviewer, - survey=survey, form_id=form_id, description=NON_RESPONSE_DESC, - instance_id=instance_id, - household_member=member, household=non_response.household, - xml=etree.tostring(survey_tree, pretty_print=True)) - else: - description = SURVEY_DESC - save_survey_questions.delay(survey_allocation, xml_blob, media_files) - return SubmissionReport(form_id, instance_id, description) - - -def get_survey(interviewer): - return SurveyAllocation.get_allocation(interviewer) - - -def get_survey_allocation(interviewer): - return SurveyAllocation.get_allocation_details(interviewer) - - -class SubmissionReport: - form_id = None - instance_id = None - description = None - - def __init__(self, form_id, instance_id, report_details): - self.form_id = form_id - self.instance_id = instance_id - self.description = report_details - - -def disposition_ext_and_date(name, extension, show_date=True): - if name is None: - return 'attachment;' - if show_date: - name = "%s_%s" % (name, date.today().strftime("%Y_%m_%d")) - return 'attachment; filename=%s.%s' % (name, extension) - - -def response_with_mimetype_and_name( - mimetype, name, extension=None, show_date=True, file_path=None, - use_local_filesystem=False, full_mime=False): - if extension is None: - extension = mimetype - if not full_mime: - mimetype = "application/%s" % mimetype - if file_path: - try: - if not use_local_filesystem: - default_storage = get_storage_class()() - wrapper = FileWrapper(default_storage.open(file_path)) - response = StreamingHttpResponse(wrapper, mimetype=mimetype) - response['Content-Length'] = default_storage.size(file_path) - else: - wrapper = FileWrapper(open(file_path)) - response = StreamingHttpResponse(wrapper, mimetype=mimetype) - response['Content-Length'] = os.path.getsize(file_path) - except IOError: - response = HttpResponseNotFound( - _(u"The requested file could not be found.")) - else: - response = HttpResponse(content_type=mimetype) - response['Content-Disposition'] = disposition_ext_and_date( - name, extension, show_date) - return response - - -class HttpResponseNotAuthorized(HttpResponse): - status_code = 401 - - def __init__(self): - HttpResponse.__init__(self) - self['WWW-Authenticate'] =\ - 'Basic realm="%s"' % Site.objects.get_current().name - - -class BaseOpenRosaResponse(HttpResponse): - status_code = 201 - - def __init__(self, *args, **kwargs): - super(BaseOpenRosaResponse, self).__init__(*args, **kwargs) - if self.status_code > 201: - self.reason_phrase = self.content - self[OPEN_ROSA_VERSION_HEADER] = OPEN_ROSA_VERSION - tz = pytz.timezone(settings.TIME_ZONE) - dt = datetime.now(tz).strftime('%a, %d %b %Y %H:%M:%S %Z') - self['Date'] = dt - self['X-OpenRosa-Accept-Content-Length'] = DEFAULT_CONTENT_LENGTH - self['Content-Type'] = DEFAULT_CONTENT_TYPE - - -class OpenRosaResponse(BaseOpenRosaResponse): - status_code = 201 - - def __init__(self, *args, **kwargs): - super(OpenRosaResponse, self).__init__(*args, **kwargs) - # wrap content around xml - self.content = ''' - - %s -''' % self.content - self['X-OpenRosa-Accept-Content-Length'] = len(self.content) - - -class OpenRosaResponseNotFound(OpenRosaResponse): - status_code = 404 - - -class OpenRosaResponseBadRequest(OpenRosaResponse): - status_code = 400 - - -class OpenRosaResponseNotAllowed(OpenRosaResponse): - status_code = 405 - - -class OpenRosaRequestForbidden(OpenRosaResponse): - status_code = 403 - - -class OpenRosaRequestConflict(OpenRosaResponse): - status_code = 409 - - -class OpenRosaServerError(OpenRosaResponse): - status_code = 500 - - -def http_basic_interviewer_auth(func): - @wraps(func) - def _decorator(request, *args, **kwargs): - if request.META.has_key('HTTP_AUTHORIZATION'): - authmeth, auth = request.META['HTTP_AUTHORIZATION'].split(' ', 1) - logger.debug('request meta: %s' % - request.META['HTTP_AUTHORIZATION']) - if authmeth.lower() == 'basic': - auth = auth.strip().decode('base64') - username, password = auth.split(':', 1) - try: - request.user = ODKAccess.objects.get( - user_identifier=username, odk_token=password, is_active=True).interviewer - #Interviewer.objects.get(mobile_number=username, odk_token=password) - return func(request, *args, **kwargs) - except ODKAccess.DoesNotExist: - return OpenRosaResponseNotFound() - return HttpResponseNotAuthorized() - return _decorator - - -def http_digest_interviewer_auth(func): - @wraps(func) - def _decorator(request, *args, **kwargs): - if request.META.has_key('HTTP_HOST'): - realm = request.META['HTTP_HOST'] - else: - realm = Site.objects.get_current().name - digestor = Digestor(method=request.method, - path=request.get_full_path(), realm=realm) - if request.META.has_key('HTTP_AUTHORIZATION'): - logger.debug('request meta: %s' % - request.META['HTTP_AUTHORIZATION']) - try: - parsed_header = digestor.parse_authorization_header( - request.META['HTTP_AUTHORIZATION']) - if parsed_header['realm'] == realm: - odk_access = ODKAccess.objects.get(user_identifier=parsed_header[ - 'username'], is_active=True) - # interviewer = Interviewer.objects.get(mobile_number=parsed_header['username'], is_blocked=False) - authenticator = SimpleHardcodedAuthenticator(server_realm=realm, - server_username=odk_access.user_identifier, - server_password=odk_access.odk_token) - if authenticator.secret_passed(digestor): - request.user = odk_access.interviewer - return func(request, *args, **kwargs) - except ODKAccess.DoesNotExist: - return OpenRosaResponseNotFound() - except ValueError, err: - return OpenRosaResponseBadRequest() - response = HttpResponseNotAuthorized() - response['www-authenticate'] = digestor.get_digest_challenge() - return response - return _decorator - - -def get_zipped_dir(dirpath): - zipf = InMemoryZip() - for root, dirs, files in os.walk(dirpath): - for filename in files: - f = open(os.path.join(root, filename)) - zipf.append(filename, f.read()) - f.close() - return zipf.read() diff --git a/survey/odk/views.py b/survey/odk/views.py deleted file mode 100644 index 09a65d1a..00000000 --- a/survey/odk/views.py +++ /dev/null @@ -1,287 +0,0 @@ -from datetime import datetime -import pytz -import os -import base64 -import random -import logging -from functools import wraps -from django.template.loader import render_to_string -from django.views.decorators.http import require_GET, require_POST -from django.views.decorators.http import require_http_methods -from django.views.decorators.csrf import csrf_exempt -from django.shortcuts import render_to_response, get_object_or_404, render -from django.http import HttpResponse, HttpResponseBadRequest, \ - HttpResponseRedirect, HttpResponseForbidden, Http404, StreamingHttpResponse -from django.core.servers.basehttp import FileWrapper -from django.core.files.storage import get_storage_class -from django.contrib.auth.decorators import login_required, permission_required -from django.core.urlresolvers import reverse -from django.core.exceptions import PermissionDenied -from django.conf import settings -from django.template import RequestContext, loader, Context -from survey.odk.utils.log import audit_log, Actions, logger -from survey.odk.utils.odk_helper import get_survey_allocation, process_submission, disposition_ext_and_date, \ - get_zipped_dir, HouseholdNumberAlreadyExists, \ - response_with_mimetype_and_name, OpenRosaResponseBadRequest, OpenRosaRequestForbidden, OpenRosaRequestConflict, \ - OpenRosaResponseNotAllowed, OpenRosaResponse, OpenRosaResponseNotFound, OpenRosaServerError, \ - BaseOpenRosaResponse, HttpResponseNotAuthorized, http_digest_interviewer_auth, NotEnoughHouseholds -from survey.models import Survey, Interviewer, Household, ODKSubmission, Answer, Batch, SurveyHouseholdListing, \ - HouseholdListing, SurveyAllocation -from django.utils.translation import ugettext as _ -from django.contrib.sites.models import Site -from survey.utils.query_helper import get_filterset -from survey.models import BatchLocationStatus -from survey.interviewer_configs import LEVEL_OF_EDUCATION, NUMBER_OF_HOUSEHOLD_PER_INTERVIEWER -from survey.interviewer_configs import MESSAGES -from collections import OrderedDict - - -def get_survey_xform(allocation): - interviewer, survey = allocation.interviewer, allocation.survey - template_file = "odk/survey_form-no-repeat.xml" - if BatchLocationStatus.objects.filter(batch__survey=survey, non_response=True).exists(): - template_file = 'odk/non-response-no-repeat.xml' - registered_households = interviewer.generate_survey_households(survey) - batches = interviewer.ea.open_batches(survey) - # batches_map = - loop_starters = set() - map(lambda batch: loop_starters.update(batch.loop_starters()), batches) - loop_enders = set() - map(lambda batch: loop_enders.update(batch.loop_enders()), batches) - loop_boundaries = OrderedDict() - map(lambda batch: loop_boundaries.update( - batch.loop_back_boundaries()), batches) - return render_to_string(template_file, { - 'interviewer': interviewer, - # interviewer.households.filter(survey=survey, ea=interviewer.ea).all(), - 'registered_households': registered_households, - 'title': '%s - %s' % (survey, ', '.join([batch.name for batch in batches])), - 'survey': survey, - 'allocation': allocation, - 'survey_batches': batches, - 'messages': MESSAGES, - 'loop_starters': loop_starters, - 'loop_enders': loop_enders, - 'loop_boundaries': loop_boundaries, - 'answer_types': dict([(cls.__name__.lower(), cls.choice_name()) for cls in Answer.supported_answers()]) - }) - - -def get_household_list_xform(interviewer, survey, house_listing): - selectable_households = None - # total_households = house_listing.households.count() - # if total_households > 0: - # selectable_households = [idx+1 for idx in range(total_households)] - return render_to_string("odk/household_listing-repeat.xml", { - 'interviewer': interviewer, - 'survey': survey, - 'educational_levels': LEVEL_OF_EDUCATION, - 'messages': MESSAGES, - 'selectable_households': selectable_households, - }) - - -def get_on_response_xform(interviewer, survey): - batches = interviewer.ea.open_batches(survey) - return render_to_string("odk/survey_form-no-repeat.xml", { - 'interviewer': interviewer, - # interviewer.households.filter(survey=survey, ea=interviewer.ea).all(), - 'registered_households': registered_households, - 'title': '%s - %s' % (survey, ', '.join([batch.name for batch in batches])), - 'survey': survey, - 'survey_batches': batches, - 'messages': MESSAGES, - 'answer_types': dict([(cls.__name__.lower(), cls.choice_name()) for cls in Answer.supported_answers()]) - }) - - -@login_required -@permission_required('auth.can_view_aggregates') -def download_submission_attachment(request, submission_id): - odk_submission = ODKSubmission.objects.get(pk=submission_id) - filename = '%s-%s-%s.zip' % (odk_submission.survey.name, - odk_submission.household_member.pk, odk_submission.interviewer.pk) - attachment_dir = os.path.join( - settings.SUBMISSION_UPLOAD_BASE, str(odk_submission.pk), 'attachments') - response = HttpResponse(content_type='application/zip') - response['Content-Disposition'] = 'attachment; filename="%s"' % filename - response.write(get_zipped_dir(attachment_dir)) - return response - - -@login_required -@permission_required('auth.can_view_aggregates') -def submission_list(request): - odk_submissions = ODKSubmission.objects.all().order_by('-created') - search_fields = ['interviewer__name', 'interviewer__ea__name', 'survey__name', - 'household_member__household__house_number', 'household_member__surname', - 'household_member__first_name', 'form_id', 'instance_id'] - if request.GET.has_key('q'): - odk_submissions = get_filterset( - odk_submissions, request.GET['q'], search_fields) - return render(request, 'odk/submission_list.html', {'submissions': odk_submissions, - 'placeholder': 'interviewer, house, member, survey', - 'request': request}) - - -@http_digest_interviewer_auth -@require_GET -def form_list(request): - """ - This is where ODK Collect gets its download list. - """ - interviewer = request.user - #get_object_or_404(Interviewer, mobile_number=username, odk_token=token) - # to do - Make fetching households more e - allocation = get_survey_allocation(interviewer) - if allocation and interviewer.ea.open_batches: - audit_log(Actions.USER_FORMLIST_REQUESTED, request.user, interviewer, - _("survey allocation %s" % allocation.survey), {}, request) - survey = allocation.survey - survey_listing = SurveyHouseholdListing.get_or_create_survey_listing( - interviewer, survey) - audit = {} - - audit_log(Actions.USER_FORMLIST_REQUESTED, request.user, interviewer, - _("Requested forms list. for %s" % interviewer.name), audit, request) - content = render_to_string("odk/xformsList.xml", { - 'allocation': allocation, - 'survey': survey, - 'interviewer': interviewer, - 'request': request, - 'survey_listing': survey_listing, - 'Const': SurveyAllocation - }) - response = BaseOpenRosaResponse(content) - response.status_code = 200 - return response - else: - return OpenRosaResponseNotFound('No survey allocated presently') - - -@http_digest_interviewer_auth -def download_xform(request, survey_id): - interviewer = request.user - survey = get_object_or_404(Survey, pk=survey_id) - allocation = get_survey_allocation(interviewer) - if allocation: - try: - if survey.has_sampling and allocation.stage in [None, SurveyAllocation.LISTING]: - if allocation.stage is None: - allocation.stage = SurveyAllocation.LISTING - allocation.save() - survey_listing = SurveyHouseholdListing.get_or_create_survey_listing( - interviewer, survey) - survey_xform = get_household_list_xform( - interviewer, survey, survey_listing.listing) - else: - survey_xform = get_survey_xform(allocation) - form_id = '%s' % allocation.pk - - audit = { - "xform": form_id - } - audit_log(Actions.FORM_XML_DOWNLOADED, request.user, interviewer, - _("'%(interviewer)s' Downloaded XML for form '%(id_string)s'.") % { - "interviewer": interviewer.name, - "id_string": form_id - }, audit, request) - response = response_with_mimetype_and_name('xml', 'survey-%s' % allocation.pk, - show_date=False, full_mime='text/xml') - response.content = survey_xform - return response - except: - raise - print 'an error occurred' - pass - return OpenRosaResponseNotFound() - - -@http_digest_interviewer_auth -def download_houselist_xform(request): - interviewer = request.user - allocation = get_survey_allocation(interviewer) - response = OpenRosaResponseNotFound() - if allocation: - survey = allocation.survey - survey_listing = SurveyHouseholdListing.get_or_create_survey_listing( - interviewer, survey) - householdlist_xform = get_household_list_xform( - interviewer, survey, survey_listing.listing) - form_id = 'allocation-%s' % allocation.id - audit = { - "xform": form_id - } - audit_log(Actions.FORM_XML_DOWNLOADED, request.user, interviewer, - _("'%(interviewer)s' Downloaded XML for form '%(id_string)s'.") % { - "interviewer": interviewer.name, - "id_string": form_id - }, audit, request) - response = response_with_mimetype_and_name('xml', 'household_listing-%s' % survey.pk, - show_date=False, full_mime='text/xml') - response.content = householdlist_xform - return response - - -@http_digest_interviewer_auth -@require_http_methods(["POST"]) -@csrf_exempt -def submission(request): - interviewer = request.user - #get_object_or_404(Interviewer, mobile_number=username, odk_token=token) - submission_date = datetime.now().isoformat() - xml_file_list = [] - html_response = False - # request.FILES is a django.utils.datastructures.MultiValueDict - # for each key we have a list of values - try: - xml_file_list = request.FILES.pop("xml_submission_file", []) - if len(xml_file_list) != 1: - return OpenRosaResponseBadRequest(u"There should be a single XML submission file.") - media_files = request.FILES.values() - submission_report = process_submission( - interviewer, xml_file_list[0], media_files=media_files) - logger.info(submission_report) - context = Context({ - 'message': settings.ODK_SUBMISSION_SUCCESS_MSG, - 'instanceID': u'uuid:%s' % submission_report.instance_id, - 'formid': submission_report.form_id, - 'submissionDate': submission_date, - 'markedAsCompleteDate': submission_date - }) - t = loader.get_template('odk/submission.xml') - audit = {} - audit_log(Actions.SUBMISSION_CREATED, request.user, interviewer, - _("'%(interviewer)s' Submitted XML for form '%(id_string)s'. Desc: '%(desc)s'") % { - "interviewer": interviewer.name, - "desc": submission_report.description, - "id_string": submission_report.form_id - }, audit, request) - response = BaseOpenRosaResponse(t.render(context)) - response.status_code = 201 - response['Location'] = request.build_absolute_uri(request.path) - return response - except NotEnoughHouseholds: - desc = 'Not enough households' - audit_log(Actions.SUBMISSION_REQUESTED, request.user, interviewer, - _("Failed attempted to submit XML for form for interviewer: '%(interviewer)s'. desc: '%(desc)s'") % { - "interviewer": interviewer.name, - "desc": desc - }, {'desc': desc}, request, logging.WARNING) - return OpenRosaRequestForbidden(u"Not Enough Households") - except HouseholdNumberAlreadyExists: - desc = 'House number already exists' - audit_log(Actions.SUBMISSION_REQUESTED, request.user, interviewer, - _("Failed attempted to submit XML for form for interviewer: '%(interviewer)s'. desc: '%(desc)s'") % { - "interviewer": interviewer.name, - "desc": desc - }, {'desc': desc}, request, logging.WARNING) - # return OpenRosaRequestConflict(u'Household Number Already exists') - return OpenRosaResponseNotAllowed(u'Household Number Already exists') - except Exception, ex: - audit_log(Actions.SUBMISSION_REQUESTED, request.user, interviewer, - _("Failed attempted to submit XML for form for interviewer: '%(interviewer)s'. desc: '%(desc)s'") % { - "interviewer": interviewer.name, - "desc": str(ex) - }, {'desc': str(ex)}, request, logging.WARNING) - return OpenRosaServerError(u"An error occurred. Please try again") diff --git a/survey/services/__init__.py b/survey/services/__init__.py deleted file mode 100644 index 7d057b3d..00000000 --- a/survey/services/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__author__ = 'mnandri' diff --git a/survey/services/completion_rates_calculator.py b/survey/services/completion_rates_calculator.py deleted file mode 100644 index 4c88fc3f..00000000 --- a/survey/services/completion_rates_calculator.py +++ /dev/null @@ -1,92 +0,0 @@ -from survey.models import Location -from survey.models import Household -from decimal import * - - -class BatchCompletionRates: - getcontext().prec = 6 - - def __init__(self, batch): - self.batch = batch - - def calculate_percent(self, numerator, denominator): - try: - return Decimal(numerator) * 100 / Decimal(denominator) - except InvalidOperation: - return 0 - - def percent_completed_households(self, location, survey, ea=None): - all_households = Household.all_households_in(location, survey, ea) - return self.percentage_completed(all_households) - - def percentage_completed(self, all_households): - completed_households = filter( - lambda household: household.has_completed_batch(self.batch), all_households) - return self.calculate_percent(len(completed_households), all_households.count()) - - -class BatchLocationCompletionRates(BatchCompletionRates): - getcontext().prec = 6 - - def __init__(self, batch, location=None, ea=None, specific_households=None): - self.batch = batch - self.ea = ea - self.location = location - if specific_households: - self.all_households = Household.objects.filter( - pk__in=specific_households) - else: - self.all_households = Household.all_households_in( - self.location, batch.survey, ea) - - def percent_completed_households(self): - all_households = self.all_households - completed_households = filter( - lambda household: household.has_completed_batch(self.batch), all_households) - return self.calculate_percent(len(completed_households), all_households.count()) - - def interviewed_households(self): - _interviewed_households = [] - for household in self.all_households: - attributes = {'household': household, - 'date_interviewed': household.date_interviewed_for(self.batch), - 'number_of_member_interviewed': household.total_members_interviewed(self.batch)} - _interviewed_households.append(attributes) - return _interviewed_households - - -class BatchHighLevelLocationsCompletionRates(BatchCompletionRates): - - def __init__(self, batch, locations, ea=None): - self.batch = batch - self.locations = locations - self.ea = ea - - def attributes(self): - _completion_rates = [] - for location in self.locations: - attribute = {'location': location, - 'total_households': Household.all_households_in(location, self.batch.survey, self.ea).count(), - 'completed_households_percent': self.percent_completed_households(location, self.batch.survey, self.ea)} - _completion_rates.append(attribute) - return _completion_rates - - -class BatchSurveyCompletionRates: - - def __init__(self, location_type): - self.location_type = location_type - self.locations = Location.objects.filter(type=location_type) - - def get_completion_formatted_for_json(self, survey): - all_batches = survey.batches.all() - completion_rates_dict = {} - number_of_batches = len(all_batches) - - for location in self.locations: - percent_completed = 0.0 - percent_completed = reduce(lambda percent_completed, rate: percent_completed + rate, - map(lambda batch: BatchLocationCompletionRates(batch, location).percent_completed_households(), all_batches)) - completion_rates_dict[location.name.upper()] = Decimal( - percent_completed) / Decimal(number_of_batches) # if survey.is_open_for(location) else -1 - return completion_rates_dict diff --git a/survey/services/csv_uploader.py b/survey/services/csv_uploader.py deleted file mode 100644 index 9f620815..00000000 --- a/survey/services/csv_uploader.py +++ /dev/null @@ -1,62 +0,0 @@ -import csv -from datetime import datetime, timedelta -from django.utils.timezone import utc -from survey.models import UploadErrorLog - -from survey.interviewer_configs import UPLOAD_ERROR_LOG_EXPIRY - - -FIRST_LINE = 0 - - -class CSVUploader: - - def __init__(self, file): - self.file = file - - def file_is_not_csv(self): - return '\0' in self.file.read() - - def headers(self): - return csv.reader(self.file).next() - - def split_content(self): - if self.file_is_not_csv(): - return [], [] - self.read_file_from_begging() - csv_file = csv.reader(self.file) - all_rows = [row for row in csv_file] - headers = all_rows[FIRST_LINE] - all_rows.pop(FIRST_LINE) - return headers, all_rows - - def read_file_from_begging(self): - self.file.seek(FIRST_LINE) - - -class UploadService(object): - MODEL = None - - def __init__(self, _file): - self.file = _file - self.csv_uploader = CSVUploader(self.file) - self.clean_db() - - def clean_db(self): - one_month_before_today = datetime.utcnow().replace( - tzinfo=utc) - timedelta(days=UPLOAD_ERROR_LOG_EXPIRY) - all_entries_before_one_month = UploadErrorLog.objects.filter( - model=self.MODEL, created__lte=one_month_before_today) - all_entries_before_one_month.delete() - - def log_error(self, row_number, error): - UploadErrorLog.objects.create(model=self.MODEL, filename=self.file.name[ - :19], row_number=row_number, error=error) - - @staticmethod - def remove_trailing(name, in_array, exclude=None): - new_array = [header.replace(name, '') for header in in_array] - if exclude: - new_array = filter( - lambda element: not element.endswith(exclude), new_array) - return new_array diff --git a/survey/services/ea_upload.py b/survey/services/ea_upload.py deleted file mode 100644 index bef63dea..00000000 --- a/survey/services/ea_upload.py +++ /dev/null @@ -1,85 +0,0 @@ -from survey.models import UploadErrorLog, EnumerationArea, Location, LocationType -from survey.services.csv_uploader import UploadService - - -class UploadEA(UploadService): - MODEL = 'EA' - - @classmethod - def parents_locations_match(cls, location, given_parents): - location_parents = location.get_ancestors().values_list('name', flat=True) - check = [parent_name in location_parents for parent_name in given_parents] - return check.count(True) == len(given_parents) - - def check_errors_(self, index, row, headers, skip_column, lowest_location_column): - lowest_location_name = row[lowest_location_column] - location = Location.objects.filter( - name=lowest_location_name, parent__name__iexact=row[skip_column - 1].lower()) - if not location.exists(): - self.log_error(index + 1, 'There is no %s with name: %s, in %s.' % - (headers[lowest_location_column].lower(), row[lowest_location_column], row[skip_column - 1])) - return - if not self.parents_locations_match(location[0], row[:skip_column - 1]): - self.log_error(index + 1, 'The location hierarchy %s >> %s does not exist.' % - ((' >> '.join(row[:skip_column])), lowest_location_name)) - return - return location[0] - - def check_location_errors(self, index, row, headers): - first_ea_column_number = headers.index('EA') - return self.check_errors_(index, row, headers, - skip_column=first_ea_column_number, lowest_location_column=-2) - - def save_ea(self, index, row, location, survey): - first_ea_column_number = -3 - second_ea_column_number = -1 - ea_name = row[first_ea_column_number] or row[second_ea_column_number] - if ea_name: - ea = EnumerationArea.objects.get_or_create( - name=ea_name, survey=survey)[0] - ea.locations.add(location) - else: - self.log_error(index + 1, 'Enumeration Area name required.') - - def create_ea(self, reader, headers, survey): - for index, row in enumerate(reader): - location = self.check_location_errors(index, row, headers) - if location: - self.save_ea(index, row, location, survey) - - def upload(self, survey): - headers, reader = self.csv_uploader.split_content() - cleaned_headers = self.remove_trailing('Name', in_array=headers) - if not cleaned_headers: - UploadErrorLog.objects.create(model=self.MODEL, filename=self.file.name, - error='Enumeration Areas not uploaded. %s is not a valid csv file.' % self.file.name) - else: - self.create_ea(reader, cleaned_headers, survey) - - -class UploadEACSVLayoutHelper(object): - - def __init__(self): - self.headers = self._header_format() - - @classmethod - def _header_format(cls): - types = list(LocationType.objects.exclude( - name__iexact="country").values_list('name', flat=True)) - types.insert(-1, 'EA') - types.append('EA') - return types - - def table_layout_example(self): - headers = map(lambda header: header.lower(), self.headers) - if headers != ['ea', 'ea']: - row1 = [header + '_0' for header in headers] - row1[-1] = '' - row2 = list(row1) - row2[-2] += '_b' - row3 = [header + '_1' for header in headers] - row3[-3] = '' - row4 = list(row3) - row4[-1] += '_b' - return [row1, row2, row3, row4] - return [["No Location/LocationType added yet. Please add those first."]] diff --git a/survey/services/export_interviewers.py b/survey/services/export_interviewers.py deleted file mode 100644 index bb919418..00000000 --- a/survey/services/export_interviewers.py +++ /dev/null @@ -1,33 +0,0 @@ -from survey.models import Interviewer, LocationType -import operator - - -class ExportInterviewersService: - - def __init__(self, export_fields): - self.interviewers = Interviewer.objects.all() - self.HEADERS = export_fields - - def formatted_responses(self): - _formatted_responses = [] - headers = [loc_type.name.upper() for loc_type in - LocationType.objects.exclude(name__iexact='country') - if loc_type != LocationType.smallest_unit()] - headers.extend([entry.upper() for entry in self.HEADERS]) - _formatted_responses.append(','.join(headers)) - interviewer_records = [] - for interviewer in self.interviewers: - info = {} - info['mobile_numbers'] = ','.join( - [access.user_identifier for access in interviewer.ussd_access]) - info['odk_id'] = ','.join( - [access.user_identifier for access in interviewer.odk_access]) - row = [str(loc.name) for loc in interviewer.ea.parent_locations()] - row.extend(['"%s"' % str(getattr(interviewer, entry, - info.get(entry, ''))) for entry in self.HEADERS]) - interviewer_records.append(row) - interviewer_records = sorted( - interviewer_records, key=operator.itemgetter(*range(len(headers)))) - _formatted_responses.extend( - map(lambda row: ','.join(row), interviewer_records)) - return _formatted_responses diff --git a/survey/services/export_questions.py b/survey/services/export_questions.py deleted file mode 100644 index 75a84565..00000000 --- a/survey/services/export_questions.py +++ /dev/null @@ -1,72 +0,0 @@ -from survey.models import Question, QuestionTemplate - - -class ExportQuestionsService: - HEADERS = "Question Text; Group; Answer Type; Options" - - def __init__(self, batch=None): - self.questions = self._get_questions(batch) - print self.questions - - def _get_questions(self, batch): - if batch: - return batch.questions.all() - return Question.objects.all() - - def formatted_responses(self): - _formatted_responses = [self.HEADERS] - for question in self.questions: - if question.group: - text = '%s, %s, %s' % (question.text.replace( - '\r\n', ' '), question.group.name, question.answer_type.upper()) - _formatted_responses.append(text) - self._append_options(question, _formatted_responses) - return _formatted_responses - - def _append_options(self, question, formatted_response): - options = list(question.options.order_by('order')) - if options: - formatted_response[-1] += '; %s' % options.pop(0).text - for option in options: - formatted_response.append('; ; ; %s' % option.text) - - -def get_question_template_as_dump(questions): - HEADERS = "Question Code,Question Text,Answer Type,Options,Group,Module" - _formatted_responses = [HEADERS, ] - map(lambda question: - _formatted_responses.append('%s,%s,%s,%s,%s,%s' % - (question.identifier, question.text.replace('\r\n', ' '), question.answer_type.upper(), - '|'.join( - [opt.to_text for opt in question.options.all()]), - question.group.name, question.module.name) - ), questions) - return _formatted_responses - - -def get_batch_question_as_dump(questions): - HEADERS = "Question Code,Question Text,Answer Type,Options,Logic,Group,Module" - _formatted_responses = [HEADERS, ] - map(lambda question: - _formatted_responses.append('%s,%s,%s,%s,%s,%s,%s' % - (question.identifier, question.text.replace('\r\n', ' '), question.answer_type.upper(), - '|'.join( - [opt.to_text for opt in question.options.all()]), - get_logic_print(question), question.group.name, question.module.name) - ), questions) - return _formatted_responses - - -def get_logic_print(question): - content = [] - for flow in question.flows.exclude(validation_test__isnull=True): - # desc = flow.desc - # if desc.startswith(flow.validation_test): - # desc = desc[len(flow.validation_test)+1:] - next_question = flow.next_question - identifier = '' - if next_question: - identifier = next_question.identifier - content.append(' '.join([flow.validation_test, ' and '.join(flow.params_display()), # this a gamble for between ques - flow.desc or '', identifier])) - return ' | '.join(content) diff --git a/survey/services/location_upload.py b/survey/services/location_upload.py deleted file mode 100644 index 574764bd..00000000 --- a/survey/services/location_upload.py +++ /dev/null @@ -1,53 +0,0 @@ -from django.template.defaultfilters import slugify -# from rapidsms.contrib.locations.models import LocationType, Location -from survey.models import LocationTypeDetails, UploadErrorLog, Location, LocationType, EnumerationArea -from survey.models.locations import * -from survey.services.csv_uploader import UploadService -from django.conf import settings - - -class UploadLocation(UploadService): - MODEL = 'LOCATIONS' - EA_INDEX = None - - def _get_location_types(self, headers): - if headers[-2].lower().replace('name', '') == 'ea': - self.EA_INDEX = len(headers) - 2 - headers.pop(self.EA_INDEX) - headers.pop(self.EA_INDEX) # this is total households - location_types = [] - map(lambda header: location_types.append(LocationType.objects.get( - name__iexact=header, slug=slugify(header))), headers) - return location_types - - def _create_locations(self, csv_rows, location_types): - country = Location.objects.all().get(type__name__iexact='country') - for index, row in enumerate(csv_rows): - location = country - for x_index, cell_value in enumerate(row): - print "try" - try: - if x_index == self.EA_INDEX: - ea, _ = EnumerationArea.objects.get_or_create(name=cell_value.strip(), - total_households=row[x_index + 1].strip() or - settings.DEFAULT_TOTAL_HOUSEHOLDS_IN_EA) - ea.locations.add(location) - print "b4 save" - ea.save() - break - print cell_value.strip(), location_types[x_index], "type" - location, _ = Location.objects.get_or_create( - name=cell_value.strip(), type=location_types[x_index], parent=location) - except Exception, ex: - print 'could not load entry: ', x_index, ' reason ', str(ex) - - def upload(self): - headers, rows = self.csv_uploader.split_content() - cleaned_headers = self.remove_trailing( - 'Name', in_array=headers, exclude='Code') - if not cleaned_headers: - UploadErrorLog.objects.create(model=self.MODEL, filename=self.file.name, - error='Locations not uploaded. %s is not a valid csv file.' % self.file.name) - else: - location_types = self._get_location_types(cleaned_headers) - self._create_locations(rows, location_types) diff --git a/survey/services/location_weights_upload.py b/survey/services/location_weights_upload.py deleted file mode 100644 index 8d080adf..00000000 --- a/survey/services/location_weights_upload.py +++ /dev/null @@ -1,51 +0,0 @@ -from django.utils.timezone import utc -from survey.models import LocationWeight, UploadErrorLog, Location -from survey.services.csv_uploader import UploadService -from survey.models.locations import * - - -class UploadLocationWeights(UploadService): - MODEL = 'WEIGHTS' - - @classmethod - def parents_locations_match(cls, location, given_parents): - location_parents = location.get_ancestors().values_list('name', flat=True) - check = [parent_name in location_parents for parent_name in given_parents] - return check.count(True) == len(given_parents) - - def check_location_errors(self, index, row, headers): - lowest_location = row[-2] - location = Location.objects.filter( - name=lowest_location, parent__name__iexact=row[-3].lower()) - if not location.exists(): - self.log_error(index + 1, 'There is no %s with name: %s, in %s.' % - (headers[-2].lower(), row[-2], row[-3])) - return - if not self.parents_locations_match(location[0], row[:-3]): - self.log_error( - index + 1, 'The location hierarchy %s does not exist.' % ((' >> '.join(row[:-1])))) - return - return location - - def save_weight(self, index, row, location, survey): - try: - LocationWeight.objects.create( - location=location[0], selection_probability=float(row[-1]), survey=survey) - except ValueError, e: - self.log_error( - index + 1, 'Selection probability must be a number.') - - def create_locations_weights(self, reader, headers, survey): - for index, row in enumerate(reader): - location = self.check_location_errors(index, row, headers) - if location: - self.save_weight(index, row, location, survey) - - def upload(self, survey): - headers, reader = self.csv_uploader.split_content() - cleaned_headers = self.remove_trailing('Name', in_array=headers) - if not cleaned_headers: - UploadErrorLog.objects.create(model=self.MODEL, filename=self.file.name, - error='Location weights not uploaded. %s is not a valid csv file.' % self.file.name) - else: - self.create_locations_weights(reader, cleaned_headers, survey) diff --git a/survey/services/results_download_service.py b/survey/services/results_download_service.py deleted file mode 100644 index 83ca4081..00000000 --- a/survey/services/results_download_service.py +++ /dev/null @@ -1,255 +0,0 @@ -from survey.models import LocationTypeDetails, Location, LocationType, Household, HouseholdMember, \ - HouseholdMemberGroup, Answer, MultiChoiceAnswer, MultiSelectAnswer, NumericalAnswer, QuestionOption, Interview -from survey.utils.views_helper import get_ancestors -from django.core.mail import send_mail, EmailMessage -from django.conf import settings -from datetime import datetime -import csv -import StringIO -import string -from collections import OrderedDict -import dateutils -from survey.odk.utils.log import logger - - -class ResultComposer: - - def __init__(self, user, results_download_service): - self.results_download_service = results_download_service - self.user = user - - def send_mail(self): - attachment_name = '%s.csv' % (self.results_download_service.batch.name if self.results_download_service.batch - else self.results_download_service.survey.name) - subject = 'Completion report for %s' % attachment_name - text = 'Completion report for %s. Date: %s' % ( - attachment_name, datetime.now()) - print 'commencing...' - try: - mail = EmailMessage(subject, text, settings.DEFAULT_EMAIL_SENDER, [ - self.user.email, ]) - data = self.results_download_service.generate_interview_reports() - #data = [[unicode('"%s"' % entry) for entry in entries] for entries in data] - f = StringIO.StringIO() - writer = csv.writer(f) - map(lambda row: writer.writerow(row), data) - #data = [','.join([unicode('"%s"' % entry) for entry in entries]) for entries in data] - f.seek(0) - mail.attach(attachment_name, f.read(), 'text/csv') - f.close() - sent = mail.send() - print 'Emailed!! ', sent - except Exception, ex: - print 'error while sending mail: %s', str(ex) - - -class ResultsDownloadService(object): - AS_TEXT = 1 - AS_LABEL = 0 - answers = None - # MEMBER_ATTRS = { - # 'id' : 'Household ID', - # 'name' : 'Name', 'Age', 'Date of Birth', 'Gender' - # } - - def __init__(self, survey=None, batch=None, restrict_to=None, specific_households=None, multi_display=AS_TEXT): - self.batch = batch - self.survey, self.questions = self._set_survey_and_questions(survey) - self.locations = [] - if restrict_to: - map(lambda loc: self.locations.extend( - loc.get_leafnodes(include_self=True)), restrict_to) - self.specific_households = specific_households - self.multi_display = int(multi_display) - - def _set_survey_and_questions(self, survey): - if self.batch: - return self.batch.survey, self.batch.survey_questions - survey_questions = [] - map(lambda batch: survey_questions.extend( - list(batch.survey_questions)), survey.batches.all()) - return survey, survey_questions - - def set_report_headers(self): - header = [loc.name for loc in LocationType.objects.exclude( - name__iexact="country")] - - other_headers = ['EA', 'Household Number', 'Family Name', - 'First Name', 'Age', 'Date of Birth', 'Gender'] - header.extend(other_headers) - header.extend(self.question_headers()) - return header - - def question_headers(self): - header = [] - # loop_starters = self.batch.loop_starters() - for question in self.questions: - # if question in loop_starters: - # boundary = question.loop_boundary() - # loop_questions = self.batch.loop_backs_questions()[boundary] - # for i in range(settings.LOOP_QUESTION_REPORT_DEPT-1): - # map(lambda q: header.append(q.identifier), loop_questions) - header.append(question.identifier) - return header - - def get_summarised_answers(self): - data = [] - q_opts = {} - if self.specific_households is None: - all_households = Household.objects.filter( - listing__survey_houselistings__survey=self.survey) - else: - all_households = Household.objects.filter(pk__in=self.specific_households, - listing__survey_houselistings__survey=self.survey) - locations = list(set(all_households.values_list( - 'listing__ea__locations', flat=True))) - for location_id in locations: - households_in_location = all_households.filter( - listing__ea__locations=location_id) - household_location = Location.objects.get(id=location_id) - location_ancestors = household_location.get_ancestors(include_self=True).\ - exclude(parent__isnull='country').values_list( - 'name', flat=True) - answers = [] - for household in households_in_location: - for member in household.members.all(): - try: - answers = list(location_ancestors) - member_gender = 'Male' if member.gender == HouseholdMember.MALE else 'Female' - answers.extend([household.listing.ea.name, household.house_number, '%s-%s' % - (member.surname, member.first_name), str( - member.age), - member.date_of_birth.strftime( - settings.DATE_FORMAT), - member_gender]) - for question in self.questions: - reply = member.reply(question) - if question.answer_type in [MultiChoiceAnswer.choice_name(), - MultiSelectAnswer.choice_name()]\ - and self.multi_display == self.AS_LABEL: - label = q_opts.get((question.pk, reply), None) - if label is None: - try: - label = question.options.get( - text__iexact=reply).order - except QuestionOption.DoesNotExist: - label = reply - q_opts[(question.pk, reply)] = label - reply = str(label) - answers.append(reply.encode('utf8')) - data.append(answers) - except Exception, ex: - print 'Error ', str(ex) - return data - - def get_interview_answers(self): - report = [] - member_reports = OrderedDict() - val_list_args = ['interview__ea__locations__name', - 'interview__ea__name', 'interview__householdmember__household__house_number', - 'interview__householdmember__surname', 'interview__householdmember__first_name', - 'interview__householdmember__date_of_birth', 'interview__householdmember__gender', ] - parent_loc = 'interview__ea__locations' - for i in range(LocationType.objects.count() - 2): - parent_loc = '%s__parent' % parent_loc - val_list_args.insert(0, '%s__name' % parent_loc) - filter_args = {} - if self.locations: - filter_args['interview__ea__locations__in'] = self.locations - if self.specific_households: - filter_args[ - 'interview__householdmember__household__in'] = self.specific_households - for answer_type in Answer.answer_types(): - query_args = list(val_list_args) - value = 'value' - if answer_type in [MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name()]: - value = 'value__text' - if self.multi_display == self.AS_LABEL: - value = 'value__order' - query_args.append(value) - answer_class = Answer.get_class(answer_type) - # print 'using query_args ', query_args - answer_data = answer_class.objects.filter(interview__batch=self.batch, **filter_args).\ - values_list('interview__householdmember__pk', 'question__pk', *query_args).\ - order_by('interview__ea__locations', 'interview__ea', - 'interview__householdmember__household', 'interview__householdmember__pk', - 'pk', 'loop_id') - # .distinct( - # 'interview__ea__locations', - # 'interview__ea', - # 'interview__householdmember__household', - # #'interview__householdmember__pk', - # #'question__pk' - # ) - - answer_data = list(answer_data) - # print 'answer data ', len(answer_data) - # now grab member reports - for data in answer_data: - hm_pk, question_pk = data[:2] - report_data = list(data[2:]) - hm_data = member_reports.get(hm_pk, None) - if hm_data is None: - report_data.insert(-3, str(dateutils.relativedelta(datetime.utcnow().date(), - report_data[-3]).years)) - report_data[-3] = report_data[-3].strftime( - settings.DATE_FORMAT) - report_data[-2] = 'M' if report_data[-2] else 'F' - member_details = [unicode(md).encode( - 'utf8') for md in report_data[:-1]] - hm_data = OrderedDict([('mem_details', member_details), ]) - hm_question_data = hm_data.get(question_pk, []) - hm_question_data.append( - unicode(report_data[-1]).encode('utf8')) - hm_data[question_pk] = hm_question_data - member_reports[hm_pk] = hm_data - - for hm in member_reports.values(): - answers = hm.pop('mem_details', []) - # for question_pk, response_data in hm.items(): - loop_starters = self.batch.loop_starters() - loop_enders = self.batch.loop_enders() - started = False - dept = 0 - for question in self.questions: - # loop_extras = [] - # boundary = question.loop_boundary() - # if question in loop_starters: - # loop_extras = [] - # loop_questions = self.batch.loop_backs_questions()[boundary] - # for i in range(1, settings.LOOP_QUESTION_REPORT_DEPT): - # for q in loop_questions: - # question_responses = hm.get(question.pk, []) - # if len(question_responses) > i: - # resp = question_responses[i] - # else: - # resp = '' - # loop_extras.append(resp) - # import pdb; pdb.set_trace() - # if question in loop_enders: - # answers.extend(loop_extras) - answers.append(' > '.join(hm.get(question.pk, ['', ]))) - report.append(answers) - return report - - def generate_report(self): - data = [self.set_report_headers(), ] - data.extend(self.get_summarised_answers()) - return data - - def generate_interview_reports(self): - data = [self.set_report_headers(), ] - data.extend(self.get_interview_answers()) - return data - - def _get_ancestors_names(self, household_location, exclude_type='country'): - location_ancestors = get_ancestors( - household_location, include_self=True) - if exclude_type: - exclude_location = Location.objects.filter( - type__name__iexact=exclude_type.lower()) - for location in exclude_location: - location_ancestors.remove(location) - result = [ancestor.name for ancestor in location_ancestors] - result.reverse() - return result diff --git a/survey/services/simple_indicator_service.py b/survey/services/simple_indicator_service.py deleted file mode 100644 index e9d48a21..00000000 --- a/survey/services/simple_indicator_service.py +++ /dev/null @@ -1,54 +0,0 @@ -from django.utils.datastructures import SortedDict - - -class SimpleIndicatorService(object): - - def __init__(self, formula, location_parent): - self.count = formula.get_count_type() - self.survey = formula.indicator.batch.survey - self.location_parent = location_parent - - def hierarchical_count(self): - return self.hierarchical_count_for(self.location_parent) - - def hierarchical_count_for(self, location_parent): - return self.count.hierarchical_result_for(location_parent, self.survey) - - def get_location_names_and_data_series(self): - options, locations_names = self._arrange_answers_per_options() - data_series = self.to_high_chart_format(options) - return data_series, locations_names - - def _arrange_answers_per_options(self): - options = {} - locations_names = [] - for location, answers in (self.hierarchical_count()).items(): - locations_names.append(str(location.name)) - for option, answer in answers.items(): - if not options.has_key(option): - options[option] = [] - options[option].append(answer) - return options, locations_names - - @classmethod - def to_high_chart_format(cls, options): - data_series = [] - for key, value in options.items(): - data_series.append(SortedDict({ - 'name': str(key), - 'data': value - })) - return data_series - - def tabulated_data_series(self): - tabulated_data = [] - first_level_locations = self.location_parent.get_children().order_by('name')[ - :10] - for location in first_level_locations: - for child_location, answers in self.hierarchical_count_for(location).items(): - tab_data = SortedDict({location.type.name: location.name}) - tab_data[child_location.type.name] = child_location.name - tab_data.update(answers) - tab_data.update({'Total': sum(answers.values())}) - tabulated_data.append(tab_data) - return tabulated_data diff --git a/survey/static/asset/css/_notes/dwsync.xml b/survey/static/asset/css/_notes/dwsync.xml deleted file mode 100644 index 1a1c508b..00000000 --- a/survey/static/asset/css/_notes/dwsync.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/survey/static/asset/css/bootstrap.min.css b/survey/static/asset/css/bootstrap.min.css deleted file mode 100644 index 0ab74371..00000000 --- a/survey/static/asset/css/bootstrap.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.3.6 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:1px;padding-left:1px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/survey/static/asset/js/_notes/dwsync.xml b/survey/static/asset/js/_notes/dwsync.xml deleted file mode 100644 index 93989a5a..00000000 --- a/survey/static/asset/js/_notes/dwsync.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/survey/static/asset/js/bootstrap.min.js b/survey/static/asset/js/bootstrap.min.js deleted file mode 100644 index 133aeecb..00000000 --- a/survey/static/asset/js/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under the MIT license - */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/survey/static/attrchange.js b/survey/static/attrchange.js deleted file mode 100755 index dd18562a..00000000 --- a/survey/static/attrchange.js +++ /dev/null @@ -1,124 +0,0 @@ -/* -A simple jQuery function that can add listeners on attribute change. -http://meetselva.github.io/attrchange/ - -About License: -Copyright (C) 2013-2014 Selvakumar Arumugam -You may use attrchange plugin under the terms of the MIT Licese. -https://github.com/meetselva/attrchange/blob/master/MIT-License.txt - */ -(function($) { - function isDOMAttrModifiedSupported() { - var p = document.createElement('p'); - var flag = false; - - if (p.addEventListener) { - p.addEventListener('DOMAttrModified', function() { - flag = true - }, false); - } else if (p.attachEvent) { - p.attachEvent('onDOMAttrModified', function() { - flag = true - }); - } else { return false; } - p.setAttribute('id', 'target'); - return flag; - } - - function checkAttributes(chkAttr, e) { - if (chkAttr) { - var attributes = this.data('attr-old-value'); - - if (e.attributeName.indexOf('style') >= 0) { - if (!attributes['style']) - attributes['style'] = {}; //initialize - var keys = e.attributeName.split('.'); - e.attributeName = keys[0]; - e.oldValue = attributes['style'][keys[1]]; //old value - e.newValue = keys[1] + ':' - + this.prop("style")[$.camelCase(keys[1])]; //new value - attributes['style'][keys[1]] = e.newValue; - } else { - e.oldValue = attributes[e.attributeName]; - e.newValue = this.attr(e.attributeName); - attributes[e.attributeName] = e.newValue; - } - - this.data('attr-old-value', attributes); //update the old value object - } - } - - //initialize Mutation Observer - var MutationObserver = window.MutationObserver - || window.WebKitMutationObserver; - - $.fn.attrchange = function(a, b) { - if (typeof a == 'object') {//core - var cfg = { - trackValues : false, - callback : $.noop - }; - //backward compatibility - if (typeof a === "function") { cfg.callback = a; } else { $.extend(cfg, a); } - - if (cfg.trackValues) { //get attributes old value - this.each(function(i, el) { - var attributes = {}; - for ( var attr, i = 0, attrs = el.attributes, l = attrs.length; i < l; i++) { - attr = attrs.item(i); - attributes[attr.nodeName] = attr.value; - } - $(this).data('attr-old-value', attributes); - }); - } - - if (MutationObserver) { //Modern Browsers supporting MutationObserver - var mOptions = { - subtree : false, - attributes : true, - attributeOldValue : cfg.trackValues - }; - var observer = new MutationObserver(function(mutations) { - mutations.forEach(function(e) { - var _this = e.target; - //get new value if trackValues is true - if (cfg.trackValues) { - e.newValue = $(_this).attr(e.attributeName); - } - if ($(_this).data('attrchange-status') === 'connected') { //execute if connected - cfg.callback.call(_this, e); - } - }); - }); - - return this.data('attrchange-method', 'Mutation Observer').data('attrchange-status', 'connected') - .data('attrchange-obs', observer).each(function() { - observer.observe(this, mOptions); - }); - } else if (isDOMAttrModifiedSupported()) { //Opera - //Good old Mutation Events - return this.data('attrchange-method', 'DOMAttrModified').data('attrchange-status', 'connected').on('DOMAttrModified', function(event) { - if (event.originalEvent) { event = event.originalEvent; }//jQuery normalization is not required - event.attributeName = event.attrName; //property names to be consistent with MutationObserver - event.oldValue = event.prevValue; //property names to be consistent with MutationObserver - if ($(this).data('attrchange-status') === 'connected') { //disconnected logically - cfg.callback.call(this, event); - } - }); - } else if ('onpropertychange' in document.body) { //works only in IE - return this.data('attrchange-method', 'propertychange').data('attrchange-status', 'connected').on('propertychange', function(e) { - e.attributeName = window.event.propertyName; - //to set the attr old value - checkAttributes.call($(this), cfg.trackValues, e); - if ($(this).data('attrchange-status') === 'connected') { //disconnected logically - cfg.callback.call(this, e); - } - }); - } - return this; - } else if (typeof a == 'string' && $.fn.attrchange.hasOwnProperty('extensions') && - $.fn.attrchange['extensions'].hasOwnProperty(a)) { //extensions/options - return $.fn.attrchange['extensions'][a].call(this, b); - } - } -})(jQuery); \ No newline at end of file diff --git a/survey/static/attrchange_ext.js b/survey/static/attrchange_ext.js deleted file mode 100755 index 187033e8..00000000 --- a/survey/static/attrchange_ext.js +++ /dev/null @@ -1,97 +0,0 @@ -/* -An extension for attrchange jQuery plugin -http://meetselva.github.io/attrchange/ - -About License: -Copyright (C) 2013-2014 Selvakumar Arumugam -You may use attrchange ext plugin under the terms of the MIT Licese. -https://github.com/meetselva/attrchange/blob/master/MIT-License.txt - */ -$.fn.attrchange.extensions = { /*attrchange option/extension*/ - disconnect: function (o) { - if (typeof o !== 'undefined' && o.isPhysicalDisconnect) { - return this.each(function() { - var attrchangeMethod = $(this).data('attrchange-method'); - if (attrchangeMethod == 'propertychange' || attrchangeMethod == 'DOMAttrModified') { - $(this).off(attrchangeMethod); - } else if (attrchangeMethod == 'Mutation Observer') { - $(this).data('attrchange-obs').disconnect(); - } else if (attrchangeMethod == 'polling') { - clearInterval($(this).data('attrchange-polling-timer')); - } - }).removeData(['attrchange-method', 'attrchange-status']); - } else { //logical disconnect - return this.data('attrchange-status', 'disconnected'); //set a flag that prevents triggering callback onattrchange - } - }, - remove: function (o) { - return $.fn.attrchange.extensions['disconnect'].call(this, {isPhysicalDisconnect: true}); - }, - getProperties: function (o) { - var attrchangeMethod = $(this).data('attrchange-method'); - var pollInterval = $(this).data('attrchange-pollInterval'); - return { - method: attrchangeMethod, - isPolling: (attrchangeMethod == 'polling'), - pollingInterval: (typeof pollInterval === 'undefined')?0:parseInt(pollInterval, 10), - status: (typeof attrchangeMethod === 'undefined')?'removed': $(this).data('attrchange-status') - } - }, - reconnect: function (o) {//reconnect possible only when there is a logical disconnect - return this.data('attrchange-status', 'connected'); - }, - polling: function (o) { - if (o.hasOwnProperty('isComputedStyle') && o.isComputedStyle == 'true') { /* extensive and slow - polling to check on computed style properties */ - return this.each(function(i, _this) { - if (!o.hasOwnProperty('properties') || - Object.prototype.toString.call(o.properties) !== '[object Array]' || - o.properties.length == 0) { return false; } //return if no properties found - var attributes = {}; //store computed properties - for (var i = 0; i < o.properties.length; i++) { - attributes[o.properties[i]] = $(this).css(o.properties[i]); - } - var _this = this; - $(this).data('attrchange-polling-timer', setInterval(function () { - var changes = {}, hasChanges = false; // attrName: { oldValue: xxx, newValue: yyy} - for (var comuptedVal, i = 0; i < o.properties.length; i++){ - comuptedVal = $(_this).css(o.properties[i]); - if (attributes[o.properties[i]] !== comuptedVal) { - hasChanges = true; - changes[o.properties[i]] = {oldValue: attributes[o.properties[i]], newValue: comuptedVal}; - attributes[o.properties[i]] = comuptedVal //add the attribute to the orig - } - } - if (hasChanges && $(_this).data('attrchange-status') === 'connected') { //disconnected logically - o.callback.call(_this, changes); - } - }, (o.pollInterval)?o.pollInterval: 1000)).data('attrchange-method', 'polling').data('attrchange-pollInterval', o.pollInterval).data('attrchange-status', 'connected'); - }); - } else { - return this.each(function(i, _this) { /* this one is programmatic polling */ - var attributes = {}; - for (var attr, i=0, attrs=_this.attributes, l=attrs.length; i div { - display: inline-block; - width: 150%; - position: relative; - top: 0; -} -.has-switch > div.switch-animate { - -webkit-transition: left 0.5s; - -moz-transition: left 0.5s; - -o-transition: left 0.5s; - transition: left 0.5s; -} -.has-switch > div.switch-off { - left: -50%; -} -.has-switch > div.switch-on { - left: 0%; -} -.has-switch input[type=radio], -.has-switch input[type=checkbox] { - display: none; -} -.has-switch span, -.has-switch label { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - cursor: pointer; - position: relative; - display: inline-block; - height: 100%; - padding-bottom: 4px; - padding-top: 4px; - font-size: 14px; - line-height: 20px; -} -.has-switch span.switch-mini, -.has-switch label.switch-mini { - padding-bottom: 4px; - padding-top: 4px; - font-size: 10px; - line-height: 9px; -} -.has-switch span.switch-small, -.has-switch label.switch-small { - padding-bottom: 3px; - padding-top: 3px; - font-size: 12px; - line-height: 18px; -} -.has-switch span.switch-large, -.has-switch label.switch-large { - padding-bottom: 9px; - padding-top: 9px; - font-size: 16px; - line-height: normal; -} -.has-switch label { - text-align: center; - margin-top: -1px; - margin-bottom: -1px; - z-index: 100; - width: 34%; - border-left: 1px solid #cccccc; - border-right: 1px solid #cccccc; - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #f5f5f5; - background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); - background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); - background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); - background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); - border-color: #e6e6e6 #e6e6e6 #bfbfbf; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #e6e6e6; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.has-switch label:hover, -.has-switch label:focus, -.has-switch label:active, -.has-switch label.active, -.has-switch label.disabled, -.has-switch label[disabled] { - color: #ffffff; - background-color: #e6e6e6; - *background-color: #d9d9d9; -} -.has-switch label:active, -.has-switch label.active { - background-color: #cccccc \9; -} -.has-switch label i { - color: #000; - text-shadow: 0 1px 0 #fff; - line-height: 18px; - pointer-events: none; -} -.has-switch span { - text-align: center; - z-index: 1; - width: 33%; -} -.has-switch span.switch-left { - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-left-radius: 4px; - -webkit-border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - border-bottom-left-radius: 4px; -} -.has-switch span.switch-right { - color: #333333; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); - background-color: #f0f0f0; - background-image: -moz-linear-gradient(top, #e6e6e6, #ffffff); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#e6e6e6), to(#ffffff)); - background-image: -webkit-linear-gradient(top, #e6e6e6, #ffffff); - background-image: -o-linear-gradient(top, #e6e6e6, #ffffff); - background-image: linear-gradient(to bottom, #e6e6e6, #ffffff); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6', endColorstr='#ffffffff', GradientType=0); - border-color: #ffffff #ffffff #d9d9d9; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #ffffff; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.has-switch span.switch-right:hover, -.has-switch span.switch-right:focus, -.has-switch span.switch-right:active, -.has-switch span.switch-right.active, -.has-switch span.switch-right.disabled, -.has-switch span.switch-right[disabled] { - color: #333333; - background-color: #ffffff; - *background-color: #f2f2f2; -} -.has-switch span.switch-right:active, -.has-switch span.switch-right.active { - background-color: #e6e6e6 \9; -} -.has-switch span.switch-primary, -.has-switch span.switch-left { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #005fcc; - background-image: -moz-linear-gradient(top, #0044cc, #0088cc); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0044cc), to(#0088cc)); - background-image: -webkit-linear-gradient(top, #0044cc, #0088cc); - background-image: -o-linear-gradient(top, #0044cc, #0088cc); - background-image: linear-gradient(to bottom, #0044cc, #0088cc); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0044cc', endColorstr='#ff0088cc', GradientType=0); - border-color: #0088cc #0088cc #005580; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #0088cc; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.has-switch span.switch-primary:hover, -.has-switch span.switch-left:hover, -.has-switch span.switch-primary:focus, -.has-switch span.switch-left:focus, -.has-switch span.switch-primary:active, -.has-switch span.switch-left:active, -.has-switch span.switch-primary.active, -.has-switch span.switch-left.active, -.has-switch span.switch-primary.disabled, -.has-switch span.switch-left.disabled, -.has-switch span.switch-primary[disabled], -.has-switch span.switch-left[disabled] { - color: #ffffff; - background-color: #0088cc; - *background-color: #0077b3; -} -.has-switch span.switch-primary:active, -.has-switch span.switch-left:active, -.has-switch span.switch-primary.active, -.has-switch span.switch-left.active { - background-color: #006699 \9; -} -.has-switch span.switch-info { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #41a7c5; - background-image: -moz-linear-gradient(top, #2f96b4, #5bc0de); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#2f96b4), to(#5bc0de)); - background-image: -webkit-linear-gradient(top, #2f96b4, #5bc0de); - background-image: -o-linear-gradient(top, #2f96b4, #5bc0de); - background-image: linear-gradient(to bottom, #2f96b4, #5bc0de); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff2f96b4', endColorstr='#ff5bc0de', GradientType=0); - border-color: #5bc0de #5bc0de #28a1c5; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #5bc0de; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.has-switch span.switch-info:hover, -.has-switch span.switch-info:focus, -.has-switch span.switch-info:active, -.has-switch span.switch-info.active, -.has-switch span.switch-info.disabled, -.has-switch span.switch-info[disabled] { - color: #ffffff; - background-color: #5bc0de; - *background-color: #46b8da; -} -.has-switch span.switch-info:active, -.has-switch span.switch-info.active { - background-color: #31b0d5 \9; -} -.has-switch span.switch-success { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #58b058; - background-image: -moz-linear-gradient(top, #51a351, #62c462); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#51a351), to(#62c462)); - background-image: -webkit-linear-gradient(top, #51a351, #62c462); - background-image: -o-linear-gradient(top, #51a351, #62c462); - background-image: linear-gradient(to bottom, #51a351, #62c462); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff51a351', endColorstr='#ff62c462', GradientType=0); - border-color: #62c462 #62c462 #3b9e3b; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #62c462; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.has-switch span.switch-success:hover, -.has-switch span.switch-success:focus, -.has-switch span.switch-success:active, -.has-switch span.switch-success.active, -.has-switch span.switch-success.disabled, -.has-switch span.switch-success[disabled] { - color: #ffffff; - background-color: #62c462; - *background-color: #4fbd4f; -} -.has-switch span.switch-success:active, -.has-switch span.switch-success.active { - background-color: #42b142 \9; -} -.has-switch span.switch-warning { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #f9a123; - background-image: -moz-linear-gradient(top, #f89406, #fbb450); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f89406), to(#fbb450)); - background-image: -webkit-linear-gradient(top, #f89406, #fbb450); - background-image: -o-linear-gradient(top, #f89406, #fbb450); - background-image: linear-gradient(to bottom, #f89406, #fbb450); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff89406', endColorstr='#fffbb450', GradientType=0); - border-color: #fbb450 #fbb450 #f89406; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #fbb450; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.has-switch span.switch-warning:hover, -.has-switch span.switch-warning:focus, -.has-switch span.switch-warning:active, -.has-switch span.switch-warning.active, -.has-switch span.switch-warning.disabled, -.has-switch span.switch-warning[disabled] { - color: #ffffff; - background-color: #fbb450; - *background-color: #faa937; -} -.has-switch span.switch-warning:active, -.has-switch span.switch-warning.active { - background-color: #fa9f1e \9; -} -.has-switch span.switch-danger { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #d14641; - background-image: -moz-linear-gradient(top, #bd362f, #ee5f5b); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#bd362f), to(#ee5f5b)); - background-image: -webkit-linear-gradient(top, #bd362f, #ee5f5b); - background-image: -o-linear-gradient(top, #bd362f, #ee5f5b); - background-image: linear-gradient(to bottom, #bd362f, #ee5f5b); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffbd362f', endColorstr='#ffee5f5b', GradientType=0); - border-color: #ee5f5b #ee5f5b #e51d18; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #ee5f5b; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.has-switch span.switch-danger:hover, -.has-switch span.switch-danger:focus, -.has-switch span.switch-danger:active, -.has-switch span.switch-danger.active, -.has-switch span.switch-danger.disabled, -.has-switch span.switch-danger[disabled] { - color: #ffffff; - background-color: #ee5f5b; - *background-color: #ec4844; -} -.has-switch span.switch-danger:active, -.has-switch span.switch-danger.active { - background-color: #e9322d \9; -} -.has-switch span.switch-default { - color: #333333; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); - background-color: #f0f0f0; - background-image: -moz-linear-gradient(top, #e6e6e6, #ffffff); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#e6e6e6), to(#ffffff)); - background-image: -webkit-linear-gradient(top, #e6e6e6, #ffffff); - background-image: -o-linear-gradient(top, #e6e6e6, #ffffff); - background-image: linear-gradient(to bottom, #e6e6e6, #ffffff); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6', endColorstr='#ffffffff', GradientType=0); - border-color: #ffffff #ffffff #d9d9d9; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #ffffff; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.has-switch span.switch-default:hover, -.has-switch span.switch-default:focus, -.has-switch span.switch-default:active, -.has-switch span.switch-default.active, -.has-switch span.switch-default.disabled, -.has-switch span.switch-default[disabled] { - color: #333333; - background-color: #ffffff; - *background-color: #f2f2f2; -} -.has-switch span.switch-default:active, -.has-switch span.switch-default.active { - background-color: #e6e6e6 \9; -} diff --git a/survey/static/bootstrap-switch/bootstrap-switch.js b/survey/static/bootstrap-switch/bootstrap-switch.js deleted file mode 100755 index fd69923e..00000000 --- a/survey/static/bootstrap-switch/bootstrap-switch.js +++ /dev/null @@ -1,310 +0,0 @@ -/*! ============================================================ - * bootstrapSwitch v1.6 by Larentis Mattia @SpiritualGuru - * http://www.larentis.eu/ - * - * Enhanced for radiobuttons by Stein, Peter @BdMdesigN - * http://www.bdmdesign.org/ - * - * Project site: - * http://www.larentis.eu/switch/ - * ============================================================ - * Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * ============================================================ */ - -!function ($) { - "use strict"; - - $.fn['bootstrapSwitch'] = function (method) { - var inputSelector = 'input[type!="hidden"]'; - var methods = { - init: function () { - return this.each(function () { - var $element = $(this) - , $div - , $switchLeft - , $switchRight - , $label - , $form = $element.closest('form') - , myClasses = "" - , classes = $element.attr('class') - , color - , moving - , onLabel = "ON" - , offLabel = "OFF" - , icon = false; - - $.each(['switch-mini', 'switch-small', 'switch-large'], function (i, el) { - if (classes.indexOf(el) >= 0) - myClasses = el; - }); - - $element.addClass('has-switch'); - - if ($element.data('on') !== undefined) - color = "switch-" + $element.data('on'); - - if ($element.data('on-label') !== undefined) - onLabel = $element.data('on-label'); - - if ($element.data('off-label') !== undefined) - offLabel = $element.data('off-label'); - - if ($element.data('icon') !== undefined) - icon = $element.data('icon'); - - $switchLeft = $('') - .addClass("switch-left") - .addClass(myClasses) - .addClass(color) - .html(onLabel); - - color = ''; - if ($element.data('off') !== undefined) - color = "switch-" + $element.data('off'); - - $switchRight = $('') - .addClass("switch-right") - .addClass(myClasses) - .addClass(color) - .html(offLabel); - - $label = $('