text:
<file_sep>strong#payment_section_title = t(:section_title, scope: 'projects.contributions.review')

#catarse_paypal_express_form
  = simple_form_for :payment, url: pay_path(resource_params) do |f|
    = f.input :pay_fee, as: :hidden, input_html: { value: 1, 'data-pay-payment-fee' => '' }
    section
      .row
        .large-6.columns.submit
          = f.button :submit,
            t(:confirm, scope: 'projects.contributions.review'),
            data: { disable_with: t(:processing, scope: 'projects.contributions.review') }
<file_sep>module CatarsePaypalExpress
  class PaymentCheckout < Payment
    def perform
      purchase = gateway.purchase(amount_in_cents,
        ip:       attributes.fetch(:user_ip),
        payer_id: attributes.fetch(:payer_id),
        token:    resource.payment_token
      )

      Event.new(resource, attributes.merge(purchase.params)).process
      if purchase.params['transaction_id']
        resource.update_attributes(
          payment_id: purchase.params['transaction_id']
        )
      end
    end
  end
end
<file_sep>require 'spec_helper'

describe CatarsePaypalExpress::PaymentSetup do
  subject { described_class.new(payable_resource, {}) }
  let(:payable_resource) { Contribution.new }

  describe '#gateway' do
    before do
      allow(subject).to       receive(:gateway).and_call_original
      allow(PaymentEngine).to receive(:configuration).and_return(paypal_config)
    end

    context 'when we have the paypal configuration' do
      let(:paypal_config) do
        {
          paypal_password:  '<PASSWORD>',
          paypal_signature: 'signature',
          paypal_username:  'username'
        }
      end

      it 'returns an instance of PaypalExpressGateway' do
        allow(ActiveMerchant::Billing::PaypalExpressGateway).to receive(:new).with(
          login:     PaymentEngine.configuration[:paypal_username],
          password:  <PASSWORD>[:paypal_<PASSWORD>],
          signature: PaymentEngine.configuration[:paypal_signature]
        ).and_return('gateway instance')
        expect(subject.gateway).to eql('gateway instance')
      end
    end

    context 'when we do not have the paypal configuration' do
      let(:paypal_config) { {} }

      it 'returns nil' do
        expect(subject.gateway).to be_nil
      end
    end
  end
end
<file_sep>module CatarsePaypalExpress
  class Event
    attr_reader :attributes, :resource

    def initialize(resource, attributes)
      @resource, @attributes = resource, attributes
    end

    def process
      store_notification

      if attributes['checkout_status'] == 'PaymentActionCompleted'
        resource.confirm!
      elsif attributes['payment_status']
        case attributes['payment_status'].downcase
        when 'completed'
          resource.confirm!
        when 'refunded'
          resource.refund!
        when 'canceled_reversal'
          resource.cancel!
        when 'expired', 'denied'
          resource.pendent!
        else
          resource.wait_confirmation! if resource.pending?
        end
      end
    end

    private

    def store_notification
      extra_data = if attributes['charset']
        JSON.parse(attributes.to_json.force_encoding(attributes['charset']).encode('utf-8'))
      else
        attributes
      end
      notification_attributes = attributes.slice(:resource_id).
        merge(extra_data: extra_data)

      PaymentEngine.create_payment_notification(notification_attributes)
    end
  end
end
<file_sep># CatarsePaypalExpress [![Build Status](https://travis-ci.org/MicroPasts/catarse_paypal_express.svg?branch=master)](https://travis-ci.org/MicroPasts/catarse_paypal_express)

Catarse paypal express integration with [Catarse](http://github.com/danielweinmann/catarse) crowdfunding platform.

## Installation

Add this line to your Catarse application's Gemfile and
run `bundle install` in your terminal:

    # Gemfile
    gem 'catarse_paypal_express'

Since this gem is a Rails Engine intented to work on Catarse's applications,
you need to mount it in a route and configure some keys.

    # config/routes.rb
    mount CatarsePaypalExpress::Engine => "/", :as => "catarse_paypal_express"

Inside your Rails console (accessible via `bundle exec rails console`),
create appropriate settings:

    Configuration.create!(name: "currency_charge", value: "GBP")
    Configuration.create!(name: "paypal_username", value: "USERNAME")
    Configuration.create!(name: "paypal_password", value: "<PASSWORD>")
    Configuration.create!(name: "paypal_signature", value: "SIGNATURE")

    # Any value is accepted as `true`
    Configuration.create!(name: "paypal_test", value: "1")

## To go out of the sandbox

Run `bundle exec rails console` and destroy the setting saying
you're in the sandbox.

    Configuration.find_by(name: "paypal_test").destroy
    Rails.cache.delete("/configurations/paypal_test")

## Rails 3.2.x and Rails 4 support

If you are using the Rails 3.2.x on Catarse's code, you can use the version `1.0.0`.

For Rails 4 support use the `2.0.0` version.

## Development environment setup

Clone the repository:

    $ git clone git://github.com/devton/catarse_paypal_express.git

Initialize dummy database:

    $ cd catarse_paypal_express/test/dummy
    $ bundle exec rake db:create
    $ cd ../../

And see the test suite passing running:

    $ bundle exec rspec

## Contributing

1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request

This project rocks and uses MIT-LICENSE.
<file_sep>PaymentEngine.new(CatarsePaypalExpress::Interface.new).save
<file_sep>module CatarsePaypalExpress
  class Payment
    attr_reader :resource, :attributes

    def initialize(resource, attributes)
      @resource, @attributes = resource, attributes
    end

    def gateway
      @gateway ||= CatarsePaypalExpress::Gateway.instance
    end

    def amount_in_cents
      (fee_calculator.gross_amount * 100).round
    end

    def fee_calculator
      @fee_calculator and return @fee_calculator

      calculator_class = if fees_paid_user?
        TransactionAdditionalFeeCalculator
      else
        TransactionInclusiveFeeCalculator
      end

      @fee_calculator = calculator_class.new(resource.value)
    end

    def fees_paid_user?
      ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include? attributes[:pay_fee]
    end
  end
end
<file_sep>require 'spec_helper'

describe CatarsePaypalExpress::Event do
  subject { described_class.new(contribution, data.with_indifferent_access) }
  let(:contribution) { double('Contribution', id: 333, pending?: true) }

  describe '#process' do
    before do
      expect(PaymentEngine).to receive(:create_payment_notification).
        with('resource_id' => { 'contribution_id' => contribution.id }, 'extra_data' => data)
    end

    context "when data['checkout_status'] == 'PaymentActionCompleted'" do
      before do
        expect(contribution).to receive(:confirm!)
      end
      let(:data) do
        {
          'checkout_status' => 'PaymentActionCompleted',
          'resource_id'     => { 'contribution_id' => contribution.id }
        }
      end

      it 'should call confirm' do
        subject.process
      end
    end

    context "some real data with revert op" do
      before do
        expect(contribution).to receive(:refund!)
      end
      let(:data) do
        {
          "mc_gross" => "-150.00",
          "protection_eligibility" => "Eligible",
          "payer_id" => "4DK6S6Q75Z5YS",
          "address_street" => "AV. SAO CARLOS, 2205 - conj 501/502 Centro",
          "payment_date" => "09:55:14 Jun 26, 2013 PDT",
          "payment_status" => "Refunded",
          "charset" => "utf-8",
          "address_zip" => "13560-900",
          "first_name" => "Marcius",
          "mc_fee" => "-8.70",
          "address_country_code" => "BR",
          "address_name" => "<NAME>",
          "notify_version" => "3.7",
          "reason_code" => "refund",
          "custom" => "",
          "address_country" => "Brazil",
          "address_city" => "São Carlos",
          "verify_sign" => "AbedXpvDaliC7hltYoQrebkEQft7A.y6bRnDvjPIIB1Mct8-aDGcHkcV",
          "payer_email" => "<EMAIL>",
          "parent_txn_id" => "78T862320S496750Y",
          "txn_id" => "9RP43514H84299332",
          "payment_type" => "instant",
          "last_name" => "Milori",
          "address_state" => "São Paulo",
          "receiver_email" => "<EMAIL>",
          "payment_fee" => "",
          "receiver_id" => "BVUB4EVC7YCWL",
          "item_name" => "Apoio para o projeto A Caça (La Chasse) no valor de R$ 150",
          "mc_currency" => "BRL",
          "item_number" => "",
          "residence_country" => "BR",
          "handling_amount" => "0.00",
          "transaction_subject" => "Apoio para o projeto A Caça (La Chasse) no valor de R$ 150",
          "payment_gross" => "",
          "shipping" => "0.00",
          "ipn_track_id" => "18c487e6abca4",
          'resource_id' => { 'contribution_id' => contribution.id }
        }
      end

      it 'should call refund' do
        subject.process
      end
    end

    context "when it's a refund message" do
      before do
        expect(contribution).to receive(:refund!)
      end
      let(:data) do
        {
          'payment_status' => 'refunded',
          'resource_id'    => { 'contribution_id' => contribution.id }
        }
      end

      it 'should call refund' do
        subject.process
      end
    end

    context "when it's a completed message" do
      before do
        expect(contribution).to receive(:confirm!)
      end
      let(:data) do
        {
          'payment_status' => 'Completed',
          'resource_id'    => { 'contribution_id' => contribution.id }
        }
      end

      it 'should call confirm' do
        subject.process
      end
    end

    context "when it's a cancelation message" do
      before do
        expect(contribution).to receive(:cancel!)
      end
      let(:data) do
        {
          'payment_status' => 'canceled_reversal',
          'resource_id'    => { 'contribution_id' => contribution.id }
        }
      end

      it 'should call cancel' do
        subject.process
      end
    end

    context "when it's a payment expired message" do
      before do
        expect(contribution).to receive(:pendent!)
      end
      let(:data) do
        {
          'payment_status' => 'expired',
          'resource_id'    => { 'contribution_id' => contribution.id }
        }
      end

      it 'should call pendent' do
        subject.process
      end
    end

    context "all other values of payment_status" do
      before do
        expect(contribution).to receive(:wait_confirmation!)
      end
      let(:data) do
        {
          'payment_status' => 'other',
          'resource_id'    => { 'contribution_id' => contribution.id }
        }
      end

      it 'should call waiting' do
        subject.process
      end
    end
  end
end
<file_sep>en:
  projects:
    contributions:
      edit:
        payment-method:
          paypal_express:
            title: PayPal
      refund:
        error: OOPS! An error occured while asking for the refund.
        success: Refund requested successfuly.
      review:
        confirm: Confirm payment
        paypal: PayPal
        processing: Redirecting to PayPal...
        section_title: You will be directed to PayPal site to complete payment.
      checkout:
        paypal_cancel: Your PayPal payment was canceled. Please try again.
        paypal_description: Contribution to project %{project_name} with %{value}
        paypal_error: Ooops. There was an error while sending your payment to PayPal. Please try again.
        success: Your back was successfully made. Thanks a lot!
  shared:
    payments:
      form:
        payment-method:
          paypal_express:
            title: PayPal
<file_sep>CatarsePaypalExpress::Engine.routes.draw do
  scope 'paypal_express', controller: 'paypal_express', path: '' do
    get  :review
    post :pay
    get  :success
    get  :cancel
    post :ipn
  end
end
<file_sep>require 'spec_helper'

describe CatarsePaypalExpress::TransactionInclusiveFeeCalculator do
  # Float net amount is 966.9245647969052
  let(:transaction_value) { 1000.0 }
  subject { described_class.new(transaction_value) }

  it 'has gross amount equal to transaction value' do
    expect(subject.gross_amount).to eql(transaction_value)
  end

  it 'rounds net amount down by two decimal places' do
    expect(subject.net_amount).to eql(966.92)
  end

  it 'has fees matching with the rounding' do
    expect(subject.fees).to eql(33.08)
  end
end
<file_sep>require 'spec_helper'

describe CatarsePaypalExpress::PaypalExpressController do
  routes { CatarsePaypalExpress::Engine.routes }

  I18N_SCOPE = CatarsePaypalExpress::PaypalExpressController::I18N_SCOPE
  before do
    PaymentEngine.stub(:create_payment_notification)
    controller.stub(:main_app).and_return(main_app)
    controller.stub(:current_user).and_return(current_user)
    controller.stub(:gateway).and_return(gateway) # to be removed
    allow_any_instance_of(CatarsePaypalExpress::Payment).to receive(:gateway).and_return(gateway)
    controller.stub(:resource).and_return(contribution)
  end

  subject{ response }
  let(:gateway){ double('gateway') }
  let(:main_app){ double('main_app') }
  let(:current_user) { double('current_user') }
  let(:project){ double('project', id: 1, name: 'test project') }
  let(:contribution){ double('contribution', {
    id: 1,
    key: 'contribution key',
    payment_id: 'payment id',
    project: project,
    pending?: true,
    value: 20,
    display_value: 'R$ 20,00',
    price_in_cents: 2000,
    user: current_user,
    payer_name: 'foo',
    payer_email: '<EMAIL>',
    payment_token: 'token',
    address_street: 'test',
    address_number: '123',
    address_complement: '123',
    address_neighbourhood: '123',
    address_city: '123',
    address_state: '123',
    address_zip_code: '123',
    address_phone_number: '123',
    payment_method: 'paypal_express'
  }) }

  describe 'GET review' do
    before do
      get :review, id: contribution.id
    end

    it 'renders review template' do
      expect(response).to render_template(:review)
    end

    it 'renders review template' do
      expect(response).to_not render_template(layout: 'application')
    end
  end

  describe "POST pay" do
    let(:params) do
      {
        "utf8"=>"✓",
        "authenticity_token"=>"<KEY>
        "payment"=>{
          "pay_fee"=>"1",
        },
        "commit"=>"Confirm payment",
        "contribution_id"=>contribution.id.to_s
      }
    end

    context 'when successul' do
      before do
        allow(main_app).to     receive(:new_project_contribution_path).and_return('success url')
        allow(gateway).to      receive(:redirect_url_for).with('ABCD').and_return('success url')
        allow(gateway).to      receive(:setup_purchase).and_return(success_response)
        allow(contribution).to receive(:update_attributes)
      end
      let(:success_response) do
        double('success_response',
          token:  'ABCD',
          params: { 'correlation_id' => '123' }
        )
      end

      it 'setups purchase using payment service' do
        expect(gateway).to receive(:setup_purchase).with(
          anything,
          hash_including(
            :cancel_return_url,
            :currency_code,
            :description,
            :ip,
            :notify_url,
            :return_url,
          )
        ).and_return(success_response)
        post :pay, params
      end

      context 'when user is paying fees' do
        it 'charge calculator\'s gross_amount in the service' do
          allow_any_instance_of(
            CatarsePaypalExpress::TransactionAdditionalFeeCalculator
          ).to receive(:gross_amount).and_return(42.0)
          expect(gateway).to receive(:setup_purchase).with(
            4200,
            anything
          ).and_return(success_response)
          post :pay, params
        end

        it 'updates contribution say so' do
          expect(contribution).to receive(:update_attributes).
            with(hash_including(payment_service_fee_paid_by_user: true))
          post :pay, params
        end
      end

      context 'when user is not paying fees' do
        before { params['payment']['pay_fee'] = '0' }

        it 'charge calculator\'s gross_amount in the service' do
          allow_any_instance_of(
            CatarsePaypalExpress::TransactionInclusiveFeeCalculator
          ).to receive(:gross_amount).and_return(42.0)
          expect(gateway).to receive(:setup_purchase).with(
            4200,
            anything
          ).and_return(success_response)
          post :pay, params
        end

        it 'updates contribution say so' do
          expect(contribution).to receive(:update_attributes).
            with(hash_including(payment_service_fee_paid_by_user: false))
          post :pay, params
        end
      end

      it 'updates contribution with payment information' do
        expect(contribution).to receive(:update_attributes).with(
          hash_including(
            payment_method: 'paypal_express',
            payment_token:  'ABCD'
          )
        )
        post :pay, params
      end

      it 'redirects to successful contribution page' do
        post :pay, params
        expect(response).to redirect_to('success url')
      end
    end

    context 'when response raises a exception' do
      before do
        allow(gateway).to receive(:setup_purchase).and_raise(StandardError)
        allow(main_app).to receive(:new_project_contribution_path).and_return('error url')
      end

      it 'should assign flash error' do
        post :pay, params
        expect(flash[:alert]).to eql(I18n.t('paypal_error', scope: I18N_SCOPE))
      end

      it 'redirects to new contribution page' do
        post :pay, params
        expect(response).to redirect_to('error url')
      end
    end
  end

  describe 'GET success' do
    let(:success_details) do
      double('success_details',
        params: {
          'checkout_status' => 'PaymentActionCompleted',
          'transaction_id'  => '12345'
        }
      )
    end
    let(:params) do
      {
        :contribution_id=>contribution.id.to_s,
        :extra_data=>{
          :payer_id=>"J6QPTZJ3VUYEC",
          :resource_id=>{"contribution_id"=>contribution.id.to_s},
          :user_ip=>"172.16.58.3",
          "timestamp"=>"2014-07-02T11:16:54Z",
          "ack"=>"Success",
          "correlation_id"=>"644ae52867181",
          "version"=>"72",
          "build"=>"11624049",
          "token"=>"EC-7UD01139H9289551L",
          "transaction_id"=>"5S529942KX785625K",
          "parent_transaction_id"=>nil,
          "receipt_id"=>nil,
          "transaction_type"=>"express-checkout",
          "payment_type"=>"instant",
          "payment_date"=>"2014-07-02T11:16:54Z",
          "gross_amount"=>"20.00",
          "gross_amount_currency_id"=>"GBP",
          "fee_amount"=>"0.88",
          "fee_amount_currency_id"=>"GBP",
          "tax_amount"=>"0.00",
          "tax_amount_currency_id"=>"GBP",
          "exchange_rate"=>nil,
          "payment_status"=>"Completed",
          "pending_reason"=>"none",
          "reason_code"=>"none",
          "protection_eligibility"=>"Eligible",
          "protection_eligibility_type"=>"ItemNotReceivedEligible,UnauthorizedPaymentEligible",
          "secure_merchant_account_id"=>"36X593ULD53T6",
          "success_page_redirect_requested"=>"false",
          "Token"=>"EC-7UD01139H9289551L",
          "PaymentInfo"=>{
            "TransactionID"=>"5S529942KX785625K",
            "ParentTransactionID"=>nil,
            "ReceiptID"=>nil,
            "TransactionType"=>"express-checkout",
            "PaymentType"=>"instant",
            "PaymentDate"=>"2014-07-02T11:16:54Z",
            "GrossAmount"=>"20.00",
            "FeeAmount"=>"0.88",
            "TaxAmount"=>"0.00",
            "ExchangeRate"=>nil,
            "PaymentStatus"=>"Completed",
            "PendingReason"=>"none",
            "ReasonCode"=>"none",
            "ProtectionEligibility"=>"Eligible",
            "ProtectionEligibilityType"=>"ItemNotReceivedEligible,UnauthorizedPaymentEligible",
            "SellerDetails"=>{
              "SecureMerchantAccountID"=>"36X593ULD53T6"
            }
          },
          "SuccessPageRedirectRequested"=>"false"
        }
      }
    end

    before do
      allow(main_app).to receive(:new_project_contribution_path).and_return('create contribution url')
      allow(gateway).to  receive(:purchase).and_return(success_details)
    end

    it 'completes the payment' do
      expect(gateway).to receive(:purchase).with(
        contribution.price_in_cents,
        ip:       request.remote_ip,
        payer_id: params[:PayerID],
        token:    contribution.payment_token
      ).and_return(success_details)
      get :success, params
    end

    context 'when purchase is successful' do
      before do
        allow(contribution).to receive(:confirm!)
        allow(contribution).to receive(:update_attributes)
        allow(main_app).to     receive(:project_contribution_path).
          and_return('contribution url')
      end

      it 'fetches more information about transaction' do
        expect_any_instance_of(CatarsePaypalExpress::Event).to receive(:process)
        get :success, params
      end

      it 'defines the payment_id in the contribution' do
        expect(contribution).to receive(:update_attributes).with(payment_id: '12345')
        get :success, params
      end

      it 'confirms contribution' do
        expect(contribution).to receive(:confirm!)
        get :success, params
      end

      it 'redirects to new contribution page' do
        get :success, params
        expect(response).to redirect_to('contribution url')
      end

      it 'should assign flash message' do
        get :success, params
        expect(flash[:notice]).to eql(I18n.t('success', scope: I18N_SCOPE))
      end
    end

    context 'when paypal purchase raises some error' do
      before do
        allow(gateway).to receive(:purchase).and_raise('error')
      end

      it 'should assign flash error' do
        get :success, params
        expect(flash[:alert]).to eql(I18n.t('paypal_error', scope: I18N_SCOPE))
      end

      it 'redirects to new contribution url' do
        get :success, params
        expect(response).to redirect_to('create contribution url')
      end
    end
  end

  describe 'GET cancel' do
    before do
      allow(main_app).to receive(:new_project_contribution_path).
        and_return('create contribution url')
    end
    let(:params) do
      {
        'contribution_id' => contribution.id.to_s,
        'token'           => '<PASSWORD>'
      }
    end

    it 'should show for user the flash message' do
      get :cancel, params
      expect(flash[:alert]).to eql(I18n.t('paypal_cancel', scope: I18N_SCOPE))
    end

    it 'redirects to new contribution url' do
      get :cancel, params
      expect(response).to redirect_to('create contribution url')
    end
  end

  describe 'POST ipn' do
    let(:ipn_data) do
      {
        "mc_gross" => "50.00",
        "protection_eligibility" => "Eligible",
        "address_status" => "unconfirmed",
        "payer_id" => "S7Q8X88KMGX5S",
        "tax" => "0.00",
        "address_street" => "Rua Tatui, 40 ap 81\r\nJardins",
        "payment_date" => "09:03:01 Nov 05, 2012 PST",
        "payment_status" => "Completed",
        "charset" => "windows-1252",
        "address_zip" => "01409-010",
        "first_name" => "Paula",
        "mc_fee" => "3.30",
        "address_country_code" => "BR",
        "address_name" => "<NAME>",
        "notify_version" => "3.7",
        "custom" => "",
        "payer_status" => "verified",
        "address_country" => "Brazil",
        "address_city" => "Sao Paulo",
        "quantity" => "1",
        "verify_sign" => "ALBe4QrXe2sJhpq1rIN8JxSbK4RZA.Kfc5JlI9Jk4N1VQVTH5hPYOi2S",
        "payer_email" => "<EMAIL>",
        "txn_id" => "3R811766V4891372K",
        "payment_type" => "instant",
        "last_name" => "Rizzo",
        "address_state" => "SP",
        "receiver_email" => "<EMAIL>",
        "payment_fee" => "",
        "receiver_id" => "BVUB4EVC7YCWL",
        "txn_type" => "express_checkout",
        "item_name" => "Back project",
        "mc_currency" => "BRL",
        "item_number" => "",
        "residence_country" => "BR",
        "handling_amount" => "0.00",
        "transaction_subject" => "Back project",
        "payment_gross" => "",
        "shipping" => "0.00",
        "ipn_track_id" => "5865649c8c27"
      }
    end
    let(:contribution) do
      double(:contribution,
        payment_id:     ipn_data['txn_id'],
        payment_method: 'paypal_express'
      )
    end
    let(:notification) { double }

    before do
      allow_any_instance_of(
        CatarsePaypalExpress::Notification
      ).to receive(:notification).and_return(notification)
    end

    context 'when is a valid ipn data' do
      before do
        allow(contribution).to receive(:update_attributes)
        allow(notification).to receive(:acknowledge).and_return(true)
        allow_any_instance_of(CatarsePaypalExpress::Event).to receive(:process)
      end

      it 'validates notification' do
        expect(notification).to receive(:acknowledge)
        post :ipn, ipn_data
      end

      it 'fetches more information about transaction' do
        expect_any_instance_of(
          CatarsePaypalExpress::Event
        ).to receive(:process)
        post :ipn, ipn_data
      end

      it 'updates contribution with new information' do
        expect(contribution).to receive(:update_attributes).with(
          payer_email:         ipn_data['payer_email'],
          payment_service_fee: ipn_data['mc_fee']
        )
        post :ipn, ipn_data
      end

      it 'responds with 200 HTTP status' do
        post :ipn, ipn_data
        expect(subject.status).to eql(200)
      end

      it 'renders empty body' do
        post :ipn, ipn_data
        expect(subject.body.strip).to be_empty
      end
    end

    context "when is not valid ipn data" do
      before do
        notification.stub(:acknowledge).and_return(false)
      end
      let(:ipn_data) do
        {
          "mc_gross" => "50.00",
          "payment_status" => 'confirmed',
          "txn_id" => "3R811766V4891372K",
          'payer_email' => '<EMAIL>',
          'mc_fee' => '0.0'
        }
      end

      it 'validates notification' do
        expect(notification).to receive(:acknowledge)
        post :ipn, ipn_data
      end

      it 'skips fetching for more information about transaction' do
        expect_any_instance_of(
          CatarsePaypalExpress::Event
        ).to_not receive(:process)
        post :ipn, ipn_data
      end

      it 'skips any update to contribution with received data' do
        expect(contribution).to_not receive(:update_attributes).with(
          payer_email:         ipn_data['payer_email'],
          payment_service_fee: ipn_data['mc_fee']
        )
        post :ipn, ipn_data
      end

      it 'responds with 500 HTTP status' do
        post :ipn, ipn_data
        expect(subject.status).to eql(500)
      end

      it 'renders empty body' do
        post :ipn, ipn_data
        expect(subject.body.strip).to be_empty
      end
    end
  end

  describe '#resource' do
    subject { controller }
    before do
      allow(controller).to receive(:params).and_return(params)
    end

    context 'when a contribution id is given' do
      before do
      end
      let(:params) { { contribution_id: '1' } }

      it 'delegates to PaymentEngine.find_payment passing contribution_id' do
        allow(PaymentEngine).to receive(:find_payment).with(contribution_id: '1').and_return(contribution)
        expect(subject.resource).to eql(contribution)
      end
    end

    context "when we have an txn_id that does not return contribution but a parent_txn_id that does" do
      before do
        allow(PaymentEngine).to receive(:find_payment).with(payment_id: '1').and_return(nil)
      end
      let(:params) { { 'txn_id' => '1', 'parent_txn_id' => '2' } }

      it 'delegates to PaymentEngine.find_payment passing parent_txn_id' do
        allow(PaymentEngine).to receive(:find_payment).with(payment_id: '2').and_return(contribution)
        expect(subject.resource).to eql(contribution)
      end
    end

    context "when we do not have any kind of id" do
      before do
        allow(controller).to receive(:resource).and_call_original
      end
      let(:params) { {} }

      it 'returns nil' do
        expect(subject.resource).to be_nil
      end
    end

    context "when we have an txn_id" do
      let(:params) { { 'txn_id' => '1' } }

      it 'delegates to PaymentEngine.find_payment passing txn_id' do
        allow(PaymentEngine).to receive(:find_payment).with(payment_id: '1').and_return(contribution)
        expect(subject.resource).to eql(contribution)
      end
    end
  end
end
<file_sep>require 'float_extensions'

module CatarsePaypalExpress
  class TransactionInclusiveFeeCalculator < TransactionFeeCalculatorBase
    using FloatExtensions

    # Base calculation of fees
    # 3.4% + 20p
    def net_amount
      ((transaction_value - 0.2) / 1.034).floor_with_two_decimal_places
    end
  end
end
<file_sep>class Configuration
  def self.[](value)
    '42'
  end
end

class Contribution
  def self.find(*)
    new
  end

  def display_value(*); end
  def price_in_cents(*); end

  def project
    @project ||= Project.new
  end
end

class PaymentEngine
  class << self
    def create_payment_notification(*); end
    def find_payment(*);                end
    def register(*);                    end
  end

  def initialize(*); end
  def save(*);       end
end

class Project
  def name(*); end
end
<file_sep>module CatarsePaypalExpress
  class PaymentSetup < Payment
    include Engine.routes.url_helpers

    attr_reader :response
    delegate :token, to: :response

    def perform
      @response = gateway.setup_purchase(amount_in_cents,
        cancel_return_url: cancel_return_url,
        currency_code:     ::Configuration[:currency_charge],
        description:       description,
        ip:                attributes.fetch(:user_ip),
        notify_url:        notify_url,
        return_url:        return_url
      )

      Event.new(resource, attributes.merge(response.params)).process
      resource.update_attributes(
        payment_method:                   CatarsePaypalExpress::Interface.new.name,
        payment_service_fee_paid_by_user: fees_paid_user?,
        payment_token:                    response.token
      )
    end

    def checkout_url
      gateway.redirect_url_for(token)
    end

    protected

    def description
      I18n.t('paypal_description',
        scope:        PaypalExpressController::I18N_SCOPE,
        project_name: resource.project.name,
        value:        "#{fee_calculator.gross_amount} #{::Configuration[:currency_charge]}"
      )
    end

    def return_url
      success_url(
        attributes.fetch(:resource_id).merge(host: Configuration[:base_url])
      )
    end

    def cancel_return_url
      cancel_url(
        attributes.fetch(:resource_id).merge(host: Configuration[:base_url])
      )
    end

    def notify_url
      ipn_url(host: Configuration[:base_url])
    end
  end
end
<file_sep>require 'active_merchant'
require "catarse_paypal_express/engine"
require "catarse_paypal_express/gateway"
require "catarse_paypal_express/contribution_actions"
require "catarse_paypal_express/event"
require "catarse_paypal_express/notification"
require "catarse_paypal_express/payment"
require "catarse_paypal_express/payment_setup"
require "catarse_paypal_express/payment_checkout"
require "catarse_paypal_express/transaction_fee_calculator_base"
require "catarse_paypal_express/transaction_additional_fee_calculator"
require "catarse_paypal_express/transaction_inclusive_fee_calculator"

module CatarsePaypalExpress
end
<file_sep>module CatarsePaypalExpress
  class Interface
    def account_path
      false
    end

    def name
      'paypal_express'
    end

    def can_do_refund?
      true
    end

    def direct_refund(contribution)
      CatarsePaypalExpress::ContributionActions.new(contribution).refund
    end

    def fee_calculator(value)
      TransactionAdditionalFeeCalculator.new(value)
    end

    def locale
      'en'
    end

    def payment_path(resource)
      params = {
        "#{ActiveModel::Naming.param_key(resource)}_id".to_sym => resource.to_param
      }
      CatarsePaypalExpress::Engine.routes.url_helpers.review_path(params)
    end

    def payout_class
      nil
    end
  end
end
<file_sep>module CatarsePaypalExpress
  class Notification
    attr_reader :attributes, :resource

    def initialize(resource, attributes)
      @resource, @attributes = resource, attributes
    end

    def save
      Event.new(resource, attributes).process
      resource.update_attributes(
        payer_email:         attributes['payer_email'],
        payment_service_fee: attributes['mc_fee']
      )
    end

    def valid?
      resource &&
        notification.acknowledge &&
        (resource.payment_method == Interface.new.name || resource.payment_method.nil?)
    end

    protected

    def notification
      @notification ||= Paypal::Notification.new(attributes[:raw])
    end
  end
end
<file_sep>ActiveMerchant::Billing::PaypalExpressGateway.default_currency = Configuration[:currency_charge]

if Configuration[:paypal_test]
  ActiveMerchant::Billing::Base.mode = :test
end
<file_sep>module CatarsePaypalExpress
  class PaypalExpressController < ApplicationController
    include ActiveMerchant::Billing::Integrations

    layout :false

    I18N_SCOPE = 'projects.contributions.checkout'
    helper_method :resource_params

    def review
    end

    def pay
      begin
        attributes = params.merge(
          pay_fee:     params[:payment][:pay_fee],
          resource_id: resource_params,
          user_ip:     request.remote_ip
        )
        payment = PaymentSetup.new(resource, attributes)
        payment.perform

        redirect_to payment.checkout_url
      rescue Exception => e
        Rails.logger.info "-----> #{e.inspect}"
        flash.alert = t('paypal_error', scope: I18N_SCOPE)
        redirect_to new_resource_path
      end
    end

    def success
      begin
        attributes = {
          payer_id:    params[:PayerID],
          resource_id: resource_params,
          user_ip:     request.remote_ip
        }
        checkout = PaymentCheckout.new(resource, attributes)
        checkout.perform

        flash.notice = t('success', scope: I18N_SCOPE)
        redirect_to resource_path
      rescue Exception => e
        Rails.logger.info "-----> #{e.inspect}"
        flash.alert = t('paypal_error', scope: I18N_SCOPE)
        redirect_to new_resource_path
      end
    end

    def cancel
      flash.alert = t('paypal_cancel', scope: I18N_SCOPE)
      redirect_to new_resource_path
    end

    def ipn
      attributes   = resource_params.merge(params).merge(raw: request.raw_post)
      notification = Notification.new(resource, attributes)
      if notification.valid?
        notification.save
      else
        return render status: 500, nothing: true
      end
      return render status: 200, nothing: true
    rescue Exception => e
      return render status: 500, text: e.inspect
    end

    def resource
      @resource ||= begin
        payment_id = params.slice(:txn_id, :parent_txn_id).compact.first.try(:last)
        filter     = if payment_id.present?
          { payment_id: payment_id }
        else
          resource_params
        end
        PaymentEngine.find_payment(filter.with_indifferent_access)
      end
    end

    private

    def resource_params
      @resource_params ||= Hash[*params.slice(:contribution_id, :match_id).first]
    end

    def resource_path
      main_app.public_send("project_#{resource_type}_path",
        id:         resource,
        project_id: resource.project
      )
    end

    def new_resource_path
      main_app.public_send("new_project_#{resource_type}_path", resource.project)
    end

    def resource_type
      resource_params.keys.first[0..-4]
    end
  end
end

languages: Markdown Slim Ruby YAML


text:
<file_sep>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Assignment3
{
    enum UnitTypes
    {
        Metric, Imperial
    }
}
<file_sep>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Assignment3
{
    class BMICalculator
    {
        // only stores input
        // getter method returns input value with no parameter
        // setter method void method with one parameter
        // uses methods to return output
        // should be usable by any object

        private string name = "No name";
        private double height = 0;
        private double weight = 0;
        private UnitTypes unit;

        // returns value stored in filed name
        public string GetName()
        {
            return name;
        }

        public void SetName(string stringValue)
        {
            // value validation string.IsNullOrEmpty()
            if (!string.IsNullOrEmpty(stringValue))
            {
                name = stringValue;
            }
        }

        // setter method overwrites value save in instance variable name by new value
        public double GetHeight()
        {
            return height;
        }

        public void SetHeight(double doubleValue)
        {
            if (doubleValue >= 0)
            {
                height = doubleValue;
            }
        }

        public double GetHeightInches()
        {
            return height;
        }

        public void SetHeightInches(double doubleValue)
        {
            if (doubleValue >= 0)
            {
                height = doubleValue;
            }
        }

        public double GetWeight()
        {
            return weight;
        }

        public void SetWeight(double doubleValue)
        {
            if (doubleValue >= 0)
            {
                weight = doubleValue;
            }
        }

        public UnitTypes GetUnit()
        {
            return unit;
        }

        public void SetUnit(UnitTypes value)
        {
            unit = value;
        }

        // metric BMI = (weight / height)²
        // imperial BMI = (weight / height)²*703
        public double CalculateBMI()
        {
            // imperial
            double bmiValue = 0.0;
            double conversionFactor = 0.0;

            if (unit == UnitTypes.Imperial)
            {
                conversionFactor = 703.0;
            }
            else
            {
                conversionFactor = 1.0;
            }

            bmiValue = conversionFactor * (weight / (height * height));

            return bmiValue;
        }

        /// <summary>
        /// outputs weight category based on BMI
        /// </summary>
        /// <returns>weight category based on BMI</returns>
        public string BmiWeightCategory()
        {
            double bmi = CalculateBMI();
            string stringOut = "";

            if (bmi > 40)
            {
                stringOut = "Overweight (Obesity class III)";
            }
            else if (bmi > 35)
            {
                stringOut = "Overweight (Obesity class II)";
            }
            else if (bmi > 30)
            {
                stringOut = "Overweight (Obesity class I)";
            }
            else if (bmi > 25)
            {
                stringOut = "Overweight (Pre-obesity)";
            }
            else if (bmi > 18.5)
            {
                stringOut = "Normal weight";
            }
            else
            {
                stringOut = "Undeweight";
            }

            return stringOut;
        }

            /// <summary>
            /// displays the weights that correspond to the normal BMI values
            /// </summary>
            /// <returns></returns>
            public string BmiWeightRange()
        {
            double bmi = CalculateBMI();
            double weight1;
            double weight2;
            string stringOut = "";

            if (unit == UnitTypes.Metric && (bmi >= 18.5 && bmi <= 24.9))
            {
                weight1 = Math.Round(18.50 * (height * height));
                weight2 = Math.Round(24.90 * (height * height));

                stringOut = ($"Normal weight should be {weight1} to {weight2} kg");
            }
            else if (unit == UnitTypes.Imperial && (bmi >= 18.5 && bmi <= 24.9))
            {
                weight1 = Math.Round(18.50 * (height * height) / 703);
                weight2 = Math.Round(24.90 * (height * height) / 703);

                stringOut = ($"Normal weight should be {weight1} to {weight2} lbs");
            }
            else
            {
                stringOut = " ";
            }

                return stringOut;
            }
    }
}
<file_sep>using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Assignment3
{
    public partial class MainForm : Form
    {
        private BMICalculator myBMICalculator = new BMICalculator();

        // constructor
        public MainForm()
        {
            InitializeComponent();
            InitializeGUI();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// initiazlie GUI with the preset option, set defualt radioButton values
        /// encapsulate within a container component groupBox
        /// when one button is checked, rest should be unchecked
        /// </summary>
        private void InitializeGUI()
        {
            // current instance of the class
            // access members
            this.Text = "BMI calculator";

            // input
            radioButtonImperial.Checked = true;
            labelHeight.Text = "Height (ft)";
            labelWeight.Text = "Weight (lb)";

            // output
            textBoxHeightFeet.Text = "";
            labelFeet.Text = "ft";
            labelInch.Text = "in";
            textBoxWeight.Text = "";
        }

        private void ReadName()
        {
            // delete spaces at the beginning and end of text input
            textUerName.Text.Trim();
            if (!string.IsNullOrEmpty(textUerName.Text))
            {
                myBMICalculator.SetName(textUerName.Text);
            }
            else
            {
                myBMICalculator.SetName("Unknown");
            }
        }

        /// <summary>
        /// calculates BMI based on user input
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonCalculateBMI_Click(object sender, EventArgs e)
        {
            // read/validate input
            bool inputValid = ReadInputBMI();

            if (inputValid == true)
            {
                DisplayResults();
            }
        }

        /// <summary>
        /// get user input and save data in bmiCalc object
        /// if textName.Text == empty, bmiCalc.SetName (NoName);
        /// else bmiCalc.SetName (textName);
        /// read hight value (textHeight.Text) convert to double.TryParse ();
        /// read weight value, validate double.TryParse
        /// if weight == double, save it to bmiCalc
        /// else bmiCalc.CalculateBMI()
        /// </summary>
        // read the input provided on the user name textbox textUserName.Text
        private void DisplayResults()
        {
            labelCalculatedBMIOutput.Text = myBMICalculator.CalculateBMI().ToString("f2");
            // BMIWEIGHTCATEGORY
            labelWeightCategory.Text = myBMICalculator.BmiWeightCategory().ToString();
            labelBmiWeightRange.Text = myBMICalculator.BmiWeightRange().ToString();
            groupBoxResult.Text = "Results for " + myBMICalculator.GetName();
        }

        /// <summary>
        /// reads user input and validate if it's true
        /// </summary>
        /// <returns></returns>
        private bool ReadInputBMI()
        {
            bool isInputValid = false;

            ReadName();
            ReadUnitType();
            isInputValid = ReadHeight();
            isInputValid = isInputValid && ReadWeight();

            return isInputValid;
        }

        private void ReadUnitType()
        {
            if (radioButtonMetric.Checked)
            {
                myBMICalculator.SetUnit(UnitTypes.Metric);
                labelInch.Visible = false;
                labelBmiWeightRange.Visible = true;
            }
            else
            {
                myBMICalculator.SetUnit(UnitTypes.Imperial);
                labelInch.Visible = true;
            }
        }

        private bool ReadHeight()
        {
            double outValue = 0;
            double outValueInch = 0;
            double outValueTotal = 0;
            bool inputValid = double.TryParse(textBoxHeightFeet.Text, out outValue);
            bool inputValidInch = double.TryParse(textBoxHeightInches.Text, out outValueInch);

            if (inputValid)
            {
                if (outValue > 0 || outValueInch > 0)
                {
                    if (myBMICalculator.GetUnit() == UnitTypes.Imperial)
                    {
                        // convert ft to in
                        //myBMICalculator.SetHeight(outValue * 12.00);

                        outValueTotal = outValue * 12 + outValueInch;
                        myBMICalculator.SetHeight(outValueTotal);
                    }
                    else
                    {
                        // convert cm to m
                        myBMICalculator.SetHeight(outValue / 100.0);
                    }
                }
                else
                {
                    inputValid = false;
                }
            }

            if (inputValid == false)
            {
                MessageBox.Show("Invalid heigh value", "Error");
            }

            return inputValid;
        }

        /// <summary>
        /// Read height input and validates it and parse the data to BMI
        /// </summary>
        /// <returns></returns>
        private bool ReadWeight()
        {
            // output
            double outValue = 0;
            bool inputValid = double.TryParse(textBoxWeight.Text, out outValue);

            if (inputValid)
            {
                if (outValue > 0)
                {
                    if (myBMICalculator.GetUnit() == UnitTypes.Imperial)
                    {
                        myBMICalculator.SetWeight(outValue);
                    }
                    else
                    {
                        myBMICalculator.SetWeight(outValue);
                    }
                }
                else
                {
                    inputValid = false;
                }
            }

            if (inputValid == false)
            {
                MessageBox.Show("Invalid heigh value", "Error");
            }

            return inputValid;
        }

        private void labelTextCalculateBMI_Click(object sender, EventArgs e)
        {

        }

        private void labelUserName_Click(object sender, EventArgs e)
        {

        }

        private void labelDisclaimer_Click(object sender, EventArgs e)
        {

        }

        private void textBoxHeightInches_TextChanged(object sender, EventArgs e)
        {

        }

        private void labelCalculatedBMIOutput_Click(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// check radioButtonMetric check status and revise the text to oindicate unit of measurement
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void radioButtonMetric_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButtonMetric.Checked)
            {
                labelHeight.Text = "Height (cm)";
                labelWeight.Text = "Weight (kg)";
                textBoxHeightInches.Visible = false;
                labelInch.Visible = false;
                labelFeet.Visible = false;
                myBMICalculator.SetUnit(UnitTypes.Metric);
            }
        }

        /// <summary>
        /// check radioButtonImperial check status and revise the text to oindicate unit of measurement
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void radioButtonImperial_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButtonImperial.Checked)
            {
                textBoxHeightInches.Visible = true;
                labelFeet.Visible = true;
                labelInch.Visible = true;
                labelHeight.Text = "Height (ft)";
                labelFeet.Text = "ft";
                labelInch.Text = "in";
                labelWeight.Text = "Weight (lb)";
                myBMICalculator.SetUnit(UnitTypes.Imperial);
            }
        }

        private void groupBoxResult_Enter(object sender, EventArgs e)
        {
        }

        private void labelBmiWeightRange_Click(object sender, EventArgs e)
        {

        }
    }
}

branch_name: refs/heads/master

repo_language: C#

repo_name: Phytochemical/BMI


text:
<repo_name>spovetkin/demo-rest<file_sep>/src/main/java/ru/spovetkin/demo/rest/controller/MainController.java
package ru.spovetkin.demo.rest.controller;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import ru.spovetkin.demo.rest.service.CalcService;

@RequestMapping("/")
@RestController
public class MainController {

    private CalcService service;

    public MainController(@Qualifier(value = "calcServicePlusImpl") CalcService service) {
        this.service = service;
    }

    @GetMapping("calc1")
    public long calc1(@RequestParam() long a,
                      @RequestParam() long b) {
        return service.sum(a, b);
    }

    @GetMapping("calc2")
    public long calc2(@RequestParam() long a,
                      @RequestParam() long b) {
        return service.sum(a, b);
    }

}
<file_sep>/src/main/java/ru/spovetkin/demo/rest/service/CalcService.java
package ru.spovetkin.demo.rest.service;

public interface CalcService {

    long sum(long a, long b);

    long mult(long a, long b);

}
<file_sep>/src/main/resources/application.properties
spring.security.user.name=kucev
spring.security.user.password=<PASSWORD>

languages: Java INI

repo_language: Java

repo_name: spovetkin/demo-rest


text:
<file_sep># Monster_IOS_Game
IOS Game
- GAME
- Keeps A monster alive by feeding him of the food choice
- has sound
- if monster not fed he dies
- has sound

<a href='http://gph.is/1XGihhU' title='Video Walkthrough' width='' alt='Video Walkthrough'
Video here

repo_language: Markdown


text:
<repo_name>adelineclarabelle/web-project<file_sep>/style.css
h1 {
  color: red;
}

.myClassName {
  color: pink;
}

#myMainID {
  color; black;
}

p {
  color: blue;
}

.container {
  background-color: yellow;
}

.containerChild {
  background-color: white;
}

span {
  color: white
}

* {
  font-family: 'Pacifico', cursive;
}

.textRight {
  width: 48%;
  display: inline-block;
  float right
}

repo_language: CSS

repo_name: adelineclarabelle/web-project


text:
<repo_name>mathachew7/github-profile-fetcher<file_sep>/src/components/card/card.jsx
import React from "react";

// import Style
import { MainDiv, InfoStyle, AvatarStyle } from "./card.style";

const Card = ({ profile }) => {
  return (
    <MainDiv>
      {/* Avatar */}

        <AvatarStyle src={profile.avatar_url} alt={profile.name} />

      {/* Info */}
      <InfoStyle>

Username: {profile.login}

        {/* Name */}

Name: {profile.name}

        {/* Github Link */}

          GitHub Link:{" "}

            {profile.html_url}

        {/* email */}

Email: {profile.Email}

Website: {profile.blog}

        {/* Location */}

Location: {profile.location}

        {/* Public repo */}

Public Repositories: {profile.public_repos}

        {/* Info */}

Followers: {profile.followers}

Following: {profile.following}

Hireable: {profile.hireable}

Bio: {profile.bio}

Created At: {profile.created_at}

      </InfoStyle>
    </MainDiv>
  );
};

export default Card;
<file_sep>/src/components/card/card.style.jsx
import styled from "styled-components";

export const MainDiv = styled.div`

  display: flex;
  flex-direction: row;
  padding-left: 485px;
  margin-top: 20px;

`;

export const AvatarStyle = styled.img`
  width: 15em;
  padding-right: 1em;
`;

export const InfoStyle = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  font-size: 1em;
`;
<file_sep>/src/components/footer/Footer.jsx
import React, { Fragment } from "react";

// Styling
import "./footer.css";

const Footer = ({ onInputChange }) => {
  return (
    <Fragment>

Created by @mathachew7

No Copyright. Star me On Github

    </Fragment>
  );
};

export default Footer;

repo_language: JavaScript


text:
<repo_name>usmonbek-umarbekovich/Sorting-Visualizer<file_sep>/insertion_sort.py
import random
import tkinter as tk
import math
import time

class DrawingCanvas():
    """ A canvas drawing manager """

    def __init__(self, canvas, wxmin, wymin, wxmax, wymax, dmargin, y_is_flipped):
        self.canvas = canvas
        self.wxmin = wxmin
        self.wymin = wymin
        self.wxmax = wxmax
        self.wymax = wymax
        self.dmargin = dmargin
        self.y_is_flipped = y_is_flipped

        self.set_scales()

    def set_scales(self):
        """ Calculate scale parameters for the canvas's current size. """
        self.canvas.update()
        self.dxmin = self.dmargin
        self.dymin = self.dmargin
        self.dxmax = self.canvas.winfo_width() - self.dmargin - 1
        self.dymax = self.canvas.winfo_height() - self.dmargin - 1

        # Flip the coordinates to invert the result
        if self.y_is_flipped:
            self.dymin, self.dymax = self.dymax, self.dymin

        self.xscale = (self.dxmax - self.dxmin) / (self.wxmax - self.wxmin)
        self.yscale = (self.dymax - self.dymin) / (self.wymax - self.wymin)

        # Calculate 1 pixel in world coordinates
        self.xpix = 1 / self.xscale
        self.ypix = 1 / self.yscale

    def w_to_d(self, wx, wy):
        """ Map a point from world to device coordinates """
        dx = (wx - self.wxmin) * self.xscale + self.dxmin
        dy = (wy - self.wymin) * self.yscale + self.dymin
        return dx, dy

    def clear(self):
        self.canvas.delete(tk.ALL)

    def rectangle(self, wx0, wy0, wx1, wy1, fill, outline):
        """ Draw a rectangle at the indicated position in world coordinates """
        dx0, dy0 = self.w_to_d(wx0, wy0)
        dx1, dy1 = self.w_to_d(wx1, wy1)
        return self.canvas.create_rectangle(dx0, dy0, dx1, dy1, fill=fill, outline=outline)

    def rect_coords(self, obj, x0, y0, x1, y1):
        dx0, dy0 = self.w_to_d(x0, y0)
        dx1, dy1 = self.w_to_d(x1, y1)
        self.canvas.coords(obj, dx0, dy0, dx1, dy1)

    def rect_config(self, obj, fill, outline):
        self.canvas.itemconfigure(obj, fill=fill, outline=outline)

class App:
    def __init__(self):
        self.window = tk.Tk()
        self.window.title('Insertion Sort')
        self.window.protocol('WM_DELETE_WINDOW', self.kill_callback)

        self.canvas = tk.Canvas(self.window, relief=tk.RIDGE, bd=5, highlightthickness=0,
                                bg='white')
        self.canvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)

        frame = tk.Frame(self.window)
        frame.pack(side=tk.TOP, expand=True)

        label = tk.Label(frame, text='Intervals:')
        label.grid(padx=5, pady=2, row=0, column=0)
        self.intervals_entry = tk.Entry(frame, width=5, justify=tk.RIGHT)
        self.intervals_entry.grid(padx=5, pady=2, row=0, column=1)
        self.intervals_entry.insert(0, '10')

        label = tk.Label(frame, text='Time:')
        label.grid(padx=5, pady=2, row=1, column=0)
        self.elapsed = tk.Label(frame, text='')
        self.elapsed.grid(padx=5, pady=2, row=1, column=1)

        generate = tk.Button(frame, text='Generate', width=10, command=self.generate)
        generate.grid(padx=5, pady=2, row=2, column=0)

        self.insertion_button = tk.Button(frame, text='Insertion', width=10, command=self.insertion)
        self.insertion_button.grid(padx=5, pady=2, row=2, column=2)

        self.speed = tk.IntVar()
        radiobutton = tk.Radiobutton(frame, text="Slow", variable=self.speed, value=0)
        radiobutton.grid(padx=5, pady=2, row=0, column=2)
        radiobutton = tk.Radiobutton(frame, text="Fast", variable=self.speed, value=1)
        radiobutton.grid(padx=5, pady=2, row=1, column=2)

        # make the DrawingCanvas
        self.wxmin = 1
        self.wymin = 1
        self.wxmax = 100
        self.wymax = 100
        self.drawing_canvas = DrawingCanvas(self.canvas, self.wxmin, self.wymin, self.wxmax, self.wymax, 0, True)

        self.intervals_entry.focus_force()
        self.window.mainloop()

    def kill_callback(self):
        self.window.destroy()

    def generate(self):
        self.drawing_canvas.clear()
        self.values = []
        self.insertion_button['state'] = tk.NORMAL
        self.elapsed['text'] = ''

        intervals = int(self.intervals_entry.get())
        dx = 99 / intervals

        x = 1
        for _ in range(intervals):
            y = random.randint(1, 100)
            cords = (x, 0, x + dx, y)
            rect = self.drawing_canvas.rectangle(*cords, 'lightblue', 'blue')
            self.values.append((rect, *cords))

            # move to the next slice
            x += dx

    def insertion(self):
        """ perform the integration """
        self.insertion_button['state'] = tk.DISABLED
        # get parameters
        intervals = int(self.intervals_entry.get())
        dx = 99 / intervals

        if self.values:
            start = time.time()
            for i in range(intervals):
                y = self.values[i]
                self.drawing_canvas.rect_config(y[0], 'red', 'red')
                self.values[i] = self.up_down(*self.values[i], 0, 0.06, 0, 0.06)
                for j in range(i, -1, -1):
                    if not(j) or y[4] >= self.values[j-1][4]:
                        self.drawing_canvas.rect_config(y[0], 'green', 'green')
                        self.values[j] = self.up_down(*self.values[j], 0, -0.06, 0, -0.06)
                        break
                    self.values[j-1], self.values[j] = self.move_same(self.values[j][0], self.values[j-1][0],
                                                                      (self.values[j][1:] + (-dx/50, 0, -dx/50, 0)),
                                                                      (self.values[j-1][1:] + (dx/50, 0, dx/50, 0)))
            end = time.time()
            res = end - start
            self.elapsed['text'] = f'{res:.3f} seconds'
        self.insertion_button['state'] = tk.NORMAL

    def up_down(self, obj, *cords):
        x0, y0, x1, y1 = cords[:4]
        x0m, y0m, x1m, y1m = cords[4:]
        for _ in range(50):
            if not self.speed.get():
                time.sleep(0.0000000000000000001)
            self.drawing_canvas.rect_coords(obj, x0, y0, x1, y1)
            x0 += x0m
            y0 += y0m
            x1 += x1m
            y1 += y1m
            self.canvas.update()
        return obj, x0, y0, x1, y1

    def move_same(self, obj1, obj2, cords1, cords2):
        x00, y00, x10, y10 = cords1[:4]
        x00m, y00m, x10m, y10m = cords1[4:]
        x01, y01, x11, y11 = cords2[:4]
        x01m, y01m, x11m, y11m = cords2[4:]
        for _ in range(50):
            if not self.speed.get():
                time.sleep(0.00000001)
            self.drawing_canvas.rect_coords(obj1, x00, y00, x10, y10)
            self.drawing_canvas.rect_coords(obj2, x01, y01, x11, y11)
            self.canvas.update()
            x00 += x00m
            y00 += y00m
            x10 += x10m
            y10 += y10m

            x01 += x01m
            y01 += y01m
            x11 += x11m
            y11 += y11m

        return (obj1, x00, y00, x10, y10), (obj2, x01, y01, x11, y11)

if __name__ == '__main__':
    app = App()
<file_sep>/README.md
# Sorting-Visualizer
Visualize sorting algorithms with python tkinter

### Implemented algorithms so far:
* Insertion sort

branch_name: refs/heads/main

languages: Markdown Python


text:
<file_sep>Period 9 is recreating the design at https://www.teepublic.com/

Period 10 is recreating the design at http://www.pacsun.com/<file_sep># webdev
files and examples for web development class

languages: Markdown Text

repo_name: alveyworld/webdev