Compare commits

...

4 Commits

6 changed files with 81 additions and 36 deletions

View File

@ -11,6 +11,8 @@ Distributors also need to be managed directly in the database.
Since this does change very often, it isn't too much of a hassle. Since this does change very often, it isn't too much of a hassle.
An example is in `populate.sql`, also with some other entries. An example is in `populate.sql`, also with some other entries.
To import an LCSC part, simply go to the `/import/lcsc/Cxxxx` page and the part will be added to the database.
goals goals
===== =====

View File

@ -1,9 +1,6 @@
-- manufacturers -- manufacturers
INSERT INTO manufacturer (name, homepage, search) VALUES ("Espressif", "https://www.espressif.com/", "https://products.espressif.com/#/product-selector"); INSERT INTO manufacturer (name, homepage, search) VALUES ("Espressif", "https://www.espressif.com/", "https://products.espressif.com/#/product-selector");
-- packages
INSERT INTO package (name) VALUES ("module");
-- distributors -- distributors
INSERT INTO distributor (name, homepage, product_page) VALUES ("LCSC", "https://www.lcsc.com/", "https://www.lcsc.com/product-detail/%s.html"); INSERT INTO distributor (name, homepage, product_page) VALUES ("LCSC", "https://www.lcsc.com/", "https://www.lcsc.com/product-detail/%s.html");
INSERT INTO distributor (name, homepage, product_page) VALUES ("JLCPCB", "https://jlcpcb.com/", "https://jlcpcb.com/partdetail/part/%s"); INSERT INTO distributor (name, homepage, product_page) VALUES ("JLCPCB", "https://jlcpcb.com/", "https://jlcpcb.com/partdetail/part/%s");
@ -14,7 +11,7 @@ INSERT INTO distributor (name, homepage, product_page) VALUES ("Octopart", "http
INSERT INTO distributor (name, homepage, product_page) VALUES ("AliExpress", "https://www.aliexpress.com/", "https://aliexpress.com/item/%s.html"); INSERT INTO distributor (name, homepage, product_page) VALUES ("AliExpress", "https://www.aliexpress.com/", "https://aliexpress.com/item/%s.html");
-- parts -- parts
INSERT INTO part (name, description, manufacturer, datasheet, package, pincount, page) VALUES ("ESP32-S3-WROOM-1", "ESP32-S3 module, PCB antenna", (SELECT id FROM manufacturer WHERE name = "Espressif"), "https://www.espressif.com/sites/default/files/documentation/esp32-s3-wroom-1_wroom-1u_datasheet_en.pdf", (SELECT id FROM package WHERE name = "module"), 49, "https://www.espressif.com/en/module/esp32-s3-wroom-1-en"); INSERT INTO part (name, description, manufacturer, datasheet, package, page) VALUES ("ESP32-S3-WROOM-1", "ESP32-S3 module, PCB antenna", (SELECT id FROM manufacturer WHERE name = "Espressif"), "https://www.espressif.com/sites/default/files/documentation/esp32-s3-wroom-1_wroom-1u_datasheet_en.pdf", "module-49", "https://www.espressif.com/en/module/esp32-s3-wroom-1-en");
INSERT INTO part (name, description) VALUES ("ESP32-S3-WROOM-1-N4", "ESP32-S3 module, PCB antenna, 4MB flash"); INSERT INTO part (name, description) VALUES ("ESP32-S3-WROOM-1-N4", "ESP32-S3 module, PCB antenna, 4MB flash");
UPDATE part SET family=(SELECT id FROM part WHERE name = "ESP32-S3-WROOM-1") WHERE name = "ESP32-S3-WROOM-1-N4"; UPDATE part SET family=(SELECT id FROM part WHERE name = "ESP32-S3-WROOM-1") WHERE name = "ESP32-S3-WROOM-1-N4";

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>electronic parts database explorer</title> <title>electronic parts inventory manager</title>
<script type='application/javascript' src='partdb.js'></script> <script type='application/javascript' src='partdb.js'></script>
<link rel='stylesheet' href='index.css'> <link rel='stylesheet' href='index.css'>
</head> </head>
@ -25,7 +25,7 @@
<div class="name">description:</div><div class="justify"><input class="full" type="text" id="part_description"></div> <div class="name">description:</div><div class="justify"><input class="full" type="text" id="part_description"></div>
<div>details:</div> <div>details:</div>
<div class="justify"><textarea class="full" id="part_details" rows="4"></textarea></div> <div class="justify"><textarea class="full" id="part_details" rows="4"></textarea></div>
<div>package: <input type="text" id="part_package" size="4">-<input type="text" id="part_pincount" size="2"></div> <div class="name">package:</div><div class="justify"><input class="full" type="text" id="part_package"></div>
<div class="name">manufacturer:</div><div class="justify"><input class="full" type="text" id="part_manufacturer"></div> <div class="name">manufacturer:</div><div class="justify"><input class="full" type="text" id="part_manufacturer"></div>
<div class="name">mpn:</div><div class="justify"><input class="full" type="text" id="part_mpn"></div> <div class="name">mpn:</div><div class="justify"><input class="full" type="text" id="part_mpn"></div>
<div class="name">family:</div><div class="justify"><input class="full" type="text" id="part_family"></div> <div class="name">family:</div><div class="justify"><input class="full" type="text" id="part_family"></div>

View File

@ -9,7 +9,7 @@ var last_search = null;
// the collection of parts // the collection of parts
var parts = null; var parts = null;
// part field to populate // part field to populate
const fields = ["name", "description", "details", "package", "pincount", "manufacturer", "mpn", "family", "datasheet", "page", "location", "stock"]; const fields = ["name", "description", "details", "package", "manufacturer", "mpn", "family", "datasheet", "page", "location", "stock"];
// URLs to set // URLs to set
const urls = ["page","datasheet"]; const urls = ["page","datasheet"];

View File

@ -20,14 +20,6 @@ CREATE TABLE IF NOT EXISTS distributor (
product_page TEXT -- URL to product page (%s is replace by sku) product_page TEXT -- URL to product page (%s is replace by sku)
); );
-- part package
CREATE TABLE IF NOT EXISTS package (
id INTEGER AUTO_INCREMENT PRIMARY KEY, -- index
name TEXT NOT NULL UNIQUE,
family INTEGER, -- if this package is part of a more general package family
FOREIGN KEY (family) REFERENCES package (id)
);
-- the part itself -- the part itself
CREATE TABLE IF NOT EXISTS part ( CREATE TABLE IF NOT EXISTS part (
id INTEGER AUTO_INCREMENT PRIMARY KEY, -- index id INTEGER AUTO_INCREMENT PRIMARY KEY, -- index
@ -38,12 +30,10 @@ CREATE TABLE IF NOT EXISTS part (
mpn TEXT, mpn TEXT,
family INTEGER, -- if this part is part of a part family family INTEGER, -- if this part is part of a part family
datasheet TEXT, -- URL to datasheet datasheet TEXT, -- URL to datasheet
package INTEGER, package TEXT,
pincount INTEGER,
page TEXT, -- URL to product page page TEXT, -- URL to product page
FOREIGN KEY (manufacturer) REFERENCES manufacturer (id), FOREIGN KEY (manufacturer) REFERENCES manufacturer (id),
FOREIGN KEY (family) REFERENCES part (id), FOREIGN KEY (family) REFERENCES part (id)
FOREIGN KEY (package) REFERENCES package (id)
); );
-- a project (as part) can be an assembly of other parts -- a project (as part) can be an assembly of other parts

View File

@ -15,6 +15,8 @@ require 'set'
require 'mysql2' require 'mysql2'
require 'json' require 'json'
require 'sinatra' require 'sinatra'
require 'uri'
require 'net/http'
# allow dumping crashes in browser # allow dumping crashes in browser
DEBUG = false DEBUG = false
@ -77,7 +79,7 @@ end
def get_part_by_id(id) def get_part_by_id(id)
return nil unless id return nil unless id
statement = @db.prepare("SELECT part.id, part.name, part.description, part.details, part.datasheet, manufacturer.name AS manufacturer, part.mpn AS mpn, package.name AS package, part.pincount AS pincount, part.page AS page, part.family AS parent, p2.name AS family FROM part LEFT JOIN package ON package.id = part.package LEFT JOIN manufacturer ON manufacturer.id = part.manufacturer LEFT JOIN part AS p2 ON p2.id = part.family WHERE part.id = ?") statement = @db.prepare("SELECT part.id, part.name, part.description, part.details, part.datasheet, manufacturer.name AS manufacturer, part.mpn AS mpn, package.name AS package, part.page AS page, part.family AS parent, p2.name AS family FROM part LEFT JOIN package ON package.id = part.package LEFT JOIN manufacturer ON manufacturer.id = part.manufacturer LEFT JOIN part AS p2 ON p2.id = part.family WHERE part.id = ?")
part = statement.execute(id).to_a[0] part = statement.execute(id).to_a[0]
return nil unless part return nil unless part
parent = get_part_by_id(part["parent"]) parent = get_part_by_id(part["parent"])
@ -230,23 +232,16 @@ get '/delete/:id' do
return 200 return 200
end end
post '/part' do def add_part(part)
request.body.rewind
begin
part = JSON.parse(request.body.read)
rescue
halt 401, "not json"
end
puts part if DEBUG
if part["id"] then if part["id"] then
# ensure part to update exists # ensure part to update exists
statement = @db.prepare("SELECT id FROM part WHERE id = ?") statement = @db.prepare("SELECT id FROM part WHERE id = ?")
halt(401, "id not valid") if statement.execute(part["id"]).to_a.empty? raise StandardError.new("id not valid") if statement.execute(part["id"]).to_a.empty?
else else
# add new part # add new part
halt(401, "name required") unless part["name"] and part["name"].length > 0 raise StandardError.new("name required") unless part["name"] and part["name"].length > 0
statement = @db.prepare("SELECT id FROM part WHERE name = ?") statement = @db.prepare("SELECT id FROM part WHERE name = ?")
halt(401, "name already existing") unless statement.execute(part["name"]).to_a.empty? raise StandardError.new("name already existing") unless statement.execute(part["name"]).to_a.empty?
insert = @db.prepare("INSERT INTO part (name) VALUES (?)"); insert = @db.prepare("INSERT INTO part (name) VALUES (?)");
insert.execute(part["name"]) insert.execute(part["name"])
part["id"] = statement.execute(part["name"]).to_a[0]["id"] part["id"] = statement.execute(part["name"]).to_a[0]["id"]
@ -258,23 +253,22 @@ post '/part' do
if part[field] then if part[field] then
statement = @db.prepare("SELECT id FROM part WHERE name = ?") statement = @db.prepare("SELECT id FROM part WHERE name = ?")
family = statement.execute(part[field]).to_a family = statement.execute(part[field]).to_a
halt(401, "family not existing") if family.empty? raise StandardError.new("family not existing") if family.empty?
update = @db.prepare("UPDATE part SET #{field} = ? WHERE id = ?") update = @db.prepare("UPDATE part SET #{field} = ? WHERE id = ?")
update.execute(family[0]["id"], part["id"]) update.execute(family[0]["id"], part["id"])
family = get_part_by_id(family[0]["id"]) family = get_part_by_id(family[0]["id"])
end end
# update fields # update fields
fields_txt = ["name", "description", "details", "mpn", "pincount", "datasheet", "page"]; fields_txt = ["name", "description", "details", "mpn", "package", "datasheet", "page"];
fields_txt.each do |field| fields_txt.each do |field|
next unless part[field] next unless part[field]
part[field] = nil if part[field].kind_of?(String) and 0 == part[field].length part[field] = nil if part[field].kind_of?(String) and 0 == part[field].length
part[field] = part[field].to_i if part[field] and "pincount" == field
next if family and family[field] == part[field] next if family and family[field] == part[field]
update = @db.prepare("UPDATE part SET #{field} = ? WHERE id = ?") update = @db.prepare("UPDATE part SET #{field} = ? WHERE id = ?")
update.execute(part[field], part["id"]) update.execute(part[field], part["id"])
end end
# update manufacturer and package # update manufacturer and package
field_ref = ["manufacturer", "package"] field_ref = ["manufacturer"]
field_ref.each do |field| field_ref.each do |field|
part[field] = nil if part[field] and 0 == part[field].length part[field] = nil if part[field] and 0 == part[field].length
next if family and family[field] == part[field] next if family and family[field] == part[field]
@ -328,7 +322,7 @@ post '/part' do
next unless sku and !sku.empty? next unless sku and !sku.empty?
statement = @db.prepare("SELECT id FROM distributor WHERE LOWER(name) = ?") statement = @db.prepare("SELECT id FROM distributor WHERE LOWER(name) = ?")
ref = statement.execute(distributor.downcase).to_a[0] ref = statement.execute(distributor.downcase).to_a[0]
halt(401, "distributor unknown") unless ref raise StandardError.new("distributor unknown") unless ref
insert = @db.prepare("INSERT INTO distribution (distributor,part,sku) VALUES (?,?,?)"); insert = @db.prepare("INSERT INTO distribution (distributor,part,sku) VALUES (?,?,?)");
insert.execute(ref["id"], part["id"], sku) insert.execute(ref["id"], part["id"], sku)
end end
@ -355,5 +349,67 @@ post '/part' do
end end
end end
end end
end
post '/part' do
request.body.rewind
begin
part = JSON.parse(request.body.read)
rescue
halt 401, "not json"
end
puts part if DEBUG
begin
add_part(part)
rescue StandardError => e
halt 401, e.message
end
return 200
end
get '/import/lcsc/:lcsc' do
halt 401 unless params['lcsc'] and params['lcsc'] =~ /^C\d+$/i
uri = URI("https://wmsc.lcsc.com/wmsc/product/detail?productCode=#{params['lcsc']}")
res = Net::HTTP.get_response(uri)
halt 401, "could not get part" unless res.is_a?(Net::HTTPSuccess)
json = JSON.parse(res.body)
#puts json
halt 401, "part not found" unless 200 == json["code"] and json["result"]
result = json["result"]
part = {}
part["name"] = result["productModel"]
part["mpn"] = result["productModel"]
part["description"] = result["productDescEn"]
part["details"] = result["productIntroEn"]
part["manufacturer"] = result["brandNameEn"]
part["package"] = result["encapStandard"]
part["distributors"] = {"LCSC" => result["productCode"]}
part["attachments"] = result["productImages"]
part["datasheet"] = result["pdfUrl"]
existing = get_part_by_name(part["name"])
halt 401, "part name already exists" if existing
puts part
begin
add_part(part)
rescue StandardError => e
halt 401, e.message
end
i = 0
(part["attachments"] + [part["datasheet"]]).each do |attachement|
file = attachement.split("/")[-1]
dir = PUBLIC + "/" + ATTACHMENTS + "/" + part["name"]
path = "#{dir}/#{i}_#{file}"
i += 1
unless File.file?(path) then
uri = URI(attachement)
res = Net::HTTP.get_response(uri)
if (res.is_a?(Net::HTTPSuccess)) then
Dir.mkdir(dir) unless File.directory?(dir)
File.open(path, "wb") do |f|
f.write res.body
end
end
end
end
return 200 return 200
end end