User editor, baseURL change, remove octopart autofill

pull/2/head^2
willd 1 year ago
parent 1c8bdc69f6
commit 4f5df6c1fe

@ -20,6 +20,7 @@ db_engine = {}
db_metadata = {}
parts = {}
octopartURL =""
baseURL = "/parts-dev"
def getContainers():
query = "select id, name from containers order by UPPER(name);"
@ -65,11 +66,11 @@ def serveImage(img):
img_io.seek(0)
return send_file(img_io, mimetype='image/png')
@app.route('/parts', strict_slashes=True)
@app.route(baseURL, strict_slashes=True)
def index():
return render_template('partsearch.html', containers=getContainers())
return render_template('partsearch.html', containers=getContainers(), baseURL=baseURL)
@app.route('/parts/getlocationsInContainer/<containerID>')
@app.route(baseURL+'/getlocationsInContainer/<containerID>')
def get_locations_in_container(containerID):
s = 'select id, name from locations where container_id = :id order by name;'
r = db_engine.execute(text(s), id=containerID)
@ -79,7 +80,7 @@ def get_locations_in_container(containerID):
r.close()
return json.dumps(l)
@app.route('/parts/getlocationURL/<locationID>')
@app.route(baseURL+'/getlocationURL/<locationID>')
def get_locationURL(locationID):
s = 'select map from locations where id = :id;'
r = db_engine.execute(text(s), id=locationID)
@ -89,7 +90,7 @@ def get_locationURL(locationID):
r.close()
return l[0][0]
@app.route('/parts/locationEditor')
@app.route(baseURL+'/locationEditor')
def locationEditor():
query = 'select c.name as container, l.name as name, l.id, c.id as container_id from locations as l inner join containers as c on l.container_id = c.id order by container, name;'
r = db_engine.execute(text(query))
@ -97,9 +98,20 @@ def locationEditor():
for row in r:
locations.append(dict(row))
r.close()
return render_template('locationEditor.html', locations=locations, containers=getContainers())
return render_template('locationEditor.html', locations=locations, containers=getContainers(),baseURL=baseURL)
@app.route(baseURL+'/userEditor')
def userEditor():
query = 'select c.name as container, l.name as name, l.id, c.id as container_id from locations as l inner join containers as c on l.container_id = c.id order by container, name;'
query = 'select id, username from users;'
r = db_engine.execute(text(query))
users = []
for row in r:
users.append(dict(row))
r.close()
return render_template('userEditor.html', users=users, containers=getContainers(),baseURL=baseURL)
@app.route('/parts/alterLocation/<locationID>', methods=['POST'])
@app.route(baseURL+'/alterLocation/<locationID>', methods=['POST'])
@requires_auth
def alterLocation(locationID):
locationID = int(locationID)
@ -121,8 +133,30 @@ def alterLocation(locationID):
r = db_engine.execute(s, name=request.form['name'],container=request.form['container'],locationID=locationID);
r.close()
return '{"status":"ok"}'
@app.route(baseURL+'/alterUser/<userID>', methods=['POST'])
@requires_auth
def alterUser(userID):
userID = int(userID)
s = ''
if userID < 0:
# New entry
s = 'insert into users (username, password) '
s += 'values (:name, :password);'
s = text(s)
r = db_engine.execute(s,username=request.form['name'],password=request.form['password']);
r.close()
return '{"status":"ok"}'
else:
# Modify entry
s = 'update users '
s += 'set username=:name, password=:password '
s += 'where id=:userID;'
s = text(s)
r = db_engine.execute(s, name=request.form['name'],password=request.form['password'],userID=userID);
r.close()
return '{"status":"ok"}'
@app.route('/parts/getpartinfo/<partID>')
@app.route(baseURL+'/getpartinfo/<partID>')
def get_part_info(partID):
s = 'select p.id,partno,description,notes, c.name || l.name as location_descriptor, location_id, container_id, datasheet from parts as p inner join locations as l on p.location_id = l.id inner join containers as c on l.container_id = c.id where p.id = :id;'
r = db_engine.execute(text(s), id=partID)
@ -132,7 +166,7 @@ def get_part_info(partID):
r.close()
return json.dumps(l[0])
@app.route('/parts/query/<filter_dummy>/<query>') # TODO: maybe change AND to OR or maybe not
@app.route(baseURL+'/query/<filter_dummy>/<query>') # TODO: maybe change AND to OR or maybe not
def query(filter_dummy, query):
filter = request.args.to_dict()
keywords = query.split() # Default splits with spaces
@ -171,7 +205,7 @@ def query(filter_dummy, query):
r.close()
return json.dumps(l)
@app.route('/parts/map/<containerID>')
@app.route(baseURL+'/map/<containerID>')
def getMap(containerID):
s = 'select map, overlay from containers where id = :id;'
r = db_engine.execute(text(s), id=containerID)
@ -196,14 +230,14 @@ def getMap(containerID):
return serveImage(mapimage)
@app.route('/parts/getfile/<filename>')
@app.route(baseURL+'/getfile/<filename>')
def getfile(filename):
if(re.match('^[\w\-_]+.[p|P][d|D][f|F]$', filename) == None):
return 'No injections pls.'
return send_from_directory('/srv/datasheets/', filename)
@app.route('/parts/alter/<partID>', methods=['POST'])
@app.route(baseURL+'/alter/<partID>', methods=['POST'])
@requires_auth
def alter(partID):
partID = int(partID)
@ -276,7 +310,7 @@ def alter(partID):
r.close()
return '{"status":"ok", "part_id" : ' + str(new_id) + '}'
@app.route('/parts/delete/<partID>')
@app.route(baseURL+'/delete/<partID>')
@requires_auth
def delete(partID):
if int(partID) < 0:
@ -285,7 +319,7 @@ def delete(partID):
r = db_engine.execute(s, id=partID)
return '{"status":"ok"}'
@app.route('/parts/deleteLocation/<locationID>')
@app.route(baseURL+'/deleteLocation/<locationID>')
@requires_auth
def deleteLocation(locationID):
if int(locationID) < 0:
@ -293,8 +327,16 @@ def deleteLocation(locationID):
s = text('delete from locations where id=:id;')
r = db_engine.execute(s, id=locationID)
return '{"status":"ok"}'
@app.route(baseURL+'/deleteUser/<userID>')
@requires_auth
def deleteUser(userID):
if int(userID) < 0:
abort(400)
s = text('delete from users where id=:id;')
r = db_engine.execute(s, id=userID)
return '{"status":"ok"}'
@app.route('/parts/fetchOctopartSnippet/<searchTerm>')
@app.route(baseURL+'/fetchOctopartSnippet/<searchTerm>')
def fetchOctopartSnippet(searchTerm):
if octopartURL == '':
return '{"result":"octopart integration not enabled"}'
@ -334,7 +376,7 @@ if __name__ == '__main__':
app.config['SESSION_TYPE'] = 'memcached'
with open('admin.json') as f:
postgres_credentials = json.load(f)
db_engine, db_metadata = connect(postgres_credentials['username'], postgres_credentials['password'], 'parts_v2')
db_engine, db_metadata = connect(postgres_credentials['username'], postgres_credentials['password'], 'parts_v3')
parts = sqlalchemy.Table('parts', db_metadata)
try:
with open('octopartAPIkey.json') as f:
@ -350,4 +392,4 @@ if __name__ == '__main__':
'''s = select([parts]).where(parts.c.notes != '')
for row in db_engine.execute(s):
print row'''
app.run('0.0.0.0')
app.run(host='127.0.0.1', port='5001', debug=True)

@ -213,6 +213,10 @@ function show_part_info(partID) {
}).fail(function() {
console.log( "Fetching part info failed" );
});
// update URL and history
var query = $('.search-bar').val();
window.history.pushState('searching', '', 'parts?q=' + query + '&p=' + partID)
}
function perform_query() {
@ -245,6 +249,9 @@ function perform_query() {
$('#no-results').animate({opacity:1},2000);
console.log( "Query failed" );
});
// update URL and history
window.history.pushState('searching', '', 'parts?q=' + query)
}
function container_onchange() {
@ -260,20 +267,20 @@ function container_onchange() {
}
}
function octopartFetch(){
$('#magic-autofill-button').attr('disabled', 'disabled');
$.getJSON(rootURL + 'fetchOctopartSnippet/' + $('#partno-input').val()).done(function(json){
$('#magic-autofill-button').removeAttr('disabled');
if (json['result']=='ok'){
$('#description-input').val(json['snippet']);
}else{
$('#description-input').val('');
$('#description-input').attr('placeholder', json['result']);
}
}).fail(function() {
$('#magic-autofill-button').removeAttr('disabled');
});
}
// function octopartFetch(){
// $('#magic-autofill-button').attr('disabled', 'disabled');
// $.getJSON(rootURL + 'fetchOctopartSnippet/' + $('#partno-input').val()).done(function(json){
// $('#magic-autofill-button').removeAttr('disabled');
// if (json['result']=='ok'){
// $('#description-input').val(json['snippet']);
// }else{
// $('#description-input').val('');
// $('#description-input').attr('placeholder', json['result']);
// }
// }).fail(function() {
// $('#magic-autofill-button').removeAttr('disabled');
// });
// }
$(document).ready(function() {
$.ajaxSetup({ cache: false });
@ -291,6 +298,15 @@ $(document).ready(function() {
perform_query();
});
//For linking to a search query or specific part
const search_params = new URLSearchParams(window.location.search);
if(search_params.has('q')){
$('.search-bar').val(search_params.get('q')); perform_query();
}
if(search_params.has('p')){
show_part_info(search_params.get('p'));
}
// $('#partno-input').bind('blur',function() {
// if($('#partno-input').val().length > 0){
// octopartFetch();

@ -8,10 +8,10 @@
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="/parts/static/style.css">
<link rel="stylesheet" type="text/css" href="{{ baseURL }}/static/style.css">
<script src="https://use.fontawesome.com/2fef7be393.js"></script>
<script type="text/javascript" src="/parts/static/common.js"></script>
<script type="text/javascript" src="/parts/static/locationEditorScript.js"></script>
<script type="text/javascript" src="{{ baseURL }}/static/common.js"></script>
<script type="text/javascript" src="{{ baseURL }}/static/locationEditorScript.js"></script>
</head>
<body>
<h1>LOCATION EDITOR</h1>

@ -9,13 +9,13 @@
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="parts/static/style.css" media="screen and (min-width:500px)">
<link rel="stylesheet" type="text/css" href="parts/static/style-m.css" media="screen and (max-width:500px)">
<link rel="apple-touch-icon-precomposed" href="parts/static/apple-touch-icon.png">
<link rel="stylesheet" type="text/css" href="{{ baseURL }}/static/style.css" media="screen and (min-width:500px)">
<link rel="stylesheet" type="text/css" href="{{ baseURL }}/static/style-m.css" media="screen and (max-width:500px)">
<link rel="apple-touch-icon-precomposed" href="{{ baseURL }}/parts/static/apple-touch-icon.png">
<script src="https://use.fontawesome.com/2fef7be393.js"></script>
<script type="text/javascript" src="parts/static/common.js"></script>
<script type="text/javascript" src="parts/static/script.js"></script>
<script type="text/javascript" src="{{ baseURL }}/static/common.js"></script>
<script type="text/javascript" src="{{ baseURL }}/static/script.js"></script>
</head>
<body>
<h1>ELAB Part Search Engine</h1>
@ -58,7 +58,7 @@
<tr id="partno" class="details-content"><td><p></p><input type="text" name="partno-input" placeholder="Part Number" class="pinfo-input" id="partno-input"/></td></tr>
<tr id="description-head" class="details-header"><td>DESCRIPTION</td></tr>
<tr id="description" class="details-content"><td><p></p><input type="text" name="description-input" placeholder="Description" class="pinfo-input" id="description-input"/></td></tr>
<tr id="magical_autofill" style="display:none"><td><input type="button" id="magic-autofill-button" onclick="octopartFetch()" value="magical auto-fill" style="margin-bottom:6pt"/> powered by <a href="http://octopart.com" class="small_link">OctoPart™</a></td></tr>
<!-- <tr id="magical_autofill" style="display:none"><td><input type="button" id="magic-autofill-button" onclick="octopartFetch()" value="magical auto-fill" style="margin-bottom:6pt"/> powered by <a href="http://octopart.com" class="small_link">OctoPart™</a></td></tr> -->
<tr id="datasheet-head" class="details-header"><td>DATASHEET: </td></tr>
<tr id="datasheet" style="display: none"><td><input type="file" accept=".pdf" class="pinfo-input" id="datasheet-finput"></td></tr>
<tr id="datasheet" style="display: none"><td>OR: <input type="text" class="pinfo-input" name="datasheet-url-input" style="width:80%; margin-bottom: 6pt;" placeholder="Datasheet URL"></td></tr>

Loading…
Cancel
Save