Commit 316405d6 by aagusti

Widget GMAP

1 parent 91fb10a3
......@@ -253,9 +253,13 @@ class BaseView(object):
return self.route_list("Nilai Data tidak ditemukan", "error")
form.set_appstruct(values)
table = self.get_item_table(row)
resources = form.get_widget_resources()
return dict(form=form.render(readonly=True),
table=table and table.render() or None,
scripts=self.form_scripts)
scripts=self.form_scripts,
css=resources["css"],
js=resources["js"]
)
def view_upload(self, exts=('.png', '.ico')):
bindings = self.get_bindings()
......
......@@ -222,10 +222,11 @@ class ViewPartner(BaseView):
err_kode()
elif found:
err_kode()
value['is_vendor'] = 'is_vendor' in value and value[
'is_vendor'] and 1 or 0
value['is_customer'] = 'is_customer' in value and value[
'is_customer'] and 1 or 0
value['is_vendor'] = 'is_vendor' in value and \
value['is_vendor'] and 1 or 0
value['is_customer'] = 'is_customer' in value and \
value['is_customer'] and 1 or 0
value["status"] = 'status' in value and value['status'] and 1 or 0
def get_bindings(self, row=None):
......
......@@ -52,7 +52,7 @@
<!-- We recommend you use "your_style.css" to override SmartAdmin
specific styles this will also ensure you retrain your customization with each SmartAdmin update. -->
<!-- OTHER CSS -->
<!-- LOOP FORM CSS INCLUDED-->
<tal:loop tal:repeat="css_resource css">
<link rel="stylesheet" href="${home}${request.static_path(css_resource)}"
type="text/css">
......@@ -271,6 +271,7 @@
<!--?<script src="${home}/static/v3/js/select2.full.min.js"></script>-->
<!--?<script src="${home}/deform_static/scripts/file_upload.js"></script>-->
<script src="${home}/static/v3/js/osipkd.js"></script>
<!-- LOOP ON JS RESOURCE -->
<tal:loop tal:repeat="js_resource js">
<script src="${home}${request.static_path(js_resource)}"></script>
</tal:loop>
......
<script src="${home}/static/v3/js/plugin/datatables/jquery.dataTables.min.js"></script>
<script src="${home}/static/v3/js/plugin/datatables/dataTables.colVis.min.js"></script>
<script src="${home}/static/v3/js/plugin/datatables/dataTables.tableTools.min.js"></script>
<script src="${home}/static/v3/js/plugin/datatables/dataTables.bootstrap.min.js"></script>
<script src="${home}/static/v3/js/plugin/datatable-responsive/datatables.responsive.min.js"></script>
......@@ -4,7 +4,8 @@ import re
import colander
from deform import (widget, )
from opensipkd.tools import create_now, SaveFile
from opensipkd.tools.report import open_rml_row, csv_response, open_rml_pdf, pdf_response, file_response
from opensipkd.tools.report import open_rml_row, csv_response, open_rml_pdf, \
pdf_response, file_response
from pyramid.i18n import TranslationStringFactory
from pyramid.view import view_config
from sqlalchemy import (func, )
......@@ -95,7 +96,8 @@ class Views(BaseView):
path = os.path.join(base_path, 'reports')
rml_row = open_rml_row(path + '/user.row.rml')
rows = [rml_row.format(user_name=r.user_name, email=r.email,
registered_date=r.registered_date) for r in query.all()]
registered_date=r.registered_date) for r in
query.all()]
pdf, filename = open_rml_pdf(path + '/user.rml', rows=rows,
company=self.req.company,
departement=self.req.departement,
......@@ -118,7 +120,8 @@ class Views(BaseView):
values["email"] = values['email'].lower()
values["user_name"] = re.sub(' ', '', values['user_name']) # .lower()
values["security_code_date"] = create_now()
company_id = request.user and request.user.company_id or "company_id" in values and values["company_id"] or None
company_id = request.user and request.user.company_id or "company_id" in values and \
values["company_id"] or None
values["company_id"] = company_id
if 'is_api_key' in values:
values["api_key"] = generate_api_key()
......@@ -261,7 +264,7 @@ class EmailValidator(colander.Email, Validator):
email = value.lower()
q = DBSession.query(User).filter_by(email=email)
found = q.first()
if found and (not self.user or self.user.email!=found.email):
if found and (not self.user or self.user.email != found.email):
email_found()
......@@ -380,5 +383,7 @@ def user_group_set(user):
def query_register():
return DBSession.query(User.user_name, User.email,
func.to_char(User.registered_date, "DD-MM-YYYY").label("registered_date")).order_by(
func.to_char(User.registered_date,
"DD-MM-YYYY").label(
"registered_date")).order_by(
User.user_name)
import json
from colander import SchemaNode, null, Mapping, Invalid, text_, string_types
from deform.widget import Widget, _StrippedString, Select2Widget
......@@ -160,7 +162,8 @@ class BlokKavNoWidget(Widget):
result = "|".join([blok_kav_no, rt, rw])
if not blok_kav_no or not rt or not rw:
raise Invalid(field.schema, "Blok Kav No RT/RW tidak lengkap", result)
raise Invalid(field.schema, "Blok Kav No RT/RW tidak lengkap",
result)
return result
......@@ -183,6 +186,7 @@ class Select2MsWidget(Select2Widget):
template = "select2_ms.pt"
class QtyWidget(Widget):
template = "opensipkd.base:/views/widgets/qty.pt"
readonly_template = "opensipkd.base:/views/widgets/readonly/qty.pt"
......@@ -228,6 +232,7 @@ class QtyWidget(Widget):
return result
class CaptchaWidget(Widget):
"""
Renders an ``<input type="text"/>`` widget.
......@@ -275,6 +280,7 @@ class CaptchaWidget(Widget):
return null
return pstruct
class ImageWidget(Widget):
"""
Renders an ``<img src="src"/>`` widget.
......@@ -300,8 +306,72 @@ class ImageWidget(Widget):
strip = True
requirements = ()
height = "30px"
def __init__(self, **kw):
super().__init__(**kw)
def serialize(self, field, cstruct, **kw):
if cstruct in (null, None):
cstruct = ""
readonly = kw.get("readonly", self.readonly)
template = readonly and self.readonly_template or self.template
values = self.get_template_values(field, cstruct, kw)
return field.renderer(template, **values)
def deserialize(self, field, pstruct):
if pstruct is null:
return null
elif not isinstance(pstruct, string_types):
raise Invalid(field.schema, "Pstruct is not a string")
if self.strip:
pstruct = pstruct.strip()
if not pstruct:
return null
return pstruct
class MapWidget(Widget):
"""
Renders an ``<div id="map"/>`` widget.
**Attributes/Arguments**
template
The template name used to render the widget. Default:
``textinput``.
readonly_template
The template name used to render the widget in read-only mode.
Default: ``readonly/textinput``.
strip
If true, during deserialization, strip the value of leading
and trailing whitespace (default ``True``).
"""
template = "opensipkd.base:views/widgets/gmap.pt"
readonly_template = "opensipkd.base:views/widgets/gmap.pt"
map_center = [0, 0]
map_zoom = 12
gmap_key = None
gmap_control = ['Point', 'Polygon', 'LineString']
gmap_height = "400px"
gmap_width = "100%"
strip = True
html_info = {}
gmap_data_style = {
"editable": "true",
"draggable": "true",
"clickable": "true",
"removable": "true",
}
gmap_edit_url = ""
requirements = ()
def __init__(self, **kw):
super().__init__(**kw)
self.gmap_data_style = json.dumps(self.gmap_data_style)
def serialize(self, field, cstruct, **kw):
if cstruct in (null, None):
......
<span tal:define="name name|field.name;
css_class css_class|field.widget.css_class;
oid oid|field.oid;
style style|field.widget.style;
map_zoom map_zoom|field.widget.map_zoom;
map_center map_center|field.widget.map_center;
gmap_key gmap_key|field.widget.gmap_key;
gmap_height gmap_height|field.widget.gmap_height;
gmap_width gmap_width|field.widget.gmap_width;
gmap_control gmap_control|field.widget.gmap_control;
html_info html_info|field.widget.html_info;
gmap_data_style gmap_data_style|field.widget.gmap_data_style;
gmap_edit_url gmap_edit_url|field.widget.gmap_edit_url;
"
tal:omit-tag="">
<textarea id="${oid}" name="${name}" style="display:none;" readonly
tal:attributes="class string: form-control ${css_class or ''};
style style;
attributes|field.widget.attributes|{};">${cstruct}</textarea>
<div id="gmap" class="gmap"></div>
<style>
.gmap {
height: ${gmap_height};
width: ${gmap_width};
}
</style>
<script type="text/javascript">
let map_zoom = ${map_zoom};
let map_center = ${map_center};
let map;
let btnCurrent = {lat: 0, lng: 0};
let selectedFeature = null;
let btnRemove;
let geom = document.getElementById('${oid}');
let infoWindow;
let feature_data = {};
let contents = ${html_info};
let edit_url = "${gmap_edit_url}";
function initMap() {
map = new google.maps.Map(document.getElementById("gmap"), {
center: {lat: map_center[0], lng: map_center[1]},
zoom: map_zoom,
animation: google.maps.Animation.DROP,
streetViewControl: false,
mapTypeId: "satellite",
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
},
});
map.data.setStyle(${structure: gmap_data_style});
infoWindow = new google.maps.InfoWindow({
content: "",
ariaLabel: "Uluru",
});
map.data.setControls(${gmap_control});
// google.maps.event.addListener(map, 'zoom_changed', function () {
// let zoomLevel = map.getZoom();
// if (zoomLevel < 11) {
// map.data.forEach(function (e) {
// let bounds = new google.maps.LatLngBounds();
// bounds = extendBound(bounds, e);
// map.data.add({
// geometry: new google.maps.Data.Point(bounds.getCenter()),
// });
// });
// }
// });
google.maps.event.addListener(map.data, 'addfeature', function (e) {
if (map.data.getDrawingMode() !== null) {
let bounds = new google.maps.LatLngBounds();
bounds = extendBound(bounds, e.feature);
map.fitBounds(bounds);
}
});
google.maps.event.addListener(map.data, 'click', function (e) {
if (e.hasOwnProperty("feature") === true) {
selectedFeature = e.feature;
btnRemove.disabled = false;
if (e.feature.getGeometry().getType() === "Point") {
map.setCenter(e.feature.getGeometry().get());
map.setZoom(18);
}
showInfo(e);
} else {
selectedFeature = null;
btnRemove.disabled = true;
}
});
loadData();
addYourLocationButton();
bindDataLayerListeners(map.data);
}
function loadData() {
if (geom.value === "") return;
let data = JSON.parse(geom.value);
if (data !== undefined) {
map.data.forEach(function (f) {
map.data.remove(f);
});
map.data.addGeoJson(data);
}
setControls();
}
function showInfo(event) {
if (contents.length<1) return;
infoWindow.setPosition(event.latLng);
let content = "";
for (let key in contents) {
let keys = contents[key];
let value = key === "id" ? event.feature.getId() : event.feature.getProperty(key);
value = value === undefined ? "" : value
let cnt = keys[0] + ": " + value;
if (keys.length > 1) {
if (keys[1] === 'b') {
cnt = "<b>" + cnt + "</b>"
}
}
content += cnt + '<br>';
}
if (edit_url !== "" && event.feature.getId() !== undefined) {
let url = edit_url.replace("{id}", selectedFeature.getId());
content += '<a class="btn btn-info" href="{url}">Edit</a>'.replace("{url}", url);
}
infoWindow.setContent(content);
infoWindow.open(map);
}
function bindDataLayerListeners(dataLayer) {
dataLayer.addListener('addfeature', saveData);
dataLayer.addListener('removefeature', saveData);
dataLayer.addListener('setgeometry', saveData);
}
function extendBound(bounds, feature) {
if (feature === undefined) return bounds;
let featureType = feature.getGeometry().getType();
if (featureType === 'LineString') {
feature.getGeometry().getArray().forEach(function (latLng) {
bounds.extend(latLng);
});
} else if (featureType === 'Polygon') {
feature.getGeometry().getArray().forEach(function (path) {
path.getArray().forEach(function (latLng) {
bounds.extend(latLng);
});
});
}
return bounds;
}
function setControls() {
let total = 0;
let bounds = new google.maps.LatLngBounds();
map.data.forEach(function (e) {
total++;
bounds = extendBound(bounds, e);
});
if (total > 0) {
map.fitBounds(bounds);
map.data.setDrawingMode(null);
map.data.setControls(null);
let zoomLevel = map.getZoom();
if (total > 1) {
map.data.forEach(function (e) {
let bounds = new google.maps.LatLngBounds();
bounds = extendBound(bounds, e);
let properties = {};
e.forEachProperty(
function (val, key) {
properties[key] = val;
}
);
map.data.add({
geometry: new google.maps.Data.Point(bounds.getCenter()),
properties: properties,
// id:e.getId()
});
});
}
} else {
map.data.setControls(${gmap_control});
}
}
function saveData() {
map.data.toGeoJson(function (json) {
geom.value = JSON.stringify(json);
});
setControls();
}
function addYourLocationButton() {
let controlDiv = document.createElement('div');
let btnCurrent = document.createElement('button');
btnCurrent.type = "button";
btnCurrent.style.backgroundColor = '#fff';
btnCurrent.style.border = 'none';
btnCurrent.style.outline = 'none';
btnCurrent.style.width = '28px';
btnCurrent.style.height = '28px';
btnCurrent.style.borderRadius = '2px';
btnCurrent.style.boxShadow = '0 1px 4px rgba(0,0,0,0.3)';
btnCurrent.style.cursor = 'pointer';
btnCurrent.style.marginRight = '10px';
btnCurrent.style.padding = '0';
btnCurrent.title = 'Your Location';
controlDiv.appendChild(btnCurrent);
let imgCurrent = document.createElement('div');
imgCurrent.style.margin = '5px';
imgCurrent.style.width = '18px';
imgCurrent.style.height = '18px';
imgCurrent.style.backgroundImage = 'url(https://maps.gstatic.com/tactile/mylocation/mylocation-sprite-2x.png)';
imgCurrent.style.backgroundSize = '180px 18px';
imgCurrent.style.backgroundPosition = '0 0';
imgCurrent.style.backgroundRepeat = 'no-repeat';
btnCurrent.appendChild(imgCurrent);
let txtCurrent = document.createElement('i');
txtCurrent.className = "fa";
txtCurrent.ariaHidden = "true"; //<i class="fa fa-trash" aria-hidden="true"></i>
imgCurrent.appendChild(txtCurrent);
google.maps.event.addListener(map, 'center_changed', function () {
imgCurrent.style['background-position'] = '0 0';
});
btnCurrent.addEventListener('click', function () {
let imgX = 0,
animationInterval = setInterval(function () {
imgX = -imgX - 18;
imgCurrent.style['background-position'] = imgX + 'px 0';
}, 500);
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
let latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
map.setCenter(latlng);
clearInterval(animationInterval);
secondChild.style['background-position'] = '-144px 0';
});
} else {
clearInterval(animationInterval);
btnCurrent.style['background-position'] = '0 0';
}
});
// let btnRemove = document.getElementById('btnRemove');
btnRemove = document.createElement('button');
btnRemove.type = "button";
btnRemove.style.backgroundColor = '#fff';
btnRemove.style.border = 'none';
btnRemove.style.outline = 'none';
btnRemove.style.width = '28px';
btnRemove.style.height = '28px';
btnRemove.style.borderRadius = '2px';
btnRemove.style.boxShadow = '0 1px 4px rgba(0,0,0,0.3)';
btnRemove.style.cursor = 'pointer';
// btnRemove.style.marginRight = '10px';
btnRemove.style.padding = '0';
btnRemove.title = 'Delete Selected Feature';
controlDiv.appendChild(btnRemove);
let imgRemove = document.createElement('div');
imgRemove.style.margin = '5px';
imgRemove.style.width = '18px';
imgRemove.style.height = '18px';
btnRemove.appendChild(imgRemove);
let txtRemove = document.createElement('i');
txtRemove.className = "fa fa-trash";
txtRemove.ariaHidden = "true"; //<i class="fa fa-trash" aria-hidden="true"></i>
imgRemove.appendChild(txtRemove);
btnRemove.addEventListener("click", function () {
if (selectedFeature !== null) {
map.data.remove(selectedFeature);
selectedFeature = null;
btnRemove.disabled = true;
saveData();
}
});
controlDiv.index = 1;
map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(controlDiv);
}
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?v=3&key=${gmap_key}&libraries=drawing&callback=initMap">
</script>
</span>
......@@ -8,14 +8,14 @@ Pillow>=9.1.1
lxml>=4.9.0
beautifulsoup4>=4.11.1
soupsieve>=2.3.2.post1
pip~=18.1
wheel~=0.37.0
pip~=22.3.1
wheel~=0.38.4
rsa>=4.8
pyasn1~=0.4.8
Chameleon>=3.10.1
six~=1.16.0
Mako>=1.2.0
Babel~=2.10.2
Babel~=2.11.0
Beaker~=1.11.0
Pygments>=2.12.0
MarkupSafe>=2.1.1
......@@ -27,7 +27,7 @@ peppercorn~=0.6
iso8601>=1.0.2
google~=3.0.0
cachetools>=5.2.0
certifi~=2022.5.18.1
certifi~=2022.9.24
urllib3~=1.26.6
requests>=2.28.0
google-api-python-client>=2.51.0
......@@ -38,21 +38,21 @@ venusian~=3.0.0
plaster~=1.0
hupper~=1.10.3
waitress>=2.1.2
greenlet~=1.1.1
greenlet~=2.0.1
pyparsing>=3.0.9
httplib2>=0.20.4
icecream~=2.1.1
executing~=0.8.0
executing~=1.2.0
paginate~=0.5.6
idna~=3.2
asttokens~=2.0.5
asttokens~=2.1.0
setuptools>=57.4.0
uritemplate>=4.1.1
reportlab~=3.6.1
PyJWT>=2.4.0
py~=1.11.0
attrs~=21.4.0
pytest~=7.1.1
attrs~=22.1.0
pytest~=7.2.0
pluggy~=1.0.0
iniconfig~=1.1.1
cffi>=1.15.0
......@@ -65,6 +65,9 @@ pyramid_rpc
zipp~=3.8.0
papyrus~=2.4
geojson~=2.5.0
GeoAlchemy2~=0.12.1
pybind11~=2.9.2
qrcode~=7.3.1
\ No newline at end of file
qrcode~=7.3.1
exceptiongroup~=1.0.4
Shapely~=1.8.5.post1
GeoAlchemy~=0.7.2
tandur~=0.0.1b0
\ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!