apdm 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/README.md +223 -0
- data/Rakefile +1 -0
- data/apdm.gemspec +34 -0
- data/bin/apdm +6 -0
- data/features/ad_tech.feature +11 -0
- data/features/step_definitions/ad_tech_steps.rb +19 -0
- data/features/support/env.rb +5 -0
- data/fixtures/approvals/ad_tech_loader_scripts.approved.txt +42 -0
- data/fixtures/approvals/ad_tech_placeholders.approved.txt +6 -0
- data/lib/apdm.rb +24 -0
- data/lib/apdm/ad_tech.rb +96 -0
- data/lib/apdm/ad_tech/ad_loader.rb +67 -0
- data/lib/apdm/ad_tech/ads.csv +475 -0
- data/lib/apdm/ad_tech/data.rb +47 -0
- data/lib/apdm/channel.rb +87 -0
- data/lib/apdm/channels.rb +569 -0
- data/lib/apdm/credentials.rb +26 -0
- data/lib/apdm/design_elements.rb +60 -0
- data/lib/apdm/design_elements/api.rb +75 -0
- data/lib/apdm/feed.rb +65 -0
- data/lib/apdm/issues.rb +14 -0
- data/lib/apdm/issues/aasavis.rb +51 -0
- data/lib/apdm/issues/amta.rb +253 -0
- data/lib/apdm/issues/an.rb +302 -0
- data/lib/apdm/issues/auraavis.rb +154 -0
- data/lib/apdm/issues/austagderblad.rb +154 -0
- data/lib/apdm/issues/avisa-hordaland.rb +154 -0
- data/lib/apdm/issues/avisa-valdres.rb +177 -0
- data/lib/apdm/issues/ba.rb +352 -0
- data/lib/apdm/issues/bladet.rb +154 -0
- data/lib/apdm/issues/blv.rb +254 -0
- data/lib/apdm/issues/bygdeposten.rb +154 -0
- data/lib/apdm/issues/demokraten.rb +154 -0
- data/lib/apdm/issues/eikerbladet.rb +409 -0
- data/lib/apdm/issues/enebakkavis.rb +51 -0
- data/lib/apdm/issues/etl.rb +79 -0
- data/lib/apdm/issues/eub.rb +254 -0
- data/lib/apdm/issues/finnmarkdagblad.rb +302 -0
- data/lib/apdm/issues/finnmarken.rb +302 -0
- data/lib/apdm/issues/firda.rb +302 -0
- data/lib/apdm/issues/firdaposten.rb +153 -0
- data/lib/apdm/issues/fremover.rb +304 -0
- data/lib/apdm/issues/gd.rb +304 -0
- data/lib/apdm/issues/glomdalen.rb +302 -0
- data/lib/apdm/issues/ha-halden.rb +302 -0
- data/lib/apdm/issues/hadeland.rb +251 -0
- data/lib/apdm/issues/hardanger-folkeblad.rb +151 -0
- data/lib/apdm/issues/helgeland-arbeiderblad.rb +304 -0
- data/lib/apdm/issues/import.csv +12821 -0
- data/lib/apdm/issues/indre.rb +151 -0
- data/lib/apdm/issues/jarlsbergavis.rb +154 -0
- data/lib/apdm/issues/kvinnheringen.rb +202 -0
- data/lib/apdm/issues/lofot-tidende.rb +102 -0
- data/lib/apdm/issues/lofotposten.rb +304 -0
- data/lib/apdm/issues/mb.rb +103 -0
- data/lib/apdm/issues/namdalsavisa.rb +302 -0
- data/lib/apdm/issues/nordhordland.rb +103 -0
- data/lib/apdm/issues/nordlys.rb +304 -0
- data/lib/apdm/issues/oa.rb +302 -0
- data/lib/apdm/issues/op.rb +304 -0
- data/lib/apdm/issues/opdalingen.rb +150 -0
- data/lib/apdm/issues/oyene.rb +51 -0
- data/lib/apdm/issues/pd.rb +254 -0
- data/lib/apdm/issues/r-a.rb +152 -0
- data/lib/apdm/issues/ranablad.rb +304 -0
- data/lib/apdm/issues/rb.rb +302 -0
- data/lib/apdm/issues/retten.rb +151 -0
- data/lib/apdm/issues/ringblad.rb +304 -0
- data/lib/apdm/issues/rogalandsavis.rb +302 -0
- data/lib/apdm/issues/sa.rb +304 -0
- data/lib/apdm/issues/smaalenene.rb +304 -0
- data/lib/apdm/issues/sognavis.rb +254 -0
- data/lib/apdm/issues/ta.rb +304 -0
- data/lib/apdm/issues/tk.rb +304 -0
- data/lib/apdm/issues/tvedestrandsposten.rb +150 -0
- data/lib/apdm/issues/vestbyavis.rb +51 -0
- data/lib/apdm/local_paper_area.rb +21 -0
- data/lib/apdm/network.rb +49 -0
- data/lib/apdm/null_cache.rb +15 -0
- data/lib/apdm/origo.rb +46 -0
- data/lib/apdm/saxo.rb +58 -0
- data/lib/apdm/saxo/iptc.rb +69 -0
- data/lib/apdm/saxo/metadata.rb +36 -0
- data/lib/apdm/saxo/remote.rb +67 -0
- data/lib/apdm/sinatra.rb +85 -0
- data/lib/apdm/version.rb +3 -0
- data/lib/cli.rb +12 -0
- data/spec/ad_tech/ad_loader_spec.rb +27 -0
- data/spec/ad_tech/data_spec.rb +14 -0
- data/spec/ad_tech_spec.rb +70 -0
- data/spec/approvals_helper.rb +6 -0
- data/spec/channel_spec.rb +91 -0
- data/spec/channels_spec.rb +9 -0
- data/spec/credentials_integration_spec.rb +13 -0
- data/spec/credentials_spec.rb +34 -0
- data/spec/design_elements_spec.rb +132 -0
- data/spec/feed_spec.rb +49 -0
- data/spec/fixtures/ad_tech.csv +6 -0
- data/spec/fixtures/approvals/adtech/ad_loader.approved.txt +9 -0
- data/spec/fixtures/approvals/apdm_adtech/data/handles_artikkelboard.approved.txt +10 -0
- data/spec/fixtures/approvals/apdm_adtech/data/handles_bunnbanner.approved.txt +10 -0
- data/spec/fixtures/approvals/apdm_adtech/data/handles_skyskraper_1.approved.txt +10 -0
- data/spec/fixtures/approvals/apdm_adtech/data/handles_toppbanner.approved.txt +10 -0
- data/spec/fixtures/approvals/apdm_adtech/data/is_ok_with_new_bandwagon_data.approved.txt +10 -0
- data/spec/fixtures/approvals/apdm_adtech/data/loads_default_data.approved.txt +10 -0
- data/spec/fixtures/approvals/apdm_designelements/css/via_api.approved.txt +23 -0
- data/spec/fixtures/approvals/apdm_designelements/footer_html/via_api.approved.html +85 -0
- data/spec/fixtures/approvals/apdm_designelements/header_html/via_api.approved.html +40 -0
- data/spec/fixtures/approvals/apdm_designelements/tracking_scripts/api/keyword.approved.txt +54 -0
- data/spec/fixtures/approvals/apdm_designelements/tracking_scripts/api/placeholder.approved.txt +54 -0
- data/spec/fixtures/issues.csv +3 -0
- data/spec/fixtures/test.jpg +0 -0
- data/spec/fixtures/vcr_cassettes/apdm_credentials.yml +44 -0
- data/spec/fixtures/vcr_cassettes/css.yml +5588 -0
- data/spec/fixtures/vcr_cassettes/footer-html.yml +178 -0
- data/spec/fixtures/vcr_cassettes/header-html.yml +132 -0
- data/spec/fixtures/vcr_cassettes/origo_someone_credentials.yml +43 -0
- data/spec/fixtures/vcr_cassettes/origo_westerdal_credentials.yml +56 -0
- data/spec/fixtures/vcr_cassettes/site_stat.yml +172 -0
- data/spec/fixtures/vcr_cassettes/site_stat_custom_category.yml +172 -0
- data/spec/geo_spec.rb +10 -0
- data/spec/issues_etl_spec.rb +41 -0
- data/spec/mockcached.rb +36 -0
- data/spec/network_spec.rb +43 -0
- data/spec/null_cache_spec.rb +29 -0
- data/spec/origo_spec.rb +24 -0
- data/spec/saxo/iptc_spec.rb +105 -0
- data/spec/saxo/metadata_spec.rb +79 -0
- data/spec/saxo/remote_spec.rb +71 -0
- data/spec/saxo/saxo_spec.rb +82 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/vcr_helper.rb +6 -0
- data/test/fixtures/config-example.yml +6 -0
- data/test/saxo/acceptance_spec.rb +109 -0
- metadata +427 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
APDM::ISSUES['vestbyavis'] = [
|
|
2
|
+
Date.parse('2013-01-03'),
|
|
3
|
+
Date.parse('2013-01-10'),
|
|
4
|
+
Date.parse('2013-01-17'),
|
|
5
|
+
Date.parse('2013-01-24'),
|
|
6
|
+
Date.parse('2013-01-31'),
|
|
7
|
+
Date.parse('2013-02-07'),
|
|
8
|
+
Date.parse('2013-02-14'),
|
|
9
|
+
Date.parse('2013-02-21'),
|
|
10
|
+
Date.parse('2013-02-28'),
|
|
11
|
+
Date.parse('2013-03-07'),
|
|
12
|
+
Date.parse('2013-03-14'),
|
|
13
|
+
Date.parse('2013-03-21'),
|
|
14
|
+
Date.parse('2013-04-04'),
|
|
15
|
+
Date.parse('2013-04-11'),
|
|
16
|
+
Date.parse('2013-04-18'),
|
|
17
|
+
Date.parse('2013-04-25'),
|
|
18
|
+
Date.parse('2013-05-02'),
|
|
19
|
+
Date.parse('2013-05-16'),
|
|
20
|
+
Date.parse('2013-05-23'),
|
|
21
|
+
Date.parse('2013-05-30'),
|
|
22
|
+
Date.parse('2013-06-06'),
|
|
23
|
+
Date.parse('2013-06-13'),
|
|
24
|
+
Date.parse('2013-06-20'),
|
|
25
|
+
Date.parse('2013-06-27'),
|
|
26
|
+
Date.parse('2013-07-04'),
|
|
27
|
+
Date.parse('2013-07-11'),
|
|
28
|
+
Date.parse('2013-07-18'),
|
|
29
|
+
Date.parse('2013-07-25'),
|
|
30
|
+
Date.parse('2013-08-01'),
|
|
31
|
+
Date.parse('2013-08-08'),
|
|
32
|
+
Date.parse('2013-08-15'),
|
|
33
|
+
Date.parse('2013-08-22'),
|
|
34
|
+
Date.parse('2013-08-29'),
|
|
35
|
+
Date.parse('2013-09-05'),
|
|
36
|
+
Date.parse('2013-09-12'),
|
|
37
|
+
Date.parse('2013-09-19'),
|
|
38
|
+
Date.parse('2013-09-26'),
|
|
39
|
+
Date.parse('2013-10-03'),
|
|
40
|
+
Date.parse('2013-10-10'),
|
|
41
|
+
Date.parse('2013-10-17'),
|
|
42
|
+
Date.parse('2013-10-24'),
|
|
43
|
+
Date.parse('2013-10-31'),
|
|
44
|
+
Date.parse('2013-11-07'),
|
|
45
|
+
Date.parse('2013-11-14'),
|
|
46
|
+
Date.parse('2013-11-21'),
|
|
47
|
+
Date.parse('2013-11-28'),
|
|
48
|
+
Date.parse('2013-12-05'),
|
|
49
|
+
Date.parse('2013-12-12'),
|
|
50
|
+
Date.parse('2013-12-19')
|
|
51
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'rgeo'
|
|
2
|
+
require 'rgeo-geojson'
|
|
3
|
+
|
|
4
|
+
module APDM
|
|
5
|
+
class LocalPaperArea
|
|
6
|
+
|
|
7
|
+
attr_reader :feature, :factory
|
|
8
|
+
def initialize(geojson)
|
|
9
|
+
@factory = RGeo::Geographic.spherical_factory
|
|
10
|
+
@feature = RGeo::GeoJSON.decode(geojson, :json_parser => :json)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def contains?(lon, lat)
|
|
14
|
+
feature.contains? factory.point(lon, lat)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
EmptyGeoHash = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[]]]}"
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
data/lib/apdm/network.rb
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'enumerator'
|
|
2
|
+
|
|
3
|
+
module APDM
|
|
4
|
+
|
|
5
|
+
class Network
|
|
6
|
+
include Enumerable
|
|
7
|
+
|
|
8
|
+
attr_reader :channels
|
|
9
|
+
def initialize
|
|
10
|
+
@channels = []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def each
|
|
14
|
+
@channels.each {|c| yield c}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def <<(channel)
|
|
18
|
+
@channels << channel
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def find_by_label(label)
|
|
22
|
+
find do |channel|
|
|
23
|
+
channel.label == label
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def find_by_ad_tech_id(id)
|
|
28
|
+
find do |channel|
|
|
29
|
+
channel.ad_tech_id == id.to_i
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def find_by_url(url)
|
|
34
|
+
return nil unless (url && url[/\./])
|
|
35
|
+
label = url.gsub(/https?:\/\//, '')[/([^\.]+\.[^\.]+)$/, 1].gsub(/\.\w+$/, '')
|
|
36
|
+
find_by_label(label)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def random_production_channel
|
|
40
|
+
# used to display ads from a random channel
|
|
41
|
+
@channels.reject{|c| c.ad_tech_id.nil? || c.label == 'avisnavn' || c.label == 'moss-dagblad' }.sample
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# DEPRECATED
|
|
45
|
+
def find_by_domain(url)
|
|
46
|
+
find_by_url(url)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/apdm/origo.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'curb'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
# Hackish origo knowledge
|
|
5
|
+
module APDM
|
|
6
|
+
class Origo
|
|
7
|
+
|
|
8
|
+
# Found in the Origo console with
|
|
9
|
+
# Organization.find_by_title('A-pressen').children.map(&:id)
|
|
10
|
+
APRESSEN = [29, 34, 35, 39, 40, 42, 44, 47, 56, 64, 67, 69, 82, 94, 95, 99, 134, 139, 154, 155, 162, 232, 242, 264, 287, 317, 386, 401, 420, 433, 456, 462, 597, 12455, 12617, 12733, 12735, 12737, 12802, 12886, 13036, 13038, 13041, 13042]
|
|
11
|
+
|
|
12
|
+
BENGLER = [22]
|
|
13
|
+
|
|
14
|
+
ORGANIZATIONS = APRESSEN + BENGLER
|
|
15
|
+
|
|
16
|
+
def self.organization?(id)
|
|
17
|
+
ORGANIZATIONS.include?(id)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.fetch_credentials(id)
|
|
21
|
+
api.fetch_credentials(id)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.api
|
|
25
|
+
@api ||= API.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class API
|
|
29
|
+
# Credentials in Origo relate a person to an organization via a role.
|
|
30
|
+
# An 'authorized' role means that a human has actually
|
|
31
|
+
# eyeballed the credential and said: "Yeah, this is true."
|
|
32
|
+
# A 'privileged' role, gives the person god-like powers in the
|
|
33
|
+
# organization, but only works if actually authorized.
|
|
34
|
+
def fetch_credentials(id)
|
|
35
|
+
credentials = JSON.parse(Curl::Easy.http_get("#{url}#{id}").body_str)
|
|
36
|
+
credentials.select {|credential| Origo.organization?(credential["organization"]["id"]) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
def url
|
|
41
|
+
"http://origo.no/-/api/v2/credential/find?api_key=49rgnfu9tdtovs1bn2k9xbdq141thogpjbll3bn460d559gk0o&include_organization=true&user_id="
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/apdm/saxo.rb
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'net/ftp'
|
|
2
|
+
require 'open3'
|
|
3
|
+
require 'open-uri'
|
|
4
|
+
require 'logger'
|
|
5
|
+
|
|
6
|
+
require "apdm/saxo/metadata"
|
|
7
|
+
require "apdm/saxo/iptc"
|
|
8
|
+
require "apdm/saxo/remote"
|
|
9
|
+
|
|
10
|
+
module APDM
|
|
11
|
+
class Saxo
|
|
12
|
+
class InvalidConfig < Exception; end
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
attr_writer :logger
|
|
16
|
+
def logger
|
|
17
|
+
unless @logger
|
|
18
|
+
FileUtils.mkdir('log') unless File.exists?('log')
|
|
19
|
+
@logger = Logger.new('log/saxo.log')
|
|
20
|
+
end
|
|
21
|
+
@logger
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_accessor :server, :username, :password, :remote_dir
|
|
26
|
+
def initialize(options = {})
|
|
27
|
+
options.each {|k,v| validate(k, v)}
|
|
28
|
+
|
|
29
|
+
self.server = options[:server] || options['server']
|
|
30
|
+
self.username = options[:username] || options['username']
|
|
31
|
+
self.password = options[:password] || options['password']
|
|
32
|
+
self.remote_dir = (options[:remote_dir] || options['remote_dir']).upcase
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def write(metadata)
|
|
36
|
+
Metadata.new(metadata)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def transfer(photo)
|
|
40
|
+
Remote.new(self, photo).transfer
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
def validate(key, value)
|
|
45
|
+
if required?(key) && !valid?(value)
|
|
46
|
+
raise InvalidConfig.new("#{key} is required!")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def required?(key)
|
|
51
|
+
[:server, :username, :password, :remote_dir].include? key.to_sym
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def valid?(value)
|
|
55
|
+
!value.nil? && value != ""
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module APDM
|
|
2
|
+
class Saxo
|
|
3
|
+
class IPTC
|
|
4
|
+
|
|
5
|
+
attr_accessor :name, :path, :file, :source
|
|
6
|
+
def initialize(name, path, options = {})
|
|
7
|
+
self.source = options[:source]
|
|
8
|
+
self.name = name
|
|
9
|
+
self.path = path
|
|
10
|
+
self.file = "#{path.chomp('/')}/#{name}"
|
|
11
|
+
duplicate options[:file] if options[:file]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def duplicate(original)
|
|
15
|
+
FileUtils.cp(original, file)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def logger
|
|
19
|
+
APDM::Saxo.logger
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def write(data = {})
|
|
23
|
+
download if source
|
|
24
|
+
delete_metadata
|
|
25
|
+
write_metadata(data)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def download
|
|
29
|
+
logger.info "Downloading #{source}."
|
|
30
|
+
begin
|
|
31
|
+
File.open(file, "wb") do |f|
|
|
32
|
+
o = open(source)
|
|
33
|
+
r = o.read
|
|
34
|
+
f.write r
|
|
35
|
+
end
|
|
36
|
+
rescue
|
|
37
|
+
raise $!, "Problem downloading image with file: #{file} and source: #{source}. #{$!}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def delete_metadata
|
|
42
|
+
logger.info "Deleting metadata on #{file}."
|
|
43
|
+
command = "exiv2 -d a delete #{file}"
|
|
44
|
+
Open3.popen3(command) do |stdin, stdout, stderr|
|
|
45
|
+
if stderr.gets
|
|
46
|
+
logger.warn "Could not delete metadata: #{stderr.gets}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def write_metadata(data = {})
|
|
52
|
+
logger.info "Writing metadata to #{file}."
|
|
53
|
+
write_value('ObjectName', name)
|
|
54
|
+
data.each do |key, value|
|
|
55
|
+
write_value(key.to_s.capitalize, value)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def write_value(key, value)
|
|
60
|
+
command = "exiv2 -M \"set Iptc.Application2.#{key} String #{value}\" #{file}"
|
|
61
|
+
Open3.popen3(command) do |stdin, stdout, stderr|
|
|
62
|
+
if stderr.gets
|
|
63
|
+
logger.warn "Failed to write #{value} to #{key}: #{stderr.gets}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module APDM
|
|
3
|
+
class Saxo
|
|
4
|
+
class Metadata
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
def sanitize(s)
|
|
8
|
+
s.gsub(/`/, "'").gsub(/´/, '\'').gsub(/"/, '\"').gsub('<3', ':heart:').gsub(/[<>]/, '').squeeze(' ')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_iso88591(utf8_string)
|
|
12
|
+
utf8_string.encode(Encoding::ISO_8859_1, Encoding::UTF_8, :undef => :replace)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_accessor :metadata
|
|
17
|
+
|
|
18
|
+
def initialize(data = {})
|
|
19
|
+
data = data.dup
|
|
20
|
+
self.metadata = data.each {|k,v| data[k] = convert(Metadata.sanitize(v))}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to(name, path, options = {})
|
|
24
|
+
iptc = IPTC.new(name, path, options)
|
|
25
|
+
iptc.write(metadata)
|
|
26
|
+
iptc
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def convert(value)
|
|
32
|
+
Metadata.to_iso88591(value)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module APDM
|
|
2
|
+
|
|
3
|
+
class SaxoTransferError < StandardError; end
|
|
4
|
+
|
|
5
|
+
class Saxo
|
|
6
|
+
class Remote
|
|
7
|
+
|
|
8
|
+
attr_accessor :saxo, :photo, :local_size, :remote_size
|
|
9
|
+
def initialize(saxo, photo)
|
|
10
|
+
self.saxo = saxo
|
|
11
|
+
self.photo = photo
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def logger
|
|
15
|
+
APDM::Saxo.logger
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def source
|
|
19
|
+
@source ||= photo.file
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def destination
|
|
23
|
+
@destination ||= "#{saxo.remote_dir}/#{photo.name}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def transfer
|
|
27
|
+
logger.info("Begin transfer of #{source} to #{destination}")
|
|
28
|
+
Timeout.timeout(30) do
|
|
29
|
+
Net::FTP.open(saxo.server, saxo.username, saxo.password) do |ftp|
|
|
30
|
+
Timeout.timeout(20) do
|
|
31
|
+
upload(ftp)
|
|
32
|
+
end
|
|
33
|
+
Timeout.timeout(10) do
|
|
34
|
+
cleanup(ftp)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
return true if transfer_succeeded?
|
|
39
|
+
|
|
40
|
+
raise SaxoTransferError("Upload failed [#{local_size}/#{remote_size}]. Source: #{source} -- destination: #{destination}")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def upload(ftp)
|
|
44
|
+
ftp.passive = true
|
|
45
|
+
ftp.putbinaryfile(source, destination)
|
|
46
|
+
self.remote_size = ftp.size(destination)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def cleanup(ftp)
|
|
50
|
+
if transfer_succeeded?
|
|
51
|
+
File.delete(source)
|
|
52
|
+
else
|
|
53
|
+
ftp.delete(destination)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def transfer_succeeded?
|
|
58
|
+
local_size == remote_size
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def local_size
|
|
62
|
+
@local_size ||= File.size(source)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
data/lib/apdm/sinatra.rb
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require "apdm"
|
|
2
|
+
|
|
3
|
+
module Sinatra
|
|
4
|
+
module APDM
|
|
5
|
+
module Helpers
|
|
6
|
+
|
|
7
|
+
def current_channel
|
|
8
|
+
host = request.env['HTTP_X_FORWARDED_HOST'] || request.host
|
|
9
|
+
::APDM::Channel.find_by_domain(host) || ::APDM::Channel.find_by_domain("avisnavn.no")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def design_elements
|
|
13
|
+
@design_elements ||= ::APDM::DesignElements.new(current_channel.label, :api_key => self.settings.design_elements_api_key)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def ad_tech
|
|
17
|
+
ad_tech_for(::APDM::CHANNELS.random_production_channel)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def ad_tech_for(channel)
|
|
21
|
+
@ad_tech_for ||= {}
|
|
22
|
+
@ad_tech_for[channel.label] ||= ::APDM::AdTech.new(channel, :context_key => self.settings.ad_tech_context_prefix)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def ad_tech_api_js
|
|
26
|
+
# This snippet is needed by the ad_tech scripts and should be included in the head tag of pages displaying ad_tech ads
|
|
27
|
+
# Note: its an extract of code found in apdm JavaScript as per august 2012 and may be cause future breakage
|
|
28
|
+
# It hurts but it works
|
|
29
|
+
<<-js
|
|
30
|
+
window.api = {"cookie": {
|
|
31
|
+
"del": function (name,path,domain){var cookie=name+"="+((path)?";path="+path:"");cookie+=((domain)?";domain="+domain:"");cookie+=";expires=Thu, 01-Jan-1970 00:00:01 GMT";if(this.get(name)){document.cookie=cookie;}},
|
|
32
|
+
"get": function (name){var dc=document.cookie;if(dc===""){return false;}},
|
|
33
|
+
"isOn": function (){this.set("apiCookiesOnCheck","true",365);return(this.get("apiCookiesOnCheck")=="true");},
|
|
34
|
+
"set": function (name,value,expires,path,domain,secure){var fDate=new Date();var exDate=fDate.getTime();exDate+=1000*3600*24*expires;fDate.setTime(exDate);name+="="+escape(value)+((expires)?"; expires="+fDate.toGMTString():"");name+=((path)?"; path="+path:"");name+=((domain)?"; domain="+domain:"");name+=((secure)?"; secure":"");document.cookie=name;}
|
|
35
|
+
}};
|
|
36
|
+
js
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def design_elements_css_path
|
|
40
|
+
"#{request.script_name}/apdm-design-elements.css"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.registered(app)
|
|
45
|
+
app.get '/apdm-design-elements.css' do
|
|
46
|
+
headers('Content-Type' => 'text/css', 'X-Content-Type-Options' => 'nosniff')
|
|
47
|
+
design_elements.css
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
app.helpers(Sinatra::APDM::Helpers)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def apdm_configure(&blk)
|
|
54
|
+
self.instance_exec(&blk)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def set_design_elements_api_key(api_key)
|
|
58
|
+
set :design_elements_api_key, api_key
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def set_ad_tech_context_prefix(context_prefix)
|
|
62
|
+
set :ad_tech_context_prefix, context_prefix
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Here be workarounds for the diabolical evil apdm proxy
|
|
66
|
+
def enable_apdm_proxy_workarounds
|
|
67
|
+
before do
|
|
68
|
+
# Enforce a trailing slash for root requests.
|
|
69
|
+
# i.e. /vis/bandwagon ==redirects to==> /vis/bandwagon/
|
|
70
|
+
# The evil apdm proxy sets cookies on path with trailing slash (e.g. /vis/bandwagon/)
|
|
71
|
+
# so a user entering /vis/bandwagon (without trailing slash) will loose its credentials. This is the workaround.
|
|
72
|
+
if request.path_info == '/' and not request.env['REQUEST_PATH'].match(/\/$/)
|
|
73
|
+
redirect request.env['REQUEST_PATH']+"/"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Cache headers required/recommended by the apdm proxy
|
|
79
|
+
def enable_apdm_cache_headers
|
|
80
|
+
before do
|
|
81
|
+
cache_control :private, :no_cache, :no_store, :must_revalidate
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|