sketchucation logo sketchucation
    • Login
    Oops, your profile's looking a bit empty! To help us tailor your experience, please fill in key details like your SketchUp version, skill level, operating system, and more. Update and save your info on your profile page today!
    🛣️ Road Profile Builder | Generate roads, curbs and pavements easily Download

    Importing Bulk Attribute Values from CSV

    Scheduled Pinned Locked Moved Developers' Forum
    11 Posts 3 Posters 1.9k Views 3 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H Offline
      hank
      last edited by

      Hello All,

      I have worked up the script below in an attempt to allow myself to input a large number of annoyingly long attributes from a CSV file into a Dynamic Component's attribute dictionary. Yes, yes, I read several places that this has been covered extensively and that TIG had written a few scripts to accomplish this. That was not really my experience and I had trouble finding this exact function. I used TIG's script as a starting point but the documentation on dictionaries seemed thin and not all that helpful.

      I also understand that there are a number of extensions out there but I wanted to try and understand what was going on under the hood with dictionaries so I could possibly take advantage of them and hopefully help others do the same.

      Here it is:

      
      require 'sketchup.rb'
      def set_attribute(key_in, value_in)
      	model = Sketchup.active_model
      	target_dictionary = model.attribute_dictionaries['dynamic_attributes']
      	new_key = target_dictionary[key_in] = value_in
      	#puts key_in
      end
      
      def load_attributes()
      
      	list = []
      	$option_string = "please select an attribute"
      	model = Sketchup.active_model
      	attrdicts = model.attribute_dictionaries
      	attrdict = attrdicts["dynamic_attributes"]
      	attrdict.each{|key,value|
      		#puts "key; #{key} | value; #{value}"
      		$option_string += "|#{key}"
      	}
      	list << $option_string
      
      	prompts = ["What attribute would you like to input values for?"]
      	defaults = ["_leny_options"]
      	$input = UI.inputbox(prompts, defaults, list, "Select Attribute Target")
      	#puts $input
      
      	$att_string = ""
      	csv=UI.openpanel("Choose CSV File...")
      	lines=IO.readlines(csv)
      	lines.each{|line|
      		line.chomp!
      		next if line.empty?
      		new_line = line.gsub(",", " ") #replace commas
      		new_line = new_line.gsub("  ", "") #replace double spaces
      		new_line = new_line.gsub("   ", "") #replace double spaces (again)
      		new_line = new_line.gsub("& ", "&") #replace double spaces (again)
      		new_line = new_line.gsub("= ", "=") #replace double spaces (again)
      		$att_string += new_line.to_s 
      	}
      	#puts $att_string
      	set_attribute('_wall_thickness_options',$att_string)
      	#set_attribute($input,$att_string)
      end
      
      def report_attributes()
      	model = Sketchup.active_model
      	attrdicts = model.attribute_dictionaries
      	attrdict = attrdicts["dynamic_attributes"]
      	attrdict.each{|key,value|
      		puts "key; #{key} | value; #{value}"
      	}
      end
      
      

      Now for my dilemma...

      You can see that I added a drop-down prompt for a user to select a particular key that is already in the model (I'm only focusing on the 'dynamic_attributes' library though there are others). Well, the input I get from the user prompt ends up adding a key to the dictionary like so...

      If I wanted to overwrite the key _lenx_options for example, it comes in as a NEW key called ["_lenx_options"] which is not in fact setting the existing key but creating a new one.

      How can I pass the input argument correctly to the set_attribute def?

      Thanks for any help!

      1 Reply Last reply Reply Quote 0
      • sdmitchS Offline
        sdmitch
        last edited by

        Perhaps if you would share a sample model containing the dynamic component and a sample .csv file.

        Nothing is worthless, it can always be used as a bad example.

        http://sdmitch.blogspot.com/

        1 Reply Last reply Reply Quote 0
        • H Offline
          hank
          last edited by

          Sure! Here is a simple wall DC that I wanted to bring in a large list of wall assemblies:

          A basic wall dynamic component

          and here is the list of assemblies I put together in a spreadsheet - you will see that I added the "&" separator and "=" value indicator right in the spreadsheet because I was being lazy about the string cleanup...

          A list of assemblies for commercial construction

          And for convenience, here is the ruby file too!

          ruby CSV attribute importer

          1 Reply Last reply Reply Quote 0
          • TIGT Offline
            TIG Moderator
            last edited by

            A few somewhat random comments...

            Please enclose your Ruby code inside your own module.

            Do not use 'global' variables [$xxx].
            Using instance-variables [@xxx] will work within your module across its various methods...
            Although as far as I can see the $xxx ones you've made are only used in one method anyway, so they are unnecessary.

            The various DC attributes belong to each component's definition - not to the model - UNLESS that entire model is itself a DC...

            TIG

            1 Reply Last reply Reply Quote 0
            • H Offline
              hank
              last edited by

              Thanks TIG... will do. Any idea about the problem I mentioned?

              1 Reply Last reply Reply Quote 0
              • TIGT Offline
                TIG Moderator
                last edited by

                The DC dictionary attached to the model reports as this:
                ` Sketchup.active_model.attribute_dictionaries['dynamic_attributes'].each_pair{|k,v| puts "#{k} = #{v}" }

                _formatversion = 1.0
                _has_movetool_behaviors = 0.0
                _hasbehaviors = 1.0
                _lastmodified = 2017-06-04 23:49
                _lengthunits = INCHES
                _lenx_label = LenX
                _lenx_nominal = 766.4475123076386
                _leny_access = LIST
                _leny_formlabel = THICKNESS
                _leny_label = LenY
                _leny_nominal = 6.500000000000207
                _leny_options = &--------------INTERIOR--------------=0&1.500 : 0.625 GWB | 0.875 METAL FRAMING=1.500&3.125 : 0.625 GWB | 2.500 METAL FRAMING=3.125&4.250 : 0.625 GWB | 3.625 METAL FRAMING=4.250&6.625 : 0.625 GWB | 6.000 METAL FRAMING=6.625&8.625 : 0.625 GWB | 8.000 METAL FRAMING=8.625&4.875 : 0.625 GWB | 3.625 METAL FRAMING | 0.625 GWB =4.875&7.250 : 0.625 GWB | 6.000 METAL FRAMING | 0.625 GWB =7.250&9.250 : 0.625 GWB | 8.000 METAL FRAMING | 0.625 GWB =9.250&5.250 : 0.625 GWB | 3.625 METAL FRAMING | 0.500 CBB | 0.500 TILE=5.250&7.625 : 0.625 GWB | 6.000 METAL FRAMING | 0.500 CBB | 0.500 TILE=7.625&9.625 : 0.625 GWB | 8.000 METAL FRAMING | 0.500 CBB | 0.500 TILE=9.625&6.125 : 0.625 GWB | 0.625 GWB | 3.625 METAL FRAMING | 0.625 GWB | 0.625 GWB =6.125&8.500 : 0.625 GWB | 0.625 GWB | 6.000 METAL FRAMING | 0.625 GWB | 0.625 GWB =8.500&10.500 : 0.625 GWB | 0.625 GWB | 8.000 METAL FRAMING | 0.625 GWB | 0.625 GWB =10.500&7.625 : 7.625 CMU =7.625&11.625 : 11.625 CMU =11.625&8.875 : 0.625 GWB | 7.625 CMU | 0.625 GWB =8.875&12.875 : 0.625 GWB | 11.625 CMU | 0.625 GWB =12.875&--------------EXTERIOR--------------=0&5.875 : 0.625 GWB | 3.625 METAL FRAMING | 0.625 FRGWB | 1.000 FIN=5.875&8.250 : 0.625 GWB | 6.000 METAL FRAMING | 0.625 FRGWB | 1.000 FIN=8.250&10.250 : 0.625 GWB | 8.000 METAL FRAMING | 0.625 FRGWB | 1.000 FIN=10.250&6.625 : 0.625 GWB | 3.625 METAL FRAMING | 0.750 PLYWOOD | 0.625 FRGWB | 1.000 FIN =6.625&9.000 : 0.625 GWB | 6.000 METAL FRAMING | 0.750 PLYWOOD | 0.625 FRGWB | 1.000 FIN =9.000&11.000 : 0.625 GWB | 8.000 METAL FRAMING | 0.750 PLYWOOD | 0.625 FRGWB | 1.000 FIN =11.000&9.500 : 0.625 GWB | 3.625 METAL FRAMING | 0.625 FRGWB | 1.000 AIR | 3.625 BRICK =9.500&11.875 : 0.625 GWB | 6.000 METAL FRAMING | 0.625 FRGWB | 1.000 AIR | 3.625 BRICK =11.875&13.875 : 0.625 GWB | 8.000 METAL FRAMING | 0.625 FRGWB | 1.000 AIR | 3.625 BRICK =13.875&9.875 : 0.625 GWB | 7.625 CMU | 0.625 FRGWB | 1.000 FIN=9.875&13.875 : 0.625 GWB | 11.625 CMU | 0.625 FRGWB | 1.000 FIN=13.875&13.500 : 0.625 GWB | 7.625 CMU | 0.625 FRGWB | 1.000 AIR | 3.625 BRICK =13.500&17.500 : 0.625 GWB | 11.625 CMU | 0.625 FRGWB | 1.000 AIR | 3.625 BRICK =17.500&13.875 : 0.625 GWB | 8.000 CMU | 0.625 FRGWB | 1.000 AIR | 3.625 BRICK =13.875&10.250 : 0.625 GWB | 8.000 TILT WALL | 0.625 FRGWB | 1.000 FIN=10.250&10.250 : 0.625 GWB | 8.000 TILT WALL | 0.625 FRGWB | 1.000 FIN=10.250&13.875 : 0.625 GWB | 8.000 TILT WALL | 0.625 FRGWB | 1.000 AIR | 3.625 BRICK =13.875&13.875 : 0.625 GWB | 8.000 TILT WALL | 0.625 FRGWB | 1.000 AIR | 3.625 BRICK =13.875
                _leny_units = DEFAULT
                _lenz_access = TEXTBOX
                _lenz_formlabel = WALL_HEIGHT
                _lenz_label = LenZ
                _lenz_nominal = 120.0
                _lenz_units = DEFAULT
                _name = WALL_COMMERCIAL
                _scaletool_formlabel = ScaleTool
                _scaletool_label = ScaleTool
                _scaletool_units = STRING
                lenx = 174.3749999999994
                leny = 7.625
                lenz = 120
                scaletool = 124`

                I assume that you don't want this block of text associated with one 'key' ??

                You need to construct your CSV in a way that gives what you want.
                When you read it to make 'lines' you can iterate each 'line', using chomp! to snip off the \n carriage-return.
                You can then use split at ',' to access various key/value pairs ??

                At the moment I am unclear about what it is you want to end up with after running your code...
                Perhaps trying a very simple version first will help ???

                TIG

                1 Reply Last reply Reply Quote 0
                • H Offline
                  hank
                  last edited by

                  That key is actually EXACTLY what I want. I just had to do it by hard coding the $input argument in set_attribute($input,$att_string) to read "_leny_options" (a string) in order to get it to work. When I feed in $input from UI.inputbox it comes creates a separate new key called ["_leny_options"] with brackets and quotes around it. That is the problem - somehow I need to format $input so this does not happen.

                  The reason I want that key to be so long is because this is what I want the drop-down to look like:

                  screenshot

                  peripheral information...

                  drop-down attributes are stored in a &key1=value1&key2=value2&key3=value3... format in the dictionary

                  and

                  UI.inputbox will present a drop-down if given 4 arguments and the 3rd argument is a bar separated list.

                  1 Reply Last reply Reply Quote 0
                  • TIGT Offline
                    TIG Moderator
                    last edited by

                    I understand how a drop-down list is constructed for a DC.
                    Now I see beyond your complexity...

                    How about this ?

                    def set_attribute(key_in=nil, value_in='')
                      return unless key_in
                      Sketchup.active_model.set_attribute("dynamic_attributes", key_in.to_s, value_in.to_s )
                    end
                    

                    TIG

                    1 Reply Last reply Reply Quote 0
                    • H Offline
                      hank
                      last edited by

                      Thanks TIG. I'll give that a try.

                      Of course you do... that info is for anyone else reading this thread and having a similar struggle as I did.

                      1 Reply Last reply Reply Quote 0
                      • H Offline
                        hank
                        last edited by

                        Alright @TIG, I re-wrote this script trying to comply with your suggestions and it worked! Thank you for your help!

                        Here is the updated script for your entertainment:

                        
                        require 'sketchup.rb'
                        module AttModuleTop
                        	module AttModuleBottom
                        		class<<self
                        			def list_att()
                        				model = Sketchup.active_model
                        				attrdicts = model.attribute_dictionaries
                        				attrdict = attrdicts["dynamic_attributes"]
                        				if(!attrdict)
                        					UI.messagebox("AttTools is intended for use within Dynamic Component Files only")
                        				else
                        					@prompts = ["What attribute to overwrite"]
                        					@defaults = [""]
                        					@list = []
                        					@option_pairs = ""
                        					option_string = "select attribute to overwrite"
                        					attrdict.each{|key,value|
                        						@option_pairs += "key; #{key} | value; #{value}\n"
                        						option_string += "|#{key}"
                        					}
                        					@list << option_string
                        				end
                        			end
                        			def replace_att()
                        				list_att()
                        				new_prompt = "What should it be set to?"
                        				@prompts << new_prompt
                        				input = UI.inputbox(@prompts, @defaults, @list, "Select Attribute Target")
                        				set_result = Sketchup.active_model.set_attribute("dynamic_attributes", input[0], input[1] )
                        				UI.messagebox('succesfully set ' + input[0] + ' to ' + set_result + '!')
                        			end
                        			def load_att()
                        				list_att()
                        				input = UI.inputbox(@prompts, @defaults, @list, "Select Attribute Target")
                        				att_string = ""
                        				csv=UI.openpanel("Choose CSV File...")
                        				lines=IO.readlines(csv)
                        				lines.each{|line|
                        					line.chomp!
                        					next if line.empty?
                        					new_line = line.gsub(",", " ") #replace commas
                        					new_line = new_line.gsub("  ", "") #replace double spaces
                        					new_line = new_line.gsub("   ", "") #replace double spaces (again)
                        					new_line = new_line.gsub("& ", "&") #replace double spaces (again)
                        					new_line = new_line.gsub("= ", "=") #replace double spaces (again)
                        					att_string += new_line.to_s 
                        				}
                        				set_result = Sketchup.active_model.set_attribute("dynamic_attributes", input[0], att_string.to_s )
                        				UI.messagebox('succesfully set ' + input[0] + ' to ' + set_result + '!', MB_MULTILINE)
                        			end
                        			def report_att()
                        				list_att()
                        				if(@option_pairs)
                        					UI.messagebox(@option_pairs, MB_MULTILINE)
                        					#puts @option_pairs
                        				end
                        			end
                        		end # end of class
                        	end # end of module AttModuleBottom
                        end # end of module AttModuleTop
                        
                        # menus ##############################################################
                        
                        if( not file_loaded?("att_tools.rb") )
                        	main_menu = UI.menu("Plugins").add_submenu("Att Tools")
                        	main_menu.add_item("Load Attributes") {(AttModuleTop;;AttModuleBottom;;load_att)}
                        	main_menu.add_item("Replace Attribute") {(AttModuleTop;;AttModuleBottom;;replace_att)}
                        	main_menu.add_item("Report Attributes") {(AttModuleTop;;AttModuleBottom;;report_att)}
                        end
                        file_loaded("att_tools.rb")
                        
                        
                        1 Reply Last reply Reply Quote 0
                        • H Offline
                          hank
                          last edited by

                          BTW, the answer to the original question was that

                          
                          input = UI.inputbox(@prompts, @defaults, @list, "Select Attribute Target")
                          
                          

                          returns an array of values because typically the inputbox would present more than 1 option. In my case there was only one input or prompt so when I put input into

                          
                          set_result = Sketchup.active_model.set_attribute("dynamic_attributes", input, att_string.to_s )
                          
                          

                          I was actually sending in an array and thus setting a new key in the format ["whatever user put into inputbox"]. Even .to_s did not seem to help...

                          
                          set_result = Sketchup.active_model.set_attribute("dynamic_attributes", input.to_s, att_string.to_s )
                          
                          

                          The answer was to get the first item in the array like so:

                          
                          set_result = Sketchup.active_model.set_attribute("dynamic_attributes", input[0], att_string.to_s )
                          
                          
                          1 Reply Last reply Reply Quote 0
                          • 1 / 1
                          • First post
                            Last post
                          Buy SketchPlus
                          Buy SUbD
                          Buy WrapR
                          Buy eBook
                          Buy Modelur
                          Buy Vertex Tools
                          Buy SketchCuisine
                          Buy FormFonts

                          Advertisement