Tuesday, January 24, 2012

GCal4RubyFAIL

Wow, has it really been almost a year since I updated this? It's not that I haven't been coding and working (though I haven't been coding as much as I would like), I just kind of forgot about documenting it here.

Some other time, I'll show you guys my hott QR code event programs (I'm going to make a demo video--and it's not really coding, and it's not rails, but it's just a neat idea that I had to combine tech and theater), and also my neat-o e-costume-measurement-sheets (and that IS RoR and IS totally hott and also useful. Besides being a great way to keep your costume measurement data on the cloud, you can do things like put your actors in order by arm length, should you ever need to do that). Oh, and my robot that tells me when my doubling is broken! ALL OF THESE THINGS WORK AND ARE SHINY.

BUT today what I want to write about is a victory/failure I'm experiencing right now. If anyone has used the gcal4ruby gem, maybe you can help me out. I've been fighting with it for months now, and it just won't give in.

So, the original problem that started me down this whole "learn to code and make things for theaters" road was that scheduling play rehearsals SUCKS. People always say, "Can't you just use Google calendar and see when people are available?" but it doesn't work like that. Here's the breakdown of what you have to do in order to schedule, say, a week of rehearsal.


  1. Make a chart of every scene (or French scene, which is a subdivision of a scene--you have a new French scene when a character leaves or arrives. So if you are in Act 2, Scene 1, you start in French scene 1.2.a. Then Fonzie shows up, and you're in 1.2.b, just like that!), with what characters are in each scene.
  2. Make a chart on top of the first chart that tells you which actors are in each scene (remember, one actor might play many parts).
  3. Get everyone's conflicts (in gcal or whatever)
  4. Look at each time block of your rehearsals. I like to schedule in 30-minute chunks. I start with the first 30 minutes. I take all the people in the cast, throw out the names of those who are not available during that time block, and then consult the chart from step 2 to determine what scenes I could do with the people who are available.
  5. Repeat step 4. Realize that there's only one, very short scene that you can rehearse on Thursdays between 7:30 and 9. Curse.
  6. Finally figure out a half-way reasonable schedule, send to actors.
  7. Read the emails pointing out that I made this or that error in going from a calendar to another calendar to a chart and back to my first calendar, all by hand.
  8. Drink to excess.
You might have noticed that most of that could be done more accurately by computers. Computers like doing repetitive and annoying tasks. They are very good at dealing with all the little details that humans forget. When this process is assisted by computers, it is way less error prone.

After a lot of work--it seemed like every piece I built needed some other piece--I finally managed to build a rehearsal scheduler! Sort of. I can get it to work in the console. Here's some code to show you how that looks:

alisha@Madagascar:~/code/henslowe$ script/console

Loading development environment (Rails 2.3.14)

>> r= Rehearsal.find(2)

=> #<Rehearsal id: 2, start_time: "2011-11-12 21:41:00", end_time: "2011-12-13 00:41:00", act_id: nil, scene_id: nil, french_scene_id: nil, created_at: "2011-12-29 21:42:07", updated_at: "2011-12-29 21:42:07", production_id: 1>
>> p = Production.find(1)
=> [all the details about the production go here]

>> frenchies = p.play.french_scenes
=> [This part lists all the French scenes in the play and all their attributes. I took it out because it is massive.
>> frenchies.each do |fs|
?> if fs.can_rehearse?(p, r.start_time, r.end_time)
>> puts fs.full_path
>> end
>> end
1.1.a
Joel cannot rehearse 2.1.a
1.1.b
Joel cannot rehearse 2.1.b
Joel cannot rehearse 1.1.c
Joel cannot rehearse 2.1.c
Joel cannot rehearse 1.1.d
Joel cannot rehearse 2.1.d
Joel cannot rehearse 1.1.e
Joel cannot rehearse 2.1.e
Joel cannot rehearse 1.1.f
Joel cannot rehearse 2.1.f
Joel cannot rehearse 1.1.g
Joel cannot rehearse 2.1.g
1.1.h
Joel cannot rehearse 2.1.h
Joel cannot rehearse 1.1.i
1.1.j
1.1.k
Joel cannot rehearse 1.1.l
1.1.m
Joel cannot rehearse 1.1.n
1.1.o
Joel cannot rehearse 1.1.p
Joel cannot rehearse 1.1.q

In the above example, an actor named Joel is in a lot of the scenes, and he has a conflict that precludes him from attending the rehearsal. I've asked my robot slaves to tell me not just that a scene cannot be rehearsed with the available actors, but also who the problem is. If an actor has a small part in a scene, it's possible that I would want to rehearse the other actors without him. Now I can see that I have seven French scenes to choose from, assuming I want all the actors present. I can edit the rehearsal record to tell it which French scene (or scene, or act) I want to rehearse.

The problem is that this is all on the console, and it's all on my local machine. I want to turn it into a usable web application. I want the actor-users to be able to log in and enter their own conflicts instead of sending me confusing emails and expecting me to figure out that when they say "next Thursday," they don't necessarily mean the one that is coming next.

I thought it would be great to use Google calendar for that aspect, and for managing the production calendar. I could even use Google's Open Auth for logins, which is way more secure and user-friendly than managing that stuff on my server. Before I bother trying to build a webified version of what I have working in the console and THEN adding the Google calendar piece, I thought it would be simplest to just start with the calendar piece so I don't have to rip stuff out and rebuild.

After looking at a number of gems, I decided that gcal4ruby does most of what I need and doesn't seem to have a lot of extra junk. It also seems to be relatively well-documented, as RoR gems go. So, I added it to my Gemfile and ran the rake thing--no errors reported. Bundler says it's installed at /usr/lib/ruby/gems/1.8/gems/gcal4ruby-0.5.5 , right next door to all my gems that are working (like, for example, mysql).

In the gcal4ruby documentation, it says that the first thing to do is to declare and begin a Service. Now, I'm not going to lie, I have no idea where that information should even begin to live in my rails app, but I'm sure I can figure it out. However, when I try to declare a new Service, this is what I get:


>> service = Service.new
NameError: uninitialized constant Service
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.14/lib/active_support/dependencies.rb:469:in `load_missing_constant'
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.14/lib/active_support/dependencies.rb:106:in `const_missing'
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.14/lib/active_support/dependencies.rb:118:in `const_missing'
from (irb):1
>>



I'm stuck on STEP ONE. My best guess is that I don't really know what I'm doing--I really don't quite know how to use gems and plugins (is there a difference between the two? I DON'T KNOW), and I've just gotten really lucky with the ones I've used so far. But um...If anyone can point out my error, or tell me where to start looking for it, I'd really appreciate that. I'm super frustrated right now, and have been for a really long time. This is why I abandoned this ship and built the aforementioned "sort actors by shoe size" app. Because it was dead simple.

Saturday, March 5, 2011

Celebrating some small victories

Things have finally been going really really well here at Henslowe's Cloud. I feel like I've hit some point where the learning curve gets way less intense, and I can do a lot of very complex things.

However, they were a total pain in the rear to figure out, so I wanted to write down what I did, and maybe help someone else out. As you may recall, I decided to use the production as the central point of my schema. It's just a massive join table, essentially, bringing together all the different elements of a production. It makes sense, because a production is the unique confluence of a play, a set of actors, a theater, a space, a director, a designer, technicians. It has defined dates. So that was good. Then I faced this problem: How do you connect actors to characters? In two different productions at the same theater, the same actor could play Hamlet and Laertes. I had to tie that specific casting choice to the specific production. This meant that I had to put a join table on my join table, basically. Each Production :has_many castings. A casting is the confluence of a user (actor), a character, and a production. User A could play Hamlet in two different productions. User A could play Hamlet in Production 1, but Laertes in Production 2, at the same theater, and there aren't any conflicts. I know it seems needlessly complex (and IT IS), but it's working. Everything simpler created conflicts if I wanted to have multiple productions with the same play and users.

It sounds complicated, but here's how it works. (I'm just including the relevant bits of code here).


class Casting < ActiveRecord::Base
attr_accessible :character_id, :user_id, :production_id
belongs_to :production
belongs_to :character
belongs_to :user
end


So, you see, a Casting just joins a character and a user on a production. That's it!


class Production < ActiveRecord::Base
attr_accessible :castings_attributes
has_many :castings, :dependent => :destroy
accepts_nested_attributes_for :castings
has_many :actor_users, :through => :castings, :source => :user
belongs_to :play
has_many :characters, :through => :play
end


Here's one part that took me FO EVAH to figure out--that attr_accessible line MUST INCLUDE :castings_attributes . NOT just :castings.

Also, note the has_many :actor_users bit--that's the solution to the problem I posted about before, where I couldn't figure out how to use the nested_has_many_through plugin. I can just make up the "actor_user" part. I could make it "red_penguins" and I don't have to declare it anywhere. The source has to be a real model, though.

So, and here's the part where I'm really proud of myself, if a play has a certain list of characters, and that list of characters never changes, when I create a new production, I want to create all the castings too, and give them their character names. Then all the casting director has to do is select the actor to fill that role. Here's my create action from my Productions controller.


def create
@production = Production.new(params[:production])
@production.play.characters.each do |c|
@production.castings.build(:character_id => c.id)
end
if @production.save
flash[:notice] = "Successfully created production."
redirect_to @production
else
render :action => 'new'
end
end


There's probably a more elegant way to write that, but it works, and that's good enough for me.
Click here for a screenshot of the Productions/edit page, where you can see this in action.

Once I figured out all that stuff with stacking the builds, I was in business. I also wrote a thing that will build all the acts for a play as it's creating the play. I'm going to go into more detail on this than I probably need to, because I searched and searched but could not find a tutorial on how to set up a temporary variable in rails. I hope some poor shmuck like me has an easier time googling.

Here we go. Just follow "number_of_acts." That's my temporary variable.

First of all, you need to add an attr_accessor line in the model that will take the temporary variable. You also need to include the temporary variable in the attr_accessible line. Mine looked like this:

class Play < ActiveRecord::Base attr_accessible :title, :author_id, :publication_date, :genre_ids, :summary, :number_of_acts attr_accessor :number_of_acts end


Then, in my view, I defined the number_of_acts and gave it a value. (This form might look odd. I'm using Formtastic, and I'm IN LOVE. I don't have much else to say about Formtastic, other than it's really awesome and totally the way forms should be.)

<% title "New Play" %>
<% semantic_form_for @play do |f| %>
<% f.inputs do %>
<%= f.input :author, :label_method => :name %>
<%= f.input :title %>
<%= f.input :publication_date, :start_year => 1300 %>
<%= f.input :genres, :as => :check_boxes, :collection => Genre.find(:all) %>
<%= f.input :summary %>
<%= f.input :number_of_acts, :as => :numeric %>
<% end %>
<%= f.buttons %>
<% end %>

<%= link_to "Back to List", plays_path %>



So, then I take the number_of_acts and pass it to the create action in my Plays controller.

def create
@play = Play.new(params[:play])
number_of_acts = @play.number_of_acts.to_i
n = 1
number_of_acts.times do |b|
@play.acts.build(:number => n)
n +=1
end
if @play.save
flash[:notice] = "Successfully created play."
redirect_to @play
else
render :action => 'new'
end
end

Note that I even make my robot slaves set the act number as they go!

There's still a lot to do, of course. The next step is to write the acts edit page and all the stuff to do with scenes and French scenes (a French scene is generally a sub-scene unit, which lasts while all the same characters are on stage. When someone exits, it's a new French scene, although it may well be the same scene. These are very useful if you're trying to figure out doubling.). Once I get that working, I want to add fill-in predictive text to the casting interface, so you can just start typing someone's name and it will populate before you get very far into it. I have no idea how to do that, but it would be convenient.

I also need to revisit the authorization/authentication stuff. When I rearranged everything to center on the production, I'm sure I thoroughly ruined the auth work I had done. I'll probably just tear it all down and start over. I'd love to use Google OpenID or Facebook Connect for logins, because it would be easier and arguably more secure than handling it myself. I'm not sure how to do that, but I'll figure it out.

The good news is, I'm very close to a complete model. Once I have that done, I can start asking it questions, and that's where the real power is.
  • How many times has this actor played Ophelia?
  • Who is available for rehearsal on Friday night? What scenes can we do with those people?
  • Here's my casting list--have I messed up my doubling? Is any actor onstage as two characters at once?
  • How many seats are left for Friday's show?
And the list goes on.
(one more question--HTML messes up my pretty ruby indenting. Is there any way to tell it not to do that?)

So, HERE is a question:
I've learned tons about Ruby and Rails in the past year and a half. I'm learning more every day. I started with this crazy complex problem, though. What less-complex problem should I solve? I constantly see simpler things making crazy bank, and I think, "Why didn't I think of that?"

Thursday, October 28, 2010

Grasshoppers

We came unto the land whither thou sentest us, and surely it floweth with milk and honey; and this is the fruit of it...And there we saw the giants...and we were in our own sight as grasshoppers, and so we were in their sight.




I HATE being so close and yet so far. OOOOH I hate it. I have practically everything working in my models and database. Yay! but for some reason, I can't get Users to connect to Technicians (I should probably change the name of that model to TechnicalRoles or something) or Characters (via Castings). I'm using ianwhite's nested_has_many_through plugin, and I'm sure there is something I'm just not getting. Also, I wish documentation for this sort of thing was more helpful. You get what you pay for, but maybe I could be of service to the Rails community by first having someone explain their plugin to me, and then writing better documentation. Of course, maybe they're all happy with their documentation and I just need to get better at reading it.

Sigh.

Ok, so here's what I came up with.
The Production model is the nexus of everything. It's the one unique event. A theater does lots of different plays; each play is done by lots of different theaters; actors work for various theaters. A production only happens in one moment in time and is a unique combination of participants, theater spaces, text, etc. This was my breakthrough, and I'm sure it's the right thing. To that end:

class Production < ActiveRecord::Base
  attr_accessible :date
  has_many :castings
  has_many :users, :through => :castings
  has_many :technicians
  has_many :users, :through => :technicians, :source =>:users
  belongs_to :theater
  belongs_to :play
  has_many :characters, :through => :play
end

Castings is basically a join model to connect Users to Characters. The characters are also connected to their plays.

The part that I'm hung up on is connecting Characters to Users.
These work:
>> @production.characters
=> [#, #, #]

>> @user.productions
=> [#, #, #]

These don't:
>> @user.characters
>> @character.productions


It throws this error:
NameError: uninitialized constant User::Characters
    from /home/alisha/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:105:in `const_missing'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2199:in `compute_type'
    from /home/alisha/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/core_ext/kernel/reporting.rb:11:in `silence_warnings'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2195:in `compute_type'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/reflection.rb:156:in `send'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/reflection.rb:156:in `klass'
    from /home/alisha/code/henslowe/vendor/plugins/nested_has_many_through/lib/nested_has_many_through.rb:43:in `construct_nested_join_attributes'
    from /home/alisha/code/henslowe/vendor/plugins/nested_has_many_through/lib/nested_has_many_through.rb:19:in `construct_conditions'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/associations/has_many_through_association.rb:201:in `construct_sql'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/associations/association_collection.rb:21:in `initialize'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/associations/has_many_through_association.rb:6:in `initialize'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/associations.rb:1300:in `new'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/associations.rb:1300:in `characters'

I'm not Rubiriffic enough to understand that error message.

So anyway, here are the relevant bits of the models that it's looking at.
class User < ActiveRecord::Base
  has_many :castings
  has_many :characters, :through => :castings, :dependent => :destroy
  has_many :productions, :through => :castings
  has_many :technicians
  has_many :productions, :through => :technicians
end

class Character < ActiveRecord::Base
  attr_accessible :name, :play_id
  belongs_to :play
  has_many :castings
  belongs_to :user, :through => :castings
end

class Casting < ActiveRecord::Base
  attr_accessible :user, :character
  belongs_to :production
  belongs_to :character
  belongs_to :user
end

class Production < ActiveRecord::Base
  has_many :castings
  has_many :users, :through => :castings
  has_many :technicians
  has_many :users, :through => :technicians, :source =>:users
end

The problem doesn't seem to be in the Casting model -- I can get all of a user's productions, which goes through the Casting model, but I can't get all of a user's characters. I can't get all of a character's productions, but I CAN get all of a production's characters.

I'm getting NoMethod errors when I try it:

@char.productions
NoMethodError: undefined method `productions' for #
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/attribute_methods.rb:260:in `method_missing'
@char.users
NoMethodError: undefined method `users' for #
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/attribute_methods.rb:260:in `method_missing'

I'm guessing the issue lies in the Character model, but I just can't figure it out. It's probably some stupid "that was singular but by convention it should be plural" crap nonsense. And I'm tired and I feel like a grasshopper today.

Thursday, October 21, 2010

Programming is hard



Let's go shopping!

:-/

I got about half of the schema reorg described above done. Some of the "has"es identified above probably should be "belongs_to" (does a production have a theater, a play, a director, or does a theater have a production and a play has the same production and ...? Given that the production is kind of the nexus of the whole thing, maybe it *does* have everything and belong to nothing....Oh heck, I don't know). My brain is kind of fried. Doing this one-handed on six hours of interrupted sleep probably isn't helping.

I'm going to take a break and do something I'm good at: Theater History! Yay!

Why am I calling this project "Henslowe's Cloud"?
The "cloud" part is for cloud computing. Part of the idea here is to put most of a theater's necessary information in the cloud so that if your theater burns down (hey, it happens, just ask Shakespeare...but only if you're performing Henry VIII), you don't lose all your info. Also, having it in the cloud makes it so some technically savvy person (like me woot) can administer it for you remotely. You don't have to worry about anything but scraping up the money to pay her. :)

The Henslowe part is cool.

 Henslowe as played by Geoffrey Rush in Shakespeare in Love.

Philip Henslowe was a theater manager . He wasn't an actor. He bought plays, hired actors, and ensured the whole enterprise made money. He managed the Admiral's Men, which competed with Shakespeare's company.

Yes, this is actually Henslowe's Diary.

The only reason anyone knows or cares about Henslowe is his "diary," a ledger of records for his theaters. It contains entries like this: "pd vnto the tyre man for mackynge of the devells sute & sperethes & for the witche for the playe of the iij brothers the 23rd of october 1602 some of xs ixd" ("paid unto the tireman for making of the devil's suit and ??? and for the witch for the play of the three brothers, sum of 10 shillings, 9 pence"). Thanks to Henslowe, we have some good idea of how much actors were paid, which plays were popular in the time, what kind of inventory the theaters had, in terms of props and costumes, etc. Henslowe's Diary is an incredibly valuable document for theater historians, but it's madly disorganized. Just imagine what Henslowe could have done if he'd only had a relational database!

...Thus, Henslowe's Cloud. I know it's dumb and obscure and theater history in-jokey. I love it all the same.

Wednesday, October 13, 2010

Productions are complex.

Ok, seriously, why can't I ever start with a simple problem when I decide to learn a new programming paradigm? (not just a language, but a whole paradigm. My last language was PERL, which is sooo not object-oriented. My first project was building a content model for Rosetta Stone, where I work, and mapping it against new texts to determine whether or not a user could read the new text, given X units of Rosetta Stone. Harder than it sounds, since I took into account new forms of known head words).

The problem at hand:
I want to build a stage management system. I want it to do LOTS of cool things.
Examples:
  • Have you ever noticed how every theater box office website SUCKS? I mean, really every one. Even the Kennedy Center, and they've got more money than God has angels. Why do they all suck? Selling tickets is not a hard problem, people. Why not just adapt ticketing software from, like...sports or something? Those people know what they're doing.
  • Maintain actor bios so that making the program is easy. Automatically add a credit whenever the actor is cast in any play managed by the system.
  • Help with doubling. This is when one actor plays several roles. It's frequently done in Shakespeare productions, where you have 40 characters, but several of them are in only a few scenes each. I did Antony and Cleopatra with 12 actors, and there are 43 characters. Doubling is a great theatrical technique, but it sucks to figure out. It's crazy error-prone. I think that writing a program that would create doubling charts is far too complicated for me (or practically anyone--some smart person once explained to me that this is basically the traveling salesman problem, and requires AI). I'd be happy to create a system that allows a director to propose a solution and then says whether or not it will work.
  • Create part-scripts. Say you have a 16th-century play, and you want to do it old-school. Your advertising gimmick is that the rehearsal process is just like it was in Shakespeare's day (as far as we can guess). The American Shakespeare Center's Renaissance Season every spring is like this. Well, you'd want to use part-scripts (also called "rolls"). A part script is where it has the couple lines before your character speaks, what your character says, and then a big jump to the next cue for your character. In the days before printing was cheap and easy, this is how actors received their lines. Now, it is true that many of Shakespeare's plays are available in part-script form. But what if you want to cut the script a bit? What if you are doing doubling and you want each actor to have a part-script that contains all the lines for all of that actor's characters? To the best of my knowledge, practically every theater does this using cut-and-paste. No automated solutions exist (except for the one I wrote in PERL, and which I plan to do in Ruby).
  • The biggie: scheduling rehearsals. This is how most directors/stage managers schedule rehearsals. You have one spreadsheet that has all the actors' conflicts (when they can't rehearse). You have another that shows all the scenes everyone is in. By going back and forth between spreadsheets, you figure out which scenes you can rehearse at each time slot. There's a very high probability for human error here, and it takes HOURS every week.
Those are just a few of the ideas I have. I really think this could be a useful service for theaters. The last problem is the one I chose to start with (because I'm dumb and overly ambitious). My trouble here is that it's so very complex to model a theater production. Then if I want to make it scalable (so that I could have several client theaters, and the whole system wouldn't collapse if two of them did Hamlet, or if a theater decided to do You Can't Take It With You for a second time, or if an actor worked for two different client theaters), it's a whole other can of worms.

I had a lot of this working, using a spaghetti junction of has_and_belongs_to_many join table things. Then I realized that it wasn't extensible. If I were modeling only one production, it would be great. But modeling only one production is not useful. I think that what I need to do is rewrite it to use has_many :through, with a "Production" model as the nexus. I'll still need other models, like a "Casting" model to connect users with roles. It's still a mess. I think the nested_has_many_through plugin will help here, but I'm a bit nervous that it won't. If it doesn't, I'm not sure what to do. So, here's my test schema...any comments would be greatly appreciated. I haven't implemented this yet because I'm sort of nervous about it...I have subversioned my present development instance, so it shouldn't be a big deal...but oh, the obfuscatory error messages! I can hardly bring myself to confront them.

 I need to bite the bullet and just do it...but I'm reluctant.

theater
has_many :productions
has_many :users, through :productions
has_many :plays, through :productions

production
belongs_to :theater
has_many :castings
belongs_to :play

user
has_many :credits, through :castings
has_many :productions, through :castings
has_many :conflicts
has_many :theaters, through :productions
has_many :plays, through :productions
has_many :acts, through :plays
has_many :scenes, through :acts

conflict
belongs_to :user

play
has_many :productions
has_many :theaters, through :productions
has_many :acts
has_many:scenes through :acts

act
has_many :scenes
belongs_to :play

scene
belongs_to :act
casting
has_many :users
has_many :credits #I'm saying credits instead of roles because "role" is a reserved model for my authorization thing. Also, "credit" allows for technicians as well as actors.