APDM

A-Pressens Digitale Medier

Bucket for APDM specific stuff.

Usage intended for the pebble Pulp and the apps Lifeloop and Bandwagon, Strider, should it expand to include multiple papers, as well as any apps that come out of the Evergreen project.

Channels

channel = Channel.find('avisnavn')

channel.name
=> 'Avisnavn'

channel.domain
=> 'avisnavn.no'

channel.ad_tech
=> 123

# ... and loads of other handy methods

Design Elements

The api key is just an identifier, and is not used for authorization. In other words, you can make them up as you go along.

api = APDM::DesignElements.new('avisnavn', :api_key => 'bandwagon')

Each newspaper has a standard header and footer along with the css necessary to display it. This header changes regularly. We cache these for a day by default.

api.header
api.footer
api.css

SiteStat

These are tracking scripts used for analytics. It is common to give each type of page within an application its own key.

api.site_stat('personalia.bursdag.hilsen.123456')

RSS Feeds

The newspapers each have an RSS feed:

channel = Channel.find 'avisnavn'
feed = APDM::Feed.new(channel)

feed.fetch

You can specify extra parameters for the number of entries it should return:

feed.fetch("amount" => 5)

Also, newspapers sometimes set up specific sections, like 'bandwagon', or 18368. These are specified as follows:

feed.fetch("sectionId" => 'bandwagon')

AdTech

AdTech ads are managed by Elisabeth Drange Tønnessen [email protected]. We receive a standardized .csv file with ad data, keyed off of a website id. The website_id in the .csv file corresponds to the ad_tech_id in the Channels list.

When APDM says that they just want to use the "default origo ads" this doesn't mean actual origo ads, it means the ads that we have stored with this gem.

It is important, if you create the snippets ahead of time, to make sure that all the ads on a single page are served with a uniq group id. We use a large rand: "grp=12345678990". This identifies them as having been shown together, simultaneously.

Sinatra extensions and workarounds for evil APDM proxy

If you are writing a Sinatra app that is going to live behind the evil apdm proxy, there's a Sinatra extension for you.

Enable it with:

require 'apdm/sinatra'

and adding this line to your Sinatra App class:

register Sinatra::APDM

Sinatra helpers

The following helper methods will be registered:

  • current_channel Returns the current channel (based on current domain name - avisnavn.no if in development)
  • design_elements Returns the DesignElements instance for the current channel (see Design Elements above)
  • ad_tech Returns the AdTech instance for current channel
  • design_elements_css_path Returns the path (sinatra route) to where the css for current channel is published. Use in your templates to load css for channel, i.e. %link{ :href => design_elements_css_path, :rel => "stylesheet", :type => "text/css" }
  • ad_tech_api_js Returns a JavaScript snippet that defines functionality (a global window.api object with several methods) needed by the ad_tech scripts. If you get a lot of Uncaught ReferenceError: api is not defined you probably want to include the following in your template:
  %script
    = ad_tech_api_js

In order to make design elements or ad tech work properly, you need set a couple of configuration options:

  • set_design_elements_api_key The api key for design elements (see above)

  • set_ad_tech_context_prefix The context_key/context_prefix of your application (i.e. bandwagon)

Workarounds

You probably want to enable workarounds and cache headers proven to work with the evil apdm proxy (see the following example)

Example (all options enabled):

class MyApp < Sinatra::Base

  register Sinatra::APDM
  apdm_configure do
    set_design_elements_api_key "kalender"
    set_ad_tech_context_prefix "kalender"
    enable_apdm_proxy_workarounds
    enable_apdm_cache_headers
  end
  # (...)

Usage

For each page request, you can instantiate an ad_tech ad repository for your current channel. This needs to know what context (application, possibly which part of the application) the ads are being shown in.

ad_tech = AdTech.new(current_channel, :context_key => 'bandwagon') # e.g. 'bandwagon', or 'origo+profile'

ad_tech.slot(:toppbanner)
ad_tech.slot(:bunnbanner)

ad_tech.loader_scripts

It is possible to add local context in addition to the app context. For example, in Lifeloop, we specify personalia+<greeting-kind> on the greeting category pages as well as individual greeting pages.

ad_tech = AdTech.new(current_channel, :context_key => 'personalia')

ad_tech.slot(:toppbanner, :local_context => 'mothersday')

=> 'personalia+mothersday'

Saxo

Origo/Apressen Web-to-Print module.

The local newspapers use Saxo to manage their images. This module writes metadata to jpg images for print, and transfers them to an FTP server, resulting in the images being pulled directly into the saxo system at the local papers.

Options

APDM::Saxo needs to be initialized with a hash containing the server connection details. For convenience, these live in the Channel objects.

channel = Channel.find 'avisnavn'
channel.saxo_config
=> { :server => 'ftp.example.com', :username => 'username', :password => 'password', :remote_dir => 'remote/dir' }

Metadata

The metadata is a hash with the following keys:

{
  :keywords => 'some keywords',
  :byline => 'the byline',
  :headline => 'the headline',
  :credit => 'the credits',
  :source => 'the source',
  :copyright => 'the copyright',
  :caption => 'a caption'
}

Publication Dates (Issues)

Usually sometime before Christmas, someone realizes that we're running out of publication dates for some of the newspapers.

The dates of each issue are delivered to us in a variety of formats, typically an excel in an encoding which is not UTF-8, and with varying columns and date formats.

Til now, we have then massaged this in order to get a UTF-8 encoded .csv file that has the following fields:

YYYY-MM-DD;Issue Number;Acronym;NEWSPAPER NAME IN ALL CAPS

We don't use the issue number or acronym for anything. Also the acronyms are not unique.

Ideally we would just get the date and the domain name:

YYYY-MM-DD;avisnavn.no

To import this, replace the config/issues.csv file with the newly massaged replacement, and then run the command to import it:

./bin/apdm import_issues

Usage

The default behavior is to take the image located at ./localname.jpg, write the metadata to it, and transfer it to the remote destination.

saxo = APDM::Saxo.new(options)

photo = saxo.write().to('localname.jpg', '/some/path')
saxo.transfer(photo)

If a :source is provided, the image will be downloaded to /some/path/localname.jpg before proceeding.

saxo = APDM::Saxo.new(options)

photo = saxo.write().to('localname.jpg', '/some/path', :source => 'http://www.example.com/remote.jpg')
saxo.transfer(photo)

saxo.transfer returns true if it suceeded, otherwise false.

Acceptance Test

There is an acceptance test in /test. This test by itself covers 98.99% of the saxo module in the project.

To run it

  • copy test/config-example.yml to test/config.yml
  • edit to taste
  • run rspec test