diff options
Diffstat (limited to 'lib/chaos_importer.rb')
| -rw-r--r-- | lib/chaos_importer.rb | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/lib/chaos_importer.rb b/lib/chaos_importer.rb new file mode 100644 index 00000000..3a307a35 --- /dev/null +++ b/lib/chaos_importer.rb | |||
| @@ -0,0 +1,336 @@ | |||
| 1 | require 'iconv' | ||
| 2 | require 'nokogiri' | ||
| 3 | require 'lib/chaos_calendar/ical_occurrences' | ||
| 4 | require 'digest/sha1' | ||
| 5 | |||
| 6 | |||
| 7 | class ChaosXml | ||
| 8 | attr_reader :xml, :unique_name, :locale, :date, :slug | ||
| 9 | |||
| 10 | def initialize options = {} | ||
| 11 | options.each_pair do |key,value| | ||
| 12 | instance_variable_set "@#{key}", value | ||
| 13 | instance_eval("def #{key}; @#{key};end") | ||
| 14 | end | ||
| 15 | end | ||
| 16 | end | ||
| 17 | |||
| 18 | class ChaosImporter | ||
| 19 | include Enumerable | ||
| 20 | |||
| 21 | def initialize path | ||
| 22 | unless Node.root | ||
| 23 | Node.create! | ||
| 24 | end | ||
| 25 | |||
| 26 | @directory = path | ||
| 27 | @years = {} | ||
| 28 | end | ||
| 29 | |||
| 30 | # Iterates through all xml files within the @directory and yields ChaosXml | ||
| 31 | # objects with the parsed attributes. These attributes are the basic data | ||
| 32 | # needed for further processing / parsing. | ||
| 33 | def each | ||
| 34 | directories = Dir.glob("#{@directory}/*/*.xml{,.de,.en}") | ||
| 35 | |||
| 36 | directories.each do |path| | ||
| 37 | next if path =~ /index\.xml/ | ||
| 38 | |||
| 39 | chaos_id = chaos_id_from_path( path ) | ||
| 40 | options = {} | ||
| 41 | options[:xml] = Nokogiri::XML( File.new(path).read ) | ||
| 42 | options[:locale] = lang_from_path( path ) | ||
| 43 | options[:date] = options[:xml].at("//date").content.to_date | ||
| 44 | options[:slug] = chaos_id | ||
| 45 | options[:unique_name] = "updates/#{options[:date]}/#{options[:slug]}" | ||
| 46 | xml = ChaosXml.new options | ||
| 47 | |||
| 48 | yield xml | ||
| 49 | end | ||
| 50 | end | ||
| 51 | |||
| 52 | # Uses the each method to loop over the xml files and uses the attrubutes of | ||
| 53 | # the returned ChaosXml objects to do some further processing which is needed | ||
| 54 | # to create proper ActiveRecord records | ||
| 55 | def import_updates | ||
| 56 | unless @updates = Node.find_by_unique_name('updates') | ||
| 57 | @updates = Node.create!( :slug => 'updates' ) | ||
| 58 | @updates.move_to_child_of Node.root | ||
| 59 | end | ||
| 60 | |||
| 61 | self.each do |update| | ||
| 62 | author = find_or_create_author( update ) | ||
| 63 | node = find_or_create_node( update ) | ||
| 64 | html = convert_to_html( update.xml ) | ||
| 65 | page = fill_draft_with_content(node.draft, html, update.locale) | ||
| 66 | |||
| 67 | add_tags_to_page page, update.xml, "update" | ||
| 68 | add_event_to_node node, update.xml if page.tag_list.include?("event") | ||
| 69 | page.user = author | ||
| 70 | page.save | ||
| 71 | puts node.unique_name | ||
| 72 | end | ||
| 73 | |||
| 74 | Node.all.each {|node| node.publish_draft!} | ||
| 75 | end | ||
| 76 | |||
| 77 | def lang_from_path path | ||
| 78 | case path | ||
| 79 | when /\.de$/ then :de | ||
| 80 | when /\.en$/ then :en | ||
| 81 | else | ||
| 82 | :de | ||
| 83 | end | ||
| 84 | end | ||
| 85 | |||
| 86 | def chaos_id_from_path path | ||
| 87 | path.sub(@directory, "").split(/\//).last.split(/\./)[0] | ||
| 88 | end | ||
| 89 | |||
| 90 | def find_or_create_author update | ||
| 91 | login = update.xml.at("//author").content rescue "webmaster" | ||
| 92 | puts login | ||
| 93 | |||
| 94 | # password = Digest::SHA1.hexdigest("#{Time.now+rand(100).days}") | ||
| 95 | # unless author = User.find_by_login(login) | ||
| 96 | # author = User.create!( | ||
| 97 | # :login => login, | ||
| 98 | # :email => "#{login}@example.com", | ||
| 99 | # :password => password, | ||
| 100 | # :password_confirmation => password | ||
| 101 | # ) | ||
| 102 | # end | ||
| 103 | |||
| 104 | # author | ||
| 105 | |||
| 106 | |||
| 107 | end | ||
| 108 | |||
| 109 | def find_or_create_node update | ||
| 110 | year = update.date.year | ||
| 111 | |||
| 112 | unique_name_array = update.unique_name.split("/") | ||
| 113 | |||
| 114 | unless @years[year] || (@years[year] = Node.find_by_unique_name("updates/#{year}")) | ||
| 115 | @years[year] = Node.create :slug => year | ||
| 116 | @years[year].move_to_child_of @updates | ||
| 117 | end | ||
| 118 | |||
| 119 | unless node = Node.find_by_unique_name(update.unique_name) | ||
| 120 | node = Node.create :slug => update.slug | ||
| 121 | node.move_to_child_of @years[year] | ||
| 122 | end | ||
| 123 | |||
| 124 | node | ||
| 125 | end | ||
| 126 | |||
| 127 | def fill_draft_with_content draft, html, lang | ||
| 128 | I18n.locale = lang | ||
| 129 | |||
| 130 | options = { | ||
| 131 | :title => html.xpath("//title")[0].content, | ||
| 132 | :abstract => html.xpath("//abstract")[0].content, | ||
| 133 | :body => extract_body(html) | ||
| 134 | } | ||
| 135 | |||
| 136 | draft.update_attributes options | ||
| 137 | draft | ||
| 138 | end | ||
| 139 | |||
| 140 | def extract_body html, excluded_tags=[] | ||
| 141 | default_excluded_tags = [ | ||
| 142 | "DTSTART", | ||
| 143 | "DTEND", | ||
| 144 | "DURATION", | ||
| 145 | "LOCATION", | ||
| 146 | "GEO", | ||
| 147 | "SUMMARY", | ||
| 148 | "URL" | ||
| 149 | ] | ||
| 150 | |||
| 151 | excluded_tags = (default_excluded_tags + excluded_tags).uniq | ||
| 152 | |||
| 153 | body = "" | ||
| 154 | element = html.xpath("//abstract")[0].next_sibling | ||
| 155 | |||
| 156 | while element do | ||
| 157 | body << element.to_s unless excluded_tags.include? element.name | ||
| 158 | element = element.next_sibling | ||
| 159 | end | ||
| 160 | |||
| 161 | body | ||
| 162 | end | ||
| 163 | |||
| 164 | def add_tags_to_page page, chaospage, *custom_tags | ||
| 165 | tag_list = custom_tags | ||
| 166 | |||
| 167 | chaospage.xpath("//flags").each do |node| | ||
| 168 | node.each do |k,v| | ||
| 169 | case k | ||
| 170 | when "calendar" | ||
| 171 | tag_list << "event" | ||
| 172 | when "pm" | ||
| 173 | tag_list << "pressemitteilung" | ||
| 174 | end | ||
| 175 | end | ||
| 176 | end | ||
| 177 | |||
| 178 | # Getting rid of duplicate flags | ||
| 179 | tag_list.uniq! | ||
| 180 | |||
| 181 | page.tag_list = tag_list.join(",") | ||
| 182 | page.save | ||
| 183 | end | ||
| 184 | |||
| 185 | def add_event_to_node node, chaospage | ||
| 186 | rrule = get_rrule(chaospage) | ||
| 187 | |||
| 188 | event_options = { | ||
| 189 | :start_time => get_start_time(chaospage), | ||
| 190 | :end_time => get_end_time(chaospage), | ||
| 191 | :allday => is_allday?(chaospage), | ||
| 192 | :rrule => rrule, | ||
| 193 | :custom_rrule => is_custom_rrule?(rrule), | ||
| 194 | :location => get_location(chaospage), | ||
| 195 | :url => get_url(chaospage), | ||
| 196 | :latitude => get_latitude(chaospage), | ||
| 197 | :longitude => get_logitude(chaospage) | ||
| 198 | } | ||
| 199 | |||
| 200 | unless tmp_event = node.event | ||
| 201 | tmp_event = Event.create! event_options.merge({:node_id => node.id}) | ||
| 202 | else | ||
| 203 | tmp_event.update_attributes event_options | ||
| 204 | end | ||
| 205 | end | ||
| 206 | |||
| 207 | def get_start_time chaospage | ||
| 208 | chaospage.at("//ical:DTSTART").content || raise("DTSTART not found") | ||
| 209 | end | ||
| 210 | |||
| 211 | def get_end_time chaospage | ||
| 212 | dtstart = chaospage.at("//ical:DTSTART") | ||
| 213 | dtend = chaospage.at("//ical:DTEND") | ||
| 214 | duration = chaospage.at("//ical:DURATION") | ||
| 215 | |||
| 216 | if dtend | ||
| 217 | return dtend.content | ||
| 218 | elsif duration | ||
| 219 | parsed_duration = Ical_occurrences.duration_to_fixnum(duration.content) | ||
| 220 | return (dtstart.content.to_time + parsed_duration) | ||
| 221 | else | ||
| 222 | raise("Neiter DTEND nor DURATION found") | ||
| 223 | end | ||
| 224 | end | ||
| 225 | |||
| 226 | def is_allday? chaospage | ||
| 227 | !chaospage.at("//ical:DTSTART").[]("VALUE").nil? | ||
| 228 | end | ||
| 229 | |||
| 230 | def get_rrule chaospage | ||
| 231 | if rrule = chaospage.at("//ical:RRULE") | ||
| 232 | rrtxt = '' | ||
| 233 | rrule.children.each do |subrule| | ||
| 234 | rule_name = subrule.name | ||
| 235 | rule_content = subrule.content.sub(/\W/,'') | ||
| 236 | |||
| 237 | next if rule_content.blank? | ||
| 238 | |||
| 239 | rrtxt += "#{rule_name}=#{rule_content};" | ||
| 240 | end | ||
| 241 | rrtxt.chomp!(';') | ||
| 242 | rrtxt | ||
| 243 | else | ||
| 244 | nil | ||
| 245 | end | ||
| 246 | end | ||
| 247 | |||
| 248 | def is_custom_rrule? rrule | ||
| 249 | default_rules = [ | ||
| 250 | "FREQ=WEEKLY;INTERVAL=1", | ||
| 251 | "FREQ=MONTHLY;INTERVAL=1", | ||
| 252 | "FREQ=YEARLY;INTERVAL=1" | ||
| 253 | ] | ||
| 254 | |||
| 255 | rrule && !default_rules.include?(rrule) ? true : false | ||
| 256 | end | ||
| 257 | |||
| 258 | def get_location chaospage | ||
| 259 | location = chaospage.at("//ical:LOCATION") | ||
| 260 | location ? location.content : nil | ||
| 261 | end | ||
| 262 | |||
| 263 | def get_url chaospage | ||
| 264 | location = chaospage.at("//ical:LOCATION") | ||
| 265 | location.[]("ALTREP") if location | ||
| 266 | end | ||
| 267 | |||
| 268 | def get_latitude chaospage | ||
| 269 | geo = chaospage.at("//ical:GEO") | ||
| 270 | geo.text.split(";")[0] if geo | ||
| 271 | end | ||
| 272 | |||
| 273 | def get_logitude chaospage | ||
| 274 | geo = chaospage.at("//ical:GEO") | ||
| 275 | geo.text.split(";")[1] if geo | ||
| 276 | end | ||
| 277 | |||
| 278 | def convert_to_html chaospage | ||
| 279 | |||
| 280 | chaospage.xpath('//paragraph').each {|sub| sub.name = "p"} | ||
| 281 | chaospage.xpath('//quote').each {|sub| sub.name = "blockquote" } | ||
| 282 | chaospage.xpath('//subtitle').each {|sub| sub.name = "h3" } | ||
| 283 | chaospage.xpath('//strong').each {|sub| sub.name = "em" } | ||
| 284 | chaospage.xpath('//stronger').each {|sub| sub.name = "strong" } | ||
| 285 | chaospage.xpath('//chapter').each {|sub| sub.name = "h2" } | ||
| 286 | |||
| 287 | chaospage.xpath('//link').each do |sub| | ||
| 288 | sub.name = "a" | ||
| 289 | href = sub.[]("ref") | ||
| 290 | sub.remove_attribute("ref") | ||
| 291 | sub.[]=("href", href) | ||
| 292 | sub.remove_attribute("type") | ||
| 293 | end | ||
| 294 | |||
| 295 | chaospage.xpath('//list').each do |sub| | ||
| 296 | if !sub.css("row item").empty? | ||
| 297 | sub.name = "table" | ||
| 298 | |||
| 299 | sub.css("row").each {|x| x.name = "tr"} | ||
| 300 | sub.css("tr item").each {|x| x.name = "td"} | ||
| 301 | elsif !sub.css("item").empty? | ||
| 302 | sub.name = "ul" | ||
| 303 | |||
| 304 | sub.css("item").each {|x| x.name = "li"} | ||
| 305 | end | ||
| 306 | end | ||
| 307 | |||
| 308 | chaospage.xpath('//media').each do |sub| | ||
| 309 | sub.name = "img" | ||
| 310 | src = sub.[]("ref") | ||
| 311 | sub.remove_attribute("src") | ||
| 312 | sub.[]=("src", src) | ||
| 313 | unless sub.content | ||
| 314 | sub.[]=("alt", sub.content) | ||
| 315 | sub.xpath('//*').each {|x| x.remove} | ||
| 316 | end | ||
| 317 | end | ||
| 318 | |||
| 319 | chaospage.xpath('//name').each do |sub| | ||
| 320 | if sub.[]("email") | ||
| 321 | mail_href = "mailto:#{sub.[]('email')}" | ||
| 322 | sub.remove_attribute("email") | ||
| 323 | sub.[]=("href", mail_href) | ||
| 324 | end | ||
| 325 | sub.name = "a" | ||
| 326 | |||
| 327 | if href = sub.[]("ref") | ||
| 328 | sub.remove_attribute("ref") | ||
| 329 | sub.[]=("href", href) | ||
| 330 | end | ||
| 331 | end | ||
| 332 | |||
| 333 | chaospage | ||
| 334 | |||
| 335 | end | ||
| 336 | end \ No newline at end of file | ||
