February 1, 2016

Fill hours worked in SAP Netweaver Automatically

Continuing the theme of automation, one of the most repetitive tasks if you work for a big company is timesheets. So I set out to rectify this by scripting it!

Start with you configuration, I named mine hours.ini:

[DEFAULT]
url = FILL_ME_IN
username = FILL_ME_IN
password = FILL_ME_IN

then we need the magic of Selenium to do the heavy lifting, so we install it:

$ pip3 install selenium

I called this script, unsurprisingly hours.py:

#!/usr/bin/env python3
"""
hours.py by Neil Grogan, 2015

A script to fill in hours automatically in SAP NetWeaver Portal
"""
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import configparser
import os

TIMEOUT = 5

def main():
    # Setup
    config = read_config()
    driver = load_website(config['url'], "SAP NetWeaver Portal")
    wait = WebDriverWait(driver, TIMEOUT)

    # Action
    login_to_website(driver, config['username'], config['password'])
    wait_for_timesheet_table(driver, wait)

    fill_in_timesheet(driver)
    submit_and_confirm_timesheet(driver, wait)

    logoff_and_close(driver, wait)

def read_config():
    config = configparser.ConfigParser()
    path = os.path.dirname(os.path.realpath(__file__))
    config.read(path + '/hours.ini')
    return config['DEFAULT']

def load_website(url, title):
    driver = webdriver.Firefox()
    driver.get(url)
    assert title in driver.title
    return driver

def login_to_website(driver, username, password):
    elem = driver.find_element_by_id("logonuidfield")
    elem.send_keys(username)
    elem = driver.find_element_by_id("logonpassfield")
    elem.send_keys(password)
    elem.send_keys(Keys.RETURN)

def wait_for_timesheet_table(driver, wait):
    # wait for ajax items to load
    elem = wait.until(EC.element_to_be_clickable((By.ID, 'L2N2')))
    elem.click()

    wait.until(EC.frame_to_be_available_and_switch_to_it('contentAreaFrame'))
    wait.until(EC.frame_to_be_available_and_switch_to_it('isolatedWorkArea'))

    # Wait until timesheet appears
    elem = wait.until(EC.presence_of_element_located(
        (By.ID, "aaaaKEBH.VcCatTableWeek.WORKDATE1_InputField.0")))

def fill_in_timesheet(driver):
    # Fill in days of the week with 8 hours each
    for i in range(1,6):
        elem = driver.find_element_by_id("aaaaKEBH.VcCatTableWeek.WORKDATE"+str(i)+"_InputField.0")
        elem.click()
        elem.clear()
        elem.send_keys('8')

def submit_and_confirm_timesheet(driver, wait):
    # Wait for javascript to digest our fast hours entry :)
    time.sleep(0.2)

    # Start to Click Review Button
    elem = driver.find_element_by_id("aaaaKEBH.VcCatRecordEntryView.ButtonNext")
    elem.click()

    # Click Save/Confirm
    elem = wait.until(EC.presence_of_element_located(
        (By.ID, "aaaaLBOD.VcGenericButtonView.Save_com_sap_xss_hr_cat_record_vac_review_VcCatRecordReview")))
    elem.click()

    # Wait until confirmation of save
    elem = wait.until(EC.presence_of_element_located(
        (By.ID, "aaaaLMJA.WDMsgBox.MessageArea-txt")))
    assert "Your data has been saved" or "No data was changed" in elem.text

def logoff_and_close(driver, wait):
    # Start logging off
    driver.switch_to.default_content() # get back out of iframe
    driver.find_element_by_css_selector("#buttonlogoff > span.button_inner").click() # Click logoff
    driver.find_element_by_css_selector("div.button_middle").click() # Click yes to logoff
    wait.until(lambda driver:driver.title.lower().startswith('home'))
    driver.close()

if __name__ == '__main__':
    main()
code automation programming python
January 10, 2016

Automated Push” Restaurant menu

I love trying to automate the world, it just feels like magic some of the time! I also really enjoy information coming to me, instead of having to seek it. As we are still only in 2016, we have no world killing Artificial Intelligence (yet). So we have to start small, ease the first world problems! So I decided to make my workplaces’ restaurant menu come to me!

I decided to write it in Ruby and use push notifications, rather than email or SMS. It uses a web automation framework called Selenium, which is available in many languages, including Javascript, Python, Java and obviously Ruby!

#!/usr/bin/env ruby
#Encoding: UTF-8
require 'nokogiri'
require 'net/http'
require 'open-uri' 
require 'rushover'
TOKEN="" # Fill this in, get from Pushover
USER="" # Fill this in, get from Pushover

page_url = "http://www.ksg.ie/restaurants/ericsson/athlone/menu/index.php"
today = Time.now.strftime("%A")
items = []
page = Nokogiri::HTML(open(page_url, :proxy => nil))
page.xpath("//h2[contains(.,'#{today}')]/following-sibling::div[contains(@class, 'content')]/h4[contains(.,'Main')]/following-sibling::p[position() >= 0 and not(position() > 3)]").each do |node|
 items << node.text
end
items[0] = "\n\n " + items[0]
items.map { |word| "\n\n=#{word}" }

client = Rushover::Client.new(TOKEN)
resp = client.notify(USER, items.join("\n\n"), :sound => "classical", :device => "iPhone", :priority => 0, :title => "Today's Menu")
resp.ok? # => true

# Can check response from pushover if you want to verify it's sent
#puts resp.ok?

I have it set to run as a cron job on my work laptop, with an added bonus that it doesn’t run when I’m on holiday as the laptop is off. Here’s an example of the cron job:

# m h  dom mon dow   command
45 10 * * 1,2,3,4,5 $HOME/canteen-menu.rb

As you can see in the comments, the format for cron (on Ubuntu Linux) is: Minute, Hour, Day of Month, Month, Day of Week, Command.

So the time it runs is 10:45, any day of month, any month, only run Mon-Fri (1,2,3,4,5) and run our script command, easy!

code programming ruby automation
January 2, 2016

Adventures with Docker

I’m a huge fan of Docker ever since I started to use it, approximately a year after it was started. It’s one of those technologies where when you start to use it, you immediately know it’s going to be a sea change in how things are done in the industry. It’s definitely going to be more impactful then Virtual Machines.

We’ve used Docker to great effect where I work. We’re building a huge system called Ericsson Network Manager, which will manage the networks of the future. As such, it’s a hugely complex problem to compartmentalise parts of the product, to be able to just use the part that you work directly on.

There has been two approaches inside the company:

  1. Encapsulate the whole system
  2. Just encapsulate the product

In my product area, we’ve focused on number 2. We did this because we wanted to focus on two things:

  1. Automated functional testing
  2. Automated integration testing

And again, number 2 was the main catalyst, as the existing infrastructure we had in place was very compromised. We have a central Continuous Integration service, but with over 100 teams, they can’t suit every team and every product.

So we decided to start small and just have Docker prove itself by taking over parts of our infrastructure where we ran integration tests. We have had many issues, but they have been mostly with the immaturity of Docker, for which Docker hasn’t provided a centralised solution (like cleaning up unused images, which can fill up a whole disk and then bad things happen). Elsewhere companies like Spotify and Google have written custom solutions to many of these such issues.

The biggest advantages we’ve gotten have been:

  • Experience with Docker
  • A cleaner deployment for local development
  • Much more reliable and predictable integration tests
  • Less work to do with local deployment

Disadvantages:

  • More infrastructure that’s not core to the product
  • More to learn (some people can be resistant to new technology!)
  • More maintenance when things go wrong

Overall for us as a product area I think Docker has been a worthwhile investment for sure. It’s definitely not as stable as Virtual Machines (which at this point is very old technology), but the possibilities it brings to speed up development and deployment are really exciting to me!

docker
December 1, 2015

Deliveries App

Deliveries helps you track packages from major shopping (Amazon, Apple) as well as nearly every major package distribution company like DHL, UPS and FedEx. I’ve used the app for years and it’s really impressed me.

It can sync via iCloud or the Junecloud cloud service by the makers of the app, Junecloud (not confusing at all, hey!). It started on the Mac as a simple dashboard widget (for those of you who remember those, rarely used these days), but it’s since grown to have its own Mac app also. Considering many people will have a lot of packages coming from everywhere this Christmas, it’s definitely a really useful app to have!

app review ios review
November 22, 2015

Adventures with React

Recently I’d seen a post on Hacker News about a course for the React JavaScript Framework. I haven’t done much with JavaScript, apart from a course in work, which was focused on the language itself and jQuery. React is a front end framework, which allows you to build reusable components (and generate them with data.)

The only real experience I have is writing an application (using Node.js) for my MSc course which was an AJAX application for interacting with Amazon SimpleDB. It was fun to write and weird that I could use the same language from the front-end to the back-end.

Back to the React course, I completed it over a week, taking my time to break things (it’s the only way to get an appreciation for how things work!). It’s a really nicely laid out course, where you build a market for fish (a lot more exciting then it sounds). It doesn’t really cover any CSS or HTML, so you should at least know that before starting.

What I really liked in particular was the build tools, including Gulp. Gulp is like Make, Maven, Gradle, Ant etc. It’s very flexible and fast, with hundreds of modules on Node Package Manager to choose from. Part of building the app, you can make the assets (HTML+CSS etc) smaller (minification), run a web server and open your browser with the app automatically. Combined with a nifty tool called BrowserSync, it’s the fastest feedback cycle I’ve probably ever had. That makes it fantastic if your new to this kind of thing, nothing inspires happiness more than seeing instant results!

Also the course does a pretty good job at taking you from today’s JavaScript standard (ES5) to the more modern equivalents (ES6, ES7). JavaScript in time is set to become a much nicer language to write, the only problem is compatibility. Luckily the course touches on Babel which happily transpiles all your fancy newer JavaScript down the widely supported standard of ES5. The compatibility will probably be painful, it always is - judging by Python (2 to 3), Perl and all the other languages which have sought to remove cruft.

All in all I’d very much recommend it. I even re-wrote Grogan Burners (My Fathers business) website to use React components after!