<file_sep>require "novaposhta/api"

require "novaposhta/city"
require "novaposhta/order"
require "novaposhta/warehouse"

require "novaposhta/parsers/city_parser"
require "novaposhta/parsers/close_parser"
require "novaposhta/parsers/order_parser"
require "novaposhta/parsers/warehouse_parser"

module Novaposhta
  def self.new(key = nil)
    Novaposhta::Api.new(key.nil? ? self.api_key : key)
  end

  def self.api_key
    @@api_key
  end

  def self.api_key=(key)
    @@api_key = key
  end
end<file_sep>require "net/http"
require "nokogiri"
require "pdfkit"
require "tempfile"

module Novaposhta
  class Api

    WEB_SERVICE_POINT = "http://orders.novaposhta.ua/xml.php"
    PRINTED_FORM_TTH_POINT = "http://orders.novaposhta.ua/pformn.php"
    PRINTED_FORM_MARKERS_POINT = "http://orders.novaposhta.ua/print_formm.php"

    attr_accessor :api_key

    def initialize(api_key)
      @api_key = api_key
    end

    def order(orders=[])
      return [] if orders.empty?
      xml = create_request_xml do |xml|
        orders.each do |o|
          order = o.is_a?(Hash) ? Novaposhta::Order.new(o) : o
          xml.order(order.hash.select{|k,v| !v.is_a?(Hash)}) do |xml_order|
            order.hash.select{|k, v| v.is_a?(Hash)}.each do |k, v|
              xml_order.send(k, v)
            end
          end
        end
      end

      request_and_parse(xml, Novaposhta::Parser::OrderParser.new(orders)).orders
    end

    def close(ttn_ids=[])
      ttn_ids = [ttn_ids] if !ttn_ids.is_a?(Array)
      return {} if ttn_ids.empty?
      xml = create_request_xml do |xml|
        ttn_ids.each do |ttn_id|
          xml.close ttn_id
        end
      end

      request_and_parse(xml, Novaposhta::Parser::CloseParser.new).closes
    end

    def cities
      xml = create_request_xml do |xml|
        xml.city
      end
      request_and_parse(xml, Novaposhta::Parser::CityParser.new).cities.sort {|x,y| x.ru.downcase <=> y.ru.downcase}
    end

    def city_warehouses
      xml = create_request_xml do |xml|
        xml.citywarehouses
      end
      request_and_parse(xml, Novaposhta::Parser::CityParser.new).cities.sort {|x,y| x.ru.downcase <=> y.ru.downcase}
    end

    def warehouses(city_name = nil)
      xml = create_request_xml do |xml|
        xml.warenhouse
        xml.filter city_name if !city_name.nil?
      end
      request_and_parse(xml, Novaposhta::Parser::WarehouseParser.new).warehouses
    end

    def warehouse(warehouse_id, city_name = nil)
      selected = warehouses(city_name).select{ |w| w.id == warehouse_id.to_i }
      raise "Warehouse with #{warehouse_id} not found in city #{city_name}" if selected.empty?
      return selected[0]
    end

    def printed_form_ttn(ttn_id, options = {})
      options = {:copy_count => 1,
                 :o => ttn_id,
                 :token => api_key,
                 :file_to => Tempfile.new(['ttn', '.pdf']).path
                }.merge(options)
      options[:file_to] = options[:file_to].path if options[:file_to].is_a?(File)

      # TODO check that ttn_id not exists
      url_options = {:o => options[:o], :num_copy => options[:copy_count], :token => options[:token]}
      kit = PDFKit.new("#{PRINTED_FORM_TTH_POINT}?#{url_options.map{|k,v| "#{k}=#{v}"}.join("&")}", :orientation => "Landscape")
      kit.to_file(options[:file_to])
    end

    def printed_form_markers(ttn_id, options = {})
      options = {:o => ttn_id,
                 :token => api_key,
                 :file_to => Tempfile.new(['markers', '.pdf']).path
      }.merge(options)
      options[:file_to] = options[:file_to].path if options[:file_to].is_a?(File)

      # TODO check that ttn_id not exists
      url_options = {:o => options[:o], :token => options[:token]}
      kit = PDFKit.new("#{PRINTED_FORM_MARKERS_POINT}?#{url_options.map{|k,v| "#{k}=#{v}"}.join("&")}", :orientation => "Landscape")
      kit.to_file(options[:file_to])
    end

    private
    def create_request_xml(&xml)
      builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
        xml.file {
          xml.auth api_key
          yield xml
        }
      end

      builder.to_xml
    end

    def request_and_parse(xml, parser)
      result = request(xml)
      parse(result, parser)
    end

    def request(xml)
      uri = URI(WEB_SERVICE_POINT)
      req = Net::HTTP::Post.new(uri.request_uri)
      req.body = xml
      req.content_type = "text/xml"

      http = Net::HTTP.new(uri.host, uri.port)
      http.request(req).body
    end

    def parse(xml, parser)
      p = Nokogiri::XML::SAX::Parser.new(parser)
      p.parse(xml)
      parser
    end

    def to_pdf(uri)
      uri = URI.parse(uri) if uri.is_a?(String)

      http = Net::HTTP.new(uri.host, uri.port)
      request = Net::HTTP::Get.new(uri.request_uri)

      response = http.request(request)
      if !response.response['Location'].nil?
        return to_pdf(URI(response.response['Location']))
      end


    end
  end
end<file_sep>module Novaposhta
  class Order

    DEFAULT_DATE = Date.today

    attr_reader :hash

    def initialize(hash = {})
      @hash = hash
    end

    def saturday?
      %w(true 1).include?(@hash[:saturday].to_s)
    end

    def convert
      date = DEFAULT_DATE if date.nil?
      date = date.strftime('%Y-%m-%d') if !date.is_a?(String)
      date_desired  = date_desired.strftime('%Y-%m-%d') if !date_desired.nil? and !date_desired.is_a?(String)
    end

    def method_missing(method_name, *args)
      mname = method_name.id2name
      len = args.length
      if mname.chomp!('=') && method_name != :[]=
        if len != 1
          raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
        end
        modifiable[new_ostruct_member(mname)] = args[0]
      elsif len == 0 && method_name != :[]
        @hash[method_name]
      else
        ""
      end
    end
  end

  module PayType
    CASH = "1"
    CASHLESS = "2"
  end

  module Payer
    RECIPIENT = "0"
    SENDER = "1"
    THIRD_PERSON = "2"
  end

  module REDELIVERY_PAYMENT_PAYER
    RECIPIENT = "1"
    SENDER = "2"
  end

  module REDELIVERY_TYPE
    DOCUMENTS = "1"
    MONEY = "2"
    CONTAINERS = "3"
    PRODUCT = "4"
    OTHER = "5"
  end
end<file_sep>require "nokogiri/xml/sax/document"

module Novaposhta
  module Parser
    class CityParser < Nokogiri::XML::SAX::Document

      def initialize
        @cities = []
        @current_city = {}
        @city_element = false
        @tag_content = ''
      end

      def cities
        return @cities
      end

      def start_element(name, attrs = [])
        return if name != "city" && !@city_element
        case name
          when "city"
            @current_city = {}
            @city_element = true
          when "id"
            @tag_content = "id"
          when "nameRu"
            @tag_content = "ru"
          when "nameUkr"
            @tag_content = "ukr"
        end
      end

      def characters(string)
        return if @tag_content.empty?
        @current_city[@tag_content.to_sym] = string
        @tag_content = ''
      end

      def end_element(name)
        if name == "city"
          @city_element = false
          @cities << Novaposhta::City.new(@current_city) if !@current_city.empty?
        end
      end
    end
  end
end
<file_sep># A sample Gemfile
source "https://rubygems.org"

gemspec

gem "pdfkit", :git => "<EMAIL>:azomazo/pdfkit.git"<file_sep>module Novaposhta
  class Warehouse

    def initialize(hash)
      @hash = hash
    end

    def id
      @hash[:id].to_i
    end

    def max_weight_allowed
      @hash[:max_weight_allowed].to_i
    end

    def method_missing(method_name, *args)
      if @hash.key?(method_name)
        @hash[method_name]
      else
        super
      end
    end
  end
end<file_sep>module Novaposhta
  module Parser
    class CloseParser < Nokogiri::XML::SAX::Document

      attr_reader :closes

      def initialize
        @closes = {}
        @current_close = {}
        @close_element = false
        @tag_content = ''
      end

      def start_element(name, attrs = [])
        return if name.downcase != "close" && !@close_element
        case name.downcase
          when "close"
            pattrs = parse_attr(attrs)
            @current_close = pattrs[:id]
            @close_element = true
        end
      end

      def characters(string)
        return if @tag_content.empty?
        @closes[@current_close] = string
        @tag_content = ''
      end

      def end_element(name)
        if name.downcase == "close"
          @close_element = false
        end
      end

      private
      def parse_attr(attrs = [])
        pattrs = {}
        attrs.each do |attr|
          if attr[0].downcase == "id"
            pattrs[:id] = attr[1]
          end
        end
        pattrs
      end
    end
  end
end
<file_sep>require "nokogiri/xml/sax/document"

module Novaposhta
  module Parser
    class WarehouseParser < Nokogiri::XML::SAX::Document
      def initialize
        @warehouses = []
        @current_warehouse = {}
        @warehouse_element = false
        @tag_content = ''
      end

      def warehouses
        return @warehouses
      end

      def start_element(name, attrs = [])
        return if name != "warenhouse" && !@warehouse_element
        case name
          when "warenhouse"
            @current_warehouse = {}
            @warehouse_element = true
          when "city"
            @tag_content = "city_ukr"
          when "cityRu"
            @tag_content = "city_ru"
          when "wareId"
            @tag_content = "id"
          when "address"
            @tag_content = "address_ukr"
          when "addressRu"
            @tag_content = "address_ru"
          when "weekday_reseiving_hours"
            @tag_content = "weekday_receiving_hours"
          when "saturday_reseiving_hours"
            @tag_content = "saturday_receiving_hours"
          else
            @tag_content = name
        end
      end

      def characters(string)
        return if @tag_content.empty?
        @current_warehouse[@tag_content.to_sym] = string
        @tag_content = ''
      end

      def end_element(name)
        if name == "warenhouse"
          @warehouse_element = false
          @warehouses << Novaposhta::Warehouse.new(@current_warehouse)
        end
      end
    end
  end
end<file_sep>Gem::Specification.new do |s|
  s.name        = 'novaposhta'
  s.version     = '0.0.0'
  s.date        = '2013-02-17'
  s.summary     = "Nobvaposhta API"
  s.description = ""
  s.authors     = ["<NAME>"]
  s.email       = '<EMAIL>'
  s.files       = Dir['lib/**/*.rb']
  s.homepage    = 'https://github.com/azomazo/novaposhta'

  s.add_dependency("nokogiri", "~> 1.5.6")

  s.add_development_dependency "rspec", "~> 2.11.0"
end<file_sep>module Novaposhta
  module Parser
    class OrderParser < Nokogiri::XML::SAX::Document
      attr_reader :orders

      def initialize(orders)
        @orders = orders
      end

      def start_element(name, attrs = [])
        return if name.downcase != "order"
        case name.downcase
          when "order"
            order_id, ttn_id = parse_attrs(attrs)
            set_ttn_id(order_id, ttn_id)
        end
      end

      private
      def set_ttn_id(order_id, ttn_id)
        @orders.each { |o|
          if o.is_a?(Hash) and o[:order_id] == order_id.to_s
            o[:ttn_id] = ttn_id.to_s
          elsif o.order_id == order_id.to_s
            o.ttn_id = ttn_id.to_s
          end
        }
      end

      def parse_attrs(attrs=[])
        order_id = ""
        ttn_id = ""
        attrs.each do |attr|
          if attr[0].downcase == "id"
            order_id = attr[1]
          elsif attr[0].downcase == "np_id"
            ttn_id = attr[1]
          end
        end

        return order_id, ttn_id
      end
    end
  end
end