Skip to content
Toggle navigation
Projects
Groups
Snippets
Help
irul
/
opensipkd-base
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Wiki
Settings
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit 2daa1056
authored
Jul 02, 2022
by
aagusti
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
perbaikan report
1 parent
c453d871
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
302 additions
and
117 deletions
opensipkd/base/__init__.py
opensipkd/base/models/users.py
opensipkd/base/security.py
opensipkd/base/tools/api.py
opensipkd/base/views/api.py
opensipkd/base/views/groups.py
opensipkd/base/views/jabatan.py
opensipkd/base/views/jabatan_report.py
opensipkd/base/views/user.py
opensipkd/base/views/user_rpc.py
opensipkd/detable/detable.py
opensipkd/jsonrpc_auth/__init__.py
requirements.txt
opensipkd/base/__init__.py
View file @
2daa105
...
...
@@ -26,7 +26,7 @@ import datetime, decimal
from
sqlalchemy
import
engine_from_config
,
or_
from
.security
import
(
group_finder
,
get_user
,
get_user
,
MySecurityPolicy
,
)
from
.models
import
(
DBSession
,
...
...
@@ -392,15 +392,14 @@ def main(global_config, **settings):
# config.include('pyramid_beaker')
# config.include('pyramid_chameleon')
authn_policy
=
AuthTktAuthenticationPolicy
(
'sosecret'
,
callback
=
group_finder
,
hashalg
=
'sha512'
)
authz_policy
=
ACLAuthorizationPolicy
()
config
.
set_authentication_policy
(
authn_policy
)
#
authn_policy = AuthTktAuthenticationPolicy(
#
'sosecret', callback=group_finder, hashalg='sha512')
#
#
authz_policy = ACLAuthorizationPolicy()
config
.
set_security_policy
(
MySecurityPolicy
(
settings
[
"session.secret"
]))
#
config.set_authentication_policy(authn_policy)
# config.set_security_policy(authz_policy)
config
.
set_authorization_policy
(
authz_policy
)
# config.set_authorization_policy(authz_policy)
config
.
add_request_method
(
get_user
,
'user'
,
reify
=
True
)
config
.
add_request_method
(
get_title
,
'title'
,
reify
=
True
)
config
.
add_request_method
(
get_company
,
'company'
,
reify
=
True
)
...
...
@@ -426,6 +425,7 @@ def main(global_config, **settings):
# config.add_notfound_view(RemoveSlashNotFoundViewFactory())
config
.
add_static_view
(
'static'
,
'opensipkd.base:static'
,
cache_max_age
=
3600
)
config
.
add_static_view
(
'deform_static'
,
'deform:static'
)
# config.add_view('.views.api.echoGateway')
# config.add_static_view('files', get_params('static_files'))
# Captcha
...
...
opensipkd/base/models/users.py
View file @
2daa105
...
...
@@ -188,7 +188,6 @@ class RootFactory:
acl_name
=
'group:{}'
.
format
(
gp
.
group_id
)
self
.
__acl__
.
append
((
Allow
,
acl_name
,
gp
.
perm_name
))
def
init_model
():
ziggurat_model_init
(
User
,
Group
,
UserGroup
,
GroupPermission
,
UserPermission
,
UserResourcePermission
,
GroupResourcePermission
,
Resource
,
...
...
opensipkd/base/security.py
View file @
2daa105
...
...
@@ -40,3 +40,54 @@ def get_user(request):
# if user_id is not None:
# user = DBSession.query(User).get(user_id)
# return user
from
pyramid.authentication
import
AuthTktCookieHelper
from
pyramid.authorization
import
ACLHelper
,
Authenticated
,
Everyone
class
MySecurityPolicy
:
def
__init__
(
self
,
secret
):
self
.
helper
=
AuthTktCookieHelper
(
secret
)
def
identity
(
self
,
request
):
# define our simple identity as None or a dict with userid and principals keys
identity
=
self
.
helper
.
identify
(
request
)
if
identity
is
None
:
return
None
userid
=
identity
[
'userid'
]
# identical to the deprecated request.unauthenticated_userid
# verify the userid, just like we did before with groupfinder
principals
=
group_finder
(
userid
,
request
)
# assuming the userid is valid, return a map with userid and principals
if
principals
is
not
None
:
return
{
'userid'
:
userid
,
'principals'
:
principals
,
}
def
authenticated_userid
(
self
,
request
):
# defer to the identity logic to determine if the user id logged in
# and return None if they are not
identity
=
request
.
identity
if
identity
is
not
None
:
return
identity
[
'userid'
]
def
permits
(
self
,
request
,
context
,
permission
):
# use the identity to build a list of principals, and pass them
# to the ACLHelper to determine allowed/denied
identity
=
request
.
identity
principals
=
set
([
Everyone
])
if
identity
is
not
None
:
principals
.
add
(
Authenticated
)
principals
.
add
(
identity
[
'userid'
])
principals
.
update
(
identity
[
'principals'
])
return
ACLHelper
()
.
permits
(
context
,
principals
,
permission
)
def
remember
(
self
,
request
,
userid
,
**
kw
):
return
self
.
helper
.
remember
(
request
,
userid
,
**
kw
)
def
forget
(
self
,
request
,
**
kw
):
return
self
.
helper
.
forget
(
request
,
**
kw
)
opensipkd/base/tools/api.py
View file @
2daa105
...
...
@@ -31,6 +31,7 @@ def auth_from(request, field=None):
# bypass cek authentication for development
if
http_userid
==
'admin'
and
request
.
devel
:
return
user
time_stamp
=
validate_time
(
request
)
if
field
:
header
=
json_rpc_header
(
http_userid
,
user
.
security_code
,
time_stamp
)
...
...
opensipkd/base/views/api.py
0 → 100644
View file @
2daa105
# from pyramid_rpc.amfgateway import PyramidGateway
from
pyramid_rpc.jsonrpc
import
jsonrpc_method
from
opensipkd.tools.api
import
JsonRpcInvalidDataError
,
JsonRpcInvalidLoginError
from
ziggurat_foundations.models.services.user
import
UserService
from
..models
import
Partner
,
User
def
get_profile_
(
user
):
partner
=
Partner
.
query
()
.
filter_by
(
email
=
user
.
email
)
.
first
()
if
not
partner
:
raise
JsonRpcInvalidDataError
result
=
dict
(
user_name
=
user
.
user_name
,
nik
=
partner
.
kode
,
email
=
partner
.
email
,
mobile
=
partner
.
mobile
,
nama
=
partner
.
nama
,
)
return
dict
(
data
=
result
)
@jsonrpc_method
(
method
=
'get_profile'
,
endpoint
=
'rpc-user'
)
def
get_profile
(
request
,
data
):
"""
Digunakan untuk memperoleh profile user yang sedang login
parameter
@param request: Request
@param data: Dict(user_name=user_name/email, password=password)
@return:
"""
user
=
request
.
user
print
(
"User"
,
user
)
data
=
type
(
data
)
==
list
and
data
[
0
]
or
data
user
=
User
.
get_by_identity
(
data
[
"user_name"
])
if
not
user
or
not
UserService
.
check_password
(
user
,
data
[
'password'
]):
raise
JsonRpcInvalidLoginError
return
get_profile_
(
user
)
# services = {
# 'get_profile.echo': get_profile
# could include other functions as well
# }
#
# echoGateway = PyramidGateway(services)
opensipkd/base/views/groups.py
View file @
2daa105
...
...
@@ -87,7 +87,7 @@ class Views(BaseView):
return
r
@view_config
(
route_name
=
'group-add'
,
renderer
=
'templates/
group/add
.pt'
,
route_name
=
'group-add'
,
renderer
=
'templates/
form
.pt'
,
permission
=
'user-edit'
)
def
view_add
(
self
):
return
super
(
Views
,
self
)
.
view_add
()
...
...
@@ -122,13 +122,13 @@ class Views(BaseView):
return
values
@view_config
(
route_name
=
'group-view'
,
renderer
=
'templates/
group/edit
.pt'
,
route_name
=
'group-view'
,
renderer
=
'templates/
form
.pt'
,
permission
=
'user-view'
)
def
view_view
(
self
):
return
super
(
Views
,
self
)
.
view_view
()
@view_config
(
route_name
=
'group-edit'
,
renderer
=
'templates/
group/edit
.pt'
,
route_name
=
'group-edit'
,
renderer
=
'templates/
form
.pt'
,
permission
=
'user-edit'
)
def
view_edit
(
self
):
return
super
(
Views
,
self
)
.
view_edit
()
...
...
@@ -142,7 +142,7 @@ class Views(BaseView):
self
.
ses
.
flash
(
ts
)
@view_config
(
route_name
=
'group-delete'
,
renderer
=
'templates/
group/delete
.pt'
,
route_name
=
'group-delete'
,
renderer
=
'templates/
form
.pt'
,
permission
=
'user-edit'
)
def
view_delete
(
self
):
return
super
(
Views
,
self
)
.
view_delete
()
...
...
opensipkd/base/views/jabatan.py
View file @
2daa105
import
colander
import
os
from
deform
import
(
widget
,
)
from
opensipkd.tools.buttons
import
btn_view
,
btn_add
,
btn_edit
,
btn_delete
,
btn_close
from
opensipkd.tools.report
import
csv_response
,
open_rml_pdf
,
open_rml_row
,
pdf_response
from
pyramid.view
import
(
view_config
,
)
from
.partner_base
import
NamaSchema
from
..models
import
(
DBSession
,
Jabatan
,
Eselon
Eselon
,
Departemen
)
from
..views
import
BaseView
,
deferred_jenis
...
...
@@ -79,6 +82,12 @@ class ViewJabatan(BaseView):
self
.
edit_schema
=
EditSchema
self
.
table
=
Jabatan
self
.
list_schema
=
ListSchema
# self.list_buttons = (btn_view, btn_add, btn_edit, btn_delete, btn_close)
def
get_bindings
(
self
,
row
=
None
):
return
dict
(
daftar_jenis
=
JENIS
,
daftar_eselon
=
daftar_eselon
())
@view_config
(
route_name
=
'jabatan'
,
renderer
=
'templates/table.pt'
,
permission
=
'jabatan'
)
...
...
@@ -151,13 +160,42 @@ class ViewJabatan(BaseView):
else
:
nama_jenis
=
'Fungsional'
d
=
{}
d
[
'id'
]
=
k
[
0
]
d
[
'value'
]
=
k
[
2
]
+
' ('
+
nama_jenis
+
')'
d
[
'kode'
]
=
k
[
1
]
d
[
'nama'
]
=
k
[
2
]
d
=
{
'id'
:
k
[
0
],
'value'
:
k
[
2
]
+
' ('
+
nama_jenis
+
')'
,
'kode'
:
k
[
1
],
'nama'
:
k
[
2
]}
r
.
append
(
d
)
return
r
elif
url_dict
[
'act'
]
==
'csv'
:
query
=
query_reg
(
request
)
row
=
query
.
first
()
header
=
row
.
keys
()
rows
=
[]
for
item
in
query
.
all
():
rows
.
append
(
list
(
item
))
filename
=
'jabatan.csv'
value
=
{
'header'
:
header
,
'rows'
:
rows
,
}
return
csv_response
(
request
,
value
,
filename
)
elif
url_dict
[
'act'
]
==
'pdf'
:
query
=
query_reg
(
request
)
_here
=
os
.
path
.
dirname
(
__file__
)
# get current folder -> views
path
=
os
.
path
.
dirname
(
_here
)
# mundur 1 level
path
=
os
.
path
.
join
(
path
,
'reports'
)
rml_row
=
open_rml_row
(
path
+
'/jabatan.row.rml'
)
rows
=
[]
for
r
in
query
.
all
():
s
=
rml_row
.
format
(
kode
=
r
.
kode
,
nama
=
r
.
nama
,
status
=
r
.
status
and
"Aktif"
or
"Pasif"
)
rows
.
append
(
s
)
pdf
,
filename
=
open_rml_pdf
(
path
+
'/jabatan.rml'
,
rows
=
rows
,
company
=
request
.
company
,
departement
=
request
.
session
[
'departemen_nm'
],
address
=
request
.
address
,
alamat
=
Departemen
.
query_id
(
request
.
session
[
'departemen_id'
])
.
first
(),
periode
=
'01-01-2017 s.d 31-12-2017'
)
return
pdf_response
(
request
,
pdf
,
filename
)
@view_config
(
route_name
=
'jabatan-add'
,
renderer
=
'templates/form.pt'
,
...
...
@@ -213,3 +251,11 @@ class ViewJabatan(BaseView):
err_nama
()
elif
found
:
err_nama
()
def
query_reg
(
request
):
return
DBSession
.
query
(
Jabatan
.
kode
,
Jabatan
.
nama
,
Jabatan
.
status
,
)
.
\
filter
(
Jabatan
.
status
==
1
)
.
\
order_by
(
Jabatan
.
id
)
opensipkd/base/views/jabatan_report.py
View file @
2daa105
import
os
import
uuid
# from ..tools import row2dict, xls_reader
from
email.utils
import
parseaddr
from
datetime
import
datetime
from
sqlalchemy
import
not_
,
func
,
or_
,
desc
from
sqlalchemy.orm
import
aliased
from
pyramid.view
import
(
view_config
,
)
from
pyramid.httpexceptions
import
(
HTTPFound
,
)
import
json
import
colander
from
deform
import
(
Form
,
widget
,
ValidationFailure
,
)
from
..views
import
(
ColumnDT
,
DataTables
,
BaseView
,
)
from
opensipkd.tools.report
import
(
open_rml_row
,
open_rml_pdf
,
pdf_response
,
csv_response
,
get_logo
,
)
from
opensipkd.tools
import
(
dmy
,
date_from_str
,
thousand
,
STATUS
)
)
from
pyramid.view
import
(
view_config
,
)
from
..models
import
DBSession
from
..models
import
(
Jabatan
,
Departemen
,
)
from
..views
import
(
BaseView
,
)
class
view_rpt
(
BaseView
):
...
...
opensipkd/base/views/user.py
View file @
2daa105
...
...
@@ -3,8 +3,8 @@ import re
import
colander
from
deform
import
(
widget
,
)
from
opensipkd.tools
import
create_now
from
opensipkd.tools.report
import
open_rml_row
,
csv_response
,
open_rml_pdf
,
pdf_response
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
pyramid.i18n
import
TranslationStringFactory
from
pyramid.view
import
view_config
from
sqlalchemy
import
(
func
,
)
...
...
@@ -95,14 +95,17 @@ class Views(BaseView):
departement
=
self
.
req
.
departement
,
address
=
self
.
req
.
address
,
base_path
=
base_path
)
return
pdf_response
(
self
.
req
,
pdf
,
filename
)
filename
=
os
.
path
.
basename
(
filename
)
resp
=
pdf_response
(
self
.
req
,
pdf
,
filename
)
# save_file = SaveFile('/tmp')
# r = save_file.save(pdf, filename=filename)
# resp = file_response(self.req, filename=r)
return
resp
def
form_validator
(
self
,
form
,
value
):
if
"company_id"
in
value
and
not
value
[
"company_id"
]:
value
[
"company_id"
]
=
None
def
save_request
(
self
,
values
,
row
=
None
):
request
=
self
.
req
values
[
"email"
]
=
values
[
'email'
]
.
lower
()
...
...
@@ -156,11 +159,9 @@ class Views(BaseView):
add_member_count
(
gid
)
return
row
def
after_add
(
self
,
row
,
values
):
pass
@view_config
(
route_name
=
'user-add'
,
renderer
=
'templates/form.pt'
,
permission
=
'user-view'
)
...
...
@@ -168,15 +169,13 @@ class Views(BaseView):
return
super
(
Views
,
self
)
.
view_add
()
# user, remain = insert(request, values)
def
get_values
(
self
,
row
,
istime
=
False
):
d
=
super
(
Views
,
self
)
.
get_values
(
row
,
istime
)
d
[
"groups"
]
=
user_group_set
(
row
)
return
d
@view_config
(
route_name
=
'user-edit'
,
renderer
=
'templates/
user/edit
.pt'
,
route_name
=
'user-edit'
,
renderer
=
'templates/
form
.pt'
,
permission
=
'user-edit'
)
def
view_edit
(
self
):
return
super
(
Views
,
self
)
.
view_edit
()
...
...
@@ -187,14 +186,12 @@ class Views(BaseView):
def
view_view
(
self
):
return
super
(
Views
,
self
)
.
view_view
()
@view_config
(
route_name
=
'user-delete'
,
renderer
=
'templates/form.pt'
,
permission
=
'user-edit'
)
def
view_delete
(
self
):
return
super
(
Views
,
self
)
.
view_delete
()
def
delete_msg
(
self
,
row
):
data
=
dict
(
uid
=
row
.
id
,
email
=
row
.
email
)
return
_
(
...
...
@@ -202,13 +199,11 @@ class Views(BaseView):
default
=
'User ${email} ID ${uid} has been deleted'
,
mapping
=
data
)
def
before_delete
(
self
,
row
):
gid_list
=
user_group_set
(
row
)
for
gid
in
gid_list
:
reduce_member_count
(
gid
)
def
query_id
(
self
):
q
=
DBSession
.
query
(
User
)
.
filter_by
(
id
=
self
.
req
.
matchdict
[
'id'
])
if
self
.
req
.
user
.
company_id
:
...
...
opensipkd/base/views/user_rpc.py
View file @
2daa105
...
...
@@ -281,37 +281,6 @@ def login(request, data):
return
login_
(
request
,
data
)
# , permission='web-service'
def
get_profile_
(
user
):
# is_list = type(data) is list
# dat = is_list and data[0] or data
# user = get_user(data)
# if not user or not UserService.check_password(user, data['password']):
# raise JsonRpcInvalidLoginError
partner
=
Partner
.
query
()
.
filter_by
(
user_id
=
user
.
id
)
.
first
()
if
not
partner
:
raise
JsonRpcInvalidDataError
result
=
dict
(
user_name
=
user
.
user_name
,
nik
=
partner
.
kode
,
email
=
partner
.
email
,
mobile
=
partner
.
mobile
,
nama
=
partner
.
nama
,
)
# result = is_list and [result] or result
return
dict
(
data
=
result
)
@jsonrpc_method
(
method
=
'get_profile'
,
endpoint
=
'rpc-user'
)
def
get_profile
(
request
,
data
):
# Digunakan untuk memperoleh profile user
# parameter user_name, password
auth_from_rpc
(
request
)
user
=
get_user
(
data
)
if
not
user
or
not
UserService
.
check_password
(
user
,
data
[
'password'
]):
raise
JsonRpcInvalidLoginError
return
get_profile_
(
user
)
# , permission='web-service'
...
...
opensipkd/detable/detable.py
View file @
2daa105
...
...
@@ -116,7 +116,10 @@ class DeTable(field.Field):
params
=
params
and
f
"?{params}"
or
""
btn_close_js
=
"{window.location = '/'; return false;}"
btn_add_js
=
"{window.location = o
%
sUri+'/add
%
s';}"
%
(
tableid
,
params
)
btn_edit_js
=
"{window.location = o
%
sUri+'/'+m
%
sID+'/edit
%
s'}"
%
(
tableid
,
tableid
,
params
)
btn_edit_js
=
"""{
if (m
%
sID) window.location = o
%
sUri+'/'+m
%
sID+'/edit
%
s';
else alert('Pilih Baris');
}"""
%
(
tableid
,
tableid
,
tableid
,
params
)
btn_view_js
=
"{window.location = o
%
sUri+'/'+m
%
sID+'/view
%
s';}"
%
(
tableid
,
tableid
,
params
)
btn_delete_js
=
"{window.location = o
%
sUri+'/'+m
%
sID+'/delete
%
s';}"
%
(
tableid
,
tableid
,
params
)
btn_csv_js
=
"{window.location = o
%
sUri+'/csv/act
%
s';}"
%
(
tableid
,
params
)
...
...
opensipkd/jsonrpc_auth/__init__.py
View file @
2daa105
from
pyramid_rpc.jsonrpc
import
jsonrpc_method
import
logging
import
venusian
from
pyramid.httpexceptions
import
HTTPFound
from
pyramid.security
import
NO_PERMISSION_REQUIRED
from
pyramid_rpc.jsonrpc
import
jsonrpc_method
as
json_rpc_base
,
MethodPredicate
,
BatchedRequestPredicate
,
\
EndpointPredicate
,
jsonrpc_renderer
,
DEFAULT_RENDERER
,
add_jsonrpc_endpoint
,
add_jsonrpc_method
,
JsonRpcError
,
\
exception_view
,
JsonRpcRequestInvalid
,
parse_request_GET
,
parse_request_POST
from
opensipkd.base.tools.api
import
auth_from_rpc
from
opensipkd.base.views.user_login
import
get_login_headers
log
=
logging
.
getLogger
(
__name__
)
def
setup_request
(
endpoint
,
request
):
""" Parse a JSON-RPC request body."""
if
request
.
method
==
'GET'
:
parse_request_GET
(
request
)
elif
request
.
method
==
'POST'
:
parse_request_POST
(
request
)
else
:
log
.
debug
(
'unsupported request method "
%
s"'
,
request
.
method
)
raise
JsonRpcRequestInvalid
if
hasattr
(
request
,
'batched_rpc_requests'
):
log
.
debug
(
'handling batched rpc request'
)
# the checks below will look at the subrequests
return
if
request
.
rpc_version
!=
'2.0'
:
log
.
debug
(
'id:
%
s invalid rpc version
%
s'
,
request
.
rpc_id
,
request
.
rpc_version
)
raise
JsonRpcRequestInvalid
if
request
.
rpc_method
is
None
:
log
.
debug
(
'id:
%
s invalid rpc method'
,
request
.
rpc_id
)
raise
JsonRpcRequestInvalid
log
.
debug
(
'handling id:
%
s method:
%
s'
,
request
.
rpc_id
,
request
.
rpc_method
)
class
MethodPredicate
(
object
):
def
__init__
(
self
,
val
,
config
):
self
.
method
=
val
def
text
(
self
):
return
'jsonrpc method =
%
s'
%
self
.
method
phash
=
text
def
__call__
(
self
,
context
,
request
):
user
=
auth_from_rpc
(
request
)
headers
=
get_login_headers
(
request
,
user
)
response
=
HTTPFound
(
location
=
request
.
route_url
(
'home'
),
headers
=
headers
)
# response = request.response
request
.
response
.
set_cookie
(
'userid'
,
value
=
str
(
user
.
id
),
max_age
=
31536000
)
# max_age = year
return
getattr
(
request
,
'rpc_method'
,
None
)
==
self
.
method
def
includeme
(
config
):
""" Set up standard configurator registrations. Use via:
.. code-block:: python
config = Configurator()
config.include('pyramid_rpc.jsonrpc')
Once this function has been invoked, two new directives will be
available on the configurator:
- ``add_jsonrpc_endpoint``: Add an endpoint for handling JSON-RPC.
- ``add_jsonrpc_method``: Add a method to a JSON-RPC endpoint.
"""
if
not
hasattr
(
config
.
registry
,
'jsonrpc_endpoints'
):
config
.
registry
.
jsonrpc_endpoints
=
{}
config
.
add_view_predicate
(
'jsonrpc_method'
,
MethodPredicate
)
config
.
add_view_predicate
(
'jsonrpc_batched'
,
BatchedRequestPredicate
)
config
.
add_route_predicate
(
'jsonrpc_endpoint'
,
EndpointPredicate
)
config
.
add_renderer
(
DEFAULT_RENDERER
,
jsonrpc_renderer
)
config
.
add_directive
(
'add_jsonrpc_endpoint'
,
add_jsonrpc_endpoint
)
config
.
add_directive
(
'add_jsonrpc_method'
,
add_jsonrpc_method
)
config
.
add_view
(
exception_view
,
context
=
JsonRpcError
,
permission
=
NO_PERMISSION_REQUIRED
)
requirements.txt
View file @
2daa105
sqlalchemy~=1.4.22
zope.sqlalchemy
pyramid~=2.0
pyramid_beaker
colander~=1.8.3
deform~=2.0.15
transaction~=3.0.1
colorama~=0.4.4
Pillow
~=8.3
.1
lxml
~=4.6.3
beautifulsoup4
~=4.9.3
soupsieve
~=2.2.
1
pip
~=18.1
Pillow
>=9.1
.1
lxml
>=4.9.0
beautifulsoup4
>=4.11.1
soupsieve
>=2.3.2.post
1
pip
>=22.1.2
wheel~=0.37.0
rsa
~=4.7.2
rsa
>=4.8
pyasn1~=0.4.8
Chameleon
~=3.9
.1
Chameleon
>=3.10
.1
six~=1.16.0
Mako
~=1.1.4
Babel
~=2.9.1
Mako
>=1.2.0
Babel
>=2.10.3
Beaker~=1.11.0
Pygments
~=2.10
.0
MarkupSafe
~=2.0
.1
Pygments
>=2.12
.0
MarkupSafe
>=2.1
.1
Genshi~=0.7.5
pytz
~=2021
.1
pytz
>=2022
.1
WebOb~=1.8.7
translationstring~=1.4
peppercorn~=0.6
iso8601
~=0.1.16
iso8601
>=1.0.2
google~=3.0.0
cachetools
~=4.2.2
certifi
~=2021.5.30
cachetools
>=5.2.0
certifi
>=2022.6.15
urllib3~=1.26.6
requests
~=2.26
.0
google-api-python-client
~=2.19
.0
requests
>=2.28
.0
google-api-python-client
>=2.51
.0
python-dateutil~=2.8.2
alembic
~=1.6.5
alembic
>=1.8.0
passlib~=1.7.4
venusian~=3.0.0
plaster~=1.0
hupper~=1.10.3
waitress
~=2.0.0
waitress
>=2.1.2
greenlet~=1.1.1
pyparsing
~=2.4.7
httplib2
~=0.19.1
pyparsing
>=3.0.9
httplib2
>=0.20.4
icecream~=2.1.1
executing~=0.8.0
paginate~=0.5.6
idna~=3.2
chardet~=4.0.0
asttokens~=2.0.5
setuptools~=56.0.0
uritemplate~=3.0.1
zipp~=3.5.0
setuptools>=57.4.0
uritemplate>=4.1.1
reportlab~=3.6.1
PyJWT~=2.1.0
qrcode~=7.3.1
PyJWT>=2.4.0
py~=1.11.0
attrs~=21.4.0
toml~=0.10.2
pytest~=7.1.1
pluggy~=1.0.0
iniconfig~=1.1.1
\ No newline at end of file
iniconfig~=1.1.1
cffi>=1.15.0
cryptography>=37.0.2
jwt>=1.3.1
pikepdf>=5.1.5
packaging>=21.3
pycparser>=2.21
pyramid_rpc
\ No newline at end of file
Write
Preview
Markdown
is supported
Attach a file
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to post a comment