add server (only with basic search for now)
This commit is contained in:
parent
99ad35f618
commit
8649abb725
|
@ -0,0 +1,78 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>electronic parts database explorer</title>
|
||||
<script type='application/javascript'>
|
||||
"use strict";
|
||||
|
||||
// the last search query
|
||||
var last_search = null;
|
||||
|
||||
function search()
|
||||
{
|
||||
const terms = document.getElementById('terms');
|
||||
if (terms && terms.value && terms.value.length >= 3) {
|
||||
console.log(terms.value);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
last_search = '/search/' + terms.value;
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', last_search, true);
|
||||
xhr.onload = function() {
|
||||
if (this.responseURL.endsWith(last_search)) {
|
||||
populate(JSON.parse(this.response));
|
||||
}
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
console.log("search call failed");
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function populate(parts)
|
||||
{
|
||||
const results = document.getElementById('results');
|
||||
results.innerHTML = null;
|
||||
for (const part of parts) {
|
||||
console.log(part);
|
||||
const tr = document.createElement('tr');
|
||||
for (const key of ["name", "description"]) {
|
||||
const td = document.createElement('td');
|
||||
td.innerHTML = part[key];
|
||||
tr.appendChild(td);
|
||||
}
|
||||
results.appendChild(tr);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
table {
|
||||
border: 1px #E1E3F2 solid;
|
||||
width: 500px;
|
||||
resize: both;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<form>
|
||||
<input type="text" id="terms" name="terms" placeholder="search terms" oninput="search()"><br>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th>description</th>
|
||||
<th>stock</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="results"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/env ruby
|
||||
# encoding: utf-8
|
||||
# ruby: 3.0.2
|
||||
=begin
|
||||
server to query part database
|
||||
|
||||
to install sinatra
|
||||
sudo pacman -S ruby-sinatra ruby-webrick
|
||||
pikaur -S ruby-mysql2
|
||||
=end
|
||||
require 'set'
|
||||
require 'mysql2'
|
||||
require 'json'
|
||||
require 'sinatra'
|
||||
|
||||
DEBUG = true
|
||||
PARTS_LIMIT = 100
|
||||
CREDENTIALS = "credentials.json"
|
||||
raise "database information #{CREDENTIALS} do not exist" unless File.file? CREDENTIALS
|
||||
|
||||
# open server
|
||||
configure do
|
||||
if DEBUG then
|
||||
set :show_exceptions, true
|
||||
set :logging, true
|
||||
else
|
||||
set :show_exceptions, false
|
||||
set :environment, :production
|
||||
set :logging, false
|
||||
end
|
||||
set :protection, :except => :json_csrf
|
||||
|
||||
set :bind, 'localhost'
|
||||
set :port, 4244
|
||||
set :public_folder, "public"
|
||||
set :static, true
|
||||
end
|
||||
|
||||
before do
|
||||
response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
response.headers["Access-Control-Allow-Headers"] = "Content-Type"
|
||||
if request.request_method == 'OPTIONS'
|
||||
response.headers["Access-Control-Allow-Methods"] = "GET,POST"
|
||||
halt 200
|
||||
end
|
||||
|
||||
# all replies are only JSON
|
||||
content_type 'application/json'
|
||||
# open database
|
||||
credentials = {}
|
||||
JSON.parse(IO.read(CREDENTIALS)).each {|key,value| credentials[key.to_sym] = value}
|
||||
Mysql2::Client.default_query_options.merge!(:as => :hash)
|
||||
@db = Mysql2::Client.new(credentials)
|
||||
end
|
||||
|
||||
after do
|
||||
response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
response.headers["Access-Control-Allow-Headers"] = "Content-Type"
|
||||
end
|
||||
|
||||
get '/' do
|
||||
redirect to('/index.html')
|
||||
end
|
||||
|
||||
def get_part_by_id(id)
|
||||
statement = @db.prepare("SELECT part.name, part.description, part.datasheet, manufacturer.name AS manufacturer, package.name AS package, part.pincount AS pincount, part.page AS page, part.family AS family FROM part LEFT JOIN package ON package.id = part.package LEFT JOIN manufacturer ON manufacturer.id = part.manufacturer WHERE part.id = ?")
|
||||
part = statement.execute(id, :as => :hash).to_a[0]
|
||||
return nil unless part
|
||||
# merge with family info
|
||||
while part and part["family"] do
|
||||
family = statement.execute(part["family"]).to_a[0]
|
||||
part["family"] = nil # reset info
|
||||
break unless family
|
||||
family.each do |k,v|
|
||||
part[k] ||= v
|
||||
end
|
||||
end
|
||||
# clean up
|
||||
delete = ["family"]
|
||||
delete.each do |k|
|
||||
part.delete k
|
||||
end
|
||||
return part
|
||||
end
|
||||
|
||||
def get_part_by_name(name)
|
||||
statement = @db.prepare("SELECT id FROM part WHERE part.name = ?")
|
||||
id = statement.execute(name).to_a[0]
|
||||
if id then
|
||||
return get_part_by_id(id["id"])
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
get '/part/:name' do
|
||||
part = get_part_by_name(params['name'])
|
||||
halt 404 unless part
|
||||
part.to_json
|
||||
end
|
||||
|
||||
get '/search/:terms' do
|
||||
terms = params['terms'].split(" ")
|
||||
terms.keep_if {|term| term.length >= 3}
|
||||
ids = Set.new
|
||||
part_statement = @db.prepare("SELECT id FROM part WHERE name LIKE ?")
|
||||
terms.each do |term|
|
||||
part_statement.execute("%#{term}%").each do |row|
|
||||
ids << row["id"]
|
||||
end
|
||||
end
|
||||
puts ids
|
||||
parts = ids.collect {|id| get_part_by_id(id)}
|
||||
parts.compact!
|
||||
parts = parts[0, PARTS_LIMIT]
|
||||
parts.sort! {|x,y| x["name"] <=> y["name"]}
|
||||
parts.to_json
|
||||
end
|
Loading…
Reference in New Issue