# West Tamar Council — Advertised Planning Applications # # Source: https://www.wtc.tas.gov.au/advertised-planning-applications/ # # Page structure — all entries on one page, grouped by h2 headings: # #

92 Sunset Boulevard, Clarence Point

#

# APPLICANT: J & E West
# PROPOSAL: Residential - Dwelling & Outbuilding
# LOCATION: 92 Sunset Boulevard, Clarence Point
# CLOSES: 5pm on 16 April 2026 #

# #

Proposal description

require "nokogiri" require "uri" require "fileutils" require_relative "../lib/scraper_helpers" require_relative "../lib/util" require_relative "../lib/log" TABLE = ENV.fetch("TABLE_NAME") URL = "https://www.wtc.tas.gov.au/advertised-planning-applications/" DOWNLOAD_ATTACHMENTS = ENV["DOWNLOAD_ATTACHMENTS"] == "1" DOWNLOAD_DIR = ENV["DOWNLOAD_DIR"] || "/app/downloads" DB.ensure_table!(TABLE) def safe_name(s) = s.to_s.gsub(/[^\w\-.]+/, "_") def download_pdf(url, council_reference) return nil if url.to_s.strip.empty? dir = File.join(DOWNLOAD_DIR, "westtamar", safe_name(council_reference)) FileUtils.mkdir_p(dir) fname = safe_name(File.basename(URI.parse(url).path)) fname = "document.pdf" if fname.empty? path = File.join(dir, fname) body = Http.get(url, headers: { "Accept" => "application/pdf,*/*", "Referer" => URL }) File.binwrite(path, body) puts " saved #{fname} (#{body.bytesize} bytes)" "/files/westtamar/#{safe_name(council_reference)}/#{fname}" rescue StandardError => e Log.warn "westtamar", "Download failed for #{url}: #{e.class} #{e.message}" nil end # Parse "KEY: VALUE
" pairs from a

node def parse_strong_labels(p_node) kv = {} return kv unless p_node # Replace
with newlines so we can split cleanly html = p_node.inner_html.gsub(//i, "\n") Nokogiri::HTML.fragment(html).text.split("\n").each do |line| line = line.gsub(/\u00a0|\s+/, " ").strip next if line.empty? if (m = line.match(/\A([A-Z][A-Z\s]{1,20}):\s*(.+)\z/)) kv[m[1].strip.upcase] = m[2].strip end end kv end html = Http.get(URL) doc = Nokogiri::HTML(html) items = [] # Walk h2 elements; collect their following siblings until the next h2 doc.css("h2").each do |h2| sibling_nodes = [] sib = h2.next_sibling while sib break if sib.element? && sib.name == "h2" sibling_nodes << sib if sib.element? sib = sib.next_sibling end next if sibling_nodes.empty? # Find the

containing APPLICANT/PROPOSAL/LOCATION/CLOSES labels label_p = sibling_nodes.find { |n| n.name == "p" && n.text =~ /APPLICANT|PROPOSAL|LOCATION|CLOSES/i } kv = parse_strong_labels(label_p) # Find the