Thursday, May 29, 2014

Pair programming

I'm currently taking the EdX course CS 169.1x from Berkeley, just for fun. I signed up ages ago but of course when it started I was busy and ignored it, so now I'm starting about four weeks late and catching up real fast.

Unfortunately, among other drawbacks, this means I'm completing a few weeks of homework in a couple evenings and don't have the time to schedule any pair programming sessions. I think pair programming would be a really interesting way to approach this and will probably try and do some when I am working at a more leisurely pace, but until then: here is my replacement discussion of pair v solo programming.

As luck would have it, I spent an hour or so the other day with a senior dev at work both trying to debug a routing issue I was having. While he knows a lot more than I do, he admitted that routing and MVC are not things he is too familiar with, so he was jumping around the code looking for what was happening, and going slow enough that I could follow along. In this case, as I mentioned, I had spent time debugging this before coming to him for help, so one of the helpful things here was that I could see where he followed the same process I had (yay!) and where he did something different, that I perhaps hadn't thought of. I'm not sure if I would have gotten the same value from just coming to him without going through it myself first. From my reading of the forums, many of the students in this class have been doing exactly that - doing the homework and then setting up a peer programming session to re-do it. When this pairs them with somebody who has no idea what they are doing, they might not get the learning experience I did, but they get the opportunity to try and explain what they did the first time which should help solidify the concepts.

In my work session, the other guy couldn't figure out what was going on either. Eventually when we'd clarified exactly what we expected to happen and what was happening, he said that it was time to start doing some simplifying refactoring to remove some of the apparently unused scaffolding code around everything - and as soon as he removed the routing registration from inside an area, the Url.Action calls began working. Neither of us knew why, and I still have to talk to one of our more MVC-knowledgeable colleagues to figure it out - but that step is one that would have taken me far far longer to go to on my own!

So, peer progamming experience: as the junior dev, I've found it very useful. Later in this course I might get the chance to be the more knowledgeable one, and we'll see how that goes.

Thursday, March 27, 2014

app download statistics

Currently, I write my app targeted at windows phone 7.1, which means it runs on all windows phones out there. There are some nice new features in the windows phone 8 sdk, and it seems like a lot of effort to run multiple versions, so I have been thinking about just ditching wp7 and building only for wp8. Before doing that, however, I thought it would be nice to have some actual numbers on what phones are running my app.

I thought I'd have to add this in my own analytics call, but it turns out the Dev Center offers an excel export of your download stats that includes the phone OS Version for each purchaser (along with other info like manufacturer, mobile operator, phone language, and somehow even user gender and age group). So I downloaded that report for the last year and found 

- 140 users with wp7
- 225 users with wp8
- 78 users with unspecified OS versions (not sure how that works)

looks like over a third of my users are still on 7, so I should definitely make sure the app still works for them!

Tuesday, January 21, 2014

Automated Calendar Planning


My initial interest in AI planning comes from a promise I made to a friend years ago, which I was unable to keep. He was an amateur theater director, and he was complaining about the time and difficulty of building a rehearsal schedule, where constraints included
- actors available at different times
- rehearsal slots of different length
- scenes of different length
- scenes that contained varying combinations of actors
- a hard constraint that each scene be rehearsed at least once before the play opened (I think he actually said at least twice, ideally three times)
- a soft constraint that scenes would ideally be rehearsed in order
- the reluctant possibility that if it were impossible to have all the actors for a scene together for long enough, you could provide stand-ins to allow others to rehearse

As an over-ambitious junior programing student, I said I could build something that would do that! Unfortunately (although perhaps unsurprisingly), it turned out to be quite a bit more difficult than I had imagined, and I never got around to it. Last year, I started looking into the problem again and I did build a much simplified version for an unrelated scenario (given x conference attendees who have expressed preferences from 1-y for each of y conference workshops held over z timeslots, which attendees should go to which workshops?) but the Automated Theater Rehearsal Planner was still beyond me. In late January, as I poked through the list of uncompleted projects, I once again came across this one and decided to look seriously into it - as a professional programmer, surely this is something I can do! And in a serendipitous moment, while looking up resources on scheduling algorithms I discovered that this course had begun a few days earlier, so I signed up for it and this is now an active project that I hope to get into a useful state this year.

Some background info on the problem for reference

To understand the general domain of scheduling, I started at the broadest relevant page, http://en.wikipedia.org/wiki/Automated_planning_and_scheduling, and after looking through it selected these relevant links
- http://en.wikipedia.org/wiki/Preference-based_planning, which is relevant because of the soft constraints listed, such as preferring to rehearse in order and have all actors in a scene together
- http://en.wikipedia.org/wiki/Scheduling_(computing) (if one considers the director or the rehearsal space to be the resource being scheduled) which led to http://en.wikipedia.org/wiki/Job_Shop_Scheduling and eventually on to http://en.wikipedia.org/wiki/Genetic_algorithm_scheduling and finally to http://en.wikipedia.org/wiki/Scheduling_(computing)#Scheduling_optimization_problems and to this flow shop scheduling demonstration app: http://posh-wolf.herokuapp.com/ (although flow-shop appears more restricted than my problem). 


Since it seemed likely that at least a broadly similar tool existed, I searched for free tools to try out in order to clarify what I needed to build exactly. (Or if perhaps it already existed). When searching for basic 'scheduling tools' or 'resource planning', the results tended to be about processes, such as scheduling a factory line and supplier logistics for organising delivery schedules - it may turn out that this is the same problem, but they don't quite seem to match. When I searched for 'rehearsals' (or similar), there were no specifically relevant results, however I found that there is a ton of software available for employee scheduling. Since it seemed that this should have significant overlap with the problem I'm looking at, it seemed worth installing a couple of the freeware options to check them out.
http://www.kappix.com/download.htm
http://www.simulation.co.uk/

I found that the basic issue with these tools is that employee scheduling operates on the assumption that you have a fixed number of slots to fill for each session, and are moving the people into the slots (and in the more complex tools, matching the people to the required tasks for each slot). For the rehearsals, I have a variable number of slots, based on how many people can attend, and I want to see which scenes (tasks) are relevant to the largest number of these people. 

Now, for the Creative Challenge project in the class: I'm going to get my scheduler app into a user-friendly state and post it to the class.

Sunday, January 19, 2014

Deploying New Relic on Azure



I created a New Relic account a while ago, but never got around to deploying it, and then when I tried to log in again it told me
a) my account was not verified, I needed to click the verification link in the email
b) the verification link in the email was not valid
I emailed support and never heard back, so just gave up on it.

Today I saw they were doing a new promotion of access to Tutes+, which looked useful, so I figured it was time to sign up for a new account (as the old one was completely empty anyway, easier to abandon it than work through support). I signed up for a new account and chose to deploy the python agent. They provided some pretty easy-looking instructions, and I went to open up the django local copy of my python site, and it wouldn't run. Somehow, I have no idea why, I seem not to have python2.7 on my machine any more. So I had to reinstall that, following the steps at http://docs.python-guide.org/en/latest/starting/install/win/. Since I was resetting it all, I figured I'd try better practices this time and installed virtualenv as well.

C:\Dropbox\code\optimization\SimplySchedule>virtualenv --python=C:\Python27\python27.exe scheduler

(env)>pip install newrelic

....
   ***************************************************************************

   WARNING: The optional C extension components of the Python agent could
   not be compiled. This can occur where a compiler is not present on the
   target system or the Python installation does not have the corresponding
   developer package installed. The Python agent will instead be installed
   without the extensions. The consequence of this is that although the
   Python agent will still run, JSON encoding/decoding speedups will not be
   available, nor will some of the non core features of the Python agent.

   INFO: Only pure Python agent was installed.

   ***************************************************************************
....

I guess I probably don't have a C compiler installed, now I feel bad.

but....then it said I needed to relaunch the app with a different init string. Or, since I don't have access to actually do the launching, I need to edit the wsgi file.
modify the WSGI script file/module for your web application and insert at the start of that file:
import newrelic.agent
newrelic.agent.initialize('newrelic.ini')
I had no idea where that was at first, but after some googling (and temporary confusion between this and the wfastcgi.py file) I found the wsgi file in django.core and added those lines, but got an import error trying to start the server. I eventually realised that I had installed newrelic for python3, not python2 (sigh) but while noodling around looking for hints on that, I found a more detailed new relic setup guide which mentioned you should edit the newrelic.ini file to choose a real log file location and set the name of the app. So I did pip2 install newrelic, copied the files to the site-packages directory, and python27 manage.py runserver : everything works!

While I was fiddling with everything, I also took the time to set up a slightly more useful index/home page for the site. Then when I went to push to github for deployment (which is super nice) the local repository wasn't associated with the remote - maybe I lost all the old settings when I installed windows 8.1 over the preview? So I also had to reassociate my local copy of the repository with github to push to that - and it's running with the new pages :)

Sunday, January 5, 2014

SQLite woes, cont.


At the end of my last post, I could launch the app and view events, but then got a Null Reference Exception while saving the events into the db. Oddly enough, debugging through the code showed that I was getting a SQLite error code 11 on *every second event*, and then getting a Null Reference Exception after a set of events.The call stack showed:

{System.NullReferenceException: NullReferenceException
   at Community.CsharpSqlite.Sqlite3.sqlite3BtreeClose(Btree& p)
   at Community.CsharpSqlite.Sqlite3.sqlite3_close(sqlite3 db)
   at SQLiteClient.SQLiteConnection.Dispose()
   at PAX7.Utilicode.DBHelper.Close()
   at PAX7.Utilicode.DBHelper.Finalize()}

perhaps I'm calling close twice? I added a breakpoint in the DBHelper.Close() method to check this. I also wrapped the code inside the method in a try-catch-throw to Little Watson block to avoid crashing the app with this exception.

{System.NullReferenceException: NullReferenceException
   at Community.CsharpSqlite.Sqlite3.sqlite3FindIndex(sqlite3 db, String zName, String zDb)
   at Community.CsharpSqlite.Sqlite3.sqlite3StartTable(Parse pParse, Token pName1, Token pName2, Int32 isTemp, Int32 isView, Int32 isVirtual, Int32 noErr)
   at Community.CsharpSqlite.Sqlite3.yy_reduce(yyParser yypParser, Int32 yyruleno)
   at Community.CsharpSqlite.Sqlite3.sqlite3Parser(yyParser yyp, Int32 yymajor, Token yyminor, Parse pParse)
   at Community.CsharpSqlite.Sqlite3.sqlite3RunParser(Parse pParse, String zSql, String& pzErrMsg)
   at Community.CsharpSqlite.Sqlite3.sqlite3Prepare(sqlite3 db, String zSql, Int32 nBytes, Int32 saveSqlFlag, Vdbe pReprepare, Vdbe& ppStmt, String& pzTail)
   at Community.CsharpSqlite.Sqlite3.sqlite3LockAndPrepare(sqlite3 db, String zSql, Int32 nBytes, Int32 saveSqlFlag, Vdbe pOld, Vdbe& ppStmt, String& pzTail)
   at Community.CsharpSqlite.Sqlite3.sqlite3_prepare(sqlite3 db, String zSql, Int32 nBytes, Vdbe& ppStmt, String& pzTail)
   at Community.CsharpSqlite.Sqlite3.sqlite3_exec(sqlite3 db, String zSql, dxCallback xCallback, Object pArg, String& pzErrMsg)
   at Community.CsharpSqlite.Sqlite3.sqlite3InitCallback(Object pInit, Int64 argc, Object p2, Object NotUsed)
   at Community.CsharpSqlite.Sqlite3.sqlite3InitOne(sqlite3 db, Int32 iDb, String& pzErrMsg)
   at Community.CsharpSqlite.Sqlite3.sqlite3Init(sqlite3 db, String& pzErrMsg)
   at Community.CsharpSqlite.Sqlite3.sqlite3ReadSchema(Parse pParse)
   at Community.CsharpSqlite.Sqlite3.sqlite3LocateTable(Parse pParse, Int32 isView, String zName, String zDbase)
   at Community.CsharpSqlite.Sqlite3.sqlite3SrcListLookup(Parse pParse, SrcList pSrc)
   at Community.CsharpSqlite.Sqlite3.sqlite3Insert(Parse pParse, SrcList pTabList, ExprList pList, Select pSelect, IdList pColumn, Int32 onError)
   at Community.CsharpSqlite.Sqlite3.sqlite3Insert(Parse pParse, SrcList pTabList, ExprList pList, Int32 null_4, IdList pColumn, Int32 onError)
   at Community.CsharpSqlite.Sqlite3.yy_reduce(yyParser yypParser, Int32 yyruleno)
   at Community.CsharpSqlite.Sqlite3.sqlite3Parser(yyParser yyp, Int32 yymajor, Token yyminor, Parse pParse)
   at Community.CsharpSqlite.Sqlite3.sqlite3RunParser(Parse pParse, String zSql, String& pzErrMsg)
   at Community.CsharpSqlite.Sqlite3.sqlite3Prepare(sqlite3 db, String zSql, Int32 nBytes, Int32 saveSqlFlag, Vdbe pReprepare, Vdbe& ppStmt, String& pzTail)
   at Community.CsharpSqlite.Sqlite3.sqlite3LockAndPrepare(sqlite3 db, String zSql, Int32 nBytes, Int32 saveSqlFlag, Vdbe pOld, Vdbe& ppStmt, String& pzTail)
   at Community.CsharpSqlite.Sqlite3.sqlite3_prepare_v2(sqlite3 db, String zSql, Int32 nBytes, Vdbe& ppStmt, Int32 dummy)
   at SQLiteClient.SQLiteCommand.ExecuteNonQuery[T](T toInsert)
   at PAX7.Utilicode.DBHelper.Insert[T](T obj, String statement)
   at PAX7.Model.Schedule.sqlInsertEvent(Event newEvent)}


this one
{System.NullReferenceException: NullReferenceException
   at Community.CsharpSqlite.Sqlite3.DbHasProperty(sqlite3 D, Int32 I, UInt16 P)
   at Community.CsharpSqlite.Sqlite3.sqlite3Init(sqlite3 db, String& pzErrMsg)
   at Community.CsharpSqlite.Sqlite3.sqlite3ReadSchema(Parse pParse)
   at Community.CsharpSqlite.Sqlite3.sqlite3LocateTable(Parse pParse, Int32 isView, String zName, String zDbase)
   at Community.CsharpSqlite.Sqlite3.sqlite3SrcListLookup(Parse pParse, SrcList pSrc)
   at Community.CsharpSqlite.Sqlite3.sqlite3Insert(Parse pParse, SrcList pTabList, ExprList pList, Select pSelect, IdList pColumn, Int32 onError)
   at Community.CsharpSqlite.Sqlite3.sqlite3Insert(Parse pParse, SrcList pTabList, ExprList pList, Int32 null_4, IdList pColumn, Int32 onError)
   at Community.CsharpSqlite.Sqlite3.yy_reduce(yyParser yypParser, Int32 yyruleno)
   at Community.CsharpSqlite.Sqlite3.sqlite3Parser(yyParser yyp, Int32 yymajor, Token yyminor, Parse pParse)
   at Community.CsharpSqlite.Sqlite3.sqlite3RunParser(Parse pParse, String zSql, String& pzErrMsg)
   at Community.CsharpSqlite.Sqlite3.sqlite3Prepare(sqlite3 db, String zSql, Int32 nBytes, Int32 saveSqlFlag, Vdbe pReprepare, Vdbe& ppStmt, String& pzTail)
   at Community.CsharpSqlite.Sqlite3.sqlite3LockAndPrepare(sqlite3 db, String zSql, Int32 nBytes, Int32 saveSqlFlag, Vdbe pOld, Vdbe& ppStmt, String& pzTail)
   at Community.CsharpSqlite.Sqlite3.sqlite3_prepare_v2(sqlite3 db, String zSql, Int32 nBytes, Vdbe& ppStmt, Int32 dummy)
   at SQLiteClient.SQLiteCommand.ExecuteNonQuery[T](T toInsert)
   at PAX7.Utilicode.DBHelper.Insert[T](T obj, String statement)
   at PAX7.Model.Schedule.sqlInsertEvent(Event newEvent)}


{SQLiteClient.SQLiteException: Unknown error
   at SQLiteClient.SQLiteCommand.ExecuteNonQuery[T](T toInsert)
   at PAX7.Utilicode.DBHelper.Insert[T](T obj, String statement)}

{System.NullReferenceException: NullReferenceException
   at Community.CsharpSqlite.Sqlite3.ENC(sqlite3 db)
   at Community.CsharpSqlite.Sqlite3.sqlite3VdbeExec(Vdbe p)
   at Community.CsharpSqlite.Sqlite3.sqlite3Step(Vdbe p)
   at Community.CsharpSqlite.Sqlite3.sqlite3_step(Vdbe pStmt)
   at Community.CsharpSqlite.Sqlite3.sqlite3_exec(sqlite3 db, String zSql, dxCallback xCallback, Object pArg, String& pzErrMsg)
   at Community.CsharpSqlite.Sqlite3.sqlite3InitCallback(Object pInit, Int64 argc, Object p2, Object NotUsed)
   at Community.CsharpSqlite.Sqlite3.sqlite3InitOne(sqlite3 db, Int32 iDb, String& pzErrMsg)
   at Community.CsharpSqlite.Sqlite3.sqlite3Init(sqlite3 db, String& pzErrMsg)
   at Community.CsharpSqlite.Sqlite3.sqlite3ReadSchema(Parse pParse)
   at Community.CsharpSqlite.Sqlite3.sqlite3LocateTable(Parse pParse, Int32 isView, String zName, String zDbase)
   at Community.CsharpSqlite.Sqlite3.sqlite3SrcListLookup(Parse pParse, SrcList pSrc)
   at Community.CsharpSqlite.Sqlite3.sqlite3Insert(Parse pParse, SrcList pTabList, ExprList pList, Select pSelect, IdList pColumn, Int32 onError)
   at Community.CsharpSqlite.Sqlite3.sqlite3Insert(Parse pParse, SrcList pTabList, ExprList pList, Int32 null_4, IdList pColumn, Int32 onError)
   at Community.CsharpSqlite.Sqlite3.yy_reduce(yyParser yypParser, Int32 yyruleno)
   at Community.CsharpSqlite.Sqlite3.sqlite3Parser(yyParser yyp, Int32 yymajor, Token yyminor, Parse pParse)
   at Community.CsharpSqlite.Sqlite3.sqlite3RunParser(Parse pParse, String zSql, String& pzErrMsg)
   at Community.CsharpSqlite.Sqlite3.sqlite3Prepare(sqlite3 db, String zSql, Int32 nBytes, Int32 saveSqlFlag, Vdbe pReprepare, Vdbe& ppStmt, String& pzTail)
   at Community.CsharpSqlite.Sqlite3.sqlite3LockAndPrepare(sqlite3 db, String zSql, Int32 nBytes, Int32 saveSqlFlag, Vdbe pOld, Vdbe& ppStmt, String& pzTail)
   at Community.CsharpSqlite.Sqlite3.sqlite3_prepare_v2(sqlite3 db, String zSql, Int32 nBytes, Vdbe& ppStmt, Int32 dummy)
   at SQLiteClient.SQLiteCommand.ExecuteNonQuery[T](T toInsert)
   at PAX7.Utilicode.DBHelper.Insert[T](T obj, String statement)
   at PAX7.Model.Schedule.sqlInsertEvent(Event newEvent)}



and this, seriously?

{SQLiteClient.SQLiteException: SQL logic error or missing database
   at SQLiteClient.SQLiteCommand.ExecuteNonQuery[T](T toInsert)
   at PAX7.Utilicode.DBHelper.Insert[T](T obj, String statement)}

+        ex    {SQLiteClient.SQLiteException: no such table: events
   at SQLiteClient.SQLiteCommand.ExecuteNonQuery[T](T toInsert)
   at PAX7.Utilicode.DBHelper.Insert[T](T obj, String statement)}    System.Exception {SQLiteClient.SQLiteException}

+        $exception    {System.ObjectDisposedException: Cannot access a closed file.
   at System.IO.__Error.FileNotOpen()
   at System.IO.FileStream.get_Length()
   at System.IO.IsolatedStorage.IsolatedStorageFileStream.get_Length()
   at Community.CsharpSqlite.Sqlite3.sqlite3_fileSize(sqlite3_file id, Int32& pSize)
   at Community.CsharpSqlite.Sqlite3.sqlite3OsFileSize(sqlite3_file id, Int32& pSize)
   at Community.CsharpSqlite.Sqlite3.sqlite3PagerPagecount(Pager pPager, Int32& pnPage)
   at Community.CsharpSqlite.Sqlite3.hasHotJournal(Pager pPager, Int32& pExists)
   at Community.CsharpSqlite.Sqlite3.sqlite3PagerSharedLock(Pager pPager)
   at Community.CsharpSqlite.Sqlite3.lockBtree(BtShared pBt)
   at Community.CsharpSqlite.Sqlite3.sqlite3BtreeBeginTrans(Btree p, Int32 wrflag)
   at Community.CsharpSqlite.Sqlite3.schemaIsValid(Parse pParse)
   at Community.CsharpSqlite.Sqlite3.sqlite3Prepare(sqlite3 db, String zSql, Int32 nBytes, Int32 saveSqlFlag, Vdbe pReprepare, Vdbe& ppStmt, String& pzTail)
   at Community.CsharpSqlite.Sqlite3.sqlite3LockAndPrepare(sqlite3 db, String zSql, Int32 nBytes, Int32 saveSqlFlag, Vdbe pOld, Vdbe& ppStmt, String& pzTail)
   at Community.CsharpSqlite.Sqlite3.sqlite3_prepare_v2(sqlite3 db, String zSql, Int32 nBytes, Vdbe& ppStmt, Int32 dummy)
   at SQLiteClient.SQLiteCommand.ExecuteNonQuery[T](T toInsert)
   at PAX7.Utilicode.DBHelper.Insert[T](T obj, String statement)
   at PAX7.Model.Schedule.sqlInsertEvent(Event newEvent)}    System.Exception {System.ObjectDisposedException}


while events from all four days are visible on first load, only events on friday are saved back to the database (inspected with isolated storage explorer). It has correctly saved the star and reminder for the event I added to my schedule, however.