Commit 6b9ddff

mo khan <mo@mokhan.ca>
2015-11-14 17:17:20
import from csv export.
1 parent 0cfdbd5
Changed files (5)
app/models/csv/exercise.rb
@@ -0,0 +1,5 @@
+class Csv::Exercise
+  attr_accessor :name, :weight_kg, :weight_lb
+  attr_accessor :sets
+end
+
app/models/csv/import.rb
@@ -1,11 +1,49 @@
+require "csv"
+
 class Csv::Import
+  attr_reader :user, :program
+
   def initialize(user, program)
+    @user = user
+    @program = program
   end
 
   def can_parse?(directory)
-    puts `ls -al #{directory}`
+    File.exist?(database_file(directory))
   end
 
   def import_from(directory)
+    ActiveRecord::Base.transaction do
+      ::CSV.foreach(database_file(directory)).drop(1).each do |row|
+        import(row)
+      end
+    end
+  end
+
+  private
+
+  def database_file(dir)
+    "#{dir}/spreadsheet-stronglifts.csv"
+  end
+
+  def import(row)
+    workout_row = Csv::Workout.map_from(row, user)
+
+    workout = program.workouts.find_by(name: workout_row.workout)
+    training_session = user.begin_workout(
+      workout,
+      workout_row.date,
+      workout_row.body_weight_lb.to_f)
+    training_session.exercise_sessions.destroy_all
+    workout.exercise_workouts.each do |exercise_workout|
+      row = workout_row.exercises.find do |x|
+        x.name.downcase == exercise_workout.exercise.name.downcase
+      end
+      training_session.train(
+        exercise_workout.exercise,
+        row.weight_lb,
+        row.sets
+      )
+    end
   end
 end
app/models/csv/workout.rb
@@ -0,0 +1,28 @@
+class Csv::Workout
+  attr_accessor :date, :note, :workout
+  attr_accessor :body_weight_kg, :body_weight_lb
+  attr_accessor :exercises
+
+  def self.map_from(row, user)
+    workout = new
+    day, month, year = row[0].split('/')
+    year = "20#{year}"
+    workout.date = user.timezone.local_to_utc(Time.utc(year, month, day))
+    workout.note = row[1]
+    workout.workout = row[2]
+    workout.body_weight_kg = row[3]
+    workout.body_weight_lb = row[4]
+    workout.exercises = []
+    all_exercises = row[5..(row.size)]
+    # skip additional exercises for now
+    all_exercises.take(3 * 8).each_slice(8) do |slice|
+      exercise = Csv::Exercise.new
+      exercise.name = slice[0]
+      exercise.weight_kg = slice[1]
+      exercise.weight_lb = slice[2]
+      exercise.sets = slice[3..(slice.size)]
+      workout.exercises << exercise
+    end
+    workout
+  end
+end
app/models/user.rb
@@ -9,7 +9,7 @@ class User < ActiveRecord::Base
   validates :username, presence: true, format: { with: USERNAME_REGEX }, uniqueness: true
   validates :email, presence: true, email: true, uniqueness: true
   validates_acceptance_of :terms_and_conditions
-  
+
   after_create :create_profile
 
   def timezone
@@ -58,11 +58,10 @@ class User < ActiveRecord::Base
       user.authenticate(password)
     end
   end
-  
+
   private
-  
+
     def create_profile
       self.profile = Profile.create!(user: self, gender: nil, social_tolerance: nil)
     end
-  
 end
spec/models/csv/import_spec.rb
@@ -0,0 +1,102 @@
+require 'rails_helper'
+
+describe Csv::Import do
+  include_context "stronglifts_program"
+  subject { Csv::Import.new(user, program) }
+  let(:user) { create(:user) }
+
+  describe "#can_parse?" do
+    let(:directory) { Dir.mktmpdir }
+
+    context "when the directory contains a csv export" do
+      before :each do
+        FileUtils.touch("#{directory}/spreadsheet-stronglifts.csv")
+      end
+
+      it "returns true" do
+        expect(subject.can_parse?(directory)).to be_truthy
+      end
+    end
+
+    context "when the directory does not have csv export" do
+      it "returns false" do
+        expect(subject.can_parse?(directory)).to be_falsey
+      end
+    end
+  end
+
+  describe "#import_from" do
+    let(:directory) { Dir.mktmpdir }
+    let(:backup_file) do
+      Rails.root.join("spec", "fixtures", "spreadsheet-stronglifts.csv")
+    end
+
+    before :each do
+      `cp #{backup_file} #{directory}`
+    end
+
+    after :each do
+      FileUtils.remove_entry(directory)
+    end
+
+    it "imports each training session" do
+      subject.import_from(directory)
+      training_session = user.training_sessions.order(:occurred_at).first
+
+      expected_date = user.timezone.local_to_utc(Time.utc(2015, 03, 02))
+      expect(training_session.occurred_at).to eql(expected_date)
+      expect(training_session.workout).to eql(workout_a)
+      expect(training_session.body_weight).to eql(205.0)
+      expect(training_session.exercise_sessions.count).to eql(3)
+      expect(
+        training_session.exercise_sessions.map { |x| x.exercise.name }
+      ).to match_array(["Squat", "Bench Press", "Barbell Row"])
+    end
+
+    it "imports the completed squat exercise" do
+      subject.import_from(directory)
+
+      training_session = user.training_sessions.order(:occurred_at).first
+      squat_session = training_session.progress_for(squat)
+
+      expect(squat_session.target_weight).to eql(45.0)
+      expect(squat_session.sets[0]).to eql("5")
+      expect(squat_session.sets[1]).to eql("5")
+      expect(squat_session.sets[2]).to eql("5")
+      expect(squat_session.sets[3]).to eql("5")
+      expect(squat_session.sets[4]).to eql("5")
+    end
+
+    it "imports the completed bench exercise" do
+      subject.import_from(directory)
+
+      training_session = user.training_sessions.order(:occurred_at).first
+      bench_session = training_session.progress_for(bench_press)
+      expect(bench_session.target_weight).to eql(65.0)
+      expect(bench_session.sets[0]).to eql("5")
+      expect(bench_session.sets[1]).to eql("5")
+      expect(bench_session.sets[2]).to eql("5")
+      expect(bench_session.sets[3]).to eql("5")
+      expect(bench_session.sets[4]).to eql("5")
+    end
+
+    it "imports the completed barbell row exercise" do
+      subject.import_from(directory)
+
+      training_session = user.training_sessions.order(:occurred_at).first
+      row_session = training_session.progress_for(barbell_row)
+      expect(row_session.target_weight).to eql(65.0)
+      expect(row_session.sets[0]).to eql("5")
+      expect(row_session.sets[1]).to eql("5")
+      expect(row_session.sets[2]).to eql("5")
+      expect(row_session.sets[3]).to eql("5")
+      expect(row_session.sets[4]).to eql("5")
+    end
+
+    it "excludes items that have already been imported" do
+      subject.import_from(directory)
+      subject.import_from(directory)
+      expect(user.training_sessions.count).to eql(100)
+    end
+  end
+end