Browse Source

Add manufacturers logos; Add cron stuff for it

master
squeaky otter 6 years ago
parent
commit
9468058d92
  1. 1
      .gitignore
  2. 12
      controllers/edits.py
  3. 39
      crons.py
  4. 16
      forms.py
  5. 37
      migrations/versions/2e1affad10af_.py
  6. 50
      models.py
  7. 61
      stockazng.py
  8. 82
      templates/edit/manufacturers_logos.jinja2
  9. 1
      templates/layout.jinja2

1
.gitignore

@ -14,3 +14,4 @@ uploads/barcodes/*
uploads/attachments/*
uploads/cache/*
uploads/thumbs/*
uploads/logos/*

12
controllers/edits.py

@ -1,5 +1,6 @@
from flask import Blueprint, render_template
from flask import Blueprint, render_template, jsonify
from flask.ext.security import login_required
from models import db, ManufacturerLogo
bp_edits = Blueprint('bp_edits', __name__)
@ -13,3 +14,12 @@ def edit_view(view):
return render_template('about.jinja2')
return render_template('edit/{0}.jinja2'.format(view))
@bp_edits.route('/edit/manufacturers/logos/<int:pid>/del', methods=['DELETE', 'PUT'])
@login_required
def logo_del(pid):
logo = ManufacturerLogo.query.get_or_404(pid)
db.session.delete(logo)
db.session.commit()
return jsonify({"Result": "OK"})

39
crons.py

@ -1,7 +1,7 @@
import os
import logging
from logging.handlers import RotatingFileHandler
from models import PartAttachment, Part
from models import PartAttachment, Part, ManufacturerLogo
import urllib
from posixpath import basename
import urlparse
@ -20,26 +20,34 @@ TRIANGLE = {
}
def illuminatis(cfg, _file, size, _logger):
if _file.is_remote and _file.remote_cached:
def illuminatis(cfg, _file, size, barcode_id, _logger):
if 'is_remote' in dir(_file) or 'remote_cached' in dir(_file):
if _file.is_remote and _file.remote_cached:
remote = True
else:
remote = False
else:
remote = False
if remote:
_logger.info("Cached file")
infile = os.path.join(cfg['UPLOADS_DEFAULT_DEST'],
'cache',
str(Part.BARCODE_TYPE),
str(barcode_id),
_file.filename)
else:
_logger.info("Else file is not remote OR not cached yet")
infile = os.path.join(cfg['UPLOADS_DEFAULT_DEST'],
'attachments',
str(Part.BARCODE_TYPE),
str(barcode_id),
_file.filename)
outfile = os.path.join(cfg['UPLOADS_DEFAULT_DEST'],
'thumbs',
str(Part.BARCODE_TYPE),
str(barcode_id),
"thumb_%s.%s" % (size, _file.filename))
the_dir = os.path.join(cfg['UPLOADS_DEFAULT_DEST'], 'thumbs', str(Part.BARCODE_TYPE))
the_dir = os.path.join(cfg['UPLOADS_DEFAULT_DEST'], 'thumbs', str(barcode_id))
if not os.path.isdir(the_dir):
_logger.info("Creating dir %s" % the_dir)
os.makedirs(the_dir)
@ -70,13 +78,18 @@ def c_cron_thumbs(config, db):
logger.info("Started creating thumbnails.")
images = PartAttachment.query.filter(PartAttachment.simple_type == "IMAGE").all()
for i in images:
logger.info("[{0}]: {1}".format(i.id, i.orig_filename))
illuminatis(config, i, "mini", logger)
illuminatis(config, i, "small", logger)
illuminatis(config, i, "medium", logger)
illuminatis(config, i, "preview", logger)
logger.info("[IMAGES][{0}]: {1}".format(i.id, i.orig_filename))
illuminatis(config, i, "mini", Part.BARCODE_TYPE, logger)
illuminatis(config, i, "small", Part.BARCODE_TYPE, logger)
illuminatis(config, i, "medium", Part.BARCODE_TYPE, logger)
illuminatis(config, i, "preview", Part.BARCODE_TYPE, logger)
logos = ManufacturerLogo.query.all()
for i in logos:
logger.info("[LOGOS][{0}]: {1}".format(i.id, i.orig_filename))
illuminatis(config, i, "mini", ManufacturerLogo.BARCODE_TYPE, logger)
illuminatis(config, i, "small", ManufacturerLogo.BARCODE_TYPE, logger)
logger.info("Finished creating thumbnails.")

16
forms.py

@ -3,7 +3,7 @@ from wtforms import StringField, SubmitField, BooleanField
from flask_wtf.file import FileField
from wtforms.validators import DataRequired
from flask_security import RegisterForm
from models import db, Part, PartMeasurementUnit, Storage, Footprint, PartAttachment
from models import db, Part, PartMeasurementUnit, Storage, Footprint, PartAttachment, Manufacturer
from wtforms_alchemy import model_form_factory
from wtforms.ext.sqlalchemy.fields import QuerySelectField
@ -32,6 +32,10 @@ def get_footprints():
return Footprint.query.order_by('name').all()
def get_manufacturers():
return Manufacturer.query.order_by('name').all()
def default_pmu():
return PartMeasurementUnit.query.filter(PartMeasurementUnit.name == 'Pieces').first()
@ -67,3 +71,13 @@ class PartAttachmentForm(ModelForm):
remote_uri = StringField('Alternative remote URI')
submit = SubmitField('Upload')
class ManufacturerLogoForm(ModelForm):
class Meta:
model = PartAttachment
manufacturer = QuerySelectField(query_factory=get_manufacturers, allow_blank=False)
logo = FileField('Image', [DataRequired()])
submit = SubmitField('Upload')

37
migrations/versions/2e1affad10af_.py

@ -0,0 +1,37 @@
"""Add manufacturers logo
Revision ID: 2e1affad10af
Revises: 2233c3e66d21
Create Date: 2015-10-19 17:28:41.927215
"""
# revision identifiers, used by Alembic.
revision = '2e1affad10af'
down_revision = '2233c3e66d21'
from alembic import op
import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.create_table('manufacturer_logo',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('filename', sa.String(length=255), nullable=True),
sa.Column('orig_filename', sa.String(length=255), nullable=True),
sa.Column('hash', sa.String(length=255), nullable=True),
sa.Column('mimetype', sa.String(length=255), nullable=True),
sa.Column('filesize', sa.Integer(), nullable=True),
sa.Column('manufacturer_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['manufacturer_id'], ['manufacturer.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('hash')
)
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_table('manufacturer_logo')
### end Alembic commands ###

50
models.py

@ -118,6 +118,49 @@ class PartMeasurementUnit(db.Model):
return u"{0} ({1})".format(self.name, self.short_name)
class ManufacturerLogo(db.Model):
BARCODE_TYPE = 45
id = db.Column(db.Integer, primary_key=True)
filename = db.Column(db.String(255), unique=False, nullable=True)
orig_filename = db.Column(db.String(255), unique=False, nullable=True)
hash = db.Column(db.String(255), unique=True, nullable=True)
mimetype = db.Column(db.String(255), unique=False, nullable=True)
filesize = db.Column(db.Integer, unique=False, nullable=True, default=0) # stored as bytes
manufacturer_id = db.Column(db.Integer(), db.ForeignKey('manufacturer.id'), nullable=False)
def get_uploads_url(self, fs=False):
if fs:
return os.path.join(current_app.config.get('UPLOADS_DEFAULT_DEST'),
"attachments",
str(self.BARCODE_TYPE),
self.filename)
else:
return os.path.join('/uploads',
"attachments",
str(self.BARCODE_TYPE),
self.filename)
def get_thumbs_url(self, size, fs=False):
if size not in ['mini', 'small']:
return None
filename = "thumb_{0}.{1}".format(size, self.filename)
if fs:
return os.path.join(current_app.config.get('UPLOADS_DEFAULT_DEST'),
'thumbs',
str(self.BARCODE_TYPE),
filename)
else:
return os.path.join('/uploads',
'thumbs',
str(self.BARCODE_TYPE),
filename)
class Manufacturer(db.Model):
BARCODE_TYPE = 15
id = db.Column(db.Integer, primary_key=True)
@ -131,9 +174,13 @@ class Manufacturer(db.Model):
barcode_str = db.Column(db.String(13), nullable=True, info={'label': 'Barcode string'}, default="0000000000000")
part_manufacturers = db.relationship('PartManufacturer', backref='manufacturer', lazy='dynamic')
manufacturer_logo = db.relationship('ManufacturerLogo', backref='manufacturer', lazy='dynamic')
__mapper_args__ = {"order_by": name}
def __repr__(self):
return u"{0}".format(self.name)
class Distributor(db.Model):
BARCODE_TYPE = 20
@ -361,7 +408,8 @@ BARCODE_TYPES = [
{'model': 'project', 'id': Project.BARCODE_TYPE},
{'model': 'storage_category', 'id': StorageCategory.BARCODE_TYPE},
{'model': 'storage', 'id': Storage.BARCODE_TYPE},
{'model': 'part', 'id': Part.BARCODE_TYPE}
{'model': 'part', 'id': Part.BARCODE_TYPE},
{'model': 'manufacturer_logo', 'id': ManufacturerLogo.BARCODE_TYPE}
]

61
stockazng.py

@ -9,7 +9,7 @@ from flask_bootstrap import Bootstrap
from flask_debugtoolbar import DebugToolbarExtension
from flask.ext.uploads import UploadSet, DOCUMENTS, IMAGES, TEXT, DATA, configure_uploads
from models import db, user_datastore, Part, PartAttachment
from models import db, user_datastore, Part, PartAttachment, ManufacturerLogo, BARCODE_TYPES
from controllers.main import bp_main
from controllers.ajax_parts import bp_ajax_parts
@ -25,7 +25,7 @@ from controllers.autocompletes import bp_autocompletes
import texttable
import magic
from dbseed import make_db_seed
from forms import ExtendedRegisterForm
from forms import ExtendedRegisterForm, ManufacturerLogoForm
from crons import c_cron_cache, c_cron_thumbs
from posixpath import basename
@ -69,7 +69,9 @@ security = Security(app, user_datastore,
allowed_extensions = DOCUMENTS + IMAGES + DATA + TEXT + tuple('pdf'.split(" "))
parts_attachments = UploadSet('attachments', extensions=allowed_extensions)
manufacturers_logos = UploadSet('attachments', extensions=IMAGES)
configure_uploads(app, parts_attachments)
configure_uploads(app, manufacturers_logos)
git_version = ""
gitpath = os.path.join(os.getcwd(), ".git")
@ -185,6 +187,45 @@ def parts_attachment_new(pid):
return redirect(url_for("bp_parts.parts_edit", pid=pid, _anchor="AddPartTabAttachments"))
@app.route('/edit/manufacturers/logos', methods=['GET', 'POST'])
@login_required
def new_manufacturers_logos():
logos = ManufacturerLogo.query.all()
form = ManufacturerLogoForm()
if form.validate_on_submit():
if 'logo' in request.files:
_file = request.files['logo']
if _file and _file.filename != '':
# Calculate destination file hash
_str = "{0} {1}".format(_file.filename,
str(datetime.now()))
_hash = sha1()
_hash.update(_str)
_hashi = _hash.hexdigest()
filename = manufacturers_logos.save(storage=_file,
folder=str(ManufacturerLogo.BARCODE_TYPE),
name="{0}.".format(_hashi))
fname = os.path.join(app.config['UPLOADS_DEFAULT_DEST'], 'attachments', filename)
filesize = os.stat(fname).st_size
a = ManufacturerLogo(manufacturer_id=form.manufacturer.data.id,
filename=basename(filename),
orig_filename=_file.filename,
hash=_hashi,
filesize=filesize)
db.session.add(a)
db.session.commit()
return redirect(url_for('new_manufacturers_logos'))
return render_template('edit/manufacturers_logos.jinja2', logos=logos, form=form)
# Used in development
@app.route('/uploads/<path:stuff>', methods=['GET'])
def get_uploads_stuff(stuff):
@ -263,6 +304,22 @@ def cron_cache():
c_cron_cache(app.config, db)
@manager.command
def mkdirs():
""" Create needed dirs if doesn't exists """
def mkdir(path):
if not os.path.isdir(path):
print("Creating: %s" % path)
os.makedirs(path)
mkdir(app.config['UPLOADS_DEFAULT_DEST'])
mkdir(os.path.join(app.config['UPLOADS_DEFAULT_DEST'], 'attachments'))
for i in BARCODE_TYPES:
for ii in ['attachments', 'barcodes', 'thumbs', 'cache']:
mkdir(os.path.join(app.config['UPLOADS_DEFAULT_DEST'], ii, str(i['id'])))
manager.add_command('db', MigrateCommand)
if __name__ == '__main__':

82
templates/edit/manufacturers_logos.jinja2

@ -0,0 +1,82 @@
{% extends "layout.jinja2" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block content %}
<form enctype="multipart/form-data" class="form-inline" id="newManufacturerLogo" method="POST" action="{{ url_for('new_manufacturers_logos') }}">
{{ form.hidden_tag() }}
{{ wtf.form_errors(form, hiddens="only") }}
{{ wtf.form_field(form.logo, 'horizontal', horizontal_columns=('lg', 3, 9)) }}
{{ wtf.form_field(form.manufacturer, 'horizontal', horizontal_columns=('lg', 3, 9)) }}
{{ wtf.form_field(form.submit, button_map={'submit': 'success'}) }}
</form>
<hr/>
<table id="TableParts" class="table table-striped table-bordered" cellspacing="0" width="100%">
<thead>
<tr>
<th>Logo</th>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Logo</th>
<th>Name</th>
<th>Description</th>
</tr>
</tfoot>
<tbody>
{% for i in logos %}
<tr class="part_row" data-id="{{ i.id }}">
<td>
<a href="{{ url_for('bp_edits.logo_del', pid=i.id) }}" class="del" data-name="{{ i.manufacturer.name }}">
<i class="fa fa-remove" title="Edit part"></i>
</a>
<img rel="popover" src="{{ i.get_thumbs_url('mini') }}" data-img-small="{{ i.get_thumbs_url('small') }}" />
</td>
<td>{{ i.manufacturer.name }}</td>
<td>{{ i.description }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
{% block scripts %}
<script>
$('#contentBody').on("click", ".del", function () {
var item = $(this);
var id = $(this).parent().parent().data().id;
var item_name = $(this).data().name;
if (!confirm("Are you sure to delete " + item_name + " logo ?")) {
return false;
}
$.ajax({
url: '/edit/manufacturers/logos/' + id + '/del',
type: 'DELETE',
success: function (result) {
item.parent().parent().remove()
}
});
return false;
});
$('img[rel=popover]').popover({
html: true,
trigger: 'hover',
content: function () {
return '<img src="'+$(this).data('img-small') + '" />';
}
});
</script>
{% endblock %}

1
templates/layout.jinja2

@ -80,6 +80,7 @@
<li><a href="{{ url_for('bp_edits.edit_view', view="projects") }}"><i class="fa fa-cogs fa-fw"></i> Projects</a></li>
<li><a href="{{ url_for('bp_edits.edit_view', view="footprints") }}"><i class="fa fa-cogs fa-fw"></i> Footprints</a></li>
<li><a href="{{ url_for('bp_edits.edit_view', view="manufacturers") }}"><i class="fa fa-cogs fa-fw"></i> Manufacturers</a></li>
<li><a href="{{ url_for('new_manufacturers_logos') }}"><i class="fa fa-file-image-o fa-fw"></i> Manufacturers Logos</a></li>
<li><a href="{{ url_for('bp_edits.edit_view', view="distributors") }}"><i class="fa fa-cogs fa-fw"></i> Distributors</a></li>
<li><a href="{{ url_for('bp_edits.edit_view', view="storage") }}"><i class="fa fa-cogs fa-fw"></i> Storage</a></li>
<li><a href="{{ url_for('bp_edits.edit_view', view="parts_units") }}"><i class="fa fa-cogs fa-fw"></i> Parts units</a></li>