Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Admin creates csv upload #164

Merged
merged 5 commits into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions app/assets/stylesheets/application.tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,23 @@
@apply h-20;
}

input[type='file'] {
@apply py-2;
@apply text-base;
}

input[type='file']::file-selector-button {
@apply border-0;
@apply m-0;
@apply p-0;
@apply w-0;
}

input[type='date'],
input[type='file'],
input[type='password'],
input[type='text'],
select,
textarea {
@apply bg-off-black;
@apply block;
Expand All @@ -41,13 +55,22 @@
@apply w-80;
}

input[type='file']:invalid,
select:invalid {
@apply text-dark-gray;
}

input:focus[type='date'],
input:focus[type='file'],
input:focus[type='password'],
input:focus[type='text'],
select:focus,
textarea:focus,
input:hover[type='date'],
input:hover[type='file'],
input:hover[type='password'],
input:hover[type='text'],
select:hover,
textarea:hover {
@apply border-pink;
@apply ring-0;
Expand Down
23 changes: 23 additions & 0 deletions app/controllers/admin/csv_uploads_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class Admin::CsvUploadsController < ApplicationController
expose(:csv_upload)

def create
if csv_upload.save
ParseCsvUploadJob.perform_later(csv_upload.id)
flash.notice = "CSV Upload successfully created"
redirect_to admin_csv_upload_path(csv_upload)
else
flash.alert = csv_upload.errors.full_messages.join(",")
redirect_to new_admin_csv_upload_path
end
end

private

def csv_upload_params
safe_params = params.require(:csv_upload).permit(:parser_class_name)
safe_params[:original_filename] = params[:file].original_filename
safe_params[:data] = params[:file].read
safe_params
end
end
3 changes: 0 additions & 3 deletions app/controllers/admin/model_counts_controller.rb

This file was deleted.

3 changes: 3 additions & 0 deletions app/controllers/model_counts_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ModelCountsController < ApplicationController
expose(:model_counts) { ModelCounts.calculate }
end
9 changes: 9 additions & 0 deletions app/jobs/parse_csv_upload_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class ParseCsvUploadJob < ApplicationJob
def perform(csv_upload_id)
csv_upload = CsvUpload.find_by(id: csv_upload_id)
return unless csv_upload

parser = csv_upload.parser_class_name.constantize
parser.parse(csv_upload)
end
end
11 changes: 11 additions & 0 deletions app/models/csv_upload.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require "csv"

class CsvUpload < ApplicationRecord
validates_presence_of :data, :original_filename, :parser_class_name

def parsed_data
CSV.parse(data)
rescue CSV::MalformedCSVError
nil
end
end
21 changes: 21 additions & 0 deletions app/parsers/wells_fargo_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class WellsFargoParser
def self.parse(csv_upload)
table = csv_upload.parsed_data
return unless table

wf_checking = FinancialAccount.wf_checking

table.each do |row|
amount_cents = (row[1].to_r * 100).to_i
posted_on = Date.strptime(row[0], "%m/%d/%Y")

attrs = {
amount_cents: amount_cents,
description: row[4],
posted_on: posted_on
}

wf_checking.financial_transactions.create!(attrs)
end
end
end
8 changes: 8 additions & 0 deletions app/views/admin/csv_uploads/new.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
%h1 New CSV Upload

= form_with model: [:admin, csv_upload], multipart: true do |form|
%select#csv_upload_parser_class_name(name="csv_upload[parser_class_name]" required="true")
%option(value="" disabled="true" selected hidden) pick parser
%option(value="WellsFargoParser") WellsFargoParser
%input#file_picker(required="true" type="file" name="file")
= form.button "create"
1 change: 1 addition & 0 deletions app/views/admin/csv_uploads/show.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
%h1 CSV Upload #{csv_upload.id}
11 changes: 8 additions & 3 deletions app/views/dashboard/show.html.haml
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
%h1 Dashboard

%h2 Public Pages
%p= link_to "Artsy Viewer", artsy_viewer_path
%p= link_to "Cybertail", cybertail_path
%p= link_to "Financial Report", financial_report_path(year: Time.now.year)
%p= link_to "Fairing Direball", faring_direball_path
%p= link_to "Reading List", reading_list_path(year: Time.now.year)
%p= link_to "Root", root_path
%p= link_to "Style Pages", article_styles_path
%p= link_to "Wishlist", wishlist_path

%h2 Private Pages
%p= link_to "Cybertail", cybertail_path
%p= link_to "Financial Report", financial_report_path(year: Time.now.year)
%p= link_to "Model Counts", model_counts_path
%p= link_to "Today", today_path

%h2 Admin Pages
%p= link_to "Books", new_admin_book_path
%p= link_to "Gift Ideas", admin_gift_ideas_path
%p= link_to "Model Counts", admin_model_counts_path
%p= link_to "Post Bin", admin_post_bin_requests_path
%p= link_to "Project List", admin_projects_path

Expand Down
6 changes: 6 additions & 0 deletions app/views/static/form.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
%input(type="date")
%textarea(placeholder="write a note")
%input(type="password" placeholder="password")
%input(required="true" type="file")
%select(required="true")
%option(value="" disabled="true" selected hidden) pick parser
%option first
%option second
%option third
%button(type="submit") submit

%h2 Another Form
Expand Down
42 changes: 21 additions & 21 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,46 @@
get "dashboard", to: "dashboard#show"
get "faring_direball", to: "faring_direball#index"
get "financial_reports/:year", to: "financial_reports#show", as: :financial_report
get "model_counts", to: "model_counts#index", as: :model_counts
get "reading-list/:year", to: "reading_list#index", as: :reading_list
get "today", to: "today#show"
get "today", to: "today#show", as: :today
get "wishlist", to: "wishlist#index"
get "work_weeks/:target", to: "work_weeks#show", as: :work_week

resources :gift_ideas, only: %i[update]
scope :style do
get :article, to: "static#article", as: "article_styles"
get :color, to: "static#color", as: "color_styles"
get :flashes, to: "static#flashes", as: "flashes_styles"
get :form, to: "static#form", as: "form_styles"
get :table, to: "static#table", as: "table_styles"
end

get "sign_in", to: "password#new", as: :sign_in
post "sign_in", to: "password#create"
get "sign_out", to: "password#clear", as: :sign_out

namespace :api do
namespace :v1 do
get :ping, to: "ping#show"
post :post_bin, to: "post_bin#create"
post :raw_hooks, to: "raw_hooks#create"

namespace :word_rot do
get :killswitch, to: "killswitch#show"
end
end
end
resources :gift_ideas, only: %i[update]

namespace :admin do
get "model_counts", to: "model_counts#index", as: :model_counts

resources :books, only: %i[create edit new update]
resources :csv_uploads, only: %i[create new show]
resources :gift_ideas
resources :hooks, only: %i[create edit index]
resources :post_bin_requests, only: %i[index show]
resources :projects, only: %i[create index update]
resources :raw_hooks, only: %i[show]
end

scope :style do
get :article, to: "static#article", as: "article_styles"
get :color, to: "static#color", as: "color_styles"
get :flashes, to: "static#flashes", as: "flashes_styles"
get :form, to: "static#form", as: "form_styles"
get :table, to: "static#table", as: "table_styles"
namespace :api do
namespace :v1 do
get :ping, to: "ping#show"
post :post_bin, to: "post_bin#create"
post :raw_hooks, to: "raw_hooks#create"

namespace :word_rot do
get :killswitch, to: "killswitch#show"
end
end
end

root to: "static#root"
Expand Down
10 changes: 10 additions & 0 deletions db/migrate/20240213142420_create_csv_uploads.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class CreateCsvUploads < ActiveRecord::Migration[7.1]
def change
create_table :csv_uploads do |t|
t.text :data
t.string :parser_class_name
t.string :original_filename
t.timestamps
end
end
end
Empty file added spec/csv_files/empty.csv
Empty file.
1 change: 1 addition & 0 deletions spec/csv_files/one_wf_transaction.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"12/29/2023","0.89","","","random fee"
7 changes: 7 additions & 0 deletions spec/factories/csv_upload.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FactoryBot.define do
factory :csv_upload do
data { "123,foo,true" }
original_filename { "sample-data.csv" }
parser_class_name { "SampleParser" }
end
end
41 changes: 41 additions & 0 deletions spec/jobs/parse_csv_upload_job_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require "rails_helper"

describe ParseCsvUploadJob do
let(:parser_class_name) { "WellsFargoParser" }

let(:csv_upload) do
FactoryBot.create(:csv_upload, parser_class_name: parser_class_name)
end

let(:csv_upload_id) { csv_upload.id }

context "with an invalid csv_upload_id" do
let(:csv_upload_id) { "invalid" }

it "exits early" do
job = ParseCsvUploadJob.new
expect do
job.perform(csv_upload_id)
end.to_not raise_error
end
end

context "with a CsvUpload that has an invalid parser_class_name" do
let(:parser_class_name) { "InvalidParser" }

it "raises an error" do
job = ParseCsvUploadJob.new
expect do
job.perform(csv_upload_id)
end.to raise_error(NameError)
end
end

context "with a valid CsvUpload" do
it "calls the parser with that CsvUpload" do
job = ParseCsvUploadJob.new
expect(WellsFargoParser).to receive(:parse).with(csv_upload)
job.perform(csv_upload_id)
end
end
end
44 changes: 44 additions & 0 deletions spec/models/csv_upload_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require "rails_helper"

describe CsvUpload do
describe "validation" do
context "without required attrs" do
it "is invalid" do
csv_upload = CsvUpload.new
expect(csv_upload).to_not be_valid
end
end

context "with required attrs" do
it "is valid" do
csv_upload = CsvUpload.new(
data: "foo,bar,baz",
original_filename: "dummy-data.csv",
parser_class_name: "DummyParser"
)

expect(csv_upload).to be_valid
end
end
end

describe "#parsed_data" do
let(:csv_upload) { FactoryBot.create(:csv_upload, data: data) }

context "with data that fails to parse" do
let(:data) { '"invalid' }

it "returns nil" do
expect(csv_upload.parsed_data).to eq nil
end
end

context "with data that parses" do
let(:data) { "abc,123,true" }

it "returns that parsed data" do
expect(csv_upload.parsed_data).to eq [["abc", "123", "true"]]
end
end
end
end
Loading