Skip to content

Commit

Permalink
Merge pull request #11290 from Jarsen136/issue-11282
Browse files Browse the repository at this point in the history
feat: Support CSV description file type to have on-chain attributes
  • Loading branch information
Jarsen136 authored Dec 28, 2024
2 parents 1117000 + 5a52fa0 commit 6d70084
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 52 deletions.
6 changes: 3 additions & 3 deletions components/massmint/descriptionTabs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ export const descriptionTabs = {
label: 'CSV',
fileStructureDescription: `
\`\`\`properties
file,name,description,price
file1.jpg,Image1,This is an image,100
file2.jpg,Image2,,200
file,name,description,attributes,price
file1.jpg,Image1,This is an image,color:white;expression:happy,100
file2.jpg,Image2,,color:blue;expression:shy,200
file3.jpg,,This is another image,
\`\`\`
`,
Expand Down
91 changes: 47 additions & 44 deletions composables/massmint/parsers/parseCsv.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,73 @@
import type { OpenSeaAttribute } from '@kodadot1/hyperdata'
import type { Entry } from './common'
import { isValidEntry, removeQuotes, validFileExtension } from './common'

function processCsvLine(line: string, csvHeaders: string[]): Partial<Entry> {
const values = line.split(',').map(value => removeQuotes(value.trim()))
const entry: Partial<Entry> = {
file: undefined,
name: undefined,
description: undefined,
price: undefined,
}

for (const [j, header] of csvHeaders.entries()) {
const value = values[j]?.trim()
if (header === 'price') {
entry.price = parseFloat(value) || undefined
}
else {
entry[header] = value || undefined
}
}
const DEFAULT_CSV_HEADERS = ['file', 'name', 'description', 'attributes', 'price']

return entry
}
const LINE_SEPARATOR = ','
const ATTRIBUTE_SEPARATOR = ';'
const ATTRIBUTE_KEY_VALUE_SEPARATOR = ':'

function parseCsvWithHeaders(lines, csvHeaders): Record<string, Entry> {
function processCsvLines(lines: string[], csvHeaders: string[]): Record<string, Entry> {
const entries: Record<string, Entry> = {}

for (const line of lines) {
const entry = processCsvLine(line.trim(), csvHeaders)
const values = line.split(LINE_SEPARATOR).map(value => removeQuotes(value.trim()))
const entry: Partial<Entry> = {
file: undefined,
name: undefined,
description: undefined,
attributes: undefined,
price: undefined,
}

for (const [j, header] of csvHeaders.entries()) {
const value = values[j]?.trim()
if (header === 'price') {
entry.price = parseFloat(value) || undefined
}
else if (header === 'attributes') {
if (value) {
const attributes: OpenSeaAttribute[] = []
const attrs: Record<string, string> = {}
value.split(ATTRIBUTE_SEPARATOR).forEach((attr) => {
const [key, val] = attr.split(ATTRIBUTE_KEY_VALUE_SEPARATOR)
if (key && val)
attrs[key.trim()] = val.trim()
attributes.push({
trait_type: key,
value: val,
})
})
entry.attributes = attributes
}
}
else {
entry[header] = value || undefined
}
}

entry.valid = isValidEntry(entry)

entries[entry.file as string] = entry as Entry
}

return entries
}

function parseCsvWithoutHeaders(lines): Record<string, Entry> {
const entries: Record<string, Entry> = {}

for (const line_ of lines) {
const line = line_.trim()
const values = line.split(',').map(value => removeQuotes(value.trim()))
const [file, name, description, price] = values.map(v => v.trim())

const entry = {
file,
name: name === '' ? undefined : name,
description: description === '' ? undefined : description,
price: parseFloat(price) || undefined,
}
entries[file] = {
...entry,
valid: isValidEntry(entry),
}
}
function parseCsvWithHeaders(lines, csvHeaders): Record<string, Entry> {
return processCsvLines(lines, csvHeaders)
}

return entries
function parseCsvWithoutHeaders(lines): Record<string, Entry> {
return processCsvLines(lines, DEFAULT_CSV_HEADERS)
}

export function parseCsv(csvData: string): Record<string, Entry> {
const { $consola } = useNuxtApp()

const lines = csvData.trim().split('\n')
const firstLine = lines[0]
const expectedHeaders = ['file', 'name', 'description', 'price']

const hasHeader = validFileExtension(firstLine)

Expand All @@ -74,7 +77,7 @@ export function parseCsv(csvData: string): Record<string, Entry> {
.map(header => header.trim().toLowerCase())

// Check that header contains expected fields
if (!csvHeaders.every(header => expectedHeaders.includes(header))) {
if (!csvHeaders.every(header => DEFAULT_CSV_HEADERS.includes(header))) {
$consola.error('CSV file has incorrect header fields.', csvHeaders)
}

Expand Down
10 changes: 5 additions & 5 deletions public/massmint/template.csv
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
file,name,description,price
art1.png,Art #1,Description for the Art #1,1
art2.png,Art #2,Description for the Art #2,1
art3.png,Art #3,Description for the Art #3,1
art4.png,Art #4,Description for the Art #4,1
file,name,description,attributes,price
art1.png,Art #1,Description for the Art #1,color:white;expression:happy,1
art2.png,Art #2,Description for the Art #2,color:blue;expression:shy,1
art3.png,Art #3,Description for the Art #3,,1
art4.png,Art #4,Description for the Art #4,,1

0 comments on commit 6d70084

Please sign in to comment.