class GeneralProductPage(ProductPage): template = 'buyersguide/product_page.html' camera_device = ExtendedYesNoField( help_text='Does this device have or access a camera?', ) camera_app = ExtendedYesNoField( help_text='Does the app have or access a camera?', ) microphone_device = ExtendedYesNoField( help_text='Does this Device have or access a microphone?', ) microphone_app = ExtendedYesNoField( help_text='Does this app have or access a microphone?', ) location_device = ExtendedYesNoField( help_text='Does this product access your location?', ) location_app = ExtendedYesNoField( help_text='Does this app access your location?', ) # What data does it collect? personal_data_collected = models.TextField( max_length=5000, blank=True, help_text='What kind of personal data does this product collect?') biometric_data_collected = models.TextField( max_length=5000, blank=True, help_text='What kind of biometric data does this product collect?') social_data_collected = models.TextField( max_length=5000, blank=True, help_text='What kind of social data does this product collect?') # How can you control your data how_can_you_control_your_data = models.TextField( max_length=5000, blank=True, help_text='How does this product let you control your data?') data_control_policy_is_bad = models.BooleanField( default=False, verbose_name='Privacy ding') # Company track record company_track_record = models.CharField( choices=TRACK_RECORD_CHOICES, default='Average', help_text='This company has a ... track record', max_length=20) track_record_is_bad = models.BooleanField(default=False, verbose_name='Privacy ding') track_record_details = models.TextField( max_length=5000, blank=True, help_text='Describe the track record of this company here.') # Offline use offline_capable = ExtendedYesNoField( help_text='Can this product be used offline?', ) offline_use_description = models.TextField( max_length=5000, blank=True, help_text='Describe how this product can be used offline.') # Artificial Intelligence uses_ai = ExtendedYesNoField(help_text='Does the product use AI?') ai_uses_personal_data = ExtendedYesNoField( help_text= 'Does the AI use your personal data to make decisions about you?') ai_is_transparent = ExtendedYesNoField( help_text='Does the company allow users to see how the AI works?') ai_helptext = models.TextField( max_length=5000, blank=True, help_text='Helpful text around AI to show on the product page', ) @classmethod def map_import_fields(cls): generic_product_import_fields = super().map_import_fields() general_product_mappings = { "Has camera device": "camera_device", "Has camera app": "camera_app", "Has microphone device": "microphone_device", "Has microphone app": "microphone_app", "Has location device": "location_device", "Has location app": "location_app", "Personal data collected": "personal_data_collected", "Biometric data collected": "biometric_data_collected", "Social data collected": "social_data_collected", "How you can control your data": "how_can_you_control_your_data", "Company track record": "company_track_record", "Show company track record privacy ding": "track_record_is_bad", "Offline capable": "offline_capable", "Offline use": "offline_use_description", "Uses AI": "uses_ai", "AI uses personal data": "ai_uses_personal_data", "AI help text": "ai_helptext", "AI is transparent": "ai_is_transparent", } # Return the merged fields return {**generic_product_import_fields, **general_product_mappings} def get_export_fields(self): """ This should be a dictionary of the fields to send to Airtable. Keys are the Column Names in Airtable. Values are the Wagtail values we want to send. """ generic_product_data = super().get_export_fields() general_product_data = { "Has camera device": self.camera_device, "Has camera app": self.camera_app, "Has microphone device": self.microphone_device, "Has microphone app": self.microphone_app, "Has location device": self.location_device, "Has location app": self.location_app, "Personal data collected": self.personal_data_collected, "Biometric data collected": self.biometric_data_collected, "Social data collected": self.social_data_collected, "How you can control your data": self.how_can_you_control_your_data, "Company track record": self.company_track_record, "Show company track record privacy ding": self.track_record_is_bad, "Offline capable": self.offline_capable, "Offline use": self.offline_use_description, "Uses AI": self.uses_ai, "AI uses personal data": self.ai_uses_personal_data, "AI is transparent": self.ai_uses_personal_data, "AI help text": self.ai_helptext, } # Merge the two dicts together. data = {**generic_product_data, **general_product_data} return data # administrative panels content_panels = ProductPage.content_panels.copy() content_panels = insert_panels_after( content_panels, 'Product Categories', [ MultiFieldPanel([ FieldPanel('camera_device'), FieldPanel('camera_app'), FieldPanel('microphone_device'), FieldPanel('microphone_app'), FieldPanel('location_device'), FieldPanel('location_app'), ], heading='Can it snoop?', classname='collapsible'), ], ) content_panels = insert_panels_after( content_panels, 'What is required to sign up', [ MultiFieldPanel( [ FieldPanel('personal_data_collected'), FieldPanel('biometric_data_collected'), FieldPanel('social_data_collected'), ], heading='What data does it collect', classname='collapsible', ), ]) content_panels = insert_panels_after( content_panels, 'How does it use this data', [ MultiFieldPanel( [ FieldPanel('how_can_you_control_your_data'), FieldPanel('data_control_policy_is_bad'), ], heading='How can you control your data', classname='collapsible', ), MultiFieldPanel([ FieldPanel('company_track_record'), FieldPanel('track_record_is_bad'), FieldPanel('track_record_details'), ], heading='Company track record', classname='collapsible'), MultiFieldPanel([ FieldPanel('offline_capable'), FieldPanel('offline_use_description'), ], heading='Offline use', classname='collapsible'), ], ) content_panels = insert_panels_after( content_panels, 'Security', [ MultiFieldPanel([ FieldPanel('uses_ai'), FieldPanel('ai_uses_personal_data'), FieldPanel('ai_is_transparent'), FieldPanel('ai_helptext'), ], heading='Artificial Intelligence', classname='collapsible'), ], ) @property def product_type(self): return "general" class Meta: verbose_name = "General Product Page"
class SoftwareProductPage(ProductPage): template = 'buyersguide/product_page.html' handles_recordings_how = models.TextField( max_length=5000, blank=True, help_text='How does this software handle your recordings') recording_alert = ExtendedYesNoField( null=True, help_text='Alerts when calls are being recorded?', ) recording_alert_helptext = models.TextField(max_length=5000, blank=True) # NullBooleanField is deprecated as of Django 3.1. # We're using it here primarily for a data migration, but we should # move to BooleanField as soon as it's safe to do so with the content we have medical_privacy_compliant = models.NullBooleanField( null=True, help_text='Compliant with US medical privacy laws?') medical_privacy_compliant_helptext = models.TextField(max_length=5000, blank=True) # Can I control it? host_controls = models.TextField(max_length=5000, blank=True) # NullBooleanField is deprecated as of Django 3.1. # We're using it here primarily for a data migration, but we should # move to BooleanField as soon as it's safe to do so with the content we have easy_to_learn_and_use = models.NullBooleanField( null=True, help_text='Is it easy to learn & use the features?', ) easy_to_learn_and_use_helptext = models.TextField(max_length=5000, blank=True) @classmethod def map_import_fields(cls): generic_product_import_fields = super().map_import_fields() software_product_mappings = { "How it handles recording": "handles_recordings_how", "Recording alert": "recording_alert", "Recording alert help text": "recording_alert_helptext", "Medical privacy compliant": "medical_privacy_compliant", "Medical privacy compliant help text": "medical_privacy_compliant_helptext", "Host controls": "host_controls", "Easy to learn and use": "easy_to_learn_and_use", "Easy to learn and use help text": "easy_to_learn_and_use_helptext", } # Return the merged fields return {**generic_product_import_fields, **software_product_mappings} def get_export_fields(self): """ This should be a dictionary of the fields to send to Airtable. Keys are the Column Names in Airtable. Values are the Wagtail values we want to send. """ generic_product_data = super().get_export_fields() software_product_data = { "How it handles recording": self.handles_recordings_how, "Recording alert": self.recording_alert, "Recording alert help text": self.recording_alert_helptext, "Medical privacy compliant": True if self.medical_privacy_compliant else False, "Medical privacy compliant help text": self.medical_privacy_compliant_helptext, "Host controls": self.host_controls, "Easy to learn and use": True if self.easy_to_learn_and_use else False, "Easy to learn and use help text": self.easy_to_learn_and_use_helptext, } data = {**generic_product_data, **software_product_data} return data content_panels = ProductPage.content_panels.copy() content_panels = insert_panels_after( content_panels, 'Product Categories', [ MultiFieldPanel([ FieldPanel('handles_recordings_how'), FieldPanel('recording_alert'), FieldPanel('recording_alert_helptext'), FieldPanel('medical_privacy_compliant'), FieldPanel('medical_privacy_compliant_helptext'), ], heading='How does it handle privacy?', classname='collapsible'), MultiFieldPanel([ FieldPanel('host_controls'), FieldPanel('easy_to_learn_and_use'), FieldPanel('easy_to_learn_and_use_helptext'), ], heading='Can I control it', classname='collapsible'), ], ) @property def product_type(self): return "software" class Meta: verbose_name = "Software Product Page"
class SoftwareProduct(Product): """ A thing you can install on your computer and our review of it """ # How does it handle privacy? handles_recordings_how = models.TextField( max_length=5000, blank=True, help_text='How does this software handle your recordings') recording_alert = ExtendedYesNoField( null=True, help_text='Alerts when calls are being recorded?', ) recording_alert_helptext = models.TextField(max_length=5000, blank=True) signup_with_email = models.BooleanField( null=True, help_text='Email required to sign up?', ) signup_with_phone = models.BooleanField( null=True, help_text='Phone number required to sign up?', ) signup_with_third_party = models.BooleanField( null=True, help_text='Third Party account required to sign up?', ) signup_methods_helptext = models.TextField( max_length=5000, blank=True, help_text= 'Describe the kind of contact information requirements for signing up for this product' ) medical_privacy_compliant = models.BooleanField( null=True, help_text='Compliant with US medical privacy laws?') medical_privacy_compliant_helptext = models.TextField(max_length=5000, blank=True) # Can I control it? host_controls = models.TextField(max_length=5000, blank=True) easy_to_learn_and_use = models.BooleanField( null=True, help_text='Is it easy to learn & use the features?', ) easy_to_learn_and_use_helptext = models.TextField(max_length=5000, blank=True) # administrative panels panels = Product.panels.copy() panels = insert_panels_after( panels, 'Minimum Security Standards for general products', [ MultiFieldPanel([ FieldPanel('signup_with_email'), FieldPanel('signup_with_phone'), FieldPanel('signup_with_third_party'), FieldPanel('signup_methods_helptext'), ], heading='How does it handle signup?', classname='collapsible'), ], ) panels = insert_panels_after( panels, 'How does it handle data sharing', [ MultiFieldPanel([ FieldPanel('handles_recordings_how'), FieldPanel('recording_alert'), FieldPanel('recording_alert_helptext'), FieldPanel('medical_privacy_compliant'), FieldPanel('medical_privacy_compliant_helptext'), ], heading='How does it handle privacy?', classname='collapsible'), ], ) panels = insert_panels_after( panels, 'How does it handle privacy?', [ MultiFieldPanel([ FieldPanel('host_controls'), FieldPanel('easy_to_learn_and_use'), FieldPanel('easy_to_learn_and_use_helptext'), ], heading='Can I control it', classname='collapsible'), ], ) def to_dict(self): model_dict = super().to_dict() model_dict['product_type'] = 'software' return model_dict
class GeneralProduct(Product): """ A thing you can buy in stores and our review of it """ # Can it snoop on me camera_device = ExtendedYesNoField( help_text='Does this device have or access a camera?', ) camera_app = ExtendedYesNoField( help_text='Does the app have or access a camera?', ) microphone_device = ExtendedYesNoField( help_text='Does this Device have or access a microphone?', ) microphone_app = ExtendedYesNoField( help_text='Does this app have or access a microphone?', ) location_device = ExtendedYesNoField( help_text='Does this product access your location?', ) location_app = ExtendedYesNoField( help_text='Does this app access your location?', ) # What data does it collect? personal_data_collected = models.TextField( max_length=5000, blank=True, help_text='What kind of personal data does this product collect?') biometric_data_collected = models.TextField( max_length=5000, blank=True, help_text='What kind of biometric data does this product collect?') social_data_collected = models.TextField( max_length=5000, blank=True, help_text='What kind of social data does this product collect?') # How can you control your data how_can_you_control_your_data = models.TextField( max_length=5000, blank=True, help_text='How does this product let you control your data?') data_control_policy_is_bad = models.BooleanField( default=False, verbose_name='Privacy ding') # Company track record track_record_choices = [('Great', 'Great'), ('Average', 'Average'), ('Needs Improvement', 'Needs Improvement'), ('Bad', 'Bad')] company_track_record = models.CharField( choices=track_record_choices, default='Average', help_text='This company has a ... track record', max_length=20) track_record_is_bad = models.BooleanField(default=False, verbose_name='Privacy ding') track_record_details = models.TextField( max_length=5000, blank=True, help_text='Describe the track record of this company here.') # Offline use offline_capable = ExtendedYesNoField( help_text='Can this product be used offline?', ) offline_use_description = models.TextField( max_length=5000, blank=True, help_text='Describe how this product can be used offline.') # Artificial Intelligence uses_ai = ExtendedYesNoField(help_text='Does the product use AI?') ai_uses_personal_data = ExtendedYesNoField( help_text= 'Does the AI use your personal data to make decisions about you?') ai_is_transparent = ExtendedYesNoField( help_text='Does the company allow users to see how the AI works?') ai_helptext = models.TextField( max_length=5000, blank=True, help_text='Helpful text around AI to show on the product page', ) # administrative panels panels = Product.panels.copy() panels = insert_panels_after( panels, 'General Product Details', [ MultiFieldPanel([ FieldPanel('camera_device'), FieldPanel('camera_app'), FieldPanel('microphone_device'), FieldPanel('microphone_app'), FieldPanel('location_device'), FieldPanel('location_app'), ], heading='Can it snoop?', classname='collapsible'), ], ) panels = insert_panels_after(panels, 'What is required to sign up', [ MultiFieldPanel( [ FieldPanel('personal_data_collected'), FieldPanel('biometric_data_collected'), FieldPanel('social_data_collected'), ], heading='What data does it collect', classname='collapsible', ), ]) panels = insert_panels_after( panels, 'How does it use this data', [ MultiFieldPanel( [ FieldPanel('how_can_you_control_your_data'), FieldPanel('data_control_policy_is_bad'), ], heading='How can you control your data', classname='collapsible', ), MultiFieldPanel([ FieldPanel('company_track_record'), FieldPanel('track_record_is_bad'), FieldPanel('track_record_details'), ], heading='Company track record', classname='collapsible'), MultiFieldPanel([ FieldPanel('offline_capable'), FieldPanel('offline_use_description'), ], heading='Offline use', classname='collapsible'), ], ) panels = insert_panels_after( panels, 'Security', [ MultiFieldPanel([ FieldPanel('uses_ai'), FieldPanel('ai_uses_personal_data'), FieldPanel('ai_is_transparent'), FieldPanel('ai_helptext'), ], heading='Artificial Intelligence', classname='collapsible'), ], ) def to_dict(self): model_dict = super().to_dict() model_dict['product_type'] = 'general' return model_dict
class GeneralProduct(Product): """ A thing you can buy in stores and our review of it """ # It uses your... camera_device = ExtendedYesNoField( help_text='Does this device have or access a camera?', ) camera_app = ExtendedYesNoField( help_text='Does the app have or access a camera?', ) microphone_device = ExtendedYesNoField( help_text='Does this Device have or access a microphone?', ) microphone_app = ExtendedYesNoField( help_text='Does this app have or access a microphone?', ) location_device = ExtendedYesNoField( help_text='Does this product access your location?', ) location_app = ExtendedYesNoField( help_text='Does this app access your location?', ) # how it handles privacy delete_data = models.BooleanField( # TO BE REMOVED? null=True, help_text='Can you request data be deleted?', ) delete_data_helptext = models.TextField( # TO BE REMOVED? max_length=5000, blank=True) parental_controls = ExtendedYesNoField( null=True, help_text='Are there rules for children?', ) child_rules_helptext = models.TextField( # TO BE REMOVED? max_length=5000, blank=True) collects_biometrics = ExtendedYesNoField( help_text='Does this product collect biometric data?', ) collects_biometrics_helptext = models.TextField(max_length=5000, blank=True) # administrative panels panels = Product.panels.copy() panels = insert_panels_after( panels, 'Minimum Security Standards for general products', [ MultiFieldPanel([ FieldPanel('camera_device'), FieldPanel('camera_app'), FieldPanel('microphone_device'), FieldPanel('microphone_app'), FieldPanel('location_device'), FieldPanel('location_app'), ], heading='Can it snoop?', classname='collapsible'), ], ) panels = insert_panels_after( panels, 'How does it handle data sharing', [ MultiFieldPanel([ FieldPanel('delete_data'), FieldPanel('delete_data_helptext'), FieldPanel('parental_controls'), FieldPanel('collects_biometrics'), FieldPanel('collects_biometrics_helptext'), FieldPanel('user_friendly_privacy_policy'), FieldPanel('user_friendly_privacy_policy_helptext'), ], heading='How does it handle privacy', classname='collapsible'), ], ) def to_dict(self): model_dict = super().to_dict() model_dict['delete_data'] = tri_to_quad(self.delete_data) model_dict['product_type'] = 'general' return model_dict
class SoftwareProduct(Product): """ A thing you can install on your computer and our review of it """ # How does it handle privacy? handles_recordings_how = models.TextField( max_length=5000, blank=True, help_text='How does this software handle your recordings') recording_alert = ExtendedYesNoField( null=True, help_text='Alerts when calls are being recorded?', ) recording_alert_helptext = models.TextField(max_length=5000, blank=True) medical_privacy_compliant = models.BooleanField( null=True, help_text='Compliant with US medical privacy laws?') medical_privacy_compliant_helptext = models.TextField(max_length=5000, blank=True) # Can I control it? host_controls = models.TextField(max_length=5000, blank=True) easy_to_learn_and_use = models.BooleanField( null=True, help_text='Is it easy to learn & use the features?', ) easy_to_learn_and_use_helptext = models.TextField(max_length=5000, blank=True) # administrative panels panels = Product.panels.copy() panels = insert_panels_after( panels, 'General Product Details', [ MultiFieldPanel([ FieldPanel('handles_recordings_how'), FieldPanel('recording_alert'), FieldPanel('recording_alert_helptext'), FieldPanel('medical_privacy_compliant'), FieldPanel('medical_privacy_compliant_helptext'), ], heading='How does it handle privacy?', classname='collapsible'), MultiFieldPanel([ FieldPanel('host_controls'), FieldPanel('easy_to_learn_and_use'), FieldPanel('easy_to_learn_and_use_helptext'), ], heading='Can I control it', classname='collapsible'), ], ) def to_dict(self): model_dict = super().to_dict() model_dict['product_type'] = 'software' return model_dict