def copy_survey(self, survey_id_tobecopied, newsurvey_name="copied survey"): method = "copy_survey" params = OrderedDict([ ("sSessionKey", self.api.session_key), ("iSurveyID_org", survey_id_tobecopied), ("sNewname", newsurvey_name), ]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Copy failed", "Invalid survey ID", "No token table", "No permission" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: raise LimeSurveyError(method, "Error") # response = {'status': 'OK', 'newsid': 326666} return response
def query(self, method, params): """ Query the LimeSurvey API Important! Despite being provided as key-value, the API treats all parameters as positional. OrderedDict should be used to ensure this, otherwise some calls may randomly fail. Parameters :param method: Name of API method to call. :type method: String :param params: Parameters to the specified API call. :type params: OrderedDict Return :return: result of API call :raise: requests.ConnectionError :raise: LimeSurveyError if the API returns an error (either http error or error message in body) """ if not self.session_key and not method == "get_session_key": raise LimeSurveyError(method, "No session open", params) # 1. Prepare the request data data = OrderedDict([ ("method", method), ("params", params), ("id", 1) # Possibly a request id for parallel use cases. ]) data_json = json.dumps(data) # 2. Query the API response = requests.post(self.url, headers=self.headers, data=data_json) if not response.ok: raise LimeSurveyError(method, "Not response.ok", response.status_code, response.content) if not 0 < len(response.content): raise LimeSurveyError(method, "Not 0 < len(response.content)", response.status_code, response.content) response_data = response.json() try: return_value = response_data.get("result") except KeyError: raise LimeSurveyError(method, "Key 'result' not in response json", response.status_code, response.content) return return_value
def set_participant_properties( self, survey_id, token_id, token_query_properties=None, token_data={}): """ Set participant properties (by token) from the specified survey. Provide either token_id or token_query_dict, not both. For token properties for querying and return, choose from: ["blacklisted", "completed", "email", "emailstatus", "firstname", "language", "lastname", "mpid", "participant_id", "remindercount", "remindersent", "sent", "tid", "token", "usesleft", "validfrom", "validuntil"] Parameters :param survey_id: ID of survey to get participant properties for. :type survey_id: Integer :param token_id: ID of participant to get properties for. :type token_id: Integer :param token_query_properties: Key(s) / value(s) to use for finding the participant among all those that are in the survey. :type token_query_properties: Dict[String, Any] :param token_properties: Keys to return from RPC call. :type token_properties: List[String] """ method = "set_participant_properties" if token_id is not None and token_query_properties is not None: raise ValueError( "Provide either token_id or token_query_dict, not both.") if token_query_properties is None: token_query_properties = {"tid": token_id} params = OrderedDict([ ("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("aTokenQueryProperties", token_query_properties), ("aTokenData", token_data) ]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: Invalid survey ID", "Error: No token table", "Error: No results were found based on your attributes.", "Error: More than 1 result was found based on your attributes.", "Error: Invalid tokenid", "No valid Data", "No permission", "Invalid Session Key" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is dict return response
def export_responses_by_token(self, survey_id, document_type="json", token=""): """ Add participants to the specified survey. Parameters :param survey_id: ID of survey to delete participants from. :type survey_id: Integer :param participant_data: List of participant detail dictionaries. :type participant_data: List[Dict] :param create_token_key: If True, generate the new token instead of using a provided value. :type create_token_key: Bool """ method = "export_responses_by_token" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("sDocumentType", document_type), ("sToken", token)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: Invalid survey ID", "No token table", "No permission" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) return response
def get_survey_properties(self, survey_id, survey_properties=[]): """ Get properties of a question in a survey. Parameters :param survey_id: ID of the survey :type survey_id: Integer :param survey_properties: (optional) properties to get, default to all. :type survey_properties: List """ method = "get_survey_properties" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("aSurveySettings", survey_properties)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "No permission", "Error: Invalid survey ID", "Invalid session key", "No valid Data" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is dict return response
def list_groups(self, survey_id): """ Return the ids and all attributes of groups belonging to survey. Parameters :param survey_id: ID of the survey containing the groups. :rtype survey_id: Integer """ method = "list_groups" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", survey_id)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: Invalid survey ID", "No groups found", "No permission" "Invalid S ession key" # typo in remotecontrol_handle.php ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is list return response
def list_questions(self, survey_id, group_id=None, language=None): """ Return a list of questions from the specified survey. Parameters :param survey_id: ID of survey to list questions from. :type survey_id: Integer :param group_id: ID of the question group to filter on. :type group_id: Integer :param language: Language of survey to return for. :type language: String """ method = "list_questions" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("iGroupID", group_id), ("sLanguage", language)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: Invalid survey ID", "Error: Invalid language", "Error: IMissmatch in surveyid and groupid", "No questions found", "No permission", "Invalid session key" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is list return response
def add_group(self, survey_id, group_title, group_description=""): """ Add an empty group with minimum details to a chosen survey. Parameters :param survey_id: ID of the survey to add the group. :type survey_id: Integer :param group_title: Name of the group. :type group_title: String :param group_description: Optional description of the group. :type group_description: String """ method = "add_group" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("sGroupTitle", group_title), ("sGroupDescription", group_description)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: Invalid survey ID", "No permission", "Invalid S ession key" # typo in remotecontrol_handle.php ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is int return response
def activate_survey(self, survey_id): """ Activate an existing survey. Parameters :param survey_id: Id of the Survey to be activated. :type survey_id: Integer """ method = "activate_survey" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", survey_id)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: Invalid survey ID", "Error: ...", # TODO: what could be output of ActivateResults? "No permission", "Invalid session key" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is list return response
def activate_tokens(self, survey_id, attribute_fields=[]): """ Parameters :param survey_id: ID of the Survey where a participants table will be created for. :type survey_id: Integer :param attribute_fields: An array of integer describing any additional attribute fiields. :type attribute_fields: Array """ method = "activate_tokens" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyId", survey_id), ("aAttributeFields", attribute_fields)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: Invalid survey ID", "Survey participants table could not be created", "No permission", "Invalid session key" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is list return response
def get_question_properties(self, question_id, question_setting='all', language_code=None): """ Get properties of a question in a survey. Parameters :param question_id: ID of the question to get properties. :type question_id: Integer :param question_setting: (optional) properties to get, default to all. :type question_setting: Array :param language_code: (optional) parameter language for multilingual questions, default are \Survey->language. :type language_code: String """ method = "get_question_properties" params = OrderedDict([("sSessionKey", self.api.session_key), ("iQuestionID", question_id), ("aQuestionSettings", question_setting), ("sLanguage", language_code)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = ["No permission", "Invalid session key"] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is list return response
def delete_participants(self, survey_id, token_ids): """ Delete participants (by token) from the specified survey. Parameters :param survey_id: ID of survey to delete participants from. :type survey_id: Integer :param token_ids: List of token IDs for participants to delete. :type token_ids: List[Integer] """ method = "delete_participants" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("aTokenIDs", token_ids)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: Invalid survey ID", "Error: No token table", "No permission", "Invalid Session Key" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is dict return response
def list_surveys(self, username=None): """ List surveys accessible to the specified username. Parameters :param username: LimeSurvey username to list accessible surveys for. :type username: String """ method = "list_surveys" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", username or self.api.username)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Invalid user", "No surveys found", "Invalid session key" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is list return response
def set_survey_properties(self, survey_id, survey_data={}): """ Set properties of an existing survey Parameters :param survey_id: ID of the survey :param survey_data: (optional) properties to set @see `get_survey_properties` :type survey_data: Dict """ method = "set_survey_properties" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("aSurveyData", survey_data)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "No permission", "Error: Invalid survey ID", "Invalid session key", "No valid Data" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is dict return response
def activate_survey(self, survey_id_tobeactivated): method = "activate_survey" params = OrderedDict([ ("sSessionKey", self.api.session_key), ("iSurveyID", survey_id_tobeactivated), ]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: Invalid survey ID", ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: raise LimeSurveyError(method, "Error") return True
def list_participants( self, survey_id, start=0, limit=1000, ignore_token_used=False, attributes=False, conditions=None): """ List participants in a survey. Parameters :param survey_id: ID of survey to invite participants from. :type survey_id: Integer :param start: Index of first token to retrieve. :type start: Integer :param limit: Number of tokens to retrieve. :type limit: Integer :param ignore_token_used: If True, tokens that have been used are not returned. :type ignore_token_used: Integer :param attributes: The extended attributes to include in the response. :type attributes: List[String] :param conditions: Key(s) / value(s) to use for finding the participant among all those that are in the survey. :type conditions: List[Dict] """ method = "list_participants" conditions = conditions or [] params = OrderedDict([ ("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("iStart", start), ("iLimit", limit), ("bUnused", ignore_token_used), ("aAttributes", attributes), ("aConditions", conditions) ]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: Invalid survey ID", "Error: No token table", "No survey participants found.", "Invalid session key", "No permission", "Invalid Session Key" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is list return response
def import_survey(self, path_to_import_survey, new_name=None, dest_survey_id=None): """ Import a survey. Allowed formats: lss, csv, txt or lsa Parameters :param path_to_import_survey: Path to survey as file to copy. :type path_to_import_survey: String :param new_name: (optional) The optional new name of the survey Important! Seems only to work if lss file is given! :type new_name: String :param dest_survey_id: (optional) This is the new ID of the survey - if already used a random one will be taken instead :type dest_survey_id: Integer """ import_datatype = splitext(path_to_import_survey)[1][1:] # TODO: Naming seems only to work with lss files - why? if import_datatype != 'lss' and new_name: warnings.warn("New naming seems only to work with lss files", RuntimeWarning) # encode import data with open(path_to_import_survey, 'rb') as f: # import data must be a base 64 encoded string import_data = b64encode(f.read()) # decoding needed because json.dumps() in method get of # class LimeSurvey can not encode bytes import_data = import_data.decode('ascii') method = "import_survey" params = OrderedDict([("sSessionKey", self.api.session_key), ("sImportData", import_data), ("sImportDataType", import_datatype), ("sNewSurveyName", new_name), ("DestSurveyID", dest_survey_id)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: ...", # TODO: Unclear what might be returned here "Invalid extension", "No permission", "Invalid session key" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is int # the new survey id return response
def close(self): """ Close an open session in LimeSurvey. """ method = "release_session_key" params = OrderedDict([("sSessionKey", self.session_key)]) response = self.query(method=method, params=params) if response == "OK": self.session_key = None else: raise LimeSurveyError(method, "Did not receive 'OK' response") return response
def import_question(self, survey_id, group_id, import_data, import_data_type, mandatory_option, question_title, question_text, question_help_text): """ Import a question from lsq file. Parameters :param survey_id: The ID of the Survey that the question will belong to. :type survey_id: Integer :param group_id: The ID of the Group that the question will belong to. :type group_id: Integer :param import_data: String containing the BASE 64 encoded data of a lsq. :type import_data: String :param import_data_type: lsq. :type import_data_type: String :param mandatory_option: Mandatory question option (default to No). :type mandatory_option: String :param question_title: new title for the question. :type question_title: String :param question_text: new question text. :type question_text: String :param question_help_text: new question help text. :type question_help_text: String """ method = "import_question" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("iGroupID", group_id), ("sImportData", import_data), ("sImportDataType", import_data_type), ("sMandatory", mandatory_option), ("sNewQuestionTitle", question_title), ("sNewqQuestion", question_text), ("sNewQuestionHelp", question_help_text)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: Invalid survey ID", "Error: IMissmatch in surveyid and groupid", "No permission", "Invalid session key" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is int return response
def get_summary(self, survey_id, stat_name="all"): """ Get participant properties of a survey. For stat name for return, choose from: ["token_count", "token_invalid", "token_sent", "token_opted_out", "token_completed", "completed_responses", "incomplete_responses", "full_responses"] Parameters :param survey_id: ID of survey :type survey_id: Integer :param stat_name: Key to return from RPC call, or "all" for everything. :type stat_name: String :return: dict with keys "token_count", "token_invalid", "token_sent", "token_opted_out", and "token_completed" with strings as values. """ method = "get_summary" params = OrderedDict([ ("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("sStatName", stat_name) ]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Invalid surveyid", "Invalid summary key", "No available data", "No permission", "Invalid session key" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is dict return response
def remind_participants(self, survey_id, min_days_between=None, max_reminders=None, token_ids=False): """ Send a reminder to participants in a survey. Returns result of sending. Parameters :param survey_id: ID of the Survey that participants belong. :type survey_id: Integer :param min_days_between: (optional) Days from last reminder. :type min_days_between: Integer :param max_reminders: (optional) Maximum reminders count. :type max_reminders: Integer :param token_ids: (optional filter) IDs of the participant to remind. :type token_ids: array """ method = "remind_participants" params = OrderedDict([ ("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("iMinDaysBetween", min_days_between), ("iMaxReminders", max_reminders), ("aTokenIds", token_ids) ]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: No survey participants table", "Error: No candidate tokens", "Error: Invalid survey ID", "No permission", "Invalid Session Key" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is list return response
def export_responses(self, survey_id, doc_type='csv', completion_status='all', response_type='short', lang_code='de'): """ Get all responses :param survey_id: ID of the survey :param doc_type: pdf, csv, xls, doc, json :param completion_status: complete, incomplete, all :param response_type: short or long :param lang_code: The language to be used :return: On success: Requested file as base 64-encoded string. On failure array with error information """ method = "export_responses" params = OrderedDict([ ("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("sDocumentType", doc_type), ("sLanguageCode", lang_code), ("sCompletionStatus", completion_status), ("sHeadingType", "full"), ("sResponseType", response_type), # ("aFields", ) ]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Invalid user", "No surveys found", "Invalid session key", "No Data, survey table does not exist." ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: return response
def delete_survey(self, survey_id): """ Delete a survey. Parameters :param survey_id: The ID of the Survey to be deleted. :type: Integer """ method = "delete_survey" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", survey_id)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = ["No permission", "Invalid session key"] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is list return response
def remind_participants(api, survey_id, token_ids, min_days_between, max_reminders): """ Send invitation emails for the specified survey participants. Parameters :param survey_id: ID of survey to invite participants from. :type survey_id: Integer :param token_ids: List of token IDs for participants to invite. :type token_ids: List[Integer] :param uninvited_only: If True, only send emails for participants that have not been invited. If False, send an invite even if already sent. :type uninvited_only: Bool """ method = "remind_participants" params = OrderedDict([ ("sSessionKey", api.session_key), ("iSurveyID", survey_id), ("iMinDaysBetween", min_days_between), ("iMaxReminders", max_reminders), ("aTokenIDs", token_ids) ]) response = api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Invalid session key", "Error: Invalid survey ID", "Error: No token table", "Error: No candidate tokens", "No permission", ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is dict return response
def add_survey(self, desired_survey_id, survey_title, default_survey_language, appearance_format): """ Add an empty survey with minimum details. Parameters :param desired_survey_id: The desired ID of the Survey to add. :type desired_survey_id: Integer :param survey_title: Title of the new Survey. :type survey_title: String :param default_survey_language: Default language of the Survey. :type default_survey_language: String :param appearance_format: (optional) Question appearance format (A, G or S) for "All on one page", "Group by Group", "Single questions", default to group by group (G). :type appearance_format: String """ method = "add_survey" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", desired_survey_id), ("sSurveyTitle ", survey_title), ("sSurveyLanguage ", default_survey_language), ("sformat ", appearance_format)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "No permission", "Invalid session key", "Faulty parameters", "Creation Failed result" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is int return response
def open(self, password): """ Open a session in LimeSurvey. Parameters :param password: LimeSurvey password to authenticate with. :type password: String """ method = "get_session_key" params = OrderedDict([("username", self.username), ("password", password)]) response = self.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = ["Invalid user name or password"] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is str self.session_key = response
def add_participants( self, survey_id, participant_data, create_token_key=True): """ Add participants to the specified survey. Parameters :param survey_id: ID of survey to add participants. :type survey_id: Integer :param participant_data: List of participant detail dictionaries. :type participant_data: List[Dict] :param create_token_key: If True, generate the new token instead of using a provided value. :type create_token_key: Bool """ method = "add_participants" params = OrderedDict([ ("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("aParticipantData", participant_data), ("bCreateToken", create_token_key) ]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Error: Invalid survey ID", "No token table", "No permission" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is list return response
def export_responses(self, survey_id, document_type, language_code=None, completion_status='all', heading_type='code', response_type='short', from_response_id=None, to_response_id=None, fields=None): """ Export responses in base64 encoded string. Parameters :param survey_id: Id of the Survey. :type survey_id: Integer :param document_type: Any format available by plugins (e.g. pdf, csv, xls, doc, json) :type document_type: String :param language_code: (optional) The language to be used. :type language_code: String :param completion_status: (optional) 'complete', 'incomplete' or 'all' :type completion_status: String :param heading_type: (optional) 'code', 'full' or 'abbreviated' :type heading_type: String :param response_type: (optional) 'short' or 'long' :type response_type: String :param from_response_id: (optional) :type from_response_id: Integer :param to_response_id: (optional) :type to_response_id: Integer :param fields: (optional) Selected fields. :type fields: Array """ method = "export_responses" params = OrderedDict([("sSessionKey", self.api.session_key), ("iSurveyID", survey_id), ("sDocumentType", document_type), ("sLanguageCode", language_code), ("sCompletionStatus", completion_status), ("sHeadingType", heading_type), ("sResponseType", response_type), ("iFromResponseID", from_response_id), ("iToResponseID", to_response_id), ("aFields", fields)]) response = self.api.query(method=method, params=params) response_type = type(response) if response_type is dict and "status" in response: status = response["status"] error_messages = [ "Language code not found for this survey.", "No Data, could not get max id.", "No Data, survey table does not exist", "No permission", "Invalid session key" ] for message in error_messages: if status == message: raise LimeSurveyError(method, status) else: assert response_type is str return response