Example #1
0
    def update_community(self, community_id,
                         args) -> (dict, MassEnergizeAPIError):
        try:
            logo = args.pop('logo', None)
            community = Community.objects.filter(id=community_id)
            if not community:
                return None, InvalidResourceError()

            if not args.get('is_geographically_focused', False):
                args['location'] = None

            community.update(**args)

            new_community = community.first()
            # if logo and new_community.logo:   # can't update the logo if the community doesn't have one already?
            #  # new_community.logo.file = logo
            #  # new_community.logo.save()
            if logo:
                cLogo = Media(file=logo,
                              name=f"{args.get('name', '')} CommunityLogo")
                cLogo.save()
                new_community.logo = cLogo
                new_community.save()

            return new_community, None
        except Exception as e:
            return None, CustomMassenergizeError(e)
Example #2
0
  def update_vendor(self, context: Context, args) -> Tuple[dict, MassEnergizeAPIError]:
    
    try:
      vendor_id = args.pop('vendor_id', None)
      communities = args.pop('communities', [])
      onboarding_contact_email = args.pop('onboarding_contact_email', None)
      website = args.pop('website', None)
      key_contact_name = args.pop('key_contact_name', None)
      key_contact_email = args.pop('key_contact_email', None)
      key_contact = {
        "name": key_contact_name,
        "email": key_contact_email
      }
      image = args.pop('image', None)
      tags = args.pop('tags', [])
      have_address = args.pop('have_address', False)
      if not have_address:
        args['location'] = None

      vendor = Vendor.objects.filter(id=vendor_id)   
      if not vendor:
        return None, InvalidResourceError()  
      vendor.update(**args)
      vendor = vendor.first()
      
      if communities:
        vendor.communities.set(communities)

      if onboarding_contact_email:
        vendor.onboarding_contact_email = onboarding_contact_email
        
      if key_contact:
        if vendor.key_contact:
          vendor.key_contact.update(key_contact)
        else:
          vendor.key_contact = key_contact

      if image:
        logo = Media(name=f"Logo-{slugify(vendor.name)}", file=image)
        logo.save()
        vendor.logo = logo
      
      if onboarding_contact_email:
        onboarding_contact = UserProfile.objects.filter(email=onboarding_contact_email).first()
        if onboarding_contact:
          vendor.onboarding_contact = onboarding_contact
    
      if tags:
        vendor.tags.set(tags)

      if website:
        vendor.more_info = {'website': website}
        
      vendor.save()
      return vendor, None

    except Exception as e:
      capture_message(str(e), level="error")
      return None, CustomMassenergizeError(e)
Example #3
0
    def update_vendor(self, context: Context,
                      args) -> (dict, MassEnergizeAPIError):
        try:
            vendor_id = args.pop('vendor_id', None)
            vendor = Vendor.objects.get(id=vendor_id)
            if not vendor:
                return None, InvalidResourceError()

            print(vendor)

            have_address = args.pop('have_address', False)
            if not have_address:
                args['location'] = None

            communities = args.pop('communities', [])
            if communities:
                vendor.communities.set(communities)

            onboarding_contact_email = args.pop('onboarding_contact_email',
                                                None)
            if onboarding_contact_email:
                vendor.onboarding_contact_email = onboarding_contact_email

            key_contact = args.pop('key_contact', {})
            if key_contact:
                if vendor.key_contact:
                    vendor.key_contact.update(key_contact)
                else:
                    vendor.key_contact = args.pop('key_contact', key_contact)

            image = args.pop('image', None)
            if image:
                logo = Media(name=f"Logo-{slugify(vendor.name)}", file=image)
                logo.save()
                vendor.logo = logo

            if onboarding_contact_email:
                onboarding_contact = UserProfile.objects.filter(
                    email=onboarding_contact_email).first()
                if onboarding_contact:
                    vendor.onboarding_contact = onboarding_contact

            tags = args.pop('tags', [])
            if tags:
                vendor.tags.set(tags)

            vendor.save()

            updated = Vendor.objects.filter(id=vendor_id).update(**args)
            return vendor, None

        except Exception as e:
            print(e)
            return None, CustomMassenergizeError(e)
Example #4
0
async def upload_file(background_tasks: BackgroundTasks, response: Response, file: UploadFile = File(...), folder: Optional[str] = Form(''), childOf: Optional[str] = Form(''), identity: str = Depends(get_jwt_identity)):
	try:
		if file.filename == '' or (len(folder) > 0 and folder[0] == '/'):
			raise SchemaValidationError
		User.objects.get(id=identity) # make sure the user exists
		if allowed_file(file.filename):
			# Handle filename collisions
			filename = file.filename
			counter = 2
			while True:
				try:
					Media.objects.get(filename=filename, folder=folder)
					newFilename = filename
					filenameSplit = newFilename.rsplit('.', 1)
					filename = filenameSplit[0] + '_' + str(counter) + '.' + filenameSplit[1]
					counter += 1
				except DoesNotExist:
					break
			mimetype = file.content_type
			if not mimetype:
				mimetype = mimetypes.guess_type(filename)
			
			if FileSettings.ENABLE_FFMPEG and FileSettings.ENABLE_FILE_PROCESSING and (mimetype[:6] == 'video/' or mimetype[:6] == 'audio/' or mimetype == 'application/x-subrip' or mimetype == 'application/ttml+xml'):
				# Process the file
				splitFilename = filename.rsplit('.', 1)
				media = Media(owner=identity, filename=splitFilename[0], folder=folder, container=True, processing=True)
				media.save()
				background_tasks.add_task(processMedia, media, file, splitFilename[1])
				#processMedia(media, file)
				response.status_code = 202
			else:
				media = Media(owner=identity, filename=filename, folder=folder)
				media.file.put(file.file, content_type=mimetype)
				media.save()

				for parent in childOf.split(','):
					try:
						if parent:
							Media.objects.get(id=parent).update(push__associatedMedia=media)
					except DoesNotExist:
						pass
			return media.serialize()
		raise SchemaValidationError
	except SchemaValidationError as e:
		raise SchemaValidationError().http_exception
	except DoesNotExist:
		raise UnauthorizedError().http_exception
	except MediaProcessingError:
		raise MediaProcessingError().http_exception
	except Exception as e:
		raise e
Example #5
0
    def create_vendor(self, ctx: Context,
                      args) -> (Vendor, MassEnergizeAPIError):
        try:
            tags = args.pop('tags', [])
            communities = args.pop('communities', [])
            image = args.pop('image', None)
            onboarding_contact_email = args.pop('onboarding_contact_email',
                                                None)
            key_contact_full_name = args.pop('key_contact_full_name', None)
            key_contact_email = args.pop('key_contact_email', None)
            website = args.pop('website', None)
            args["key_contact"] = {
                "name": key_contact_full_name,
                "email": key_contact_email
            }

            have_address = args.pop('have_address', False)
            if not have_address:
                args['location'] = None

            new_vendor = Vendor.objects.create(**args)
            if image:
                logo = Media(name=f"Logo-{slugify(new_vendor.name)}",
                             file=image)
                logo.save()
                new_vendor.logo = logo

            if onboarding_contact_email:
                onboarding_contact = UserProfile.objects.filter(
                    email=onboarding_contact_email).first()
                if onboarding_contact:
                    new_vendor.onboarding_contact = onboarding_contact

            if website:
                new_vendor.more_info = {'website': website}

            new_vendor.save()

            if communities:
                new_vendor.communities.set(communities)

            if tags:
                new_vendor.tags.set(tags)

            new_vendor.save()

            return new_vendor, None
        except Exception as e:
            capture_message(str(e), level="error")
            return None, CustomMassenergizeError(e)
Example #6
0
def add_media(request, cid):
    capsule = Capsule.objects.filter(cid=cid, deleted=False).get()
    authorized = check_authorized(Capsule, cid, request.user.username, 'add')
    if isinstance(authorized, JsonResponse):
        return authorized
    owner = request.user
    media = Media(owner=owner, capsule=capsule)
    media.save()
    media.file = request.FILES['file']
    media.save()
    return JsonResponse({
        "status": "resource created",
        "mid": media.mid
    },
                        status=200)
Example #7
0
    def update_user(self, context: Context,
                    args) -> Tuple[dict, MassEnergizeAPIError]:
        try:
            user_id = args.get('id', None)
            email = args.get('email', None)

            if not self._has_access(context, user_id, email):
                return None, CustomMassenergizeError("permission_denied")

            if context.user_is_logged_in and ((context.user_id == user_id) or
                                              (context.user_is_admin())):
                users = UserProfile.objects.filter(id=user_id)
                if not users:
                    return None, InvalidResourceError()

                profile_picture = args.pop("profile_picture", None)
                users.update(**args)
                user = users.first()

                if profile_picture:
                    if profile_picture == "reset":
                        user.profile_picture = None
                        user.save()
                    else:
                        pic = Media()
                        pic.name = f'{user.full_name} profpic'
                        pic.file = profile_picture
                        pic.media_type = 'image'
                        pic.save()

                        user.profile_picture = pic
                        user.save()

                return user, None
            else:
                return None, CustomMassenergizeError('permission_denied')

        except Exception as e:
            capture_message(str(e), level="error")
            return None, CustomMassenergizeError(e)
Example #8
0
    def create_community(self, context: Context,
                         args) -> (dict, MassEnergizeAPIError):
        try:
            logo = args.pop('logo', None)
            new_community = Community.objects.create(**args)

            have_address = args.get('is_geographically_focused', False)
            if not have_address:
                args['location'] = None

            if logo:
                cLogo = Media(file=logo,
                              name=f"{args.get('name', '')} CommunityLogo")
                cLogo.save()
                new_community.logo = cLogo

            #create a goal for this community
            community_goal = Goal.objects.create(
                name=f"{new_community.name}-Goal")
            new_community.goal = community_goal
            new_community.save()

            #now create all the pages
            aboutUsPage = AboutUsPageSettings.objects.filter(
                is_template=True).first()
            if aboutUsPage:
                aboutUsPage.pk = None
                aboutUsPage.title = f"About {new_community.name}"
                aboutUsPage.community = new_community
                aboutUsPage.is_template = False
                aboutUsPage.save()

            actionsPage = ActionsPageSettings.objects.filter(
                is_template=True).first()
            if actionsPage:
                actionsPage.pk = None
                actionsPage.title = f"Actions for {new_community.name}"
                actionsPage.community = new_community
                actionsPage.is_template = False
                actionsPage.save()

            contactUsPage = ContactUsPageSettings.objects.filter(
                is_template=True).first()
            if contactUsPage:
                contactUsPage.pk = None
                contactUsPage.title = f"Contact Us - {new_community.name}"
                contactUsPage.community = new_community
                contactUsPage.is_template = False
                contactUsPage.save()

            donatePage = DonatePageSettings.objects.filter(
                is_template=True).first()
            if donatePage:
                donatePage.pk = None
                donatePage.title = f"Take Actions - {new_community.name}"
                donatePage.community = new_community
                donatePage.is_template = False
                donatePage.save()

            homePage = HomePageSettings.objects.filter(
                is_template=True).first()
            images = homePage.images.all()
            #TODO: make a copy of the images instead, then in the home page, you wont have to create new files everytime
            if homePage:
                homePage.pk = None
                homePage.title = f"Welcome to Massenergize, {new_community.name}!"
                homePage.community = new_community
                homePage.is_template = False
                homePage.save()
                homePage.images.set(images)

            impactPage = ImpactPageSettings.objects.filter(
                is_template=True).first()
            if impactPage:
                impactPage.pk = None
                impactPage.title = f"See our Impact - {new_community.name}"
                impactPage.community = new_community
                impactPage.is_template = False
                impactPage.save()

            comm_admin: CommunityAdminGroup = CommunityAdminGroup.objects.create(
                name=f"{new_community.name}-Admin-Group",
                community=new_community)
            comm_admin.save()

            if context.user_id:
                user = UserProfile.objects.filter(pk=context.user_id).first()
                if user:
                    comm_admin.members.add(user)
                    comm_admin.save()

            owner_email = args.get('owner_email', None)
            if owner_email:
                owner = UserProfile.objects.filter(email=owner_email).first()
                if owner:
                    comm_admin.members.add(owner)
                    comm_admin.save()

            #also clone all global actions for this community
            global_actions = Action.objects.filter(is_global=True)
            for action_to_copy in global_actions:
                old_tags = action_to_copy.tags.all()
                old_vendors = action_to_copy.vendors.all()
                new_action = action_to_copy
                new_action.pk = None
                new_action.community = None
                new_action.is_published = False
                new_action.title = action_to_copy.title + f' Copy {random.randint(1,10000)}'
                new_action.save()
                new_action.tags.set(old_tags)
                new_action.vendors.set(old_vendors)

            return new_community, None
        except Exception as e:
            return None, CustomMassenergizeError(e)
Example #9
0
  def update_home_page_setting(self, args) -> (dict, MassEnergizeAPIError):
    try:
      home_page_id = args.get('id', None)
      home_page_setting = HomePageSettings.objects.filter(id=home_page_id).first()
      if not home_page_setting:
        return None, InvalidResourceError()

      #events
      if (args.get('show_featured_events', None)):
        featured_events = args.pop('featured_events', [])
        home_page_setting.featured_events.set(featured_events)
      else:
        args.pop('featured_events', [])


      #stats
      goal_updates = args.pop('goal', None)
      if (args.get('show_featured_stats', None)):
        if goal_updates and home_page_setting and home_page_setting.community and home_page_setting.community.goal:
          community_goal = home_page_setting.community.goal

          attained_number_of_actions = goal_updates.get('attained_number_of_actions', None)
          if attained_number_of_actions != None:
            community_goal.attained_number_of_actions = attained_number_of_actions
          
          target_number_of_actions = goal_updates.get('target_number_of_actions', None)
          if target_number_of_actions != None:
            community_goal.target_number_of_actions = target_number_of_actions
          

          attained_number_of_households= goal_updates.get('attained_number_of_households', None)
          if attained_number_of_households != None:
            community_goal.attained_number_of_households = attained_number_of_households
          
          target_number_of_households = goal_updates.get('target_number_of_households', None)
          if target_number_of_actions != None:
            community_goal.target_number_of_households = target_number_of_households


          attained_carbon_footprint_reduction = goal_updates.get('attained_carbon_footprint_reduction', None)
          if attained_carbon_footprint_reduction != None:
            community_goal.attained_carbon_footprint_reduction = attained_carbon_footprint_reduction
          
          target_carbon_footprint_reduction = goal_updates.get('target_carbon_footprint_reduction', None)
          if target_carbon_footprint_reduction != None:
            community_goal.target_carbon_footprint_reduction = target_carbon_footprint_reduction


          community_goal.save()
      

      #featured links
      if (not args.get('show_featured_links', False)):
        featured_links = args.pop('featured_links', None)

      #images
      current_images = home_page_setting.images.all()

      image_1 = args.pop('image_1', None)
      image_2 = args.pop('image_2', None)
      image_3 = args.pop('image_3', None)
      
      current_image_1 = current_images[0]
      current_image_2 = current_images[1]
      current_image_3 = current_images[2]


      if image_1:
        current_image_1 = Media(file=image_1, name=f"FeaturedImage1-{home_page_setting.community.name}", order=1)
        current_image_1.save()

      if image_2:
        current_image_2 = Media(file=image_2, name=f"FeaturedImage2-{home_page_setting.community.name}", order=2)
        current_image_2.save()

      if image_3:
        current_image_3 = Media(file=image_3, name=f"FeaturedImage3-{home_page_setting.community.name}", order=3)
        current_image_3.save()
      
      home_page_setting.images.set([current_image_1, current_image_2, current_image_3])
      home_page_setting.save()


      #now, update the other fields in one swing
      HomePageSettings.objects.filter(id=home_page_id).update(**args)
      return home_page_setting, None
    except Exception as e:
      capture_message(str(e), level="error")
      return None, CustomMassenergizeError(e)
Example #10
0
    def create_user(self, context: Context,
                    args) -> Tuple[dict, MassEnergizeAPIError]:
        try:

            email = args.get('email', None)
            community = get_community_or_die(context, args)

            # allow home address to be passed in
            location = args.pop('location', '')
            profile_picture = args.pop("profile_picture", None)

            if not email:
                return None, CustomMassenergizeError(
                    "email required for sign up")
            user = UserProfile.objects.filter(email=email).first()
            if not user:
                new_user: UserProfile = UserProfile.objects.create(
                    full_name=args.get('full_name'),
                    preferred_name=args.get('preferred_name', None),
                    email=args.get('email'),
                    is_vendor=args.get('is_vendor', False),
                    accepts_terms_and_conditions=args.pop(
                        'accepts_terms_and_conditions', False),
                    preferences={'color': args.get('color', '')})

                if profile_picture:
                    pic = Media()
                    pic.name = f'{new_user.full_name} profpic'
                    pic.file = profile_picture
                    pic.media_type = 'image'
                    pic.save()

                    new_user.profile_picture = pic
                    new_user.save()

            else:
                new_user: UserProfile = user
                # if user was imported but profile incomplete, updates user with info submitted in form
                if not new_user.accepts_terms_and_conditions:
                    new_user.accepts_terms_and_conditions = args.pop(
                        'accepts_terms_and_conditions', False)
                    is_vendor = args.get('is_vendor', False)
                    preferences = {'color': args.get('color')}

            community_member_exists = CommunityMember.objects.filter(
                user=new_user, community=community).exists()
            if not community_member_exists:
                # add them as a member to community
                CommunityMember.objects.create(user=new_user,
                                               community=community)

                # create their first household
                household = RealEstateUnit.objects.create(
                    name="Home",
                    unit_type="residential",
                    community=community,
                    location=location)
                new_user.real_estate_units.add(household)

            res = {"user": new_user, "community": community}
            return res, None

        except Exception as e:
            capture_message(str(e), level="error")
            return None, CustomMassenergizeError(e)
Example #11
0
    def update_community(self, context: Context,
                         args) -> Tuple[dict, MassEnergizeAPIError]:
        try:
            community_id = args.pop("community_id", None)
            website = args.pop("website", None)

            logo = args.pop("logo", None)

            # The set of zipcodes, stored as Location models, are what determines a boundary for a geograpically focussed community
            # This will work for the large majority of cases, but there may be some where a zip code overlaps a town or state boundary
            # These we can deal with by having the Location include city and or state fields
            locations = args.pop("locations", None)

            favicon = args.pop("favicon", None)
            filter_set = Community.objects.filter(id=community_id)
            if not filter_set:
                return None, InvalidResourceError()

            # let's make sure we can use this subdomain
            subdomain = args.get('subdomain', None)
            if subdomain and not can_use_this_subdomain(
                    subdomain, filter_set.first()):
                raise Exception(
                    "Subdomain is already reserved.  Please, choose a different subdomain"
                )

            filter_set.update(**args)
            community = filter_set.first()

            # TODO: check that locations have changed before going through the effort of

            geographic = args.get("is_geographically_focused", False)
            if geographic:
                geography_type = args.get("geography_type", None)
                if self._are_locations_updated(geography_type, locations,
                                               community):
                    self._update_locations(geography_type, locations,
                                           community)
                    self._update_real_estate_units_with_community(community)

            if logo:
                cLogo = Media(file=logo,
                              name=f"{args.get('name', '')} CommunityLogo")
                cLogo.save()
                community.logo = cLogo
                community.save()

            if favicon:
                cFavicon = Media(
                    file=favicon,
                    name=f"{args.get('name', '')} CommunityFavicon")
                cFavicon.save()
                community.favicon = cFavicon
                community.save()

            # let's make sure we reserve this subdomain
            if subdomain:
                reserve_subdomain(subdomain, community)

            # save custom website if specified
            if website:
                ret, err = self.add_custom_website(context, {
                    "community_id": community.id,
                    "website": website
                })
                if err:
                    raise Exception("Failed to save custom website: " +
                                    str(err))

            return community, None

        except Exception as e:
            capture_exception(e)
            return None, CustomMassenergizeError(e)
Example #12
0
    def create_community(self, context: Context,
                         args) -> Tuple[dict, MassEnergizeAPIError]:
        community = None
        try:
            subdomain = args.get('subdomain')
            if not can_use_this_subdomain(subdomain):
                raise Exception(
                    "Subdomain is already reserved.  Please, choose a different subdomain"
                )

            logo = args.pop("logo", None)

            # custom website url can be included
            website = args.pop("website", None)

            # The set of locations (zipcodes, cities, counties, states), stored as Location models, are what determines a boundary for a geograpically focussed community
            # This will work for the large majority of cases, but there may be some where a zip code overlaps a town or state boundary
            # These we can deal with by having the Location include city and or state fields
            locations = args.pop("locations", None)

            favicon = args.pop("favicon", None)
            community = Community.objects.create(**args)
            community.save()

            geographic = args.get("is_geographically_focused", False)
            if geographic:
                geography_type = args.get("geography_type", None)
                self._update_locations(geography_type, locations, community)
                self._update_real_estate_units_with_community(community)

            if logo:
                cLogo = Media(file=logo,
                              name=f"{args.get('name', '')} CommunityLogo")
                cLogo.save()
                community.logo = cLogo
            if favicon:
                cFav = Media(file=favicon,
                             name=f"{args.get('name', '')} CommunityFavicon")
                cFav.save()
                community.favicon = cFav

            # create a goal for this community
            community_goal = Goal.objects.create(name=f"{community.name}-Goal")
            community.goal = community_goal
            community.save()

            # do this before all the cloning in case of failure
            reserve_subdomain(subdomain, community)

            # save custom website if specified
            if website:
                ret, err = self.add_custom_website(context, {
                    "community_id": community.id,
                    "website": website
                })
                if err:
                    raise Exception("Failed to save custom website: " +
                                    str(err))

            # clone everything for this community
            homePage = HomePageSettings.objects.filter(
                is_template=True).first()
            images = homePage.images.all()
            # TODO: make a copy of the images instead, then in the home page, you wont have to create new files everytime
            if homePage:

                homePage.pk = None
                homePage.title = f"Welcome to Massenergize, {community.name}!"
                homePage.community = community
                homePage.is_template = False
                homePage.save()
                homePage.images.set(images)

            # now create all the pages
            if not _clone_page_settings(AboutUsPageSettings,
                                        f"About {community.name}", community):
                raise Exception("Failed to clone settings for AboutUs page")
            if not _clone_page_settings(ActionsPageSettings,
                                        f"Actions for {community.name}",
                                        community):
                raise Exception("Failed to clone settings for Actions page")
            if not _clone_page_settings(ContactUsPageSettings,
                                        f"Contact Us - {community.name}",
                                        community):
                raise Exception("Failed to clone settings for ContactUs page")
            if not _clone_page_settings(DonatePageSettings,
                                        f"Support {community.name}",
                                        community):
                raise Exception("Failed to clone settings for Donate page")
            if not _clone_page_settings(ImpactPageSettings,
                                        f"Our Impact - {community.name}",
                                        community):
                raise Exception("Failed to clone settings for Impact page")
            if not _clone_page_settings(TeamsPageSettings,
                                        f"Teams in this community", community):
                raise Exception("Failed to clone settings for Teams page")
            if not _clone_page_settings(VendorsPageSettings,
                                        f"Service Providers", community):
                raise Exception("Failed to clone settings for Vendors page")
            if not _clone_page_settings(EventsPageSettings,
                                        f"Events and Campaigns", community):
                raise Exception("Failed to clone settings for Events page")
            if not _clone_page_settings(TestimonialsPageSettings,
                                        f"Testimonials", community):
                raise Exception(
                    "Failed to clone settings for Testimonials page")

            admin_group_name = f"{community.name}-{community.subdomain}-Admin-Group"
            comm_admin: CommunityAdminGroup = CommunityAdminGroup.objects.create(
                name=admin_group_name, community=community)
            comm_admin.save()

            if context.user_id:
                user = UserProfile.objects.filter(pk=context.user_id).first()
                if user:
                    comm_admin.members.add(user)
                    comm_admin.save()

            owner_email = args.get("owner_email", None)
            if owner_email:
                owner = UserProfile.objects.filter(email=owner_email).first()
                if owner:
                    comm_admin.members.add(owner)
                    comm_admin.save()

            # Also clone all template actions for this community
            # 11/1/20 BHN: Add protection against excessive copying in case of too many actions marked as template
            # Also don't copy the ones marked as deleted!
            global_actions = Action.objects.filter(is_deleted=False,
                                                   is_global=True)
            MAX_TEMPLATE_ACTIONS = 25
            num_copied = 0

            actions_copied = set()
            for action_to_copy in global_actions:
                old_tags = action_to_copy.tags.all()
                old_vendors = action_to_copy.vendors.all()
                new_action: Action = action_to_copy
                new_action.pk = None
                new_action.is_published = False
                new_action.is_global = False

                old_title = new_action.title
                new_title = get_new_title(community, old_title)

                # first check that we have not copied an action with the same name
                if new_title in actions_copied:
                    continue
                else:
                    actions_copied.add(new_title)

                new_action.title = new_title

                new_action.save()
                new_action.tags.set(old_tags)
                new_action.vendors.set(old_vendors)

                new_action.community = community
                new_action.save()
                num_copied += 1
                if num_copied >= MAX_TEMPLATE_ACTIONS:
                    break

            return community, None
        except Exception as e:
            if community:
                # if we did not succeed creating the community we should delete it
                community.delete()
            capture_exception(e)
            return None, CustomMassenergizeError(e)
Example #13
0
def processMedia(mainMedia: Media, file: UploadFile, container: str) -> None:
	# TODO: better Media filename checking for sub media objects as there can be collisions right now which cause the decomposition to fail
	try:
		filename = None
		requestedFileName = mainMedia.filename
		createdMedia = []
		mainMedia.percentDone = 1
		mainMedia.save()
		# Use ffmpeg to move metadata to the front
		while filename == None:
			filename = str(uuid4())
			if path.isfile(f'media_processing/{filename}.{container}'):
				filename == None
		filename2 = None
		while filename2 == None:
			filename2 = str(uuid4())
			if path.isfile(f'media_processing/{filename2}.{container}'):
				filename2 == None
		if not path.isdir('media_processing'):
			mkdir('media_processing')
		with open(f'media_processing/{filename}.{container}', 'wb+') as file_obj:
			file_obj.write(file.file.read())
			file_obj.close()
		args = ['ffmpeg', '-i', f'media_processing/{filename}.{container}', '-c', 'copy', '-movflags', '+faststart', '-v', 'quiet', f'media_processing/{filename2}.{container}']
		p = Popen(args)
		if p.wait() != 0:
			raise MediaProcessingError
		remove(f'media_processing/{filename}.{container}')
		mkdir(f'media_processing/{filename}')

		args = ['ffprobe', '-show_streams', '-show_entries', 'format=duration', '-of', 'json', '-v', 'quiet', f'media_processing/{filename2}.{container}']
		p = Popen(args, stdout=PIPE)
		out = p.communicate()[0]
		if p.wait() != 0:
			raise SubprocessError
		streams = loads(out)
		duration = float(streams['format']['duration']) # in seconds
		streams = streams['streams']

		audioCount = 0
		subtitleCount = 0
		videoPass = False
		for i in range(len(streams)):
			# Create sub media objects (audio/subtitles)
			if 'codec_type' in streams[i] and 'index' in streams[i]:
				if streams[i]['codec_type'] == 'video':
					# Video generation
					if not videoPass:
						videoPass = True
						dimensions = []
						args = ['ffprobe', '-show_streams', '-show_entries', 'stream=width,height', '-of', 'json', '-v', 'quiet', f'media_processing/{filename2}.{container}']
						p = Popen(args, stdout=PIPE)
						out = p.communicate()[0]
						if p.wait() != 0:
							raise SubprocessError
						width = loads(out)['streams'][streams[i]['index']]
						height = width['height']
						width = width['width']

						if FileSettings.FORCE_DIMENSION:
							for dimension in FileSettings.VIDEO_DIMENSIONS:
								if width >= dimension[0] and height >= dimension[1]:
									dimensions.append(dimension)
									if not FileSettings.CREATE_SMALLER_DIMENSIONS:
										break
											
						if len(dimensions) == 0:
							dimensions.append(FileSettings.VIDEO_DIMENSIONS[-1])

						largestVideoFilename = None
						for j in range(len(dimensions)):
							if j == 0:
								metadata = {
									'default': True
								}
							else:
								metadata = { }
							metadata['label'] = f'{dimensions[j][1]}p'
							media = Media(owner=mainMedia.owner, filename=f'{requestedFileName.rsplit(".", 1)[0]}_{j}_{metadata["label"]}.{FileSettings.VIDEO_EXTENSION}', folder=mainMedia.folder, private=True, processing=True, metadata=metadata)
							media.save()
							createdMedia.append(media)
							video_filename = str(uuid4())
							args = ['ffmpeg', '-hide_banner', '-i', '-map', f'0:{str(streams[i]["index"])}', '-s', f'{dimensions[j][0]}x{dimensions[j][1]}', '-c:v', '-progress', 'pipe:1', f'media_processing/{filename}/{video_filename}.{FileSettings.VIDEO_EXTENSION}']
							if largestVideoFilename:
								args.insert(args.index('-i') + 1, f'media_processing/{filename}/{largestVideoFilename}.{FileSettings.VIDEO_EXTENSION}')
								args.insert(args.index('-c:v') + 1, 'copy')
							else:
								args.insert(args.index('-i') + 1, f'media_processing/{filename2}.{container}')
								if 'codec_name' in streams[i] and streams[i]['codec_name'] == FileSettings.VIDEO_CODEC:
									args.insert(args.index('-c:v') + 1, 'copy')
								else:
									args.insert(args.index('-c:v') + 1, FileSettings.VIDEO_CODEC)
							p = Popen(args, stdout=PIPE, stderr=PIPE, universal_newlines=True)
							for stdout_line in p.stdout:
								stdout_line = stdout_line.split('=')
								if stdout_line[0] == 'out_time_us':
									updateTime = float(stdout_line[1]) / 10000
									if updateTime > 0:
										media.percentDone = min(updateTime / duration, 100)
										media.save()
							if p.wait() != 0:
								raise SubprocessError
							with open(f'media_processing/{filename}/{video_filename}.{FileSettings.VIDEO_EXTENSION}', 'rb') as video_file_obj:
								mimetype = mimetypes.guess_type(f'{video_filename}.{FileSettings.VIDEO_EXTENSION}')[0]
								if mimetype == None:
									mimetype = f'video/{FileSettings.VIDEO_EXTENSION}'
								media.file.put(video_file_obj, content_type=mimetype)
							media.percentDone = 100
							media.save()
							media.processing = False
							media.save()
							mainMedia.update(push__associatedMedia=media)
							percentDone = (j + 1) / len(dimensions) / (i + 1) / (len(streams) + 1) * 100
							mainMedia.percentDone = percentDone
							mainMedia.save()
							if largestVideoFilename:
								remove(f'media_processing/{filename}/{video_filename}.{FileSettings.VIDEO_EXTENSION}')
							else:
								largestVideoFilename = video_filename
						remove(f'media_processing/{filename}/{largestVideoFilename}.{FileSettings.VIDEO_EXTENSION}')
				elif streams[i]['codec_type'] == 'audio':
					# Audio generation
					if audioCount > 0:
						metadata = {}
					else:
						metadata = {
							'default': True
						}
					if 'tags' in streams[i] and 'language' in streams[i]['tags']:
						metadata['srclang'] = str(streams[i]['tags']['language'])
						metadata['label'] = str(streams[i]['tags']['language']).title()
					media = Media(owner=mainMedia.owner, filename=f'{requestedFileName.rsplit(".", 1)[0]}_{audioCount}.{FileSettings.AUDIO_EXTENSION}', folder=mainMedia.folder, private=True, processing=True, metadata=metadata, percentDone=1)
					media.save()
					createdMedia.append(media)
					audio_filename = str(uuid4())
					args = ['ffmpeg', '-i', f'media_processing/{filename2}.{container}', '-map', f'0:{str(streams[i]["index"])}', '-c:a', '-progress', 'pipe:1', f'media_processing/{filename}/{audio_filename}.{FileSettings.AUDIO_EXTENSION}']
					if 'codec_name' in streams[i] and streams[i]['codec_name'] == FileSettings.AUDIO_CODEC:
						args.insert(args.index('-c:a') + 1, 'copy')
					else:
						args.insert(args.index('-c:a') + 1, FileSettings.AUDIO_CODEC)
					p = Popen(args, stdout=PIPE, stderr=PIPE, universal_newlines=True)
					for stdout_line in p.stdout:
						stdout_line = stdout_line.split('=')
						if stdout_line[0] == 'out_time_us':
							updateTime = float(stdout_line[1]) / 10000
							if updateTime > 0:
								media.percentDone = min(updateTime / duration, 100)
								media.save()
					if p.wait() != 0:
						raise SubprocessError					
					with open(f'media_processing/{filename}/{audio_filename}.{FileSettings.AUDIO_EXTENSION}', 'rb') as audio_file_obj:
						mimetype = mimetypes.guess_type(f'{audio_filename}.{FileSettings.AUDIO_EXTENSION}')[0]
						if mimetype == None:
							mimetype = f'audio/{FileSettings.AUDIO_EXTENSION}'
						media.file.put(audio_file_obj, content_type=mimetype)
					media.percentDone = 100
					media.save()
					media.processing = False
					media.save()
					mainMedia.update(push__associatedMedia=media)
					percentDone = (i + 1) / (len(streams) + 1) * 100
					mainMedia.percentDone = percentDone
					mainMedia.save()
					mainMedia.update(set__percentDone=percentDone)
					remove(f'media_processing/{filename}/{audio_filename}.{FileSettings.AUDIO_EXTENSION}')
					audioCount += 1
				elif streams[i]['codec_type'] == 'subtitle':
					# Subtitle generation
					if subtitleCount > 0:
						metadata = {}
					else:
						metadata = {
							'default': True
						}
					if 'tags' in streams[i] and 'language' in streams[i]['tags']:
						metadata['srclang'] = str(streams[i]['tags']['language'])
						metadata['label'] = str(streams[i]['tags']['language']).title()
					media = Media(owner=mainMedia.owner, filename=f'{requestedFileName.rsplit(".", 1)[0]}_{subtitleCount}.{FileSettings.SUBTITLE_EXTENSION}', folder=mainMedia.folder, private=True, processing=True, metadata=metadata)
					media.save()
					createdMedia.append(media)
					subtitle_filename = str(uuid4())
					args = ['ffmpeg', '-i', f'media_processing/{filename2}.{container}', '-map', f'0:{str(streams[i]["index"])}', '-progress', 'pipe:1', f'media_processing/{filename}/{subtitle_filename}.{FileSettings.SUBTITLE_EXTENSION}']
					p = Popen(args, stdout=PIPE, stderr=PIPE, universal_newlines=True)
					for stdout_line in p.stdout:
						stdout_line = stdout_line.split('=')
						if stdout_line[0] == 'out_time_us':
							updateTime = float(stdout_line[1]) / 10000
							if updateTime > 0:
								media.percentDone = min(updateTime / duration, 100)
								media.save()
					if p.wait() != 0:
						raise SubprocessError					
					with open(f'media_processing/{filename}/{subtitle_filename}.{FileSettings.SUBTITLE_EXTENSION}', 'rb') as subtitle_file_obj:
						mimetype = mimetypes.guess_type(f'{subtitle_filename}.{FileSettings.SUBTITLE_EXTENSION}')[0]
						if mimetype == None:
							mimetype = f'text/{FileSettings.SUBTITLE_EXTENSION}'
						media.file.put(subtitle_file_obj, content_type=mimetype)
					media.percentDone = 100
					media.save()
					media.processing = False
					media.save()
					mainMedia.update(push__associatedMedia=media)
					percentDone = (i + 1) / (len(streams) + 1) * 100
					mainMedia.percentDone = percentDone
					mainMedia.save()
					remove(f'media_processing/{filename}/{subtitle_filename}.{FileSettings.SUBTITLE_EXTENSION}')
					subtitleCount += 1
		
		# Poster generation
		if videoPass and len(dimensions) > 0:
			media = Media(owner=mainMedia.owner, filename=f'{requestedFileName.rsplit(".", 1)[0]}_poster.png', folder=mainMedia.folder, private=True)
			media.save()
			createdMedia.append(media)
			random.seed(int(duration * 100)) # use duration to make thumbnails generate at the same point for the same file. Collisions shouldn't matter 
			args = ['ffmpeg', '-ss', f'{round(random.random() * duration, 2)}', '-i', f'media_processing/{filename2}.{container}', '-vframes', '1', '-s', f'{dimensions[0][0]}x{dimensions[0][1]}', '-progress', 'pipe:1', '-f', 'image2', f'media_processing/{filename}/poster.png']
			random.seed() # reset seed
			p = Popen(args, stdout=PIPE, stderr=PIPE, universal_newlines=True)
			for stdout_line in p.stdout:
				stdout_line = stdout_line.split('=')
				if stdout_line[0] == 'out_time_us':
					updateTime = float(stdout_line[1]) / 10000
					if updateTime > 0:
						media.percentDone = min(updateTime / duration, 100)
						media.save()
			if p.wait() != 0:
				raise SubprocessError
			with open(f'media_processing/{filename}/poster.png', 'rb') as poster_file_obj:
				media.file.put(poster_file_obj, content_type='image/png')
			media.processing = False
			media.save()
			mainMedia.update(push__associatedMedia=media)
			media.percentDone = 100
			media.save()
			mainMedia.percentDone = 100
			mainMedia.save()
			mainMedia.update(unset__percentDone=True)
			remove(f'media_processing/{filename}/poster.png')

		# Manually reload and save to get signal to fire
		mainMedia.reload()
		mainMedia.processing = False
		mainMedia.save()
	except Exception as e:
		# Clean up media and sub-media objects
		for created in createdMedia:
			created.delete()
		mainMedia.reload()
		for subMedia in mainMedia.associatedMedia:
			subMedia = subMedia.fetch()
			if subMedia.private:
				subMedia.delete()
		mainMedia.delete()
		print_exc(e)
		# TODO: send websocket to client to inform media player that the file failed to be created
	finally:
		# Clean up temp files
		for f in listdir(f'media_processing/{filename}'):
			remove(f'media_processing/{filename}/{f}')
		rmdir(f'media_processing/{filename}')
		remove(f'media_processing/{filename2}.{container}')
		if len(listdir('media_processing')) == 0:
			rmdir('media_processing')