Sep 082014
 

Summer is over, and it’s time to really get to work on my Sabbatical project.

I did do some work this summer – I’ve read 3 of the books on my Sabbatical Reading List (and added a few more to the list) and I’ve finally de-lurked on the OpenMRS developer mailing list and in some of the online meetings, and I’ve made a decision to convert all of my course materials to Markdown (the better to track changes on GitHub – see a future post). But, it’s all been pretty passive.

So, the Friday before the Labor Day Weekend, I decided it was time to get back to the “develop code in OpenMRS” part of the project.

Since it had been a couple of months since I had set up  my development environment and tried to build the OpenMRS code, I decided that starting over from scratch (mostly) would not be a bad idea. Here is what I did:

  1. Read OpenMRS’s Getting Started as a Developer wiki page. I had already set up my OpenMRS ID, and signed up for the developer mailing list. I already had a GitHub account, as well.
  2. Read Development ProcessfromOpenMRS Developers’ Guide, and got the code:
    1. Fork openmrs-core repository.
    2. Clone my fork onto my computer.
    3. Set the OpenMRS repository as my upstream remote so that I can pull changes from the main project into my local working copy.
  3. Set up development environment based on Get Set Up from the OpenMRS Developers’ Guide. I chose the section on Manual Installation because Iwantto be able to develop code fortheOpenMRS core application. I followed the general outline of this section, but went about some of the software installations differently.
    1. Install MySQL. Because I am setting up my development environment on a Mac, I installed MySQL using Homebrew
    2. Install Maven using Homebrew
    3. Install Git using Homebrew (actually, I already had Git installed, but I made sure it was up-to-date)
    4. BuildtheOpenMRS code:
      cd openmrs-core
      maven clean install
    5. RuntheOpenMRS web app through the jetty server:
      cd webapp
      mvn jetty:run
    6. The first time you run the web app, it will take you through the Setup wizard
  4. Set up Eclipse. I already had Eclipse installed, but I made sure that my version was up-to-date. Eclipse had been updated to a new major version (Luna) since the last version I installed (Kepler). 1
  5. Git IDE Integration: Since EGit is already installed in current versions of Eclipse, and I’ve already forked and cloned the repository, I really only needed to do the To import as a Maven project section, to get the the projected into Eclipse.
  6. Build the OpenMRS code under Eclipse. I followed the steps in the following section:
    1. How to run the build
    2. How to run Junit
    3. How to Run Web Application

Now that I have a working environment that builds and runs, the next step is to choose a ticket to work on.

  1. In the past, I had just downloaded the newest version, and replaced what I already had installed. I figured that there must be a way to do the update without having to reinstall and reconfigure all of my plugins. It turns out that you can add the release’s repository to the Available Software Sites: http://wiki.eclipse.org/FAQ_How_do_I_upgrade_Eclipse%3F
May 282014
 

cue Ozzy Osbourne laughter…

This blog is coming to you direct from Amtrak Northeast Regional Train 95, where Stoney Jackson and I are on our way to POSSE 2014 at Drexel University in Philadelphia, PA. This is becoming an annual tradition for us.

So, why is it called the Coding Train? Because we are spending the 5 hour train ride writing code!

When we did this for the first time last year, we worked on the code for the grading scripts that I had started writing in bash (https://github.com/kwurst/grading-scripts/tree/bash-version). Stoney started adding error checking, and then a Python version – neither of which he finished, but we learned a lot about how GitHub works for collaborative development.

This year we discussed a number of options for what project we would sprint on (after we spent a lot of time on professor-talk about curricula, and courses, and learning outcomes, and assessment) but we ended up back on the same project. This time our starting point was the Python conversion of the original scripts that I had started in December, and which I had just begun to refactor this month (https://github.com/kwurst/grading-scripts/tree/master).

Stoney has been doing some serious refactoring on the code, adding one major new feature: a JSON configuration file so that I don’t need 15 different scripts – just different configuration files to pass to a single, more general script. He’s also undertaken a major cleanup of the code, and added the project’s first unit test!

I, on the other hand, have been installing tools that Stoney suggested – git flow and git bash prompt, and in the process having to debug my Mac’s installation of Homebrew and cleaning up my .bashrc file (being completely ignored by my shell) and my .bash_profile file (full of lots of cruft from previous installs.)

Stoney has just pushed his branch, so now it’s time for me to pull it, and test it on some data on my computer. And we’re almost to Philadelphia, so just in time…

Dec 282013
 

Now that we have the CS Department’s GitLab server set up, and CS-140 Lab 1 is rewritten and tested using the new server, I’ve started to think about how to automate my interactions with the server. I had already  written some Bash scripts to interact with the Bitbucket server to get student code, convert it to PDF files, and put it back on the server after grading. Those scripts should still work fine with GitLab, since it’s just git on a different server.

One thing that I had not been able to automate previously is the step of issuing a pull request for students to merge my grading branch into their repository. This was not too much of an issue when there were only 6 students in the summer class (so only 3 repositories per lab assignment), but it was going to take more time with ~48 students in the spring class. While reading RSS feeds, I came across a post mentioning the GitLab API. This could be the solution to my problems! And there’s a Python module for the API! I had already been writing Python scripts to make my grading easier, and had been starting to rewrite my Bash scripts in Python.

I started playing with the GitLab API in Python, and had managed to create a merge request (GitLab’s term for pull request.) I had also noticed that you could create GitLab accounts through the API. This seemed like something I should pursue – creating ~48 accounts per semester seemed like something that should be automated.

Since I intended to post my code on Github, one of the first issues I had to address is how to avoid publishing my private token for GitLab. I could have put in a dummy token before pushing my code, but I would have to remember to do that before every time I committed my code. The solution to this issue was solved through the use of the .gitignore file. If I put my token into a file, then I could add a line to my .gitignore file so that it would not be committed.

# Private GitLab Token - not to be stored in repository #
########################################################
gitlabtoken.txt

Then I could just read the token out of the file, and use that string.

# Get my private GitLab token
# stored in a file so that I can .gitignore the file
token = open('gitlabtoken.txt').readline().strip()

After importing the pyapi-gitlab module, I could use that token, along with the server’s URL to create a GitLab object. Notice, that I had to turn ssl verification off, since we only have a self-signed certificate.

# Create a GitLab object
# For our server, verify_ssl has to be False, since we have a self-signed certificate
git = gitlab.Gitlab(GITLAB_URL, token, verify_ssl=False)

Creating a user account is pretty simple using the API:

# Create the account  
success = git.createuser(name, username, password, email)

The returned success value is a boolean — either it worked, or it failed (but you can’t tell why…).

One thing that’s a bit odd about the createuser call, is that you have set a password for the user, but the notification email to the doesn’t include the password. (If you create a user account from the web interface, it generates a random password, includes it in the notification email to the user, and requires the user to change their password when first logging in.) And, the password you set doesn’t seem to work either!

So, I’m just telling the students that they should use the “Forgot Password” link to have a password reset email sent to them, and then proceed from there. (If this is ever fixed, I’ll have to generated a random password.)

Getting the class list as a CSV file from the Blackboard Grade Center is pretty easy, and the first three rows contain the student’s last name, first name, and username. I can use those three strings to generate the name, username, and email needed for the createuser API call.

The only challenge with processing the CSV file is that Blackboard puts some strange character at the beginning of the file, so the file has to be opened with utf-8 encoding. (And the header line needs to be thrown away.)

The last thing I wanted to add is a way to have optional verbose output, so that I could see if the user creation was working. (I decided that it should always notify the user if the account creation failed.)  To do this I had to learn two new things about Python: how to parse arguments1, and how to send output to stderr.

I used the argparse module:

import argparse
# Set up to parse arguments
parser = argparse.ArgumentParser()
parser.add_argument('filename', help='Blackboard CSV filename with user information')
parser.add_argument('-v', '--verbose', help='increase output verbosity', action='store_true')
args = parser.parse_args()

and used the verbose argument to determine what to print:

if not success:
    sys.stderr.write('Failed to create acccount for: '+name+ ', '+username+', '+email+'\n') 
elif args.verbose:
    sys.stderr.write('Created account for: '+name+', '+username+', '+email+'\n')

Full code is on Github here.

  1. I already knew how to do simple argument parsing, but I wanted to learn how to deal with optional arguments.