Skip to content

Commit

Permalink
WIP 2: Better more complete feature MVP
Browse files Browse the repository at this point in the history
UI polish and testing next.
  • Loading branch information
ewlarson committed Dec 19, 2024
1 parent e5c9b0c commit ebb8410
Show file tree
Hide file tree
Showing 13 changed files with 208 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def create
format.html { redirect_to admin_document_document_data_dictionaries_path(@document), notice: "Document data dictionary was successfully created." }
format.json { render :show, status: :created, location: @document_data_dictionary }
else
logger.debug("Document data dictionary could not be created. #{@document_data_dictionary.errors.full_messages}")
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @document_data_dictionary.errors, status: :unprocessable_entity }
end
Expand Down Expand Up @@ -153,6 +154,7 @@ def document_data_dictionary_params
:description,
:staff_notes,
:tags,
:csv_file,
:position
)
end
Expand Down
5 changes: 5 additions & 0 deletions app/models/document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def item_viewer
# - Publication State
has_many :document_transitions, foreign_key: "kithe_model_id", autosave: false, dependent: :destroy,
inverse_of: :document

# - Thumbnail State
has_many :document_thumbnail_transitions, foreign_key: "kithe_model_id", autosave: false, dependent: :destroy,
inverse_of: :document
Expand All @@ -34,6 +35,10 @@ def item_viewer
has_many :document_downloads, primary_key: "friendlier_id", foreign_key: "friendlier_id", autosave: false, dependent: :destroy,
inverse_of: :document

# - DocumentDataDictionaries
has_many :document_data_dictionaries, primary_key: "friendlier_id", foreign_key: "friendlier_id", autosave: false, dependent: :destroy,
inverse_of: :document

# - DocumentDistributions
has_many :document_distributions, primary_key: "friendlier_id", foreign_key: "friendlier_id", autosave: false, dependent: :destroy,
inverse_of: :document
Expand Down
29 changes: 29 additions & 0 deletions app/models/document_data_dictionary.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,31 @@
# frozen_string_literal: true

require "csv"

class DocumentDataDictionary < ApplicationRecord
include ActiveModel::Validations

# Callbacks (keep at top)
after_save :parse_csv_file

# Associations
has_one_attached :csv_file
belongs_to :document, foreign_key: :friendlier_id, primary_key: :friendlier_id
has_many :document_data_dictionary_entries, dependent: :destroy

# Validations
validates :name, presence: true
validates :csv_file, attached: true, content_type: {in: "text/csv", message: "is not a CSV file"}

validates_with DocumentDataDictionary::CsvHeaderValidator

def parse_csv_file
if csv_file.attached?
csv_data = CSV.parse(csv_file.download, headers: true)
csv_data.each do |row|
self.document_data_dictionary_entries.create!(row.to_h)
end
end
end
end

27 changes: 27 additions & 0 deletions app/models/document_data_dictionary/csv_header_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

require "csv"

# CSV Header Validation
class DocumentDataDictionary
# CsvHeaderValidator
class CsvHeaderValidator < ActiveModel::Validator
def validate(record)
valid_csv_header = true
unless valid_csv_headers?(record&.csv_file)
valid_csv_header = false
record.errors.add(:csv_file,
"Missing the required CSV header. friendlier_id, field_name, field_type, values, definition, definition_source, and parent_field_name are required.")
end

valid_csv_header
end

def valid_csv_headers?(csv_file)
headers = CSV.parse(csv_file.download)[0]
(["friendlier_id", "field_name", "field_type", "values", "definition", "definition_source", "parent_field_name"] - headers).empty?
rescue ArgumentError, ActiveStorage::FileNotFoundError
false
end
end
end
9 changes: 9 additions & 0 deletions app/models/document_data_dictionary_entry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

class DocumentDataDictionaryEntry < ApplicationRecord
# Associations
belongs_to :document_data_dictionary

# Validations
validates :friendlier_id, :field_name, :field_type, :values, presence: true
end
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@

<p>
<strong>Tags:</strong>
<%= document_data_dictionary.tags.join(', ') %>
<%= document_data_dictionary.tags %>
</p>
</div>
1 change: 1 addition & 0 deletions app/views/admin/document_data_dictionaries/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<%= f.input :description, input_html: { class: 'form-control' } %>
<%= f.input :staff_notes, input_html: { class: 'form-control' } %>
<%= f.input :tags, input_html: { class: "form-control mb-2", placeholder: "Optional", data: { controller: "taginput" } } %>
<%= f.file_field :csv_file, direct_upload: true %>
</div>

<div class="form-group">
Expand Down
2 changes: 1 addition & 1 deletion app/views/admin/document_data_dictionaries/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<%- @page_title = "GBL♦Admin - Document - Assets" %>
<%- @page_title = "GBL♦Admin - Document - Data Dictionaries" %>

<div class="row mb-2">
<div class="col">
Expand Down
83 changes: 76 additions & 7 deletions app/views/admin/document_data_dictionaries/new.html.erb
Original file line number Diff line number Diff line change
@@ -1,11 +1,80 @@
<% content_for :title, "New document data dictionary" %>
<div class="row mb-2">
<div class="col">
<%= link_to "Back to document data dictionaries", admin_document_document_data_dictionaries_path(@document) %>

<h1>New document data dictionary</h1>
<h1 style="width:100%;">
<%= @document.title %> &middot; New Data Dictionary
</h1>
</div>
</div>

<%= render "form", document_data_dictionary: @document_data_dictionary %>
<div class="row">
<div class="col-5">
<%= render "form", document_data_dictionary: @document_data_dictionary %>

<br>
<%= link_to "Back to document data dictionaries", admin_document_document_data_dictionaries_path(@document) %>
</div>

<div>
<%= link_to "Back to document data dictionaries", admin_document_document_data_dictionaries_path(@document) %>
</div>
<div class="col-6">
<h4>Example Data Dictionary CSV File</h4>
<table class="table table-bordered">
<thead>
<tr>
<th>friendlier_id*</th>
<th>field_name*</th>
<th>field_type*</th>
<th>values*</th>
<th>definition</th>
<th>definition_source</th>
<th>parent_field_name</th>
</tr>
</thead>
<tbody>
<tr>
<td>999-0010a</td>
<td>Institution</td>
<td>string</td>
<td>University of Minnesota</td>
<td>The name of the institution</td>
<td>https://www.umn.edu</td>
<td>999-0010UMN</td>
</tr>
<tr>
<td>999-0010a</td>
<td>Institution</td>
<td>string</td>
<td>University of Minnesota Duluth</td>
<td>The name of the institution</td>
<td>https://www.d.umn.edu</td>
<td>999-0010UMN</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>

<h5>CSV File Column Definitions</h5>
<dl>
<dt>friendlier_id</dt>
<dd>The associated document's friendlier id</dd>
<dt>field_name</dt>
<dd>The name of the field</dd>
<dt>field_type</dt>
<dd>The type of the field</dd>
<dt>values</dt>
<dd>The values of the field</dd>
<dt>definition</dt>
<dd>The definition of the field</dd>
<dt>definition_source</dt>
<dd>The source of the definition</dd>
<dt>parent_field_name</dt>
<dd>The parent field's name</dd>
</dl>
</div>
</div>
30 changes: 30 additions & 0 deletions app/views/admin/document_data_dictionaries/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,36 @@

<%= render @document_data_dictionary %>

<div class="col-6">
<h3>Data Dictionary Entries</h3>
<table class="table table-bordered">
<thead>
<tr>
<th>friendlier_id</th>
<th>field_name</th>
<th>field_type</th>
<th>values</th>
<th>definition</th>
<th>definition_source</th>
<th>parent_field_name</th>
</tr>
</thead>
<tbody>
<% @document_data_dictionary.document_data_dictionary_entries.each do |entry| %>
<tr>
<td><%= entry.friendlier_id %></td>
<td><%= entry.field_name %></td>
<td><%= entry.field_type %></td>
<td><%= entry.values %></td>
<td><%= entry.definition %></td>
<td><%= entry.definition_source %></td>
<td><%= entry.parent_field_name %></td>
</tr>
<% end %>
</tbody>
</table>
</div>

<div>
<%= link_to "Edit this document data dictionary", edit_admin_document_document_data_dictionary_path(@document, @document_data_dictionary) %> |
<%= link_to "Back to document data dictionaries", admin_document_document_data_dictionaries_path(@document) %>
Expand Down
4 changes: 4 additions & 0 deletions app/views/admin/documents/_form_nav.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
Additional Assets
<span class="badge badge-light"><%= @document.document_assets.count %></span>
<% end %>
<%= link_to admin_document_document_data_dictionaries_url(@document), class: "ml-2" do %>
Data Dictionaries
<span class="badge badge-light"><%= @document.document_data_dictionaries.count %></span>
<% end %>
<%= link_to admin_document_document_distributions_url(@document), class: "ml-2" do %>
Distributions
<% end %>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class CreateDocumentDataDictionaryEntries < ActiveRecord::Migration[7.2]
def change
create_table :document_data_dictionary_entries do |t|
t.references :document_data_dictionary, null: false, foreign_key: true
t.string :friendlier_id
t.string :field_name
t.string :field_type
t.string :values
t.string :definition
t.string :definition_source
t.string :parent_field_name
t.integer :position

t.timestamps
end
end
end
6 changes: 6 additions & 0 deletions test/fixtures/files/document_data_dictionary_entries.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
friendlier_id,field_name,field_type,values,definition,definition_source,parent_field_name
test,AREA,Real,6,Area of polygon,Software generated,
test,PERIMETER,Real,6,Perimeter of polygon,Software generated,
test,NAME_U,String,10,County names in upper case letters,USGS 7.5-minute quadrangles,
test,NAME_L,String,10,County names in upper and lower case letters,IGS standard,
test,NCAPC,Integer,2,Numeric county alphabetic prefix code,Indiana Bureau of Motor Vehicles,

0 comments on commit ebb8410

Please sign in to comment.