def generate(seed): reseed(seed) print('Generating PNI Homepage') pni_homepage = BuyersGuidePageFactory.create( parent=Homepage.objects.first(), title='* Privacy not included', slug='privacynotincluded', header='Be Smart. Shop Safe.', intro_text= ('How creepy is that smart speaker, that fitness tracker' ', those wireless headphones? We created this guide to help you shop for safe' ', secure connected products.'), ) print('Generating visual regression test products') create_general_product_visual_regression_product(seed, pni_homepage) create_software_product_visual_regression_product(seed, pni_homepage) print('Generating 52 ProductPages') for i in range(26): # General products general_page = GeneralProductPageFactory.create(parent=pni_homepage, ) fake_privacy_policy = ProductPagePrivacyPolicyLinkFactory( page=general_page) general_page.privacy_policy_links.add(fake_privacy_policy) general_page.save_revision().publish() # Software products software_page = SoftwareProductPageFactory.create( parent=pni_homepage, ) software_page.save_revision().publish() fake_privacy_policy = ProductPagePrivacyPolicyLinkFactory( page=software_page) software_page.privacy_policy_links.add(fake_privacy_policy) software_page.save_revision().publish() print('Crosslinking related products') product_pages = ProductPage.objects.all() total_product_pages = product_pages.count() for product_page in product_pages: # Create a new orderable 3 times. # Each page will be randomly selected from an existing factory page. for i in range(3): random_number = randint(1, total_product_pages) - 1 random_page = product_pages[random_number] related_product = RelatedProducts( page=product_page, related_product=random_page, ) related_product.save() product_page.related_product_pages.add(related_product) reseed(seed) print('Generating Buyer\'s Guide product updates') generate_fake_data(ProductUpdateFactory, 15) # TODO: link updates into products """
def generate(seed): reseed(seed) print('Generating PNI Homepage') pni_homepage = BuyersGuidePageFactory.create( parent=Homepage.objects.first(), title='* Privacy not included', slug='privacynotincluded', ) print('Generating visual regression test products') create_general_product_visual_regression_product(seed, pni_homepage) create_software_product_visual_regression_product(seed, pni_homepage) print('Generating 52 ProductPages') for i in range(26): # General products general_page = GeneralProductPageFactory.create(parent=pni_homepage,) fake_privacy_policy = ProductPagePrivacyPolicyLinkFactory(page=general_page) general_page.privacy_policy_links.add(fake_privacy_policy) general_page.save_revision().publish() # Software products software_page = SoftwareProductPageFactory.create(parent=pni_homepage,) software_page.save_revision().publish() fake_privacy_policy = ProductPagePrivacyPolicyLinkFactory(page=software_page) software_page.privacy_policy_links.add(fake_privacy_policy) software_page.save_revision().publish() print('Crosslinking related products') product_pages = ProductPage.objects.all() total_product_pages = product_pages.count() for product_page in product_pages: # Create a new orderable 3 times. # Each page will be randomly selected from an existing factory page. for i in range(3): random_number = randint(1, total_product_pages) - 1 random_page = product_pages[random_number] related_product = RelatedProducts( page=product_page, related_product=random_page, ) related_product.save() product_page.related_product_pages.add(related_product) reseed(seed) print('Generating Buyer\'s Guide product updates') generate_fake_data(ProductUpdateFactory, 15) # TODO: link updates into products """
def handle(self, *args, **options): products = Product.objects.all() buyersguide_page = self.get_or_create_buyers_guide() for product in products: # 1. Create ProductPage out of this product product = product.specific # Get the specific class # Always refresh the buyersguide_page to update treebeards pathing buyersguide_page.refresh_from_db() # Check if ProductPage exists. If it does, continue on. # This check will allow us to run this script more than once if needed if ProductPage.objects.filter(slug=product.slug).exists(): self.debug_print( f"Product '{product.slug}' already exists, skipping.") continue if isinstance(product, SoftwareProduct): new_product_page = SoftwareProductPage() specific_fields = [ 'medical_privacy_compliant', 'easy_to_learn_and_use', 'handles_recordings_how', 'recording_alert', 'recording_alert_helptext', 'medical_privacy_compliant_helptext', 'host_controls', 'easy_to_learn_and_use_helptext' ] elif isinstance(product, GeneralProduct): new_product_page = GeneralProductPage() specific_fields = [ 'camera_device', 'camera_app', 'microphone_device', 'microphone_app', 'location_device', 'location_app', 'personal_data_collected', 'biometric_data_collected', 'social_data_collected', 'how_can_you_control_your_data', 'data_control_policy_is_bad', 'track_record_choices', 'company_track_record', 'track_record_is_bad', 'track_record_details', 'offline_capable', 'offline_use_description', 'uses_ai', 'ai_uses_personal_data', 'ai_is_transparent', 'ai_helptext' ] self.debug_print( f"Treating '{product.slug}' as {new_product_page.__class__.__name__}" ) # Apply the fields that are different or may cause issues if copied directly from one model to another new_product_page.slug_en = product.slug new_product_page.title = product.name new_product_page.title_en = product.name new_product_page.product_url = product.url new_product_page.cloudinary_image = product.cloudinary_image new_product_page.live = not product.draft # If product is draft, it shall not be live. # These are the common fields between SoftwareProductPages and GeneralProductPages fields = specific_fields + [ 'slug', 'privacy_ding', 'adult_content', 'uses_wifi', 'uses_bluetooth', 'review_date', 'company', 'blurb', 'price', 'worst_case', 'signup_requires_email', 'signup_requires_phone', 'signup_requires_third_party_account', 'signup_requirement_explanation', 'how_does_it_use_data_collected', 'data_collection_policy_is_bad', 'user_friendly_privacy_policy', 'show_ding_for_minimum_security_standards', 'meets_minimum_security_standards', 'uses_encryption', 'uses_encryption_helptext', 'security_updates', 'security_updates_helptext', 'strong_password', 'strong_password_helptext', 'manage_vulnerabilities', 'manage_vulnerabilities_helptext', 'privacy_policy', 'privacy_policy_helptext', 'phone_number', 'live_chat', 'email', 'twitter' ] self.debug_print("\tSetting fields:") for field in fields: # Loop through every field for this product and copy the value # from the Product model to the Page model. self.debug_print("\t\t", field, " as ", getattr(product, field)) setattr(new_product_page, field, getattr(product, field)) self.debug_print(f"Product has image? {bool(product.image)}") self.debug_print( f"Product has cloudinary image? {bool(product.cloudinary_image)}" ) # Get the image file field, and convert it into a WagtailImage object if product.image: # Check if there is an image file. If there isn't one, don't try to copy the # FieldFile to a WagtailImage object. try: image_file = product.image.file except FileNotFoundError: image_file = None if image_file: mime = MimeTypes() mime_type = mime.guess_type( product.image.file.name) # -> ('image/jpeg', None) if mime_type: mime_type = mime_type[0].split('/')[1].upper() else: # Default to a JPEG mimetype. mime_type = 'JPEG' # Create an image out of the FileField. pil_image = PILImage.open(product.image.file) f = BytesIO() pil_image.save(f, mime_type) # Store the image as a WagtailImage object new_image_name = ntpath.basename(product.image.file.name) wagtail_image = WagtailImage.objects.create( title=new_image_name, file=ImageFile(f, name=new_image_name)) # Associate new_product_page.image with wagtail_image new_product_page.image = wagtail_image # Add the new page as a child to BuyersGuidePage. This will add a # `path` to the new_product_page and place it in the Wagtail Tree # using Django Treebeard buyersguide_page.add_child(instance=new_product_page) # Save revision and/or publish so we can add Orderables to this page. new_product_page.save() new_product_page.save_revision() self.debug_print("\tCreated", new_product_page) # Loop through all the m2ms and create Orderable objects for this new page type # Add privacy policy links for privacy_link in product.privacy_policy_links.all(): new_orderable = ProductPagePrivacyPolicyLink() new_orderable.page = new_product_page new_orderable.label = privacy_link.label new_orderable.url = privacy_link.url new_orderable.save() new_product_page.privacy_policy_links.add(new_orderable) self.debug_print("\tPrivacy Orderables added") # Add product categories for category in product.product_category.all(): new_orderable = ProductPageCategory() new_orderable.product = new_product_page new_orderable.category = category new_orderable.save() new_product_page.product_categories.add(new_orderable) self.debug_print("\tCategory Orderables added") # Add updates for update in product.updates.all(): new_orderable = ProductUpdates() new_orderable.page = new_product_page new_orderable.update = update new_orderable.save() new_product_page.updates.add(new_orderable) self.debug_print("\tUpdate Orderables added") # Attach a Votes object to each page if `Page.get_or_create_votes()` exists. if hasattr(new_product_page, 'get_or_create_votes'): new_product_page.get_or_create_votes() # Use .to_dict() to pull out the old aggregated votes product_dict = product.to_dict() votes = product_dict.get('votes', None) if votes: votes = votes.get('creepiness').get('vote_breakdown') self.debug_print(votes) values = [x for (i, x) in sorted(votes.items())] product_total = sum([ x * ((i + 1) * 20 - 10) for i, x in enumerate(values) ]) self.debug_print( f'\tOriginal votes: {values} (total score: {product_total})' ) else: # Default vote "bin" values = [0, 0, 0, 0, 0] product_total = 0 new_product_page.votes.set_votes(values) new_product_page.creepiness_value = product_total new_product_page.save() self.debug_print( f'\tNew product votes: {new_product_page.get_or_create_votes()}' ) if not product.draft: new_product_page.live = True new_product_page.save_revision().publish() else: new_product_page.save_revision() # Always good to fresh from db when using Django Treebeard. buyersguide_page.refresh_from_db() time.sleep(1) # Once all the ProductPages are added, add related_products # By writing a secondary for loop we can avoid attaching a legacy_product # to each ProductPage because they'll have slugs in common. self.debug_print("\nFinal step: Adding related products\n") # Loop through every ProductPage we now have. for product_page in ProductPage.objects.all(): # Fetch the PNI Product that this page was created from. try: product = Product.objects.get(slug=product_page.slug) except Product.DoesNotExist: self.debug_print( f"Skipping {product_page} because a ProductPage.slug={product_page.slug} was not found" ) # noqa continue # Loop through all the Product.related_products for related_product in product.related_products.all(): try: # Find the related ProductPage based on the correct slug. related_page = ProductPage.objects.get( slug=related_product.slug) except ProductPage.DoesNotExist: self.debug_print("Missing product page", product_page) continue # Create a new Orderable for the Related Product. This provides # a higher quality editing experience for Wagtail editors/admins. new_related_product = RelatedProducts() new_related_product.page = product_page new_related_product.related_product = related_page new_related_product.save() product_page.related_product_pages.add(new_related_product) self.debug_print("\tAdded related product page:", related_page)