Browse Source

Add manufacturers logos; Add cron stuff for it

master
Dashie der otter 3 years ago
parent
commit
9468058d92
9 changed files with 281 additions and 18 deletions
  1. 1
    0
      .gitignore
  2. 11
    1
      controllers/edits.py
  3. 26
    13
      crons.py
  4. 15
    1
      forms.py
  5. 37
    0
      migrations/versions/2e1affad10af_.py
  6. 49
    1
      models.py
  7. 59
    2
      stockazng.py
  8. 82
    0
      templates/edit/manufacturers_logos.jinja2
  9. 1
    0
      templates/layout.jinja2

+ 1
- 0
.gitignore View File

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

+ 11
- 1
controllers/edits.py View File

@@ -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"})

+ 26
- 13
crons.py View File

@@ -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.")


+ 15
- 1
forms.py View File

@@ -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
- 0
migrations/versions/2e1affad10af_.py View File

@@ -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 ###

+ 49
- 1
models.py View File

@@ -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}
]



+ 59
- 2
stockazng.py View File

@@ -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
- 0
templates/edit/manufacturers_logos.jinja2 View File

@@ -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
- 0
templates/layout.jinja2 View File

@@ -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>