SlideShare a Scribd company logo
1 of 29
Download to read offline
Developing
The Bachelor
What is The Bachelor?
1.   The show
     Reality dating game show


2.   Competition
     Contestants compete to be selected through elimination rounds.


3.   Limited Participants
     Only 25 participants in the TV show; a Facebook game lets the
     audience play too!
The Development Team
 Nicholas Asch
 Alice Bevan-McGregor
 Nicolas Cadou
 Blaise Laflamme
 Egor Miadzvedeu
 Zachary Allatt
Infrastructure
Libraries
Main libs
   Pyramid
   MongoEngine
   Mako
   zc.buildout
   nose and lettuce

Also
   SCSS
   futures
   apscheduler
   marrow.mailer
Architecture
Run of the mill MVC-ish
Controller
Pyramid Routes + Views

Model
Core API + Data Access Layer

View
Views + Templates
Architecture
Thin views

 @view_config(route_name='audition_enter', http_cache=0,
              renderer='bachelor:templates/show/audition/enter.mako')
 def audition_enter(self):
     return dict(show=self._show)

 @view_config(route_name='audition_enter_confirm', request_method='POST',
         xhr=True, renderer='json', http_cache=0)
 def audition_ajax(self):
     if self.request.POST.get('photo'):
         photo = json.loads(self.request.POST.get('photo'))['url']
     else:
         photo = None

    self.show_api.audition(self._show, picture=photo)

    return dict(next=self.request.route_path('audition_list'))
Architecture
Fat views (yuk!)

 @view_config(route_name='event_list', http_cache=0,
              renderer='bachelor:templates/event/list.mako')
 def list_(self):
     """ Return a list of events, customized for the user. """
     page = int(self.request.params.get('page', 1))
     reward = Tuneable.get_event_gain(self.user_api.level(self.user))
     cost = Tuneable.get_topic_cost(self.user_api.level(self.user))
     score = self.user_api.get_score()
     can_participate = cost.validate(score)
     details = cost.validate(score, True)
     notifications = self.user_api.notifications(self.user)

    allowed_cities = Tuneable.get_allowed_cities(self.user_api.level())
    current_city = self.request.session.get('current_city', None)

    if self.request.method == 'POST' and 'city' in self.request.params:
        current_city = self.request.POST.get('city', None)

     if not current_city:
         current_city = allowed_cities[0]
     elif current_city not in allowed_cities:
         current_city = 'hell' # easter egg for those who like to play
 with
                                # data

    self.request.session['current_city'] = current_city
    self.request.session['allowed_cities'] = allowed_cities

    paginated_events = Pagination(self.event_api.get_user_events(
        with_events=True, city=current_city),
        Tuneable.get_per_page('events'),
        Tuneable.get_pages_per_chunk())

    return dict(
            user_events=paginated_events.get_page_items(page),
            pagination=paginated_events,
            current_page=page,
            reward=reward,
            requirement=cost,
            can_participate=can_participate,
            req_details=details,
            notifications=notifications,
            cities=Tuneable.get_cities(),
current_city=current_city,
    allowed_cities=allowed_cities,
)
Architecture
Core API and Data Access
We keep them separate

Instead of littering the API with stuff like this

  u = UserIO.one(user_id)

  if not u:
      topic = []
  else:
      topic = u['answers'].get(event_id, {}).get(str(topic_id), [])




We use object model methods

  topic = UserEvent.get_topic_answers(user_id, event_id, topic_id)




The code then become easier to understand

  def matches(self, user_id, event_id, topic_id):
      """ Return a list of users whose answer is similar. """
      topic = UserEvent.get_topic_answers(user_id, event_id, topic_id)

     if topic is None:
         return None

     return UserEvent.matches(user_id, event_id, topic_id)
MongoDB
     schema-less
     non-relational
     full of awesomeness!

Well-suited to building clusters.

Harder to break.



The State of MongoDB in Python
Choices for MongoDB in Python are limited to MongoEngine and MongoKit.

So we rolled our own on top of raw pymongo.

But then we were even more limited.

Switching to MongoEngine.

Really nice!
Migrations
NoSQL == no migrations, right?
WRONG!!
   DB is schema-less
   But for the sake of sanity, app shouldn't be
   Data and content can change

Migration modes
   In Python, on-the-fly
   Migration scripts
   Auto-migrate on deployment
Lettuce
Is lettuce a vegetable or a testing platform?!

 Feature: Main show
     In order ward off boredom and hopefully get laid
     As a facebook user and bachelor wannabe
     I want to take part in the best of the best
     And that means playing the Bachelor game

    # Actors:
    #   - B: Bachelor
    #   - C: Contestant
    #   - S: System
    #   - F: Friend

    Scenario:   B   starts a show
        Given   B   has enough points to start a show
         When   B   initiates a new show
          And   B   selects four of his pictures
          And   B   starts the audition
         Then   B   should loose the expected points to start a show
          And   B   should not yet have access to the audition results
          And   B   should not be able to start another show

    Scenario:   S selects the audition winners
        Given   the allotted audition time has elapsed
          And   24 C or more have entered the audition
         When   S selects the 24 best matches
         Then   there should be 24 C in the show
          And   the lives of countless others shall be ruined
Lettuce
Lettuce steps map to Python code:

 Scenario:   C   looses episode 1
     Given   C   has lost episode 1
      When   C   asks for episode 1 results
      Then   C   should be informed that he failed the profound cuteness test
       And   C   should gain the points earned by episode 1 losers
       And   C   should should not have access to episode 2




 @step(u'Then C should be informed that he failed the profound cuteness
 test')
 def
 then_c_should_be_informed_that_he_failed_the_profound_cuteness_test(step):
     w.golem.as_bachelor()
     losers = w.golem.get('12_roses_losers', True)

    for l in losers:
        user = w.golem.user_api.get_user(l)
        w.golem.as_contestant(user)
        show = w.golem.show_api.get_by_id(w.golem.show_id)
        is_winner = w.golem.show_api.collect_12roses(show=show)
        assert not is_winner
Facebook Integration
Facebook lets you take advantage of an existing account, their friends, and
social sharing. Great for marketting, no fun for developers.


  # What's the purpose of this... there's no id when you ask to add the
  # app so it raises the exception before a user can register
  # ncadou: the purpose of this check is to prevent the creation of blank
  # users when the facebook API actually fails. See commit 7bfef9df70f7.
  #if not 'id' in user:
  #    # TODO log something
  #    raise Exception('Facebook API error')
  if user.get('error', None):
      return dict()
Facebook API Issues
Only the most critical ones...

     Downtime
     Speed (Latency in every request)
     Sudden Changes
     Documentation - Comparable to Early-Stage FOSS
How Your Public API Can Be
Better
K.I.S.S.

      Namespaces are one honking great idea -- let's do
      more of those!
      Seriously, hierarchical organization is easy to understand and use.


      Document every API call
      Explain what it does and any nuances.


      Document every variable
      Type, possible options (and what each option means), and if its
      required.
Mapping Game Logic to Objects
Multiplayer social games are step and role based.
System Tasks and Timers
Certain tasks have time limits or system requirements


  def make_steps():
      g = GameSteps()

      # Episode 0 - Audition
      g.add(bachelor=g.step('show_create', episode=0))
      g.add(bachelor=g.step('audition_wait', has_url=False, episode=0,
                            can_skip=True),
            contestant=g.step('audition_enter', episode=0))
      g.add(contestant=g.step('audition_wait', can_skip=True, has_url=False,
                              episode=0))
      g.add(system=g.step('select_contestants', has_url=False, is_stop=True,
                          timer=Tuneable().get_durations(0),
                          callback=callback.select_contestants))
      g.add(bachelor=g.step('audition_result', label='Reveal Audition
  Results',
                            episode=0),
            contestant=g.step('audition_result', label='Reveal Audition
  Results',
                              episode=0))
Worker Processes
MongoDB as RPC.

   Needed for timed game events.
   Needed because Facebook's API is slow.
   Background task execution.
   Immediate or scheduled.
   Captures responses and exceptions.
   Acts as a state machine with locking.
Worker Processes
MongoDB as RPC.

   Stores job data in a collection.
   Notifications via capped collection.
   Uses Futures for parallel execution of tasks.
   Uses APScheduler for timed execution of tasks.
   Atomic locking using "update if current" mechanic.
Capped Collections
MongoDB as a low-latency queue.

    Limited size, optionally limited document count.
    Ring buffer design.
    Insert order.
    Updates allowed… mostly.


    Used by MongoDB for replication.
    Tailable cursors.
    Long-poll push, like IMAP IDLE.




Live demonstration time!
Example Job Record
Stored in a permanant collection.

  {
      "_id" : ObjectId("4ea3717f9bfbb601d2000002"),
      "state" : "new", // pending, dead, cancelled, running, finished

      "callable" : "c__builtin__nprintnp1n.",
      "args" : [ "Task", 0 ],
      "kwargs" : { },

      "created" : ISODate("2011-10-23T01:44:31.446Z"),
      "creator" : [ "Lucifer", 298, 466 ],
      "owner" : null, // [ "Lucifer", 324, 456 ]

      // If scheduled, not immediate:
      "when": ISODate("...")

      // If in progress or completed...
      "acquired" : ISODate("..."),

      // If completed...
      "result" : null,
      "exception" : null,
      "completed" : ISODate("..."),
  }
Example Notification Record
Stored in the capped collection.

  // Workaround for MongoDB quirk.
  { "_id" : ObjectId("4ea371629bfbb601c8000000"), "nop" : true }

  { // New job.
      "_id" : ObjectId("4ea371769bfbb601d2000001"),
      "job_id" : ObjectId("4ea371769bfbb601d2000000"),
      "creator" : [ "Lucifer", 298, 466 ]
  }

  { // Finished job.
      "_id" : ObjectId("4ea371769bfbb601c8000001"),
      "job_id" : ObjectId("4ea371769bfbb601d2000000"),
      "creator" : [ "Lucifer", 324, 456 ],
      "result" : true
  }
Example Queue Runner
Python generators are teh win.


  def queue(collection, query=None):
      if not collection.find():
          # This is to prevent a terrible infinite busy loop while empty.
          collection.insert(dict(nop=True))

      last = None
      query = query or {}

      cursor = collection.find(query, slave_ok=True, tailable=True,
  await_data=True)

      while True:                            # Primary retry loop.
          try:
              while cursor.alive:            # Inner record loop; may time
  out.
                  for record in cursor:
                      last = record['_id']
                      yield record

         except OperationFailure:
             pass

          retry_query = {"_id": {"$gte": last}}
          retry_query.update(query)
          cursor = collection.find(retry_query, slave_ok=True,
  tailable=True, await_data=True)
Example Job Handler
Job locking to prevent accidental execution.

  def handler(self, job_id):
      # Build the dictionary update.
      update = dict(acquired=datetime.utcnow(), state="running",
              owner=self.identity)

      try:
          result = self.jobs.update(dict(_id=job_id, state="pending",
  owner=None),
                  {"$set": update}, safe=True)
      except:
          raise AcquireFailed()

      if not result['updatedExisting']: raise AcquireFailed()

      try:
          job = self.jobs.find(dict(_id=job_id), limit=1,
                  fields=['callable', 'args', 'kwargs'])[0]
      except: # This should, in theory, never happen unless MongoDB goes
  away.
          raise AcquireFailed()

      obj = pickle.loads(job['callable'].encode('ascii'))
      args = job.get('args', [])
      kwargs = job.get('kwargs', {})

      return obj(*args, **kwargs)
Questions?
Comments?

More Related Content

What's hot

Building a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing StrategiesBuilding a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing StrategiesCiaranMcNulty
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)Javier Eguiluz
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machineŁukasz Chruściel
 
Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)Python Ireland
 
Symfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusSymfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusIgnacio Martín
 
Apex 5 plugins for everyone version 2018
Apex 5 plugins for everyone   version 2018Apex 5 plugins for everyone   version 2018
Apex 5 plugins for everyone version 2018Alan Arentsen
 
Applying Compiler Techniques to Iterate At Blazing Speed
Applying Compiler Techniques to Iterate At Blazing SpeedApplying Compiler Techniques to Iterate At Blazing Speed
Applying Compiler Techniques to Iterate At Blazing SpeedPascal-Louis Perez
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modelingCodemotion
 
Curso Symfony - Clase 2
Curso Symfony - Clase 2Curso Symfony - Clase 2
Curso Symfony - Clase 2Javier Eguiluz
 
Two Trains and Other Refactoring Analogies
Two Trains and Other Refactoring AnalogiesTwo Trains and Other Refactoring Analogies
Two Trains and Other Refactoring AnalogiesKevin London
 
Intro to computer vision in .net
Intro to computer vision in .netIntro to computer vision in .net
Intro to computer vision in .netStephen Lorello
 
Diving into HHVM Extensions (PHPNW Conference 2015)
Diving into HHVM Extensions (PHPNW Conference 2015)Diving into HHVM Extensions (PHPNW Conference 2015)
Diving into HHVM Extensions (PHPNW Conference 2015)James Titcumb
 
Functional Programming in JavaScript by Luis Atencio
Functional Programming in JavaScript by Luis AtencioFunctional Programming in JavaScript by Luis Atencio
Functional Programming in JavaScript by Luis AtencioLuis Atencio
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4Javier Eguiluz
 
James Thomas - Serverless Machine Learning With TensorFlow - Codemotion Berli...
James Thomas - Serverless Machine Learning With TensorFlow - Codemotion Berli...James Thomas - Serverless Machine Learning With TensorFlow - Codemotion Berli...
James Thomas - Serverless Machine Learning With TensorFlow - Codemotion Berli...Codemotion
 
ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...
ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...
ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...Istanbul Tech Talks
 
JavaScript in 2016
JavaScript in 2016JavaScript in 2016
JavaScript in 2016Codemotion
 

What's hot (20)

Building a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing StrategiesBuilding a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing Strategies
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machine
 
informatics practices practical file
informatics practices practical fileinformatics practices practical file
informatics practices practical file
 
Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)
 
Data Validation models
Data Validation modelsData Validation models
Data Validation models
 
Symfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusSymfony 4 Workshop - Limenius
Symfony 4 Workshop - Limenius
 
Apex 5 plugins for everyone version 2018
Apex 5 plugins for everyone   version 2018Apex 5 plugins for everyone   version 2018
Apex 5 plugins for everyone version 2018
 
Applying Compiler Techniques to Iterate At Blazing Speed
Applying Compiler Techniques to Iterate At Blazing SpeedApplying Compiler Techniques to Iterate At Blazing Speed
Applying Compiler Techniques to Iterate At Blazing Speed
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
Curso Symfony - Clase 2
Curso Symfony - Clase 2Curso Symfony - Clase 2
Curso Symfony - Clase 2
 
Two Trains and Other Refactoring Analogies
Two Trains and Other Refactoring AnalogiesTwo Trains and Other Refactoring Analogies
Two Trains and Other Refactoring Analogies
 
Intro to computer vision in .net
Intro to computer vision in .netIntro to computer vision in .net
Intro to computer vision in .net
 
Diving into HHVM Extensions (PHPNW Conference 2015)
Diving into HHVM Extensions (PHPNW Conference 2015)Diving into HHVM Extensions (PHPNW Conference 2015)
Diving into HHVM Extensions (PHPNW Conference 2015)
 
Refactoring
RefactoringRefactoring
Refactoring
 
Functional Programming in JavaScript by Luis Atencio
Functional Programming in JavaScript by Luis AtencioFunctional Programming in JavaScript by Luis Atencio
Functional Programming in JavaScript by Luis Atencio
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4
 
James Thomas - Serverless Machine Learning With TensorFlow - Codemotion Berli...
James Thomas - Serverless Machine Learning With TensorFlow - Codemotion Berli...James Thomas - Serverless Machine Learning With TensorFlow - Codemotion Berli...
James Thomas - Serverless Machine Learning With TensorFlow - Codemotion Berli...
 
ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...
ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...
ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...
 
JavaScript in 2016
JavaScript in 2016JavaScript in 2016
JavaScript in 2016
 

Viewers also liked

Mp24: Fabulous Mobile Development with and without Python
Mp24: Fabulous Mobile Development with and without PythonMp24: Fabulous Mobile Development with and without Python
Mp24: Fabulous Mobile Development with and without PythonMontreal Python
 
Mp26 : How do you Solve a Problem like Santa Claus?
Mp26 : How do you Solve a Problem like Santa Claus?Mp26 : How do you Solve a Problem like Santa Claus?
Mp26 : How do you Solve a Problem like Santa Claus?Montreal Python
 
Mp26 : Connecting Startups with Talents
Mp26 : Connecting Startups with TalentsMp26 : Connecting Startups with Talents
Mp26 : Connecting Startups with TalentsMontreal Python
 
Mp25: Audio Fingerprinting and metadata correction with Python
Mp25: Audio Fingerprinting and metadata correction with PythonMp25: Audio Fingerprinting and metadata correction with Python
Mp25: Audio Fingerprinting and metadata correction with PythonMontreal Python
 
Mp25: Optical Music Recognition with Python
Mp25: Optical Music Recognition with PythonMp25: Optical Music Recognition with Python
Mp25: Optical Music Recognition with PythonMontreal Python
 
Mp26 : Tachyon, sloppiness is bliss
Mp26 : Tachyon, sloppiness is blissMp26 : Tachyon, sloppiness is bliss
Mp26 : Tachyon, sloppiness is blissMontreal Python
 
Mp25 Message Switching for Actor Based Designs
Mp25 Message Switching for Actor Based DesignsMp25 Message Switching for Actor Based Designs
Mp25 Message Switching for Actor Based DesignsMontreal Python
 

Viewers also liked (7)

Mp24: Fabulous Mobile Development with and without Python
Mp24: Fabulous Mobile Development with and without PythonMp24: Fabulous Mobile Development with and without Python
Mp24: Fabulous Mobile Development with and without Python
 
Mp26 : How do you Solve a Problem like Santa Claus?
Mp26 : How do you Solve a Problem like Santa Claus?Mp26 : How do you Solve a Problem like Santa Claus?
Mp26 : How do you Solve a Problem like Santa Claus?
 
Mp26 : Connecting Startups with Talents
Mp26 : Connecting Startups with TalentsMp26 : Connecting Startups with Talents
Mp26 : Connecting Startups with Talents
 
Mp25: Audio Fingerprinting and metadata correction with Python
Mp25: Audio Fingerprinting and metadata correction with PythonMp25: Audio Fingerprinting and metadata correction with Python
Mp25: Audio Fingerprinting and metadata correction with Python
 
Mp25: Optical Music Recognition with Python
Mp25: Optical Music Recognition with PythonMp25: Optical Music Recognition with Python
Mp25: Optical Music Recognition with Python
 
Mp26 : Tachyon, sloppiness is bliss
Mp26 : Tachyon, sloppiness is blissMp26 : Tachyon, sloppiness is bliss
Mp26 : Tachyon, sloppiness is bliss
 
Mp25 Message Switching for Actor Based Designs
Mp25 Message Switching for Actor Based DesignsMp25 Message Switching for Actor Based Designs
Mp25 Message Switching for Actor Based Designs
 

Similar to Mp24: The Bachelor, a facebook game

Designing REST API automation tests in Kotlin
Designing REST API automation tests in KotlinDesigning REST API automation tests in Kotlin
Designing REST API automation tests in KotlinDmitriy Sobko
 
Cross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineCross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineAndy McKay
 
Adopting F# at SBTech
Adopting F# at SBTechAdopting F# at SBTech
Adopting F# at SBTechAntya Dev
 
Tips and tricks for building api heavy ruby on rails applications
Tips and tricks for building api heavy ruby on rails applicationsTips and tricks for building api heavy ruby on rails applications
Tips and tricks for building api heavy ruby on rails applicationsTim Cull
 
Art & music vs Google App Engine
Art & music vs Google App EngineArt & music vs Google App Engine
Art & music vs Google App Enginethomas alisi
 
Design Summit - Rails 4 Migration - Aaron Patterson
Design Summit - Rails 4 Migration - Aaron PattersonDesign Summit - Rails 4 Migration - Aaron Patterson
Design Summit - Rails 4 Migration - Aaron PattersonManageIQ
 
SystemVerilog OOP Ovm Features Summary
SystemVerilog OOP Ovm Features SummarySystemVerilog OOP Ovm Features Summary
SystemVerilog OOP Ovm Features SummaryAmal Khailtash
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015Fernando Daciuk
 
MLSEV Virtual. From my First BigML Project to Production
MLSEV Virtual. From my First BigML Project to ProductionMLSEV Virtual. From my First BigML Project to Production
MLSEV Virtual. From my First BigML Project to ProductionBigML, Inc
 
Easy path to machine learning
Easy path to machine learningEasy path to machine learning
Easy path to machine learningwesley chun
 
Mobile, web and cloud - the triple crown of modern applications
Mobile, web and cloud -  the triple crown of modern applicationsMobile, web and cloud -  the triple crown of modern applications
Mobile, web and cloud - the triple crown of modern applicationsIdo Green
 
From Ruby to Node.js
From Ruby to Node.jsFrom Ruby to Node.js
From Ruby to Node.jsjubilem
 
AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)
AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)
AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)Amazon Web Services
 
The Final Programming Project
The Final Programming ProjectThe Final Programming Project
The Final Programming ProjectSage Jacobs
 
Extreme Swift
Extreme SwiftExtreme Swift
Extreme SwiftMovel
 
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...Stanfy
 

Similar to Mp24: The Bachelor, a facebook game (20)

Designing REST API automation tests in Kotlin
Designing REST API automation tests in KotlinDesigning REST API automation tests in Kotlin
Designing REST API automation tests in Kotlin
 
huhu
huhuhuhu
huhu
 
Python, WebRTC and You (v2)
Python, WebRTC and You (v2)Python, WebRTC and You (v2)
Python, WebRTC and You (v2)
 
Cross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineCross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App Engine
 
Adopting F# at SBTech
Adopting F# at SBTechAdopting F# at SBTech
Adopting F# at SBTech
 
SOLID Ruby, SOLID Rails
SOLID Ruby, SOLID RailsSOLID Ruby, SOLID Rails
SOLID Ruby, SOLID Rails
 
mobl
moblmobl
mobl
 
Tips and tricks for building api heavy ruby on rails applications
Tips and tricks for building api heavy ruby on rails applicationsTips and tricks for building api heavy ruby on rails applications
Tips and tricks for building api heavy ruby on rails applications
 
Art & music vs Google App Engine
Art & music vs Google App EngineArt & music vs Google App Engine
Art & music vs Google App Engine
 
Design Summit - Rails 4 Migration - Aaron Patterson
Design Summit - Rails 4 Migration - Aaron PattersonDesign Summit - Rails 4 Migration - Aaron Patterson
Design Summit - Rails 4 Migration - Aaron Patterson
 
SystemVerilog OOP Ovm Features Summary
SystemVerilog OOP Ovm Features SummarySystemVerilog OOP Ovm Features Summary
SystemVerilog OOP Ovm Features Summary
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015
 
MLSEV Virtual. From my First BigML Project to Production
MLSEV Virtual. From my First BigML Project to ProductionMLSEV Virtual. From my First BigML Project to Production
MLSEV Virtual. From my First BigML Project to Production
 
Easy path to machine learning
Easy path to machine learningEasy path to machine learning
Easy path to machine learning
 
Mobile, web and cloud - the triple crown of modern applications
Mobile, web and cloud -  the triple crown of modern applicationsMobile, web and cloud -  the triple crown of modern applications
Mobile, web and cloud - the triple crown of modern applications
 
From Ruby to Node.js
From Ruby to Node.jsFrom Ruby to Node.js
From Ruby to Node.js
 
AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)
AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)
AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)
 
The Final Programming Project
The Final Programming ProjectThe Final Programming Project
The Final Programming Project
 
Extreme Swift
Extreme SwiftExtreme Swift
Extreme Swift
 
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
 

Recently uploaded

The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxBkGupta21
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 

Recently uploaded (20)

The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptx
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 

Mp24: The Bachelor, a facebook game

  • 2. What is The Bachelor? 1. The show Reality dating game show 2. Competition Contestants compete to be selected through elimination rounds. 3. Limited Participants Only 25 participants in the TV show; a Facebook game lets the audience play too!
  • 3. The Development Team Nicholas Asch Alice Bevan-McGregor Nicolas Cadou Blaise Laflamme Egor Miadzvedeu Zachary Allatt
  • 4.
  • 6. Libraries Main libs Pyramid MongoEngine Mako zc.buildout nose and lettuce Also SCSS futures apscheduler marrow.mailer
  • 7. Architecture Run of the mill MVC-ish Controller Pyramid Routes + Views Model Core API + Data Access Layer View Views + Templates
  • 8. Architecture Thin views @view_config(route_name='audition_enter', http_cache=0, renderer='bachelor:templates/show/audition/enter.mako') def audition_enter(self): return dict(show=self._show) @view_config(route_name='audition_enter_confirm', request_method='POST', xhr=True, renderer='json', http_cache=0) def audition_ajax(self): if self.request.POST.get('photo'): photo = json.loads(self.request.POST.get('photo'))['url'] else: photo = None self.show_api.audition(self._show, picture=photo) return dict(next=self.request.route_path('audition_list'))
  • 9. Architecture Fat views (yuk!) @view_config(route_name='event_list', http_cache=0, renderer='bachelor:templates/event/list.mako') def list_(self): """ Return a list of events, customized for the user. """ page = int(self.request.params.get('page', 1)) reward = Tuneable.get_event_gain(self.user_api.level(self.user)) cost = Tuneable.get_topic_cost(self.user_api.level(self.user)) score = self.user_api.get_score() can_participate = cost.validate(score) details = cost.validate(score, True) notifications = self.user_api.notifications(self.user) allowed_cities = Tuneable.get_allowed_cities(self.user_api.level()) current_city = self.request.session.get('current_city', None) if self.request.method == 'POST' and 'city' in self.request.params: current_city = self.request.POST.get('city', None) if not current_city: current_city = allowed_cities[0] elif current_city not in allowed_cities: current_city = 'hell' # easter egg for those who like to play with # data self.request.session['current_city'] = current_city self.request.session['allowed_cities'] = allowed_cities paginated_events = Pagination(self.event_api.get_user_events( with_events=True, city=current_city), Tuneable.get_per_page('events'), Tuneable.get_pages_per_chunk()) return dict( user_events=paginated_events.get_page_items(page), pagination=paginated_events, current_page=page, reward=reward, requirement=cost, can_participate=can_participate, req_details=details, notifications=notifications, cities=Tuneable.get_cities(),
  • 10. current_city=current_city, allowed_cities=allowed_cities, )
  • 11. Architecture Core API and Data Access We keep them separate Instead of littering the API with stuff like this u = UserIO.one(user_id) if not u: topic = [] else: topic = u['answers'].get(event_id, {}).get(str(topic_id), []) We use object model methods topic = UserEvent.get_topic_answers(user_id, event_id, topic_id) The code then become easier to understand def matches(self, user_id, event_id, topic_id): """ Return a list of users whose answer is similar. """ topic = UserEvent.get_topic_answers(user_id, event_id, topic_id) if topic is None: return None return UserEvent.matches(user_id, event_id, topic_id)
  • 12. MongoDB schema-less non-relational full of awesomeness! Well-suited to building clusters. Harder to break. The State of MongoDB in Python Choices for MongoDB in Python are limited to MongoEngine and MongoKit. So we rolled our own on top of raw pymongo. But then we were even more limited. Switching to MongoEngine. Really nice!
  • 13. Migrations NoSQL == no migrations, right? WRONG!! DB is schema-less But for the sake of sanity, app shouldn't be Data and content can change Migration modes In Python, on-the-fly Migration scripts Auto-migrate on deployment
  • 14. Lettuce Is lettuce a vegetable or a testing platform?! Feature: Main show In order ward off boredom and hopefully get laid As a facebook user and bachelor wannabe I want to take part in the best of the best And that means playing the Bachelor game # Actors: # - B: Bachelor # - C: Contestant # - S: System # - F: Friend Scenario: B starts a show Given B has enough points to start a show When B initiates a new show And B selects four of his pictures And B starts the audition Then B should loose the expected points to start a show And B should not yet have access to the audition results And B should not be able to start another show Scenario: S selects the audition winners Given the allotted audition time has elapsed And 24 C or more have entered the audition When S selects the 24 best matches Then there should be 24 C in the show And the lives of countless others shall be ruined
  • 15. Lettuce Lettuce steps map to Python code: Scenario: C looses episode 1 Given C has lost episode 1 When C asks for episode 1 results Then C should be informed that he failed the profound cuteness test And C should gain the points earned by episode 1 losers And C should should not have access to episode 2 @step(u'Then C should be informed that he failed the profound cuteness test') def then_c_should_be_informed_that_he_failed_the_profound_cuteness_test(step): w.golem.as_bachelor() losers = w.golem.get('12_roses_losers', True) for l in losers: user = w.golem.user_api.get_user(l) w.golem.as_contestant(user) show = w.golem.show_api.get_by_id(w.golem.show_id) is_winner = w.golem.show_api.collect_12roses(show=show) assert not is_winner
  • 16.
  • 17. Facebook Integration Facebook lets you take advantage of an existing account, their friends, and social sharing. Great for marketting, no fun for developers. # What's the purpose of this... there's no id when you ask to add the # app so it raises the exception before a user can register # ncadou: the purpose of this check is to prevent the creation of blank # users when the facebook API actually fails. See commit 7bfef9df70f7. #if not 'id' in user: # # TODO log something # raise Exception('Facebook API error') if user.get('error', None): return dict()
  • 18. Facebook API Issues Only the most critical ones... Downtime Speed (Latency in every request) Sudden Changes Documentation - Comparable to Early-Stage FOSS
  • 19. How Your Public API Can Be Better K.I.S.S. Namespaces are one honking great idea -- let's do more of those! Seriously, hierarchical organization is easy to understand and use. Document every API call Explain what it does and any nuances. Document every variable Type, possible options (and what each option means), and if its required.
  • 20. Mapping Game Logic to Objects Multiplayer social games are step and role based.
  • 21. System Tasks and Timers Certain tasks have time limits or system requirements def make_steps(): g = GameSteps() # Episode 0 - Audition g.add(bachelor=g.step('show_create', episode=0)) g.add(bachelor=g.step('audition_wait', has_url=False, episode=0, can_skip=True), contestant=g.step('audition_enter', episode=0)) g.add(contestant=g.step('audition_wait', can_skip=True, has_url=False, episode=0)) g.add(system=g.step('select_contestants', has_url=False, is_stop=True, timer=Tuneable().get_durations(0), callback=callback.select_contestants)) g.add(bachelor=g.step('audition_result', label='Reveal Audition Results', episode=0), contestant=g.step('audition_result', label='Reveal Audition Results', episode=0))
  • 22. Worker Processes MongoDB as RPC. Needed for timed game events. Needed because Facebook's API is slow. Background task execution. Immediate or scheduled. Captures responses and exceptions. Acts as a state machine with locking.
  • 23. Worker Processes MongoDB as RPC. Stores job data in a collection. Notifications via capped collection. Uses Futures for parallel execution of tasks. Uses APScheduler for timed execution of tasks. Atomic locking using "update if current" mechanic.
  • 24. Capped Collections MongoDB as a low-latency queue. Limited size, optionally limited document count. Ring buffer design. Insert order. Updates allowed… mostly. Used by MongoDB for replication. Tailable cursors. Long-poll push, like IMAP IDLE. Live demonstration time!
  • 25. Example Job Record Stored in a permanant collection. { "_id" : ObjectId("4ea3717f9bfbb601d2000002"), "state" : "new", // pending, dead, cancelled, running, finished "callable" : "c__builtin__nprintnp1n.", "args" : [ "Task", 0 ], "kwargs" : { }, "created" : ISODate("2011-10-23T01:44:31.446Z"), "creator" : [ "Lucifer", 298, 466 ], "owner" : null, // [ "Lucifer", 324, 456 ] // If scheduled, not immediate: "when": ISODate("...") // If in progress or completed... "acquired" : ISODate("..."), // If completed... "result" : null, "exception" : null, "completed" : ISODate("..."), }
  • 26. Example Notification Record Stored in the capped collection. // Workaround for MongoDB quirk. { "_id" : ObjectId("4ea371629bfbb601c8000000"), "nop" : true } { // New job. "_id" : ObjectId("4ea371769bfbb601d2000001"), "job_id" : ObjectId("4ea371769bfbb601d2000000"), "creator" : [ "Lucifer", 298, 466 ] } { // Finished job. "_id" : ObjectId("4ea371769bfbb601c8000001"), "job_id" : ObjectId("4ea371769bfbb601d2000000"), "creator" : [ "Lucifer", 324, 456 ], "result" : true }
  • 27. Example Queue Runner Python generators are teh win. def queue(collection, query=None): if not collection.find(): # This is to prevent a terrible infinite busy loop while empty. collection.insert(dict(nop=True)) last = None query = query or {} cursor = collection.find(query, slave_ok=True, tailable=True, await_data=True) while True: # Primary retry loop. try: while cursor.alive: # Inner record loop; may time out. for record in cursor: last = record['_id'] yield record except OperationFailure: pass retry_query = {"_id": {"$gte": last}} retry_query.update(query) cursor = collection.find(retry_query, slave_ok=True, tailable=True, await_data=True)
  • 28. Example Job Handler Job locking to prevent accidental execution. def handler(self, job_id): # Build the dictionary update. update = dict(acquired=datetime.utcnow(), state="running", owner=self.identity) try: result = self.jobs.update(dict(_id=job_id, state="pending", owner=None), {"$set": update}, safe=True) except: raise AcquireFailed() if not result['updatedExisting']: raise AcquireFailed() try: job = self.jobs.find(dict(_id=job_id), limit=1, fields=['callable', 'args', 'kwargs'])[0] except: # This should, in theory, never happen unless MongoDB goes away. raise AcquireFailed() obj = pickle.loads(job['callable'].encode('ascii')) args = job.get('args', []) kwargs = job.get('kwargs', {}) return obj(*args, **kwargs)