Fixes in admin
This commit is contained in:
parent
8a3c58d969
commit
573b0c4917
9 changed files with 744 additions and 16 deletions
|
|
@ -4,6 +4,7 @@ from pydantic import BaseModel
|
||||||
from settings import config
|
from settings import config
|
||||||
#from parameciofast.fastadmin import admin_app
|
#from parameciofast.fastadmin import admin_app
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
from parameciofast.libraries.datetime import set_timezone
|
||||||
import os
|
import os
|
||||||
|
|
||||||
app=FastAPI()
|
app=FastAPI()
|
||||||
|
|
@ -13,7 +14,7 @@ yes_static=True
|
||||||
if hasattr(config, 'yes_static'):
|
if hasattr(config, 'yes_static'):
|
||||||
yes_static=config.yes_static
|
yes_static=config.yes_static
|
||||||
|
|
||||||
|
set_timezone()
|
||||||
# apps={'admin': ['paramecio2.modules.admin', 'admin_app']}
|
# apps={'admin': ['paramecio2.modules.admin', 'admin_app']}
|
||||||
|
|
||||||
# Get all apps, load.
|
# Get all apps, load.
|
||||||
|
|
|
||||||
452
parameciofast/libraries/lists.py
Normal file
452
parameciofast/libraries/lists.py
Normal file
|
|
@ -0,0 +1,452 @@
|
||||||
|
"""
|
||||||
|
Parameciofast is a series of wrappers for FastAPI, mako and others and construct a simple headless cms.
|
||||||
|
|
||||||
|
Copyright (C) 2025 Antonio de la Rosa Caballero
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#By default id is not showed
|
||||||
|
|
||||||
|
from parameciofast.libraries.pages import Pages
|
||||||
|
from parameciofast.libraries.urls import add_get_parameters
|
||||||
|
from parameciofast.libraries.i18n import I18n, PGetText
|
||||||
|
#from flask import request, session
|
||||||
|
#from parameciofast.libraries.get_data import get_query_args
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
pgettext=PGetText(__file__)
|
||||||
|
_=pgettext.gettext
|
||||||
|
|
||||||
|
class SimpleList:
|
||||||
|
"""Class for create item list from a model table
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, model, url, t, request):
|
||||||
|
"""Class for create item list from a model table
|
||||||
|
|
||||||
|
You can create lists using a WebModel. You can select the show fields, and you have features how order by field and simple searchs.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model (WebModel): A WebModel model (equivalent to database mysql table)
|
||||||
|
url (str): A string with the base url for the forms.
|
||||||
|
t (PTemplate): Template used for the class. Normally template subclassed from admin_t PTemplate
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
raw_query (boolean): If True then raw query is done, if False then query with all related fields is done
|
||||||
|
t (PTemplate): Template used for the class. Normally template subclassed from admin_t PTemplate
|
||||||
|
model (WebModel): The webmodel used for generate the admin model form
|
||||||
|
fields (list): A list with fields names of model getting of db query
|
||||||
|
fields_showed (list): A list with fields names of model showed in list
|
||||||
|
url (str): Base url used by SimpleList for generate edit, insert and other urls.
|
||||||
|
limit_pages (int): The number of items by page. By default, 20 items
|
||||||
|
order_defaults (list): Internal list used for define Ascendent and Descendent in sql queries
|
||||||
|
order_class (list): Internal list used for show icons for ascendent or descendent field selection
|
||||||
|
order (str): Default order used in order_defaults list
|
||||||
|
order_field (str): The default field used for order the list. By default is the model id field
|
||||||
|
order_by (str): The default order ASC or DESC defined in order_class list. By default is 0 or ASC
|
||||||
|
change_order (dict): Internal dict used for get all ordenable fields from model
|
||||||
|
yes_search (boolean): If True, a search form is showed, if False, the search form is hidden.
|
||||||
|
search_text (str): Used for save the search text sended via POST.
|
||||||
|
initial_num_pages (int): Initial number of pages showed in pagination.
|
||||||
|
table_div (boolean): If True, use div for create the table, if False, use standard html table for create the table.
|
||||||
|
begin_page (int): Number that is used for begin the elements to get from query
|
||||||
|
search_fields (list): The fields used for search data in form.
|
||||||
|
arr_extra_fields (list): List with the names of extra fields
|
||||||
|
arr_extra_options (list): List with a set of functions used how extra fields. This functions return lists with different options, joined with jln attribute.
|
||||||
|
jln (list): Jump line for join options by default.
|
||||||
|
ajax (boolean): If True, ajax is used for get items for the list and change page, if False, typical httpd request is used for change the items page.
|
||||||
|
request(Request): A request objets from FastAPI/Starlette for get parameters
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.raw_query=True
|
||||||
|
|
||||||
|
self.t=t
|
||||||
|
|
||||||
|
self.model=model
|
||||||
|
|
||||||
|
#if len(self.model.forms)==0:
|
||||||
|
|
||||||
|
#self.model.create_forms()
|
||||||
|
|
||||||
|
self.fields=model.fields.keys()
|
||||||
|
|
||||||
|
self.fields_showed=self.fields
|
||||||
|
|
||||||
|
self.url=url
|
||||||
|
|
||||||
|
self.limit_pages=20
|
||||||
|
|
||||||
|
self.order_defaults=['ASC', 'DESC']
|
||||||
|
|
||||||
|
self.order_class=['down', 'up']
|
||||||
|
|
||||||
|
#self.s=get_session()
|
||||||
|
|
||||||
|
#clean session
|
||||||
|
|
||||||
|
self.order='0'
|
||||||
|
|
||||||
|
self.order_field=self.model.name_field_id
|
||||||
|
|
||||||
|
self.order_by=self.order_defaults[0]
|
||||||
|
|
||||||
|
self.change_order={}
|
||||||
|
|
||||||
|
self.yes_search=True
|
||||||
|
|
||||||
|
self.search_text=''
|
||||||
|
|
||||||
|
self.initial_num_pages=20
|
||||||
|
|
||||||
|
self.table_div=False
|
||||||
|
self.request=request
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
#self.begin_page=int(request.args.get('begin_page', '0'))
|
||||||
|
#self.begin_page=int(get_query_args('begin_page', '0'))
|
||||||
|
self.begin_page=int(self.request.query_params.get('begin_page', '0'))
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
self.begin_page=0
|
||||||
|
|
||||||
|
if self.begin_page<0:
|
||||||
|
self.begin_page=0
|
||||||
|
|
||||||
|
self.search_fields=self.fields
|
||||||
|
|
||||||
|
#self.yes_options=True
|
||||||
|
|
||||||
|
self.arr_extra_fields=[_('Options')]
|
||||||
|
|
||||||
|
self.arr_extra_options=[SimpleList.standard_options]
|
||||||
|
|
||||||
|
self.jln='<br />'
|
||||||
|
|
||||||
|
self.ajax=False
|
||||||
|
|
||||||
|
def restore_fields(self):
|
||||||
|
"""Simple method for restore default fields from model
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.fields=self.model.fields.keys()
|
||||||
|
|
||||||
|
def obtain_order(self):
|
||||||
|
"""Function for set the order query defaults for list from http request query args.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#self.order=request.args.get('order', self.order) #self.getpostfiles.get.get('order', self.order)
|
||||||
|
#self.order=get_query_args('order', self.order)
|
||||||
|
self.order=self.request.query_params.get('order', self.order)
|
||||||
|
|
||||||
|
order_k=int(self.order)
|
||||||
|
|
||||||
|
#Obtain from get
|
||||||
|
"""
|
||||||
|
if 'order' in request.query.keys():
|
||||||
|
"""
|
||||||
|
#order_k=int(request.query.get('order', 0))
|
||||||
|
|
||||||
|
if order_k>1 or order_k<0:
|
||||||
|
order_k=0
|
||||||
|
|
||||||
|
self.order_by=self.order_defaults[ order_k ]
|
||||||
|
|
||||||
|
self.order=order_k
|
||||||
|
|
||||||
|
def obtain_field_search(self):
|
||||||
|
"""Function for set the field search query defaults for list from http request query args.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#self.order_field=get_query_args('order_field', self.order_field)
|
||||||
|
self.order_field=self.request.query_params.get('order_field', self.order_field)
|
||||||
|
|
||||||
|
field_k=self.order_field
|
||||||
|
|
||||||
|
"""
|
||||||
|
if 'order_field' in request.query.keys():
|
||||||
|
field_k=request.query.order_field
|
||||||
|
"""
|
||||||
|
|
||||||
|
if field_k in self.model.fields.keys():
|
||||||
|
|
||||||
|
self.order_field=field_k
|
||||||
|
|
||||||
|
for field in self.fields:
|
||||||
|
|
||||||
|
#Initialize foreignkeyfield fields too
|
||||||
|
|
||||||
|
if type(self.model.fields[field]).__name__=='ForeignKeyField':
|
||||||
|
name_related=self.model.fields[field].related_model.name
|
||||||
|
for k in self.model.fields[field].related_model.fields.keys():
|
||||||
|
self.change_order[field+'_'+k]=self.order
|
||||||
|
|
||||||
|
self.change_order[field]=self.order
|
||||||
|
|
||||||
|
if self.order==0:
|
||||||
|
self.change_order[field_k]=1
|
||||||
|
else:
|
||||||
|
self.change_order[field_k]=0
|
||||||
|
|
||||||
|
#self.order_field=self.order_field
|
||||||
|
|
||||||
|
def search(self):
|
||||||
|
"""Function for set the text order query defaults for list from http request query args.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#self.search_text=get_query_args('search_text', '')
|
||||||
|
self.search_text=self.request.query_params.get('search_text', self.search_text)
|
||||||
|
|
||||||
|
self.search_text=self.search_text.replace('"', '"')
|
||||||
|
|
||||||
|
#self.model.conditions='AND
|
||||||
|
|
||||||
|
#self.search_field=get_query_args('search_field', '')
|
||||||
|
self.search_field=self.request.query_params.get('search_field', '')
|
||||||
|
|
||||||
|
if self.search_field not in self.model.fields.keys():
|
||||||
|
self.search_field=''
|
||||||
|
|
||||||
|
if self.search_field!='' and self.search_text!='':
|
||||||
|
self.model.conditions[0]+=' AND '+self.search_field+' LIKE %s'
|
||||||
|
self.model.conditions[1].append('%'+self.search_text+'%')
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_options(self, options_func, arr_row):
|
||||||
|
"""Method for join options list returns with jln attributte separator
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
options (str): Return a string with joined options
|
||||||
|
"""
|
||||||
|
#SimpleList.standard_options(arr_row)
|
||||||
|
return self.jln.join(options_func(self.url, arr_row[self.model.name_field_id], arr_row))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def standard_options(url, id, arr_row):
|
||||||
|
"""Static method for get standar options for make things with the items row.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
options (list): Return a list of basic options for items row
|
||||||
|
"""
|
||||||
|
options=[]
|
||||||
|
options.append('<a href="'+add_get_parameters(url, op_admin=1, id=id)+'">'+_('Edit')+'</a>')
|
||||||
|
options.append('<a href="'+add_get_parameters(url, op_admin=3, id=id)+'">'+_('Delete')+'</a>')
|
||||||
|
return options
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
"""Method for show the table
|
||||||
|
|
||||||
|
The principal method of the class. The list is showed with the selected fields, search form, pagination...
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.model.yes_reset_conditions=False
|
||||||
|
|
||||||
|
self.obtain_order()
|
||||||
|
|
||||||
|
self.obtain_field_search()
|
||||||
|
|
||||||
|
self.search()
|
||||||
|
|
||||||
|
total_elements=self.model.select_count()
|
||||||
|
|
||||||
|
num_elements=self.limit_pages
|
||||||
|
|
||||||
|
link=add_get_parameters(self.url, search_text=self.search_text, search_field=self.search_field, order=self.order)
|
||||||
|
|
||||||
|
begin_page=self.begin_page
|
||||||
|
|
||||||
|
self.model.order_by='order by '+self.order_field+' '+self.order_by
|
||||||
|
|
||||||
|
if self.limit_pages>0:
|
||||||
|
|
||||||
|
self.model.limit='limit '+str(begin_page)+','+str(self.limit_pages)
|
||||||
|
|
||||||
|
list_items=self.model.select(self.fields, self.raw_query)
|
||||||
|
|
||||||
|
#print(self.model.fields.keys())
|
||||||
|
|
||||||
|
func_jscript=''
|
||||||
|
|
||||||
|
if self.ajax==True:
|
||||||
|
func_jscript='get_page()'
|
||||||
|
|
||||||
|
pages=''
|
||||||
|
|
||||||
|
if self.limit_pages>0:
|
||||||
|
pages=Pages.show( begin_page, total_elements, num_elements, link ,initial_num_pages=self.initial_num_pages, variable='begin_page', label='', func_jscript='')
|
||||||
|
|
||||||
|
self.begin_page=str(self.begin_page)
|
||||||
|
|
||||||
|
self.model.yes_reset_conditions=True
|
||||||
|
|
||||||
|
listing=self.t.load_template('utils/list.phtml', simplelist=self, list=list_items, pages=pages, ajax=self.ajax)
|
||||||
|
|
||||||
|
list_items.close()
|
||||||
|
|
||||||
|
return listing
|
||||||
|
|
||||||
|
"""
|
||||||
|
@staticmethod
|
||||||
|
def get_ajax_page(model):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class AjaxList(SimpleList):
|
||||||
|
"""Class for make a list from a table based in Ajax
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Fields example: [['Hostname', True], ['IP', True], ['Options', False]]
|
||||||
|
|
||||||
|
# arr_order_fields=['server.hostname', 'server.ip']
|
||||||
|
|
||||||
|
# 'select count(servercloud.id) as num_elements from servercloud where servercloud.user_id=%s'
|
||||||
|
# params count_query [s['cu_id']]
|
||||||
|
|
||||||
|
# str_query no order, no limit -> select server.hostname, server.ip, servercloud.id from server, servercloud where server.id=servercloud.server_id and servercloud.user_id=%s
|
||||||
|
# str_query_params -> [s['cu_id'], begin_page, limit]
|
||||||
|
|
||||||
|
def __init__(self, db, fields, arr_order_fields, count_query, str_query):
|
||||||
|
"""Class for make a list from a table based in Ajax
|
||||||
|
|
||||||
|
A class that is useful for creating listings based on database models using Ajax
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db (sql connection): A MySQL connection used for get the model.
|
||||||
|
fields (list): A list with the fields showed in table
|
||||||
|
arr_order_fields (list): A list with the sql names of selected fields for show.
|
||||||
|
count_query (str): sql segment for count query sentence. Example: select count(id) from table WHERE name=%s
|
||||||
|
str_query (str): sql segment for query sentence. Example: select id from table WHERE name=%s
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
fields (list): A list with the fields showed in table
|
||||||
|
arr_order_fields (list): A list with the sql names of selected fields for show.
|
||||||
|
limit (int): the number of items selected in query sentence.
|
||||||
|
count_query (str): sql segment for count query sentence. Example: select count(id) from table WHERE name=%s
|
||||||
|
count_query_params (list): A list with the params for parse a sql count query with %s symbols (View python help about sql sentences and connectors)
|
||||||
|
str_query (str): sql segment for query sentence. Example: select id from table WHERE name=%s
|
||||||
|
str_query_params (list): A list with the params for parse a sql query with %s symbols (View python help about sql sentences and connectors)
|
||||||
|
initial_num_pages (int): Initial number of pages showed in pagination.
|
||||||
|
db (sql connection): A MySQL connection used for get the model.
|
||||||
|
func_fields (dict): A series of functions used for a series of extra fields referring to each row of the table
|
||||||
|
initial_order_field (str): FIeld used for order the table in first execution
|
||||||
|
initial_order (int): If 0, the initial order is Ascendent, if 1, the initial order is Descendent.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.fields=fields
|
||||||
|
self.arr_order_fields=arr_order_fields
|
||||||
|
self.limit=20
|
||||||
|
self.count_query=count_query[0]
|
||||||
|
self.count_query_params=count_query[1]
|
||||||
|
self.str_query=str_query[0]
|
||||||
|
self.str_query_params=str_query[1]
|
||||||
|
self.initial_num_pages=20
|
||||||
|
self.db=db
|
||||||
|
self.func_fields={}
|
||||||
|
|
||||||
|
self.initial_order_field=''
|
||||||
|
self.initial_order=0
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
"""Method for show the table
|
||||||
|
|
||||||
|
The principal method of the class. The list is showed with the selected fields, search form, pagination...
|
||||||
|
"""
|
||||||
|
|
||||||
|
#begin_page=int(get_query_args('position', 0))
|
||||||
|
begin_page=int(self.request.query_params.get('position', 0))
|
||||||
|
|
||||||
|
#order_field=get_query_args('order_field', self.initial_order_field)
|
||||||
|
order_field=self.request.query_params.get('order_field', self.initial_order_field)
|
||||||
|
|
||||||
|
#order=get_query_args('order', self.initial_order)
|
||||||
|
|
||||||
|
order=self.request.query_params.get('order', self.initial_order)
|
||||||
|
|
||||||
|
limit=self.limit
|
||||||
|
|
||||||
|
arr_order=['ASC', 'DESC']
|
||||||
|
|
||||||
|
order_sql=''
|
||||||
|
order_params=[]
|
||||||
|
|
||||||
|
if order_field!='':
|
||||||
|
try:
|
||||||
|
order_field=int(order_field)
|
||||||
|
order=int(order)
|
||||||
|
|
||||||
|
if order_field>=0 and order_field<len(self.arr_order_fields):
|
||||||
|
order_sql='order by %s' % self.arr_order_fields[order_field]
|
||||||
|
if order>=0 and order<2:
|
||||||
|
order_sql+=' %s' % arr_order[order]
|
||||||
|
|
||||||
|
#order_params=[self.arr_order_fields[order_field]]
|
||||||
|
|
||||||
|
except:
|
||||||
|
order_field=0
|
||||||
|
order=0
|
||||||
|
|
||||||
|
|
||||||
|
rows=[]
|
||||||
|
|
||||||
|
with self.db.query(self.count_query, self.count_query_params) as cursor:
|
||||||
|
total_elements=cursor.fetchone()['num_elements']
|
||||||
|
|
||||||
|
str_query=self.str_query+' '+order_sql
|
||||||
|
|
||||||
|
params=self.str_query_params
|
||||||
|
|
||||||
|
html_pages=''
|
||||||
|
|
||||||
|
if self.limit>0:
|
||||||
|
str_query+=' limit %s, %s'
|
||||||
|
params.append(begin_page)
|
||||||
|
params.append(limit)
|
||||||
|
|
||||||
|
pages=Pages()
|
||||||
|
|
||||||
|
html_pages=_('Pages')+': '+pages.show( begin_page, total_elements, limit, '#' ,initial_num_pages=self.initial_num_pages, variable='begin_page', label='', func_jscript='')
|
||||||
|
|
||||||
|
with self.db.query(str_query, params) as cursor:
|
||||||
|
for row in cursor:
|
||||||
|
"""
|
||||||
|
c=len(self.arr_order_fields)
|
||||||
|
for x in range(c, len(row)):
|
||||||
|
key_field=list(row.keys())[x]
|
||||||
|
#print(key_field)
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
|
||||||
|
for func_field in self.func_fields:
|
||||||
|
if func_field in row:
|
||||||
|
row[func_field]=self.func_fields[func_field](row[func_field], row)
|
||||||
|
|
||||||
|
rows.append(row)
|
||||||
|
#{k:d[k] for in set(d).intersection(l)}
|
||||||
|
|
||||||
|
return {'fields': self.fields, 'rows': rows, 'html_pages': html_pages}
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleAjaxList():
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
@ -37,11 +37,6 @@ from parameciofast.libraries.i18n import I18n, PGetText
|
||||||
from parameciofast.libraries.urls import make_url, make_media_url, add_get_parameters
|
from parameciofast.libraries.urls import make_url, make_media_url, add_get_parameters
|
||||||
#from parameciofast.libraries.formsutils import csrf_token
|
#from parameciofast.libraries.formsutils import csrf_token
|
||||||
|
|
||||||
framework='flask'
|
|
||||||
|
|
||||||
if hasattr(config, 'framework'):
|
|
||||||
framework=config.framework
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def _(text):
|
def _(text):
|
||||||
|
|
@ -149,7 +144,7 @@ class PTemplate:
|
||||||
|
|
||||||
#self.add_filter(csrf_token)
|
#self.add_filter(csrf_token)
|
||||||
|
|
||||||
#self.add_filter(add_get_parameters)
|
self.add_filter(add_get_parameters)
|
||||||
|
|
||||||
self.add_filter(self.add_js)
|
self.add_filter(self.add_js)
|
||||||
|
|
||||||
|
|
|
||||||
107
parameciofast/libraries/pages.py
Normal file
107
parameciofast/libraries/pages.py
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms.
|
||||||
|
|
||||||
|
Copyright (C) 2023 Antonio de la Rosa Caballero
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from math import ceil, floor
|
||||||
|
from parameciofast.libraries.urls import add_get_parameters
|
||||||
|
from parameciofast.libraries.i18n import I18n
|
||||||
|
|
||||||
|
class Pages:
|
||||||
|
"""Simple class for create html pagination code"""
|
||||||
|
|
||||||
|
css_class='link_pages'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def show( begin_page, total_elements, num_elements, link ,initial_num_pages=20, variable='begin_page', label='', func_jscript=''):
|
||||||
|
"""Static method for create the html pagination
|
||||||
|
|
||||||
|
With this method, you can create html pagination code with automated urls for load every page. You can use it also how base for ajax pagination
|
||||||
|
|
||||||
|
Args:
|
||||||
|
begin_page (int): The number where pagination begin
|
||||||
|
total_elements (int): The total items in pages
|
||||||
|
num_elements (int): The number of items for every page
|
||||||
|
link (str): The url of every page
|
||||||
|
initial_num_pages (int): Optional. Number of pages showed in pagination, if you have 50 pages, if this value is 20, an interval of 20 pages is showed, with first pages links, and after pages links for navigate between many pages.
|
||||||
|
variable (str): Optional. The name of GET url variable used for send the first element in the query for get the page.
|
||||||
|
label (str): Optional. In the future will be used for identify some html tags
|
||||||
|
func_jscript (str): Javascript function to be executed when page url is clicked.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
pages='';
|
||||||
|
|
||||||
|
if begin_page>total_elements:
|
||||||
|
begin_page=0
|
||||||
|
|
||||||
|
# Calculamos el total de todas las páginas
|
||||||
|
|
||||||
|
total_page=ceil(total_elements/num_elements)
|
||||||
|
|
||||||
|
# Calculamos en que página nos encontramos
|
||||||
|
|
||||||
|
actual_page=ceil(begin_page/num_elements)
|
||||||
|
|
||||||
|
# Calculamos el total de intervalos
|
||||||
|
|
||||||
|
total_interval=ceil(total_page/initial_num_pages)
|
||||||
|
|
||||||
|
# Calculamos el intervalo en el que estamos
|
||||||
|
|
||||||
|
actual_interval=floor(actual_page/initial_num_pages)
|
||||||
|
|
||||||
|
# Calculamos el elemento de inicio del intervalo
|
||||||
|
|
||||||
|
initial_page=ceil(actual_interval*initial_num_pages*num_elements)
|
||||||
|
|
||||||
|
last_page=ceil(actual_interval*initial_num_pages*num_elements)+ceil(initial_num_pages*num_elements)
|
||||||
|
|
||||||
|
if last_page>total_elements:
|
||||||
|
last_page=total_elements
|
||||||
|
|
||||||
|
if initial_page>0:
|
||||||
|
initial_link=add_get_parameters(link, **{variable: '0'});
|
||||||
|
middle_link=add_get_parameters(link, **{variable: str((initial_page-num_elements)) } );
|
||||||
|
pages += "<a class=\""+Pages.css_class+"\" href=\""+initial_link+"\" onclick=\"${func_jscript}\">1</a> <a class=\""+Pages.css_class+"\" href=\""+middle_link+"\"><<</a> "
|
||||||
|
|
||||||
|
arr_pages={}
|
||||||
|
|
||||||
|
#for(x=initial_page;x<last_page;x+=num_elements)
|
||||||
|
for x in range(initial_page, last_page, num_elements):
|
||||||
|
|
||||||
|
middle_link=add_get_parameters(link, **{variable: str(x)} )
|
||||||
|
|
||||||
|
num_page=ceil(x/num_elements)+1;
|
||||||
|
arr_pages[x]="<a class=\""+Pages.css_class+"\" href=\""+middle_link+"\">"+str(num_page)+"</a> "
|
||||||
|
arr_pages[begin_page]='<span class="selected_page">'+str(num_page)+'</span> ';
|
||||||
|
pages += arr_pages[x]
|
||||||
|
|
||||||
|
|
||||||
|
if last_page<total_elements:
|
||||||
|
|
||||||
|
middle_link=add_get_parameters(link, **{variable: str(x+num_elements)} );
|
||||||
|
last_link=add_get_parameters(link, **{variable: str( ( ( total_page*num_elements ) - num_elements) ) } )
|
||||||
|
|
||||||
|
pages += "<a class=\""+Pages.css_class+"\" href=\""+middle_link+"\" onclick=\"func_jscript\">>></a> <a class=\"link_pages\" href=\""+last_link+"\" onclick=\"func_jscript\">"+I18n.lang('common', 'last', 'Last')+"</a>"
|
||||||
|
|
||||||
|
|
||||||
|
return pages
|
||||||
|
|
||||||
|
|
||||||
137
parameciofast/libraries/templates/utils/list.phtml
Normal file
137
parameciofast/libraries/templates/utils/list.phtml
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
<%def name="select_field(field)">
|
||||||
|
% if simplelist.search_field==field:
|
||||||
|
selected \
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
<%def name="set_css_arrow(simplelist, field)">
|
||||||
|
% if simplelist.order_field==field:
|
||||||
|
<i class="fa fa-arrow-${simplelist.order_class[simplelist.order]}" aria-hidden="true"></i>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
% if simplelist.yes_search:
|
||||||
|
<div class="form mb-4">
|
||||||
|
<form method="get" action="${simplelist.url}">
|
||||||
|
${_('Search')}: <input type="text" name="search_text" value="${simplelist.search_text|n}">
|
||||||
|
<select name="search_field">
|
||||||
|
% for field in simplelist.search_fields:
|
||||||
|
<option value="${simplelist.model.fields[field].name}" ${select_field(field)}>${simplelist.model.fields[field].label}</option>
|
||||||
|
% endfor
|
||||||
|
</select>
|
||||||
|
<input type="submit" value="${_('Search')}" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
% if not simplelist.table_div:
|
||||||
|
|
||||||
|
<table class="table" id="${simplelist.model.name}_table">
|
||||||
|
<thead>
|
||||||
|
<tr class="title_list">
|
||||||
|
% for field in simplelist.fields_showed:
|
||||||
|
<th class="${simplelist.model.fields[field].name}_td" scope="col"><a href="${add_get_parameters(simplelist.url, order_field=field, begin_page=simplelist.begin_page, order=simplelist.change_order[field], search_text=simplelist.search_text, search_field=simplelist.search_field)}" class="">${set_css_arrow(simplelist, field)}${simplelist.model.fields[field].label}</a></th>
|
||||||
|
% endfor
|
||||||
|
% for extra_field in simplelist.arr_extra_fields:
|
||||||
|
<td class="options_td">${ extra_field }</td>
|
||||||
|
% endfor
|
||||||
|
</th>
|
||||||
|
</thead>
|
||||||
|
<%
|
||||||
|
pos=0
|
||||||
|
%>
|
||||||
|
% for row in list:
|
||||||
|
<%
|
||||||
|
if not 'pos' in row:
|
||||||
|
row['pos']=pos
|
||||||
|
%>
|
||||||
|
<tr class="row_list">
|
||||||
|
% for field in simplelist.fields_showed:
|
||||||
|
% if simplelist.model.fields[field].escape==True:
|
||||||
|
%if type(simplelist.model.fields[field]).__name__!='ForeignKeyField':
|
||||||
|
<td class="${simplelist.model.fields[field].name}_td">${simplelist.model.fields[field].show_formatted(row[field])}</td>
|
||||||
|
% else:
|
||||||
|
<td class="${simplelist.model.fields[field].name}_td">${simplelist.model.fields[field].show_formatted(row[field])}${str(simplelist.model.fields[field].related_model.fields[simplelist.model.fields[field].named_field].show_formatted(row[field]))}</td>
|
||||||
|
% endif
|
||||||
|
% else:
|
||||||
|
%if type(simplelist.model.fields[field]).__name__!='ForeignKeyField':
|
||||||
|
<td class="${simplelist.model.fields[field].name}_td">${str(simplelist.model.fields[field].show_formatted(row[field]))|n}</td>
|
||||||
|
% else:
|
||||||
|
<td class="${simplelist.model.fields[field].name}_td">${str(simplelist.model.fields[field].related_model.fields[simplelist.model.fields[field].named_field].show_formatted(row[field]))|n}</td>
|
||||||
|
% endif
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
|
||||||
|
% for extra_field_func in simplelist.arr_extra_options:
|
||||||
|
<td class="options_td">${ simplelist.set_options(extra_field_func, row)|n }</td>
|
||||||
|
% endfor
|
||||||
|
</tr>
|
||||||
|
<%
|
||||||
|
pos+=1
|
||||||
|
%>
|
||||||
|
% endfor
|
||||||
|
</table>
|
||||||
|
|
||||||
|
% else:
|
||||||
|
|
||||||
|
<%
|
||||||
|
|
||||||
|
size_td=round(100/(len(simplelist.fields_showed)+len(simplelist.arr_extra_options)))
|
||||||
|
|
||||||
|
%>
|
||||||
|
|
||||||
|
<div class="table_list" id="${simplelist.model.name}_table">
|
||||||
|
<div class="${simplelist.model.name}_tr tr_list_div">
|
||||||
|
% for field in simplelist.fields_showed:
|
||||||
|
<div class="${simplelist.model.fields[field].name}_td fields_span_title" style="width: ${size_td}%;"><a href="${add_get_parameters(simplelist.url, order_field=field, begin_page=simplelist.begin_page, order=simplelist.change_order[field], search_text=simplelist.search_text, search_field=simplelist.search_field)}" class="">${set_css_arrow(simplelist, field)}${simplelist.model.fields[field].label}</a></div>
|
||||||
|
% endfor
|
||||||
|
% for extra_field in simplelist.arr_extra_fields:
|
||||||
|
<div class="fields_span_title options_td" style="width: ${size_td}%;">${ extra_field }</div>
|
||||||
|
% endfor
|
||||||
|
</div>
|
||||||
|
<div class="table_rows ${simplelist.model.name}_table_rows" id="${simplelist.model.name}_table_rows">
|
||||||
|
<%
|
||||||
|
pos=0
|
||||||
|
%>
|
||||||
|
% for row in list:
|
||||||
|
<div id="${simplelist.model.name}_table_${row['id']}" class="${simplelist.model.name}_table_row" style="clear: both;overflow: hidden;">
|
||||||
|
<%
|
||||||
|
if not 'pos' in row:
|
||||||
|
row['pos']=pos
|
||||||
|
%>
|
||||||
|
|
||||||
|
% for field in simplelist.fields_showed:
|
||||||
|
% if simplelist.model.fields[field].escape==True:
|
||||||
|
<div class="${simplelist.model.fields[field].name}_td fields_span_table_data" style="width: ${size_td}%;">
|
||||||
|
%if type(simplelist.model.fields[field]).__name__!='ForeignKeyField':
|
||||||
|
${simplelist.model.fields[field].show_formatted(row[field])}
|
||||||
|
% else:
|
||||||
|
${str(simplelist.model.fields[field].related_model.fields[simplelist.model.fields[field].named_field].show_formatted(row[field]))}
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
% else:
|
||||||
|
<div class="${simplelist.model.fields[field].name}_td fields_span_table_data" style="width: ${size_td}%;">
|
||||||
|
%if type(simplelist.model.fields[field]).__name__!='ForeignKeyField':
|
||||||
|
${str(simplelist.model.fields[field].show_formatted(row[field]))|n}
|
||||||
|
% else:
|
||||||
|
${str(simplelist.model.fields[field].related_model.fields[simplelist.model.fields[field].named_field].show_formatted(row[field]))|n}
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
|
||||||
|
% for extra_field_func in simplelist.arr_extra_options:
|
||||||
|
<div class="options_td fields_span_table_data" style="width: ${size_td}%;">${ simplelist.set_options(extra_field_func, row)|n }</div>
|
||||||
|
% endfor
|
||||||
|
<%
|
||||||
|
pos+=1
|
||||||
|
%>
|
||||||
|
</div>
|
||||||
|
% endfor
|
||||||
|
</div>
|
||||||
|
<br clear="all" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
% endif
|
||||||
|
<p>
|
||||||
|
% if pages!='':
|
||||||
|
${_('Pages')}: ${pages|n}
|
||||||
|
% endif
|
||||||
|
</p>
|
||||||
|
|
@ -5,7 +5,8 @@ from settings import config
|
||||||
#from parameciofast.fast import app
|
#from parameciofast.fast import app
|
||||||
from parameciofast.libraries.session import ParamecioSession
|
from parameciofast.libraries.session import ParamecioSession
|
||||||
from starlette.middleware.sessions import SessionMiddleware
|
from starlette.middleware.sessions import SessionMiddleware
|
||||||
from parameciofast.fast import app
|
from parameciofast.libraries.db.webmodel import WebModel
|
||||||
|
#from parameciofast.fast import app
|
||||||
|
|
||||||
cookie_name='paramecio_session'
|
cookie_name='paramecio_session'
|
||||||
|
|
||||||
|
|
@ -17,19 +18,38 @@ url_app=config.apps['admin'][2]
|
||||||
|
|
||||||
admin_app=FastAPI(docs_url="/docs", openapi_url="/docs/openapi.json", title='Paramecio Admin', version='0.9')
|
admin_app=FastAPI(docs_url="/docs", openapi_url="/docs/openapi.json", title='Paramecio Admin', version='0.9')
|
||||||
|
|
||||||
|
@admin_app.middleware('http')
|
||||||
|
async def check_session(request: Request, call_next):
|
||||||
|
|
||||||
|
db=WebModel.connection()
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
request.state.db=db
|
||||||
|
|
||||||
|
response = await call_next(request)
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
raise
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
# FastAPI set the middlewares in reversed order.
|
# FastAPI set the middlewares in reversed order.
|
||||||
|
|
||||||
@admin_app.middleware("http")
|
@admin_app.middleware("http")
|
||||||
async def check_session(request: Request, call_next):
|
async def check_session(request: Request, call_next):
|
||||||
|
|
||||||
#print(request.scope['route'].name)
|
path=request.scope['path']
|
||||||
|
|
||||||
response = await call_next(request)
|
if not request.session.get('login_admin', None) and path!=url_app+'login' and path!=url_app+'signup' and path!=url_app+'logout':
|
||||||
|
|
||||||
if not request.session.get('login_admin', None) and request.scope['endpoint'].__name__!='login_admin' and request.scope['endpoint'].__name__!='signup_admin' and request.scope['endpoint'].__name__!='check_login_admin' and request.scope['endpoint'].__name__!='signup_insert_admin' and request.scope['endpoint'].__name__!='logout_admin':
|
|
||||||
|
|
||||||
return RedirectResponse(request.url_for('login_admin'))
|
return RedirectResponse(request.url_for('login_admin'))
|
||||||
|
|
||||||
|
response = await call_next(request)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
|
from fastapi import Request
|
||||||
|
from parameciofast.modules.fastadmin.models.admin import UserAdmin
|
||||||
from parameciofast.modules.fastadmin import admin_app
|
from parameciofast.modules.fastadmin import admin_app
|
||||||
from parameciofast.modules.fastadmin.libraries.config import modules_admin, modules_admin_icons
|
from parameciofast.modules.fastadmin.libraries.config import modules_admin, modules_admin_icons
|
||||||
from parameciofast.libraries.mtemplates import PTemplate, env_theme
|
from parameciofast.libraries.mtemplates import PTemplate, env_theme
|
||||||
|
|
@ -6,6 +8,9 @@ from parameciofast.libraries.i18n import I18n
|
||||||
import parameciofast.modules.fastadmin.libraries.i18n
|
import parameciofast.modules.fastadmin.libraries.i18n
|
||||||
from parameciofast.fast import app
|
from parameciofast.fast import app
|
||||||
import os
|
import os
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from parameciofast.libraries.lists import SimpleList
|
||||||
|
|
||||||
|
|
||||||
env=env_theme(__file__)
|
env=env_theme(__file__)
|
||||||
|
|
||||||
|
|
@ -23,9 +28,19 @@ modules_admin.append(['fastadmin_users', 'people-circle'])
|
||||||
modules_admin_icons.append('<symbol id="people-circle" viewBox="0 0 16 16"><path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/><path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/></symbol>')
|
modules_admin_icons.append('<symbol id="people-circle" viewBox="0 0 16 16"><path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/><path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/></symbol>')
|
||||||
|
|
||||||
@admin_app.get('/ausers', response_class=HTMLResponse)
|
@admin_app.get('/ausers', response_class=HTMLResponse)
|
||||||
def fastadmin_users():
|
def fastadmin_users(request: Request):
|
||||||
|
|
||||||
i18n=I18n('fastadmin')
|
i18n=I18n('fastadmin')
|
||||||
|
|
||||||
return t.load_template('users.phtml', title=i18n.tlang('Admin'), tlang=i18n.tlang, url_for=app.url_path_for, module_selected='fastadmin_users')
|
db=request.state.db
|
||||||
|
|
||||||
|
users=UserAdmin(db)
|
||||||
|
|
||||||
|
url=app.url_path_for('fastadmin_users')
|
||||||
|
|
||||||
|
slist=SimpleList(users, url, t, request)
|
||||||
|
|
||||||
|
slist.fields_showed=['username', 'email', 'double_auth', 'last_login']
|
||||||
|
|
||||||
|
return t.load_template('users.phtml', title=i18n.tlang('Admin'), tlang=i18n.tlang, url_for=app.url_path_for, module_selected='fastadmin_users', slist=slist)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class UserAdmin(WebModel):
|
||||||
|
|
||||||
self.register(corefields.CharField('token_login'))
|
self.register(corefields.CharField('token_login'))
|
||||||
|
|
||||||
self.register(PasswordField('token_auth'))
|
#self.register(PasswordField('token_auth'))
|
||||||
|
|
||||||
self.register(PrivilegesField('privileges'))
|
self.register(PrivilegesField('privileges'))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
<%inherit file="layout.phtml"/>
|
<%inherit file="layout.phtml"/>
|
||||||
<%block name="content">
|
<%block name="content">
|
||||||
|
${slist.show()|n}
|
||||||
</%block>
|
</%block>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue