Commit 324d8d62 by aa.gusti

perbaikan template upload

1 parent 8783308f
......@@ -2,13 +2,14 @@ import locale
import logging
import re
# from opensipkd.tools.captcha import get_captcha_url
from .routes import routes
try:
from urllib import (urlencode, quote, quote_plus, )
except ImportError:
from urllib.parse import (urlencode, quote, quote_plus, )
from collections import OrderedDict
from pyramid.events import NewRequest
from pyramid.config import Configurator
from pyramid_beaker import session_factory_from_settings
......@@ -498,7 +499,7 @@ def get_module_submenus(parent_id):
q = DBSession.query(Route) \
.filter(Route.parent_id == parent_id) \
.order_bY(Route.order_id)
return [r.kode for r in query.all()]
return [r.kode for r in q.all()]
partner_idcard_url = 'partner/idcard'
......@@ -561,6 +562,7 @@ def main(global_config, **settings):
config.add_request_method(is_devel, 'devel', reify=True)
config.add_request_method(get_host, '_host', reify=True)
config.add_request_method(get_host, 'home', reify=True)
# config.add_request_method(get_captcha_url, 'captcha', reify=True)
# config.add_request_method(get_urls, 'route_urls', reify=True)
config.add_request_method(google_signin_client_id,
'google_signin_client_id', reify=True)
......@@ -592,6 +594,10 @@ def main(global_config, **settings):
os.makedirs(captcha_files)
config.add_static_view('captcha', captcha_files)
config.add_static_view('partner/files',
get_params("partner_files", settings=settings,
alternate="/tmp/partner"))
config.add_renderer('csv', 'opensipkd.tools.CSVRenderer')
config.add_renderer('json', json_renderer())
config.add_renderer('json_rpc', json_rpc())
......
......@@ -3,32 +3,25 @@ from datetime import timedelta
import colander
from deform import (
Form, ValidationFailure, widget, Button, )
from opensipkd.tools.api import JsonRpcInvalidLoginError
Form, ValidationFailure, widget, Button, FileData)
from opensipkd.base import get_params, get_urls
from opensipkd.models import (
DBSession, UserService, )
from opensipkd.tools import mem_tmp_store
from pyramid.httpexceptions import (
HTTPFound, HTTPForbidden, HTTPNotFound, HTTPInternalServerError,
HTTPSeeOther)
from pyramid.i18n import TranslationStringFactory
from pyramid.interfaces import IRoutesMapper
from pyramid.renderers import render_to_response
from pyramid.response import Response
from pyramid.security import remember
from pyramid.view import view_config
from opensipkd.base import get_params, get_urls
from opensipkd.base.tools.api import rpc_auth
from .base_views import BaseView
from opensipkd.models import (
DBSession, UserService, )
from .common import DataTables, ColumnDT
from pyramid.csrf import new_csrf_token
from .base_views import BaseView, DataTables, ColumnDT
_ = TranslationStringFactory('login')
log = logging.getLogger(__name__)
@view_config(context=HTTPNotFound, renderer='templates/404.pt')
def not_found(request):
path = request.path
......@@ -55,6 +48,33 @@ def internal_server_error(request):
# return response
class Validator(object):
def __init__(self, row):
self.row = row
class FileSchema(colander.Schema):
file_name = colander.SchemaNode(
FileData(),
widget=widget.FileUploadWidget(mem_tmp_store, size=104857600),
missing=colander.drop,
title="File"
)
description = colander.SchemaNode(
colander.String(),
missing=colander.drop,
validator=colander.Length(max=256),
)
class FilesSchema(colander.SequenceSchema):
file_name = FileSchema()
def after_bin(self, node, kw):
self["file_name"].title = ""
########
# Home #
########
......@@ -152,5 +172,3 @@ two_minutes = timedelta(1.0 / 24 / 60)
def deferred_jenis(node, kw):
values = kw.get('daftar_jenis', [])
return widget.RadioChoiceWidget(values=values)
\ No newline at end of file
......@@ -9,7 +9,6 @@ from datatables import ColumnDT
from dateutil.relativedelta import relativedelta
from deform import (widget, Form, ValidationFailure, FileData, )
from deform.widget import SelectWidget
from opensipkd.base.views.upload import tmpstore
from opensipkd.tools import dmy, get_settings, get_ext, \
date_from_str, get_random_string
from opensipkd.tools.buttons import btn_save, btn_cancel, btn_close, btn_delete, \
......@@ -19,6 +18,7 @@ from opensipkd.tools.captcha import get_captcha
from opensipkd.tools.report import csv_response, file_response
from pyramid.httpexceptions import HTTPFound, HTTPNotFound
from opensipkd.base.views.upload import tmpstore
from .common import DataTables
from .. import DBSession, get_params, get_urls
from ..scripts.initializedb import append_csv
......@@ -149,6 +149,9 @@ class BaseView(object):
self.edit_schema = ""
self.add_schema = ""
self.upload_schema = UploadSchema
self.upload_exts = (".csv", ".tsv")
self.upload_keys = ["kode"]
self.table = ""
self.home = self.req._host
self.buttons = None
......@@ -156,7 +159,6 @@ class BaseView(object):
self.bindings = {}
self.autocomplete = 'on'
self.action_suffix = "/grid/act"
self.upload_keys = ["kode"]
self.report_file = ""
self.new_buttons = {}
self.is_object = False
......@@ -263,7 +265,7 @@ class BaseView(object):
return arg
def get_bindings(self, row=None):
return {}
return {"row": row}
def next_view(self, form, **kwargs):
return
......@@ -296,11 +298,20 @@ class BaseView(object):
result = (btn_close,)
return result
def before_view(self, **kw):
return False
def view_view(self, **kwargs): # row = query_id(request).first()
request = self.req
row = self.query_id().first()
if not row:
return self.id_not_found()
before_view = self.before_view(row=row)
if before_view:
return before_view
bindings = self.get_bindings(row)
buttons = kwargs.get("buttons", None)
if not buttons:
......@@ -339,7 +350,11 @@ class BaseView(object):
buttons = (btn_post, btn_close)
return self.view_view(buttons=buttons)
def view_upload(self, exts=('.png', '.ico'), **args):
def view_upload(self, **kw):
exts = kw.get("exts")
if not exts:
exts = self.upload_exts
delimiter = args.get("delimiter")
bindings = self.get_bindings()
form = self.get_form(self.upload_schema, bindings=bindings)
......@@ -516,6 +531,9 @@ class BaseView(object):
else:
return self.next_act(**kwargs)
def get_captcha_url(self):
return get_urls("/captcha/") + get_captcha(self.req)
def view_add(self, **kwargs):
# bindings = self.get_bindings()
form = self.get_form(self.add_schema, **kwargs)
......@@ -538,6 +556,8 @@ class BaseView(object):
if isinstance(f.typ, colander.Date):
e.cstruct[f.name] = date_from_str(
e.cstruct[f.name])
if f.name == "captcha":
e.cstruct[f.name] = self.get_captcha_url()
form.set_appstruct(e.cstruct)
return self.returned_form(form, table, **kwargs)
......@@ -703,6 +723,7 @@ class BaseView(object):
def query_id(self):
q = self.db_session.query(self.table).filter_by(
id=self.req.matchdict['id'])
if self.req.user:
if hasattr(self.table, 'company_id') and self.req.user.company_id:
q = q.filter_by(company_id=self.req.user.company_id)
return q
......
......@@ -2,17 +2,17 @@ import colander
from deform import (
widget, Button,
)
from pyramid.i18n import TranslationStringFactory
from pyramid.view import (
view_config,
)
from opensipkd.base import get_id_card_folder
from opensipkd.models import DBSession, Partner
from opensipkd.models import (
ResProvinsi, ResDati2, ResKecamatan, ResDesa)
from opensipkd.models.common import ResCompany
from opensipkd.tools import Upload, img_exts
from pyramid.i18n import TranslationStringFactory
from pyramid.view import (
view_config,
)
from .company import company_widget
from .partner_base import PartnerSchema
# from .. import partner_idcard_url
......@@ -243,6 +243,7 @@ class ViewPartner(BaseView):
value["status"] = 'status' in value and value['status'] and 1 or 0
def get_bindings(self, row=None):
result = super().get_bindings(row)
provinsi_list = ResProvinsi.get_list()
dati2_list = row and row.provinsi_id and ResDati2.get_list(
row.provinsi_id) or []
......@@ -250,17 +251,19 @@ class ViewPartner(BaseView):
row.dati2_id) or []
desa_list = row and row.kecamatan_id and ResDesa.get_list(
row.kecamatan_id) or []
return dict(
result.update(dict(
provinsi_list=provinsi_list,
dati2_list=dati2_list,
kecamatan_list=kecamatan_list,
desa_list=desa_list,
company_list=ResCompany.get_list()
)
))
return result
def save_request(self, values, row=None):
if "idcard" in values and values["idcard"]:
if str(self.req.POST['upload'].decode('utf-8')) != "":
if str(self.req.POST['upload']) != "":
folder = self.get_params("idcard_folder", '/tmp/idcard')
upload = Upload(folder)
file_name = upload.save(self.req, 'upload', img_exts)
......
......@@ -5,15 +5,70 @@ from opensipkd.base.views.dati2 import dati2_widget
from opensipkd.base.views.desa import desa_widget
from opensipkd.base.views.kecamatan import kecamatan_widget
from opensipkd.base.views.provinsi import provinsi_widget
from opensipkd.models import Partner
from opensipkd.tools import mem_tmp_store
from translationstring import TranslationStringFactory
from .. import get_urls
from . import Validator
_ = TranslationStringFactory('partner')
class PartnerEmailValidator(colander.Email, Validator):
def __init__(self, row):
Validator.__init__(self, row)
colander.Email.__init__(self)
def __call__(self, node, value):
def email_found():
data = dict(email=email, rid=found.id, rname=found.nama)
ts = _(
'email-already-used',
default='Email ${email} already used by Partner ID ${rid}: ${rname}',
mapping=data)
raise colander.Invalid(node, ts)
if self.match_object.match(value) is None:
raise colander.Invalid(node, _('Invalid email format'))
email = value.lower()
q = Partner.query().filter_by(email=email)
found = q.first()
if found and (not self.row or self.row.email != found.email):
email_found()
@colander.deferred
def partner_email_validator(node, kw):
return PartnerEmailValidator(kw['row'])
class PartnerKodeValidator(Validator):
def __init__(self, row):
Validator.__init__(self, row)
def __call__(self, node, value):
def err_found():
data = dict(kode=val, rid=found.id, rnama=found.nama)
ts = _(
'kode-already-used',
default='Kode ${kode} already used by Partner ID ${rid}: ${rnama}',
mapping=data)
raise colander.Invalid(node, ts)
val = value
q = Partner.query().filter_by(kode=val)
found = q.first()
if found and (not self.row or self.row.kode != found.kode):
err_found()
@colander.deferred
def partner_kode_validator(node, kw):
return PartnerKodeValidator(kw['row'])
class NamaSchema(colander.Schema):
kode = colander.SchemaNode(
colander.String(),
validator=colander.Length(max=32),
validator=partner_kode_validator,
oid="kode",
title="Kode",
width="100pt")
......@@ -21,7 +76,10 @@ class NamaSchema(colander.Schema):
colander.String(),
validator=colander.Length(max=64),
oid="nama")
email = colander.SchemaNode(
colander.String(),
validator=partner_email_validator,
oid="email")
class PartnerSchema(NamaSchema):
nip = colander.SchemaNode(
......@@ -103,10 +161,7 @@ class PartnerSchema(NamaSchema):
missing=colander.drop,
title="Desa/Kelurahan",
oid="desa_id")
email = colander.SchemaNode(
colander.String(),
validator=colander.Length(max=128),
oid="email")
phone = colander.SchemaNode(
colander.String(),
validator=colander.Length(max=16),
......
......@@ -5,12 +5,29 @@
ext str(cstruct.get('filename').split('.')[-1:][0]).lower()|[];
fname str(cstruct.get('filename'))|'';
delete cstruct.get('delete')|'';
maxsize field.widget.size|5242880;">
${field.start_mapping()}
<img id="preview-${oid}" alt="" src="${preview_url}" style="width:100px;height:auto;display:block;"
maxsize field.widget.size|5242880;
img ['jpg', 'jpeg', 'gif', 'png', 'svg', 'eps', 'psd'];
video ['avi', 'mov', 'mpv']
">
${field.start_mapping()}
<img tal:condition="ext in img" id="preview-${oid}" alt="" src="${preview_url}"
style="width:100px;height:auto;display:block;"
onload="window.URL.revokeObjectURL(this.src);"></img>
<video tal:condition="ext in video" width="100%" height="100%" controls
src="${preview_url}" type="video/${ext}">
Your browser does not support the video tag.
</video>
<video tal:condition="ext=='mp4'" width="100%" height="100%" controls
src="${preview_url}" type="video/mp4">
Your browser does not support the video tag.
</video>
<embed tal:condition="ext=='pdf'" src="${preview_url}" width="100%" height="400">
<a id="label-${oid}" tal:condition="preview_url" class="label label-default" href="${preview_url}"
target="_blank"><i class="fa fa-search"></i> View</a>
<input type="file" name="upload" id="${oid}"
tal:attributes="style style;
accept accept|field.widget.accept;
......@@ -24,15 +41,15 @@
deform.addCallback('${oid}', function (oid) {
$('#' + oid).upload();
});
document.getElementById("${oid}").onchange = function() {
if(this.files[0].size > ${maxsize}){
document.getElementById("${oid}").onchange = function () {
if (this.files[0].size > ${maxsize}) {
alert("File is too big!");
this.value = "";
document.getElementById('preview-'+this.id).src = '';
document.getElementById('preview-' + this.id).src = '';
}
document.getElementById('preview-'+this.id).src = window.URL.createObjectURL(this.files[0]);
document.getElementById('labeldelete-'+this.id).remove();
document.getElementById('label-'+this.id).remove();
document.getElementById('preview-' + this.id).src = window.URL.createObjectURL(this.files[0]);
document.getElementById('labeldelete-' + this.id).remove();
document.getElementById('label-' + this.id).remove();
};
</script>
</tal:block>
\ No newline at end of file
......@@ -41,6 +41,7 @@
<div tal:repeat="child field"
tal:replace="structure child.render_template(item_template)"/>
</div>
<div class="row">
<div class="form-group deform-form-buttons">
<tal:loop tal:repeat="button buttons">
......
......@@ -5,11 +5,12 @@ from sqlalchemy import (
SmallInteger,
DateTime, ForeignKey
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.orm import backref
from .common import NamaModel
from .wilayah import ResProvinsi, ResDesa, ResKecamatan, ResDati2
from .base import NamaModel, StandarModel
from .meta import (Base)
from sqlalchemy.orm import relationship, backref
from .wilayah import ResProvinsi, ResDesa, ResKecamatan, ResDati2
class PartnerModel(NamaModel):
......@@ -76,6 +77,7 @@ class Partner(Base, PartnerModel):
"ResKecamatan", backref=backref('partner'))
res_desa = relationship(
"ResDesa", backref=backref('partner'))
partner_files: Mapped["PartnerFiles"] = relationship(back_populates="partner")
# npwp = Column(String(16))
# npwpd = Column(String(16))
......@@ -97,6 +99,14 @@ class Partner(Base, PartnerModel):
row = cls.query().filter_by(mobile=ident).first()
return row
class PartnerFiles(Base, StandarModel):
__tablename__ = 'partner_files'
partner_id: Mapped[int] = mapped_column(ForeignKey(Partner.id))
file_name: Mapped[str] = mapped_column(String(256))
description: Mapped[str] = mapped_column(String(256), nullable=True)
partner: Mapped["Partner"] = relationship(back_populates="partner_files")
# class PartnerUserModel(Base, DefaultModel):
# __tablename__ = 'partner_user'
# partner_id = Column(Integer, ForeignKey(Partner.id))
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!