Skip to content

Commit f12bc95

Browse files
authored
Update survey-questions.py
1 parent 71022bc commit f12bc95

File tree

1 file changed

+222
-0
lines changed

1 file changed

+222
-0
lines changed
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
from dataclasses import dataclass
2+
from typing import List, Dict, Any
3+
import csv
4+
import json
5+
import statistics
6+
7+
@dataclass
8+
class SurveyCollector:
9+
survey_id: str
10+
title: str
11+
blocks: List['Block']
12+
responses: List[Dict[str, Any]] = None
13+
14+
def __init__(self, survey_id, title):
15+
self.survey_id = survey_id
16+
self.title = title
17+
self.blocks = []
18+
self.responses = []
19+
20+
def add_block(self, block):
21+
self.blocks.append(block)
22+
23+
def collect_response(self, response):
24+
self.responses.append(response)
25+
26+
def get_block(self, block_id):
27+
for block in self.blocks:
28+
if block.block_id == block_id:
29+
return block
30+
raise ValueError("Block not found")
31+
32+
def process_responses(self):
33+
results = {}
34+
for response in self.responses:
35+
for question_id, answer in response.items():
36+
if question_id not in results:
37+
results[question_id] = []
38+
results[question_id].append(answer)
39+
40+
processed_results = {}
41+
for question_id, answers in results.items():
42+
if isinstance(answers[0], list): # For multi-select questions
43+
processed_results[question_id] = {option: answers.count([option]) for option in set(answer for answer_list in answers for answer in answer_list)}
44+
elif isinstance(answers[0], str): # For single choice or open-ended
45+
processed_results[question_id] = {option: answers.count(option) for option in set(answers)}
46+
elif isinstance(answers[0], int) or isinstance(answers[0], float): # For scale or numerical questions
47+
processed_results[question_id] = {
48+
'mean': statistics.mean(answers),
49+
'median': statistics.median(answers),
50+
'mode': statistics.mode(answers) if len(set(answers)) > 1 else answers[0],
51+
'count': len(answers)
52+
}
53+
else: # For other types, just count occurrences
54+
processed_results[question_id] = {answer: answers.count(answer) for answer in set(answers)}
55+
return processed_results
56+
57+
def export_results(self, filename='results', format='csv'):
58+
results = self.process_responses()
59+
if format == 'csv':
60+
with open(f"{filename}.csv", 'w', newline='') as csvfile:
61+
writer = csv.writer(csvfile)
62+
for question_id, data in results.items():
63+
writer.writerow([question_id])
64+
if isinstance(data, dict):
65+
for option, count in data.items():
66+
writer.writerow([option, count])
67+
else:
68+
writer.writerow([data])
69+
elif format == 'json':
70+
with open(f"{filename}.json", 'w') as jsonfile:
71+
json.dump(results, jsonfile, indent=4)
72+
else:
73+
raise ValueError("Unsupported format. Please use 'csv' or 'json'.")
74+
75+
@dataclass
76+
class Block:
77+
block_id: str
78+
questions: List['Question']
79+
80+
def add_question(self, question):
81+
self.questions.append(question)
82+
83+
@dataclass
84+
class Question:
85+
question_id: str
86+
question_text: str
87+
question_type: str
88+
options: List[str] = None
89+
90+
def validate_response(self, response):
91+
if self.question_type == 'multiple_choice':
92+
if isinstance(response, list):
93+
if not all(r in self.options for r in response):
94+
raise ValueError(f"Invalid response for question: {self.question_text}. Must be one of {self.options}")
95+
elif response not in self.options:
96+
raise ValueError(f"Invalid response for question: {self.question_text}. Must be one of {self.options}")
97+
elif self.question_type == 'open_ended':
98+
if not response:
99+
raise ValueError(f"Response cannot be empty for question: {self.question_text}")
100+
elif self.question_type == 'likert_scale' or self.question_type == 'scale':
101+
if not isinstance(response, int) or response < 0 or response > 10: # Adjust range as needed
102+
raise ValueError(f"Response must be an integer between 0 and 10 for question: {self.question_text}")
103+
return True
104+
105+
# Creating blocks and questions
106+
107+
# Demographics Block
108+
demographics = Block("Demographics", [])
109+
demographics.add_question(Question("Age", "What is your age?", "open_ended"))
110+
demographics.add_question(Question("Year", "What year are you in college?", "multiple_choice",
111+
["First Year (1)", "Sophomore (2)", "Junior (3)", "Senior (4)", "5+ Years (5)"]))
112+
demographics.add_question(Question("Ethnicity", "Are you of Spanish, Hispanic, or Latino origin?", "multiple_choice",
113+
["Yes (1)", "No (2)"]))
114+
demographics.add_question(Question("Race", "Choose one or more races that you consider yourself to be", "multiple_choice",
115+
["White or Caucasian (1)", "Black or African American (2)",
116+
"American Indian/Native American or Alaska Native (3)", "Asian (4)",
117+
"Native Hawaiian or Other Pacific Islander (5)", "Other (6)",
118+
"Prefer not to say (7)"]))
119+
demographics.add_question(Question("Household_Income", "What was your total household income before taxes during the past 12 months?", "multiple_choice",
120+
["Less than $25,000 (1)", "$25,000-$49,999 (2)", "$50,000-$74,999 (3)",
121+
"$75,000-$99,999 (4)", "$100,000-$149,999 (5)", "$150,000 or more (6)",
122+
"Prefer not to say (7)"]))
123+
124+
# Political Demographics Block
125+
demographics_political = Block("Demographics Political", [])
126+
demographics_political.add_question(Question("Election", "Do you plan to vote in the upcoming 2024 election?", "multiple_choice",
127+
["Yes (1)", "No (2)"]))
128+
demographics_political.add_question(Question("Party_Affiliation", "Generally speaking, do you usually think of yourself as a Republican, a Democrat, an Independent, or something else?", "multiple_choice",
129+
["Republican (1)", "Democrat (2)", "Independent (3)", "Other (4)", "No preference (5)"]))
130+
demographics_political.add_question(Question("R_Strength", "Would you call yourself a strong Republican or a not very strong Republican?", "multiple_choice",
131+
["Strong (1)", "Not very strong (2)"]))
132+
demographics_political.add_question(Question("D_Strength", "Would you call yourself a strong Democrat or a not very strong Democrat?", "multiple_choice",
133+
["Strong (1)", "Not very strong (2)"]))
134+
demographics_political.add_question(Question("Independent_Lean", "Do you think of yourself as closer to the Republican or Democratic party?", "multiple_choice",
135+
["Republican (1)", "Democratic (2)"]))
136+
demographics_political.add_question(Question("Political_Views", "Here is a 7-point scale on which the political views that people might hold are arranged from extremely liberal (left) to extremely conservative (right). Where would you place yourself on this scale?", "scale",
137+
["0", "1", "2", "3", "4", "5", "6", "7"]))
138+
demographics_political.add_question(Question("Party_Registration", "What political party are you registered with, if any?", "multiple_choice",
139+
["Republican (1)", "Democratic (2)", "Independent (3)", "Other (4)", "None (5)"]))
140+
141+
# Personal Political Characteristics Block
142+
personal_political = Block("Personal Political Characteristics", [])
143+
personal_political.add_question(Question("P_Efficacy", "For each question below, please choose the response that best reflects how you feel.", "likert_scale",
144+
["Strongly Disagree (1)", "Somewhat disagree (2)", "Neither agree nor disagree (3)", "Somewhat agree (4)", "Strongly agree (5)"]))
145+
personal_political.add_question(Question("P_Knowledge_Intro", "Here are a few questions about the federal government. Many people don't know the answers to these questions, so if there are some you don't know, just provide your best guess.", "intro"))
146+
personal_political.add_question(Question("P_Knowledge_1", "Do you happen to know what job or political office is now held by Kamala Harris?", "open_ended"))
147+
personal_political.add_question(Question("P_Knowledge_2", "Whose responsibility is it to determine if a law is constitutional or not? Is it the president, the Congress, or the Supreme Court?", "multiple_choice",
148+
["President", "Congress", "Supreme Court"]))
149+
personal_political.add_question(Question("P_Knowledge_3", "How much of a majority is required for the U.S. Senate and House to override a presidential veto?", "open_ended"))
150+
personal_political.add_question(Question("P_Knowledge_4", "Do you happen to know which party has the most members in the House of Representatives currently?", "open_ended"))
151+
personal_political.add_question(Question("P_Knowledge_5", "Would you say that one of the parties is more conservative than the other at the national level? Which party is more conservative?", "open_ended"))
152+
153+
# Perception of News Sources Block
154+
perception_news = Block("Perception of News Sources", [])
155+
# Add all the news sources as individual questions here. For brevity, only one example is shown:
156+
perception_news.add_question(Question("ABC_News", "ABC News", "likert_scale",
157+
["1 (Definitely not)", "2 (Probably not)", "3 (Might or might not be)", "4 (Probably is)", "5 (Definitely is)"]))
158+
159+
# Social Media Block
160+
social_media = Block("Perception of Political Information on Social Media", [])
161+
social_media.add_question(Question("Social_Media", "Social media as a source of political information and news is...", "likert_scale",
162+
["Does not apply at all (1)", "2", "3", "4", "5", "6", "Fully Applies (7)"]))
163+
164+
# Media Consumption Block
165+
media_consumption = Block("Media Consumption Questions", [])
166+
media_consumption.add_question(Question("News_1", "Where do you typically get your news?", "multiple_choice",
167+
["Print newspapers (1)", "Television (2)", "News websites (3)", "Social media (4)", "Radio (5)", "Podcasts (6)"]))
168+
media_consumption.add_question(Question("News_frequency", "How often do you consume news?", "multiple_choice",
169+
["Daily", "A few times a week", "Once a week", "A few times a month", "Rarely", "Never"]))
170+
171+
# Misinformation Threat Block
172+
misinformation_threat = Block("Mis_Threat", [])
173+
misinformation_threat.add_question(Question("Disinfo_threat", "On the scale from 0 to 10, where 0 means no risk and 10 means extreme risk, how would you rate the risk of disinformation campaigns to each of the following?", "scale",
174+
["0 (No risk)", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10 (Extreme risk)"]))
175+
misinformation_threat.add_question(Question("Disinfo_Res", "In your opinion, who is primarily responsible for combating disinformation?", "multiple_choice",
176+
["Government", "Social Media Companies", "Educational Institutions", "News Outlets", "Individuals", "Other (Please specify)"]))
177+
178+
# Adding blocks to survey
179+
survey = SurveyCollector("PSY_492", "Election Experience Survey")
180+
survey.add_block(demographics)
181+
survey.add_block(demographics_political)
182+
survey.add_block(personal_political)
183+
survey.add_block(perception_news)
184+
survey.add_block(social_media)
185+
survey.add_block(media_consumption)
186+
survey.add_block(misinformation_threat)
187+
188+
# Example of collecting a response
189+
sample_response = {
190+
"Age": "23",
191+
"Year": "Junior (3)",
192+
"Ethnicity": "No (2)",
193+
"Race": ["White or Caucasian (1)", "Other (6)"],
194+
"Household_Income": "Prefer not to say (7)",
195+
"Election": "Yes (1)",
196+
"Party_Affiliation": "Democrat (2)",
197+
"R_Strength": "Not very strong (2)",
198+
"D_Strength": "Strong (1)",
199+
"Independent_Lean": "Democratic (2)",
200+
"Political_Views": 4,
201+
"Party_Registration": "Democratic (2)",
202+
"P_Efficacy": 3,
203+
"P_Knowledge_1": "Vice President",
204+
"P_Knowledge_2": "Supreme Court",
205+
"P_Knowledge_3": "Two-thirds",
206+
"P_Knowledge_4": "Democratic",
207+
"P_Knowledge_5": "Republican",
208+
"ABC_News": 4,
209+
"Social_Media": 5,
210+
"News_1": "Television (2)",
211+
"News_frequency": "A few times a week",
212+
"Disinfo_threat": 8,
213+
"Disinfo_Res": "Social Media Companies"
214+
}
215+
216+
{if survey.collect_response(sample_response):
217+
print("Response collected successfully")
218+
else:
219+
print("Failed to collect response")
220+
221+
# Process and export results
222+
survey.export_results()

0 commit comments

Comments
 (0)