Fixes in multiple things for modern systems

This commit is contained in:
Antonio de la Rosa 2024-09-28 17:56:58 +02:00
parent c31f61aa1b
commit c9f77b191a
13 changed files with 829 additions and 81 deletions

View file

@ -1,15 +1,38 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""
Parameciofm is a series of wrappers for bottle.py, mako and others and construct a simple headless cms.
Copyright (C) 2024 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 importlib import import_module from importlib import import_module
from paramecio.citoplasma.sessions import get_session from paramecio.citoplasma.sessions import get_session
import json import json
from bottle import request from bottle import request
import gettext
import os
yes_session=False yes_session=False
i18n_module={} i18n_module={}
def load_lang(*args): def load_lang(*args):
"""A function for load the lang module dinamically
"""
for module in args: for module in args:
@ -26,9 +49,57 @@ def load_lang(*args):
# here load the language # here load the language
class PGetText:
# Dict where all gettext domain are saved -> domain=name, example, admin, libraries, pastafari2, etc...
l={}
def __init__(self, module_file):
module_dir=os.path.dirname(os.path.realpath(module_file))
module_name=os.path.basename(module_dir)
if module_name not in PGetText.l:
PGetText.l[module_name]={}
for i in I18n.dict_i18n:
if i not in PGetText.l[module_name]:
PGetText.l[module_name][i]=gettext.translation(module_name, module_dir+'/languages/', languages=[i], fallback=True)
PGetText.l[module_name][i].install()
self.module=module_name
def gettext(self, text):
return PGetText.l[self.module][I18n.get_default_lang()].gettext(text)
def pgettext(module_file):
module=os.path.dirname(os.path.realpath(module_file))
base_name=os.path.dirname(os.path.realpath(module))
l=gettext.translation(os.path.basename(base_name), module+'/languages/', languages=I18n.get_default_lang(), fallback=True)
return l.gettext
class I18n: class I18n:
"""Class for i18n tasks
Class for i18n tasks, how, strings for every language supported, for now are en-US and es-ES. You can add more languages adding
Attributes:
default_lang (str): The default string lang used when get someone
dict_i18n (list): The list with default languages. You can add more calling it static variable in settings/config.py
"""
default_lang='en-US' default_lang='en-US'
dict_i18n=['en-US', 'es-ES'] dict_i18n=['en-US', 'es-ES']
@ -42,6 +113,7 @@ class I18n:
@staticmethod @staticmethod
def get_default_lang(): def get_default_lang():
"""Static method for get the default lang"""
lang=I18n.default_lang lang=I18n.default_lang
@ -53,6 +125,15 @@ class I18n:
@staticmethod @staticmethod
def lang(module, symbol, text_default, lang=None): def lang(module, symbol, text_default, lang=None):
"""Static method for get a string from selected language
Static method used to get the string of the selected language. If there is no string in the selected language, it returns text_default.
Args:
module (str): The module to which the translation string belongs
symbol (str): Simple symbol that is useful for identify the string
text_default (str): The text used by default when there are not translation in the selected language
"""
if not lang: if not lang:
lang=I18n.get_default_lang() lang=I18n.get_default_lang()
@ -67,7 +148,12 @@ class I18n:
@staticmethod @staticmethod
def extract_value(value): def extract_value(value):
"""Static method for get values from json lang array
Args:
value (json): Lang dict in json format
"""
value=json.loads(value) value=json.loads(value)
lang=I18n.get_default_lang() lang=I18n.get_default_lang()
@ -93,3 +179,4 @@ class I18n:
return json.dumps(arr_final) return json.dumps(arr_final)
common_pgettext=PGetText(__file__)

View file

@ -15,7 +15,7 @@ except:
key_encrypt=create_key_encrypt_256(30) key_encrypt=create_key_encrypt_256(30)
session_opts={'session.data_dir': 'sessions', 'session.type': 'file', 'session.path': 'paramecio'} session_opts={'session.data_dir': 'sessions', 'session.type': 'file', 'session.path': 'paramecio'}
from itsdangerous import JSONWebSignatureSerializer #from itsdangerous import JSONWebSignatureSerializer
from bottle import request, response from bottle import request, response
import os import os
try: try:

View file

@ -10,6 +10,8 @@ from settings import config
from importlib import import_module from importlib import import_module
def start(): def start():
"""Module for create new modules for paramecio
"""
parser=argparse.ArgumentParser(description='A tool for create new modules for paramecio') parser=argparse.ArgumentParser(description='A tool for create new modules for paramecio')
@ -99,11 +101,8 @@ def regenerate_modules_config():
print("-"*60) print("-"*60)
exit(1) exit(1)
f=open('./settings/modules.py', 'w') with open('./settings/modules.py', 'w') as f:
f.write("".join(modules))
f.write("".join(modules))
f.close()
if __name__=="__main__": if __name__=="__main__":
start() start()

View file

@ -1,21 +1,46 @@
"""
Parameciofm is a series of wrappers for Bottle.py, mako and others and construct a simple headless cms.
Copyright (C) 2024 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 paramecio.cromosoma.webmodel import PhangoField from paramecio.cromosoma.webmodel import PhangoField
from paramecio.cromosoma import coreforms from paramecio.cromosoma import coreforms
from paramecio.citoplasma.i18n import I18n from paramecio.citoplasma.i18n import I18n
#from bs4 import BeautifulSoup
import bleach
class IntegerField(PhangoField): class IntegerField(PhangoField):
"""Class that figure an integer sql type field. """Class that figure an integer sql type field.
Args:
name (str): The name of new field
size (int): The size of the new field in database. By default 11.
required (bool): Boolean for define if
""" """
def __init__(self, name, size=11, required=False): def __init__(self, name, size=11, required=False):
"""
Args:
name (str): The name of field
size (int): The size of the new field in database. By default 11.
required (bool): Boolean for define if field is required or not
"""
super(IntegerField, self).__init__(name, size, required) super(IntegerField, self).__init__(name, size, required)
self.default_value=0 self.default_value=0
self.type_sql='int({})'.format(self.size)
def check(self, value): def check(self, value):
@ -60,6 +85,11 @@ class BigIntegerField(IntegerField):
""" """
def __init__(self, name, size=11, required=False):
super().__init__(name, size, required)
self.type_sql='bigint({})'.format(self.size)
def get_type_sql(self): def get_type_sql(self):
"""Method for return the sql code for this type """Method for return the sql code for this type
@ -85,6 +115,7 @@ class FloatField(PhangoField):
self.error_default="The value is zero" self.error_default="The value is zero"
self.default_value=0 self.default_value=0
self.type_sql='float'.format(self.size)
def check(self, value): def check(self, value):
@ -123,26 +154,55 @@ class FloatField(PhangoField):
return 'FLOAT NOT NULL DEFAULT "0"' return 'FLOAT NOT NULL DEFAULT "0"'
class DecimalField(FloatField): class DecimalField(FloatField):
"""PhangoField field for Decimals fields."""
def __init__(self, name, size=11, required=False):
super().__init__(name, size, required)
self.type_sql='decimal(20,2)'
def get_type_sql(self): def get_type_sql(self):
return 'DECIMAL(20, 2) NOT NULL DEFAULT "0"' return 'DECIMAL(20, 2) NOT NULL DEFAULT "0"'
class DoubleField(FloatField): class DoubleField(FloatField):
"""PhangoField field for Double fields."""
def __init__(self, name, size=11, required=False):
super().__init__(name, size, required)
self.type_sql='double'
def get_type_sql(self): def get_type_sql(self):
return 'DOUBLE NOT NULL DEFAULT "0"' return 'DOUBLE NOT NULL DEFAULT "0"'
class CharField(PhangoField): class CharField(PhangoField):
"""Simple alias for PhangoField"""
pass pass
class TextField(PhangoField): class TextField(PhangoField):
"""Class used for text fields
Class used for text fields, use TEXT sql type for the this field.
"""
def __init__(self, name, required=False): def __init__(self, name, required=False):
"""Init TextField class different to standard PhangoField
Args:
name (str): The name of new field
required (bool): Boolean for define if field is required or not
Attributes:
set_default (str): Set if the value es NOT NULL or not
"""
super().__init__(name, 11, required) super().__init__(name, 11, required)
self.type_sql='text'
self.set_default='NOT NULL' self.set_default='NOT NULL'
def get_type_sql(self): def get_type_sql(self):
@ -153,19 +213,107 @@ class TextField(PhangoField):
return 'TEXT '+self.set_default return 'TEXT '+self.set_default
class HTMLField(TextField): class LongTextField(TextField):
"""Class used for long text fields (32 bits size, 4G)
Class used for text fields, use LONGTEXT sql type for the this field.
"""
def __init__(self, name, required=False): def __init__(self, name, required=False):
"""Init TextField class different to standard PhangoField
Args:
name (str): The name of new field
required (bool): Boolean for define if field is required or not
Attributes:
set_default (str): Set if the value es NOT NULL or not
"""
super().__init__(name, required) super().__init__(name, required)
self.type_sql='longtext'
def get_type_sql(self):
"""Method for return the sql code for this type
"""
return 'LONGTEXT '+self.set_default
class HTMLField(TextField):
"""Class used for html fields
Class used for html fields, use TEXT sql type for the this field because is children of TextField. In this method self.escape is used for convert " to &quot;
"""
def __init__(self, name, required=False):
"""Init HTMLField class different to standard PhangoField
Args:
name (str): The name of new field
required (bool): Boolean for define if field is required or not
Attributes:
trusted_tags (list): A list with safe tags.
"""
super().__init__(name, required)
self.trusted_tags=[]
def check(self, value): def check(self, value):
"""Check method for html values
return re.sub('<.*?script?>', '', value) This check method use beautifulsoap for clean and format html code
"""
# leach.clean('<p>"trial"</p><script></script>', tags=('p'))
"""
soup=BeautifulSoup(value, features='html.parser')
for tag in soup.findAll(True):
if tag.name not in self.trusted_tags:
tag.hidden=True
value=soup.renderContents().decode('utf-8')
if self.escape:
return value.replace('"', '&quot;')
else:
return value
"""
value=bleach.clean('<p>"trial"</p><script></script>', tags=self.trusted_tags)
if self.escape:
return value.replace('"', '&quot;')
else:
return value
class ForeignKeyField(IntegerField): class ForeignKeyField(IntegerField):
"""Subclass of IntegerField for create Foreign keys
A subclass of IntegerField used for create foreign keys in related tables.
"""
def __init__(self, name, related_table, size=11, required=False, identifier_field='id', named_field="id", select_fields=[]): def __init__(self, name, related_table, size=11, required=False, identifier_field='id', named_field="id", select_fields=[]):
"""
Args:
name (str): Name of field
related_table (WebModel): The table-model related with this foreign key
size (int): The size of the new field in database. By default 11.
required (bool): Boolean for define if field is required or not
identifier_field (str): The Id field name from related table
named_field (str): The field from related table used for identify the row seleted from related table
select_fields (list): A series of fields names from related
"""
super(ForeignKeyField, self).__init__(name, size, required) super(ForeignKeyField, self).__init__(name, size, required)
@ -192,7 +340,7 @@ class ForeignKeyField(IntegerField):
value=super().check(value) value=super().check(value)
if value=='0' or value==0: if value=='0' or value==0:
value='NULL' value=None
return value return value
@ -206,8 +354,15 @@ class ForeignKeyField(IntegerField):
class BooleanField(IntegerField): class BooleanField(IntegerField):
"""Field for boolean values
"""
def __init__(self, name, size=1): def __init__(self, name, size=1):
"""
Args:
name (str): Name of field
size (int): The size of the new field in database. By default 11.
"""
required=False required=False
@ -219,6 +374,8 @@ class BooleanField(IntegerField):
self.default_error="Need 0 or 1 value" self.default_error="Need 0 or 1 value"
self.default_value=0 self.default_value=0
self.type_sql='tinyint(1)'
def check(self, value): def check(self, value):
self.error=False self.error=False
@ -231,6 +388,7 @@ class BooleanField(IntegerField):
if value<0 or value>1: if value<0 or value>1:
self.txt_error=self.default_error self.txt_error=self.default_error
self.error=True self.error=True
value=0
except: except:

View file

@ -1,13 +1,55 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""
Parameciofm is a series of wrappers for Bottle.py, mako and others and construct a simple headless cms.
Copyright (C) 2024 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 collections import OrderedDict from collections import OrderedDict
from html import escape from html import escape
#Forms para python3 #Forms para python3
class BaseForm: class BaseForm:
"""The class used by all forms classes
BaseForm is the base class used for all form classes.
A form class is used for generate simple html forms, how input type, text type, hidden type, etc. PhangoField classes use this forms for generate automatically forms using GenerateAdminClass and others.
"""
def __init__(self, name, value): def __init__(self, name, value):
"""
Args:
name (str): The html name for this form
value (str): The default value of this html form.
Attributes:
label (str): Label used in functions how show_form that generate a complete html form from a form class list
name (str): Name of the html form.
default_value (mixed): The default value of the form. Equal to value in typical html form.
css (str): Used for add css classes to the html form
type (str): Variable used for conventional html forms with html tag <input>
field (PhangoField): Field related with this form. Used in PhangoField.
required (boolean): If form is required or not, used in functions that generate forms.
name_field_id (str): The html id for the html form. Used for html things.
help (str): A string with help text, used in functions that generate forms.
"""
self.label=name self.label=name
self.name=name self.name=name
@ -17,26 +59,45 @@ class BaseForm:
self.field=None self.field=None
self.required=False self.required=False
self.txt_error='' self.txt_error=''
self.error=False
self.name_field_id=self.name+'_form' self.name_field_id=self.name+'_form'
self.help='' self.help=''
self.placeholder=''
def form(self): def form(self):
"""Method for returm the html code of the form
"""
return '<input type="'+self.type+'" class="'+self.css+'" name="'+self.name+'" id="'+self.name_field_id+'" value="'+self.setform(self.default_value)+'" />' return '<input type="'+self.type+'" class="'+self.css+'" name="'+self.name+'" id="'+self.name_field_id+'" value="'+self.setform(self.default_value)+'" placeholder="'+self.placeholder+'" />'
def show_formatted(self, value): def show_formatted(self, value):
"""Method for show the value of form formatted
Args:
value (mixed): The value of field form
"""
return value return value
#Method for escape value for html input. DON'T CHANGE IF YOU DON'T KNOWN WHAT ARE YOU DOING #Method for escape value for html input. DON'T CHANGE IF YOU DON'T KNOWN WHAT ARE YOU DOING
def setform(self, value): def setform(self, value):
"""A method for set the value in the form for escape and other things
Args:
value (mixed): The value of field form for set
"""
value=str(value) value=str(value)
return value.replace('"', '&quot;').replace("'", '&#39;') return value.replace('"', '&quot;').replace("'", '&#39;')
def change_name(self, new_name): def change_name(self, new_name):
"""A method for change the default form html name of the field form
Args:
new_name (str): The new name of the form. Always is finished with _form suffix
"""
self.name=new_name self.name=new_name
@ -45,6 +106,8 @@ class BaseForm:
return "" return ""
class SimpleTextForm(BaseForm): class SimpleTextForm(BaseForm):
"""Form for simple text
"""
def __init__(self, name, value): def __init__(self, name, value):
super().__init__(name, value) super().__init__(name, value)
@ -56,6 +119,8 @@ class SimpleTextForm(BaseForm):
return super().form()+' '+self.after_text return super().form()+' '+self.after_text
class TextForm(BaseForm): class TextForm(BaseForm):
"""Form for simple text form
"""
def __init__(self, name, value): def __init__(self, name, value):
super(TextForm, self).__init__(name, value) super(TextForm, self).__init__(name, value)
@ -65,6 +130,8 @@ class TextForm(BaseForm):
return '<textarea class="'+self.css+'" name="'+self.name+'" id="'+self.name+'_form">'+self.setform(self.default_value)+'</textarea>' return '<textarea class="'+self.css+'" name="'+self.name+'" id="'+self.name+'_form">'+self.setform(self.default_value)+'</textarea>'
class PasswordForm(BaseForm): class PasswordForm(BaseForm):
"""Form for password forms
"""
def __init__(self, name, value, show_password=False): def __init__(self, name, value, show_password=False):
super(PasswordForm, self).__init__(name, value) super(PasswordForm, self).__init__(name, value)
@ -79,6 +146,8 @@ class PasswordForm(BaseForm):
return value return value
class HiddenForm(BaseForm): class HiddenForm(BaseForm):
"""Form for hidden forms
"""
def __init__(self, name, value): def __init__(self, name, value):
super(HiddenForm, self).__init__(name, value) super(HiddenForm, self).__init__(name, value)
@ -86,8 +155,16 @@ class HiddenForm(BaseForm):
class SelectForm(BaseForm): class SelectForm(BaseForm):
"""Form for select html form
"""
def __init__(self, name, value, elements=OrderedDict()): def __init__(self, name, value, elements=OrderedDict()):
"""
Args:
name (str): The html name for this form
value (str): The default value of this html form
elements (OrderedDict): An ordered dict with the keys(the form value) and text label. Example, if you have a OrderedDict how {'0': 'Value selected'} in a html select form you have the next result: <select name="name"><option value="0">Value selected</option></selected>
"""
super(SelectForm, self).__init__(name, value) super(SelectForm, self).__init__(name, value)
self.arr_select=elements self.arr_select=elements
@ -107,8 +184,19 @@ class SelectForm(BaseForm):
return the_form return the_form
class SelectModelForm(SelectForm): class SelectModelForm(SelectForm):
"""Form for select html using a webmodel how base for get the data
"""
def __init__(self, name, value, model, field_name, field_value, field_parent=None): def __init__(self, name, value, model, field_name, field_value, field_parent=None):
"""
Args:
name (str): The html name for this form
value (str): The default value of this html form.
model (WebModel): The webmodel used for get the data for arr_select
field_name (str): The field used for get the name of every option in select
field_value (str): The field used for get the value of every option in select
field_parent (int): If the model have parents or children, the value of this argument
"""
super(SelectModelForm, self).__init__(name, value) super(SelectModelForm, self).__init__(name, value)
try: try:
@ -129,12 +217,16 @@ class SelectModelForm(SelectForm):
def normal_form(self): def normal_form(self):
"""Method for prepare the form hierated from SelectForm class, without parents
Method for prepare the form hierated from SelectForm class getting data from database using model attribute.
"""
self.arr_select['']='' self.arr_select['']=''
with self.model.select([self.field_name, self.field_value], True) as cur: with self.model.select([self.field_name, self.field_value], True) as cur:
for arr_value in cur: for arr_value in cur:
#print(self.model.fields[self.field_name])
self.arr_select[arr_value[self.field_value]]=self.model.fields[self.field_name].show_formatted(arr_value[self.field_name]) self.arr_select[arr_value[self.field_value]]=self.model.fields[self.field_name].show_formatted(arr_value[self.field_name])
try: try:
@ -147,6 +239,10 @@ class SelectModelForm(SelectForm):
return super().form() return super().form()
def parent_form(self): def parent_form(self):
"""Method for prepare the form hierated from SelectForm class, with parents
Method for prepare the form hierated from SelectForm class getting data from database using model attribute.
"""
self.arr_select['']='' self.arr_select['']=''
@ -168,7 +264,8 @@ class SelectModelForm(SelectForm):
arr_son[arr_value[self.field_parent]]=[] arr_son[arr_value[self.field_parent]]=[]
arr_son[arr_value[self.field_parent]].append([arr_value[self.field_value], arr_value[self.field_name]]) if arr_value[self.field_value]!=self.model.model_id:
arr_son[arr_value[self.field_parent]].append([arr_value[self.field_value], self.model.fields[self.field_name].show_formatted(arr_value[self.field_name])])
self.create_son(0, arr_son) self.create_son(0, arr_son)
@ -186,6 +283,8 @@ class SelectModelForm(SelectForm):
def create_son(self, parent_id, arr_son, separator=''): def create_son(self, parent_id, arr_son, separator=''):
"""Recursive method for generate parents and children dictionary
"""
if parent_id in arr_son: if parent_id in arr_son:
for son in arr_son[parent_id]: for son in arr_son[parent_id]:

View file

@ -1,5 +1,25 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""
Parameciofm is a series of wrappers for Bottle.py, mako and others and construct a simple headless cms.
Copyright (C) 2024 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/>.
"""
import argparse import argparse
import os,traceback import os,traceback
import sys, inspect import sys, inspect
@ -10,11 +30,18 @@ from pathlib import Path
from colorama import init, Fore, Back, Style from colorama import init, Fore, Back, Style
from importlib import import_module, reload from importlib import import_module, reload
from paramecio.cromosoma.webmodel import WebModel from paramecio.cromosoma.webmodel import WebModel
from settings import config sys.path.insert(0, os.path.realpath('.'))
#from models import books try:
from settings import config
except:
#print('You need a settings directory with a paramecio2 configuration')
#sys.exit(1)
pass
def start(): def start():
"""Function for create and update mysql tables using webmodel classes and fields how source.
"""
connection=WebModel.connection() connection=WebModel.connection()
@ -103,6 +130,32 @@ def start():
new_tables=[x for x in tables if x not in table_exists] new_tables=[x for x in tables if x not in table_exists]
# Get foreignkeys
# SELECT * FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA='catalogdev_db' AND information_schema.TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'FOREIGN KEY' ;
foreignkey_fields={}
#| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME | CONSTRAINT_TYPE |
#+--------------------+-------------------+-----------------------------------------+---------------+-------------------+-----------------+
#| def | catalogdev_db | product_id_attributesIDX | catalogdev_db | attributes | FOREIGN KEY |
#WebModel.connections
db_name=WebModel.connections['default']['db']
with connection.query('SELECT * FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA=%s AND information_schema.TABLE_CONSTRAINTS.CONSTRAINT_TYPE = %s', [db_name, 'FOREIGN KEY']) as cursor:
for row in cursor:
if not row['TABLE_NAME'] in foreignkey_fields:
foreignkey_fields[row['TABLE_NAME']]=[]
foreignkey_fields[row['TABLE_NAME']]=row['CONSTRAINT_NAME'].replace('_{}IDX'.format(row['TABLE_NAME']), '')
pass
#If don't want order #If don't want order
#new_tables=set(tables)-set(table_exists) #new_tables=set(tables)-set(table_exists)
@ -160,6 +213,23 @@ def start():
print(Style.BRIGHT+"Checking old versions of model for find changes...") print(Style.BRIGHT+"Checking old versions of model for find changes...")
for table in tables: for table in tables:
#print(table)
table_fields={table: {}}
# Field | Type | Null | Key | Default | Extra |
#| id | int(11) | NO | PRI | NULL | auto_increment |
with connection.query('describe %s' % table) as cursor:
#all_fields=cursor.fetchall()
#print(all_fields)
for row in cursor:
table_fields[table][row['Field']]={'type': row['Type'], 'key': row['Key']}
pass
#print(table_fields)
#connection.query("") #connection.query("")
#Check if new table #Check if new table
@ -182,7 +252,8 @@ def start():
for f, v in WebModel.model[table].fields.items(): for f, v in WebModel.model[table].fields.items():
if not f in WebModel.model[old_table].fields: #if not f in WebModel.model[old_table].fields:
if not f in table_fields[table]:
fields_to_add.append(f) fields_to_add.append(f)
@ -226,13 +297,15 @@ def start():
#Add index #Add index
if v.indexed==True and v_old.indexed==False: #if v.indexed==True and v_old.indexed==False:
if v.indexed==True and table_fields[table][f]['key']!='MUL':
fields_to_add_index.append(f) fields_to_add_index.append(f)
changes+=1 changes+=1
if v.indexed==False and v_old.indexed==True: #if v.indexed==False and v_old.indexed==True:
if v.indexed==False and table_fields[table][f]['key']=='MUL' and v.foreignkey==False:
fields_to_delete_index.append(f) fields_to_delete_index.append(f)
@ -240,13 +313,15 @@ def start():
#Add unique #Add unique
if v.unique==True and v_old.unique==False: #if v.unique==True and v_old.unique==False:
if v.unique==True and table_fields[table][f]['key']!='UNI':
fields_to_add_unique.append(f) fields_to_add_unique.append(f)
changes+=1 changes+=1
if v.unique==False and v_old.unique==True: #if v.unique==False and v_old.unique==True:
if v.unique==False and table_fields[table][f]['key']=='UNI':
fields_to_delete_unique.append(f) fields_to_delete_unique.append(f)
@ -254,29 +329,43 @@ def start():
#Add constraint #Add constraint
if v.foreignkey==True and v_old.foreignkey==False: #if v.foreignkey==True and v_old.foreignkey==False:
if v.foreignkey==True and table_fields[table][f]['key']!='MUL':
fields_to_add_constraint.append(f) fields_to_add_constraint.append(f)
changes+=1 changes+=1
if v.foreignkey==False and v_old.foreignkey==True: #if v.foreignkey==False and v_old.foreignkey==True:
if v.foreignkey==False and table_fields[table][f]['key']=='MUL':
fields_to_delete_constraint.append(f) if table in foreignkey_fields:
changes+=1 if f in foreignkey_fields[table]:
for f, v in WebModel.model[old_table].fields.items(): fields_to_delete_constraint.append(f)
changes+=1
# Clean fields
#for f, v in WebModel.model[old_table].fields.items():
for f, v in table_fields[table].items():
if not f in WebModel.model[table].fields: if not f in WebModel.model[table].fields:
#Add constraint #Add constraint
if v.foreignkey==True: #if v.foreignkey==True:
fields_to_delete_constraint.append(f) if table in foreignkey_fields:
changes+=1 if f in foreignkey_fields[table]:
fields_to_delete_constraint.append(f)
changes+=1
fields_to_delete.append(f) fields_to_delete.append(f)

View file

@ -1,7 +1,7 @@
from paramecio.cromosoma.corefields import CharField from paramecio.cromosoma.corefields import CharField
import re import re
mail_pattern=re.compile("\w[\w\.-]*@\w[\w\.-]+\.\w+") mail_pattern=re.compile(r"\w[\w\.-]*@\w[\w\.-]+\.\w+")
class EmailField(CharField): class EmailField(CharField):

View file

@ -1,18 +1,54 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from paramecio.cromosoma.databases.sqlalchemy import SqlClass
from paramecio.cromosoma.coreforms import BaseForm, HiddenForm
import sys import sys
import re import re
import uuid import uuid
from importlib import import_module, reload from importlib import import_module, reload
from collections import OrderedDict from collections import OrderedDict
from paramecio.cromosoma.databases.sqlalchemy import SqlClass
from paramecio.cromosoma.coreforms import BaseForm, HiddenForm
import copy import copy
import traceback import traceback
class PhangoField: class PhangoField:
"""Base class for fields used in WebModel classes
PhangoField is a class with all elements and variables that you can imagine for a database mysql field in a table. You have many types similar to mysql field types.
"""
def __init__(self, name, size=255, required=False): def __init__(self, name, size=255, required=False):
"""
Args:
name (str): The name of the field
size (int): The size of sql field.
required (bool): If the field is required or not.
Attributes:
name (str): The name of the field
label (str): A label or generic name for use in text labels used for representate the field
required (bool): If the field is required or not.
size (int): The size of sql field.
protected (bool): If the field can be updated or not in WebModel update method.
quote_close (str): In old versions was used for get more protection for sql sentences
error (bool): If error in query, set to True.
txt_error (str): Variable where the basic text error is saved
model (str): The model where this component or field live
indexed (bool): Property used for set this field how indexed in the database table.
unique (bool): Property used for set this field how unique value in the database table.
foreignkey (bool): Simple property for make more easy identify foreignkeyfields.
default_value (str): Property that define the default value for this field
update (bool): Property that define if this field is in an update operation or insert operation
check_blank (bool): Property used for check if this value cannot change if is in blank and is filled
name_form(BaseForm): Define the form, when is created forms with create_forms you can change the properties of this class
escape (bool): Property that define if make escape in show_formatted. This property control the html transformation of <>" characters in html entities.If false, convert.
file_related (bool): File related: if the field have a file related, delete the file
extra_parameters (list): Extra parameters for the form related with this field
t (PTemplate): Template manager for the form if needed
error_default (str): Error text by default
show_formatted_value (bool): Show this value formatted
help (str): Value used for help strings in tooltips in forms
"""
# The name of the field in database table # The name of the field in database table
@ -50,7 +86,7 @@ class PhangoField:
self.txt_error="" self.txt_error=""
# Themodel where this component or field live # The model where this component or field live
self.model=None self.model=None
@ -100,7 +136,7 @@ class PhangoField:
# Error by default # Error by default
self.error_default='Error: field required' self.error_default='Error: '+self.name+' field required'
# Show this value formatted # Show this value formatted
@ -109,28 +145,29 @@ class PhangoField:
# Value used for help strings in tooltips in forms # Value used for help strings in tooltips in forms
self.help='' self.help=''
# This method is used for describe the new field in a sql language format. self.type_sql='varchar({})'.format(self.size)
def get_type_sql(self): def get_type_sql(self):
"""This method is used for describe the new field in a sql language format."""
return 'VARCHAR('+str(self.size)+') NOT NULL DEFAULT "'+self.default_value+'"' return 'VARCHAR('+str(self.size)+') NOT NULL DEFAULT "'+self.default_value+'"'
def show_formatted(self, value): def show_formatted(self, value):
"""Method for format the value to show in html or others text outputs"""
return value return value
# This method for check the value
def check(self, value): def check(self, value):
"""Method for check if value is valid for this type field"""
self.error=False self.error=False
self.txt_error='' self.txt_error=''
value=str(value).strip() value=str(value).strip()
#Minimal escape for prevent basic js injection.
if self.escape==False: if self.escape==False:
value=value.replace('<', '&lt;') value=value.replace('<', '&lt;')
@ -150,6 +187,7 @@ class PhangoField:
pass pass
def create_form(self): def create_form(self):
"""Create a BaseForm object for use in forms functions and methods"""
#self.name, self.default_value, #self.name, self.default_value,
final_parameters=copy.copy(self.extra_parameters) final_parameters=copy.copy(self.extra_parameters)
@ -165,6 +203,7 @@ class PhangoField:
return form return form
def change_form(self, new_form, parameters): def change_form(self, new_form, parameters):
"""Change the base form of the field and its parameters"""
self.name_form=new_form self.name_form=new_form
@ -174,6 +213,10 @@ class PhangoField:
pass pass
class PrimaryKeyField(PhangoField): class PrimaryKeyField(PhangoField):
"""Primary key field based in PhangoField.
This field is used for create a typical id field in a mariadb/mysql table
"""
def __init__(self, name, size=11, required=False): def __init__(self, name, size=11, required=False):
super(PrimaryKeyField, self).__init__(name, size, required) super(PrimaryKeyField, self).__init__(name, size, required)
@ -181,6 +224,7 @@ class PrimaryKeyField(PhangoField):
self.name_form=HiddenForm self.name_form=HiddenForm
self.required=False self.required=False
self.error_default="The value is zero" self.error_default="The value is zero"
self.type_sql='int({})'.format(self.size)
def check(self, value): def check(self, value):
@ -217,6 +261,24 @@ class PrimaryKeyField(PhangoField):
class WebModel: class WebModel:
"""The most important class for the framework
Webmodel is a class for create objects that represent models. This models are a mirage of SQL tables. You can create fields, add indexes, foreign keys, and more.
Attributes:
arr_sql_index (dict): Internal dict used for generate mysql index in fields
arr_sql_set_index (dict): Internal dict used for generate mysql index in fields
arr_sql_unique (dict): Internal dict used for generate mysql unique values in fields
arr_sql_set_unique (dict): Internal dict used for generate mysql unique values in fields
last_query (str): The last query execute by WebModel
connection_pool (list): A list used in older versions, deprecated
first_primary_key (PrimaryKeyField): Field used for primary field and create models in database.
model (OrderedDict): Dict used for internal things and create tables.
connections (dict): A dict with the configuration of the mysql connection. You can use this element in config.py. You set elements, normally "default" with typical elements how:
host: Database host, user: The username of mysql db, password: The password of user, db: The name of the db, charset: The charset of database, normally utf8, db_type: The db_type, possible values are mysqldb or pymysql, by default, pymysql.
connection_id (str): The id by default of the selected connection from connections.
"""
__slots__=('sqlclass', 'fields', 'forms') __slots__=('sqlclass', 'fields', 'forms')
@ -248,12 +310,53 @@ class WebModel:
@staticmethod @staticmethod
def connection(): def connection():
"""Static method for make a connection using SqlClass
Returns: Return a SqlClass connection for mysql db.
"""
return SqlClass(WebModel.connections['default']) return SqlClass(WebModel.connections['default'])
# Init the class # Init the class
def __init__(self, sqlclass=None, name_field_id="id"): def __init__(self, sqlclass=None, name_field_id="id"):
"""
Args:
sqlclass (SqlClass): The SqlClass connection used for the mysql db
name_field_id (str): The name of field id of this model/mysql table
Attributes:
name (str): The name of this model correspondient to the sql table name with lower string.
label (str): Descriptive name, first is used self.name how default.
label_general (str): Descriptive general name, first is used self.name how default.
name_field_id (str): The name of field id of this model/mysql table
fields (OrderedDict): A dict with the fields of model/table based in PhangoField objects.
fields_error (OrderedDict): A dict where the errors when check data fields are saved
related (list): A list where related fields are saved.
forms (OrderedDict): A dict where forms related with fields using how base BaseForm class are saved if you use self.create_forms() method.
errors (dict): A dict where generic errors are saved.
num_errors (int): Number of errors generated by the model on query methods.
query_error (str): If error in query, saved here.
values_query (list): Where the values for a sql query for filtering are saved.
conditions (list): A list used for define the sql conditions.
First element is the sql condition, Example: 'WHERE id=%s', and second element is the variable to substitute %s, example [1]. Complete example: ['WHERE id=%s', 1]
order_by (str): Internal variable used for set the sql order str. You don't shoud change this variable if yo don't know what are you doing.
limit (str): Internal variable used for set the sql limit str.
related_models_deleted (list): Internal variable used for delete tables from db.
required_save (str): Internal variable used for required fields defined in self.fields
primary_key (str): Default name of primary key field
yes_reset_conditions (bool): If True, methods how select and update reset self.conditions. If False, self.conditions is used in next select and update executions.
updated (bool): True if the model is used for update, False if the model is used for insert or other operations.
valid_fields (list): List with the fields validated for insert or update
last_id (int): The id of last inserted row.
distinct (str): Add DISTINCT keyword to self.select method.
post (dict): A simple dictionary where post values are saved for use of fields classes
files_delete (dict): A simple dictionary that save the fields that have files related. If i delete the row in database i need delete the files related
sqlclass (SqlClass): A sql_class used for connect to db.
show_formatted (bool): If True, by default all fields are showed with formatted value using show_formatted method of PhangoField classes and children in select method. If False, raw value is showed.
enctype (bool): If True, forms generated using this model are prepared for enctype=multipart/form-data A.K.A. upload files.
model_id (int): Variable where the actual row from model selected can be saved for different things.
"""
self.cached=WebModel.global_cached self.cached=WebModel.global_cached
@ -277,7 +380,7 @@ class WebModel:
# Errors of fields of the table, for safe thread reasons. # Errors of fields of the table, for safe thread reasons.
self.fields_error=OrderedDict() #self.fields_error=OrderedDict()
#The tables related with foreignkeyfield to this table #The tables related with foreignkeyfield to this table
@ -357,12 +460,18 @@ class WebModel:
self.enctype=False self.enctype=False
self.model_id=0
self.dummy=0 self.dummy=0
# A method for add the connection # A method for add the connection
def conn(self, sqlclass): def conn(self, sqlclass):
""" Method for get the SqlClass object and prepare sql variables
Args:
sqlclass (SqlClass): A SqlClass object that present the db connection
"""
self.sqlclass=sqlclass self.sqlclass=sqlclass
# Reset conditions # Reset conditions
@ -378,6 +487,11 @@ class WebModel:
# A method for change the name of table # A method for change the name of table
def change_name(self, name): def change_name(self, name):
""" A method for change the name of table
Args;
name (str): The new name of table
"""
self.name=name self.name=name
@ -386,6 +500,7 @@ class WebModel:
# A method where create the new fields of this model # A method where create the new fields of this model
def create_fields(self): def create_fields(self):
"""Dummy method for use in children classes for add fields"""
#print([i for i in dir(self.__class__) if i[:1] != '_']) #print([i for i in dir(self.__class__) if i[:1] != '_'])
#print(dir(self)) #print(dir(self))
@ -395,6 +510,14 @@ class WebModel:
# A method for register the fields # A method for register the fields
def register(self, field_model, required=False): def register(self, field_model, required=False):
"""A method for register the fields in model class
With this method, your register your fields in the model, inside self.fields attribute. Fields are used for build the query for get the data from the sql table.
Args:
field_model (PhangoField): PhangoField object for add to model
required (bool): If True, the field is required when you insert or update a item row in table model. If False, the field is not required. If field is not required and checking fail, the model update/insert ignore it.
"""
#self.fields_required[field_model]=field_model.required #self.fields_required[field_model]=field_model.required
@ -436,6 +559,13 @@ class WebModel:
# Method for make queries # Method for make queries
def query(self, str_query, args=[], connection_id='default'): def query(self, str_query, args=[], connection_id='default'):
"""Method for make typical sql query to db
Args:
str_query (str): The str query. Use the typical format of sql python drivers, example: select * from my_table WHERE id=%s.
args (list): The arguments to substitute %s characters of the strings. The list must sequential with %s characters in the string.
connection_id (str): The connection data used for this connection, by default is "default".
"""
self.connect_to_db() self.connect_to_db()
return self.sqlclass.query(str_query, args, connection_id) return self.sqlclass.query(str_query, args, connection_id)
@ -443,6 +573,8 @@ class WebModel:
# Method for clean fields # Method for clean fields
def clean_fields(self): def clean_fields(self):
"""Method for delete fields from self.fields dict"""
clean=self.fields_to_clean clean=self.fields_to_clean
for field in self.fields_to_clean: for field in self.fields_to_clean:
del self.fields[field] del self.fields[field]
@ -451,6 +583,14 @@ class WebModel:
# External agent define if the update is in code or from external source, how a form. # External agent define if the update is in code or from external source, how a form.
def insert(self, dict_values, external_agent=True): def insert(self, dict_values, external_agent=True):
"""Insert method, for insert a row in database using a dictionary
This method is a shortcut for typical sql insert sentence.
Args:
dict_values (dict): A dict with the name of the fields how defined in PhangoField for the keys, and values for the values designed for every field.
external_agent (bool): External agent define if the update is in code or from external source, how a form.
"""
self.clean_fields() self.clean_fields()
@ -466,7 +606,6 @@ class WebModel:
try: try:
#fields, values, update_values=self.check_all_fields(dict_values, external_agent)
arr_return=self.check_all_fields(dict_values, external_agent) arr_return=self.check_all_fields(dict_values, external_agent)
if arr_return: if arr_return:
@ -476,11 +615,10 @@ class WebModel:
else: else:
return False return False
except: except Exception as e:
self.query_error='Cannot insert the new row' self.query_error='Cannot insert the new row '+str(e)
print(sys.exc_info()[0]) #print(sys.exc_info()[0])
raise return False
#return False
c=len(values) c=len(values)
@ -509,6 +647,15 @@ class WebModel:
# Update method. For update one or many rows. # Update method. For update one or many rows.
def update(self, dict_values, external_agent=True): def update(self, dict_values, external_agent=True):
"""Upate method, for update a row in database using a dictionary
This method is a shortcut for typical sql update sentence.
Args:
dict_values (dict): A dict with the name of the fields how defined in PhangoField for the keys, and values for the values designed for every field.
external_agent (bool): External agent define if the update is in code or from external source, how a form.
"""
self.clean_fields() self.clean_fields()
@ -583,7 +730,7 @@ class WebModel:
""" """
def reset_conditions(self): def reset_conditions(self):
"""Method for reset self.conditions to default values"""
self.conditions=["WHERE 1=1", []] self.conditions=["WHERE 1=1", []]
self.limit='' self.limit=''
@ -591,6 +738,19 @@ class WebModel:
#Type assoc can be assoc for return dictionaries #Type assoc can be assoc for return dictionaries
def select(self, arr_select=[], raw_query=False): def select(self, arr_select=[], raw_query=False):
"""A method for select fields from a table in db. Support for foreignkeys.
This method is a shortcut for typical sql select sentence. You can select multiple tables using ForeignKeyField class.
Args:
arr_select (dict): A list with the name of the fields how defined in PhangoField.
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
Returns:
false (bool): If return false, the db connection is down.
sql_cursor (cursor): Return cursor db for get data using loops or other if operation is successful, if not, return False.
"""
self.clean_fields() self.clean_fields()
@ -706,14 +866,29 @@ class WebModel:
# Show results in a dictionary # Show results in a dictionary
def fetch(self, cursor): def fetch(self, cursor):
""" Simple method for get a row from db using cursor
Args:
cursor (Db cursor): A typical db cursor of python sql interface standard.
Returns:
row (dict): Return a dictionary with the row selected.
"""
return cursor.fetchone() return cursor.fetchone()
def insert_id(self): def insert_id(self):
"""Method for get the id from last row inserted in table"""
return self.last_id return self.last_id
def element_exists(self, id): def element_exists(self, id):
"""Check if exist row with id in db
Args:
id (int): The id of the row to search.
"""
self.conditions=['WHERE `'+self.name_field_id+'`=%s', [id]] self.conditions=['WHERE `'+self.name_field_id+'`=%s', [id]]
@ -731,6 +906,15 @@ class WebModel:
pass pass
def select_a_row(self, id, fields_selected=[], raw_query=0): def select_a_row(self, id, fields_selected=[], raw_query=0):
"""Shortcut for get a simple row from a query
Args:
fields_selected (dict): A list with the name of the fields how defined in PhangoField.
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
Returns:
row (dict): Returns dict with the row values.
"""
self.conditions=['WHERE `'+self.name+'`.`'+self.name_field_id+'`=%s', [id]] self.conditions=['WHERE `'+self.name+'`.`'+self.name_field_id+'`=%s', [id]]
@ -752,6 +936,15 @@ class WebModel:
return row return row
def select_a_row_where(self, fields_selected=[], raw_query=0, begin=0): def select_a_row_where(self, fields_selected=[], raw_query=0, begin=0):
"""Shortcut for get a simple row from a query using self.conditions
Args:
fields_selected (dict): A list with the name of the fields how defined in PhangoField.
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
Returns:
row (dict): Returns dict with the row values.
"""
self.limit="limit "+str(begin)+", 1" self.limit="limit "+str(begin)+", 1"
@ -770,6 +963,16 @@ class WebModel:
def select_to_array(self, fields_selected=[], raw_query=0): def select_to_array(self, fields_selected=[], raw_query=0):
"""Shortcut for get a a list of rows from select sql query
Args:
fields_selected (dict): A list with the name of the fields how defined in PhangoField.
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
Returns:
row_values (dict): Returns dict with the row values.
"""
if len(fields_selected)==0: if len(fields_selected)==0:
fields_selected=list(self.fields.keys()) fields_selected=list(self.fields.keys())
@ -810,6 +1013,16 @@ class WebModel:
def select_to_dict(self, fields_selected=[], raw_query=0, integer=True): def select_to_dict(self, fields_selected=[], raw_query=0, integer=True):
"""Shortcut for get a dict of rows from select sql query
Args:
fields_selected (dict): A list with the name of the fields how defined in PhangoField.
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
Returns:
row (dict): Returns dict with the row values.
"""
if integer: if integer:
def check_index(index): def check_index(index):
return index return index
@ -855,6 +1068,15 @@ class WebModel:
# A method por count num rows affected for sql conditions # A method por count num rows affected for sql conditions
def select_count(self, field_to_count='id', raw_query=True): def select_count(self, field_to_count='id', raw_query=True):
"""Method for get a typical sql count using conditions
Args:
field_to_count (str): The field
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
Returns:
num_elecments (int): Returns the number of elements selected.
"""
# Connect to db # Connect to db
@ -901,12 +1123,19 @@ class WebModel:
# A method for delete rows using sql conditions # A method for delete rows using sql conditions
def delete(self): def delete(self):
"""Method for delete a series of rows using conditions
Returns:
bool (bool): If delete is successfully, return True, if not, return False.
"""
#self.connect_to_db() #self.connect_to_db()
#Need delete rows from other related tables save in self.related_models_deleted #Need delete rows from other related tables save in self.related_models_deleted
sql=("delete from `"+self.name+"` "+self.conditions[0]+' '+self.order_by+' '+self.limit).strip() #+' '+self.order_by+' '+self.limit
sql=("delete from `"+self.name+"` "+self.conditions[0]).strip()
result=self.query(sql, self.conditions[1], self.connection_id) result=self.query(sql, self.conditions[1], self.connection_id)
@ -923,6 +1152,15 @@ class WebModel:
return False return False
def set_conditions(self, sql_text, values:list) -> object: def set_conditions(self, sql_text, values:list) -> object:
"""Method for set conditions for a typical sql query
Args:
sql_text (str): The sql text with the conditions, Example: WHERE id=%s
values (list): A list with values for substitute %s characters for the real values filtered for not allow sql injections.
Returns:
Return the same object with self.conditions modified.
"""
self.conditions=[sql_text, values] self.conditions=[sql_text, values]
@ -930,6 +1168,15 @@ class WebModel:
@staticmethod @staticmethod
def check_in_list(in_list): def check_in_list(in_list):
"""Method for convert values to int for use in IN (1,2,3) sql sentences.
Args:
in_list (list): List with numbers items.
Returns:
sql_filtered (str): with (1,2,3) sql sentence filtered.
"""
for x in range(0, len(in_list)): for x in range(0, len(in_list)):
try: try:
@ -939,6 +1186,15 @@ class WebModel:
return '('+', '.join(in_list)+')' return '('+', '.join(in_list)+')'
def check_in_list_str(self, field, in_list): def check_in_list_str(self, field, in_list):
"""Method for convert values to int for use in IN (value1, value2, value3) sql sentences.
Args:
field (PhangoField): The PhangoField used for check the values of in_list
in_list (list): List with value items.
Returns:
sql_filtered (str): (value1, value2, value3) sql sentence filtered.
"""
for x in range(0, len(in_list)): for x in range(0, len(in_list)):
in_list[x]=str(self.fields[field].check(in_list[x])) in_list[x]=str(self.fields[field].check(in_list[x]))
@ -946,6 +1202,15 @@ class WebModel:
return '("'+'", "'.join(in_list)+'")' return '("'+'", "'.join(in_list)+'")'
def set_order(self, order:dict) -> object: def set_order(self, order:dict) -> object:
""" Method for set and complete the query with "order by" sentences.
Args:
order (dict): A dict with a field name how key, and 0 or 1 how values. 0 define order how ASC, 1 define order how DESC.
Returns:
Returns the same object for execute a query after set_order declaration.
"""
arr_order=[] arr_order=[]
arr_order.append('ASC') arr_order.append('ASC')
@ -964,6 +1229,15 @@ class WebModel:
return self return self
def set_limit(self, limit: tuple) -> None: def set_limit(self, limit: tuple) -> None:
""" Method for set and complete the query with "limit" sentences.
Args:
limit (tuple): A tuple with one or two elements. If one element, example (1), the result is "LIMIT first_element", if two elements, example (1,2), the result is "LIMIT first_element, two_element"
Returns:
Returns the same object for execute a query after set_order declaration.
"""
limit[0]=int(limit[0]) limit[0]=int(limit[0])
@ -979,6 +1253,7 @@ class WebModel:
# Method for create sql tables # Method for create sql tables
def create_table(self): def create_table(self):
"""Method for create a table from this model object"""
#self.connect_to_db() #self.connect_to_db()
@ -1089,6 +1364,12 @@ class WebModel:
# Method for drop sql tables and related # Method for drop sql tables and related
def drop(self): def drop(self):
"""Method for drop a table based in this model
Returns:
sql_str (str): Return the sql query for drop the table represented by this model
"""
return self.query('DROP TABLE '+self.name, [], self.connection_id) return self.query('DROP TABLE '+self.name, [], self.connection_id)
#Return an array with all fields #Return an array with all fields
@ -1099,6 +1380,20 @@ class WebModel:
#Check of all fields in table. #Check of all fields in table.
def check_all_fields(self, dict_values, external_agent, yes_update=False, errors_set="insert"): def check_all_fields(self, dict_values, external_agent, yes_update=False, errors_set="insert"):
"""Method for check all fields of a model for insert or update a row in table db.
Args:
dict_values (dict): The dict of values to check
external_agent (bool): If True, the query is considered manipulated by external agent and the checks are stricts, if not, checks are not stricts
yes_update (bool): If True, the check need be done for update sql sentence, if False, the check is done for insert sql sentence
errors_set (str): If insert value, the errors are set for insert sql statement, if update value, then the errors are set for update sql statement.
Returns:
wrong (bool): Return False if checking is wrong. If not False returns a tuple with fields filtered, original values as values and values filtered how update_values
fields (list): list with fields
values (dict): dict with values
update_values (dict): dict with updated values with checking
"""
fields=[] fields=[]
values=[] values=[]
@ -1227,6 +1522,7 @@ class WebModel:
#Reset the require field in fields #Reset the require field in fields
def reset_require(self): def reset_require(self):
"""Reset the require attribute in fields"""
for k, v in self.fields.items(): for k, v in self.fields.items():
@ -1237,6 +1533,7 @@ class WebModel:
#Reload the require field in fields #Reload the require field in fields
def reload_require(self): def reload_require(self):
"""Reload the require field in fields"""
for k,r in self.fields.items(): for k,r in self.fields.items():
self.fields[k].required=r self.fields[k].required=r
@ -1244,6 +1541,7 @@ class WebModel:
#Choose all fields to updated #Choose all fields to updated
def set_valid_fields(self, fields={}): def set_valid_fields(self, fields={}):
"""Choose all fields to updated"""
if len(fields)==0: if len(fields)==0:
fields=self.fields.keys() fields=self.fields.keys()
@ -1253,6 +1551,7 @@ class WebModel:
#Create a form based in table. #Create a form based in table.
def create_forms(self, arr_fields=[]): def create_forms(self, arr_fields=[]):
"""Create a form based in table."""
self.forms=OrderedDict() self.forms=OrderedDict()
@ -1270,6 +1569,12 @@ class WebModel:
return arr_fields return arr_fields
def create_form_after(self, form_after, new_form): def create_form_after(self, form_after, new_form):
"""Create form after other form
Args:
form_after (str): The name of the form where the new form is located next
new_form (BaseForm): The BaseForm or derivated class used for create the new form.
"""
new_dict=OrderedDict() new_dict=OrderedDict()
@ -1281,6 +1586,11 @@ class WebModel:
self.forms=new_dict self.forms=new_dict
def show_errors(self): def show_errors(self):
"""Get all errors of model last operation.
Returns:
error_txt (str): A string with all errors.
"""
arr_error=[] arr_error=[]
error_txt='' error_txt=''
@ -1301,6 +1611,11 @@ class WebModel:
return error_txt return error_txt
def collect_errors(self): def collect_errors(self):
"""Get all errors and save in dictionary
Returns:
errors (dict): Return a dict where the key is the field where the error exists and value is the error text.
"""
arr_error= {} arr_error= {}
error_txt='' error_txt=''
@ -1318,12 +1633,15 @@ class WebModel:
return arr_error return arr_error
def safe_query(self): def safe_query(self):
"""Method for reset require for fields.
With this method you can make queries without real checks, except mysql injection safe variables."""
self.create_forms() self.create_forms()
self.reset_require() self.reset_require()
def close(self): def close(self):
"""Method for close sqlclass db connection"""
self.sqlclass.close() self.sqlclass.close()
@ -1341,6 +1659,7 @@ class WebModel:
#del sqlclass.connection[key] #del sqlclass.connection[key]
@staticmethod @staticmethod
def escape_sql(value): def escape_sql(value):
"""Manual escape for sql, you shouldn't use it"""
value=str(value) value=str(value)
@ -1354,6 +1673,11 @@ class WebModel:
# Set post values from a post array # Set post values from a post array
def set_post_values(self, post): def set_post_values(self, post):
"""Prepare a dict with values using fields keys how base
Returns:
post (dict): Return a dict with values without checking anything.
"""
for k in self.fields.keys(): for k in self.fields.keys():

View file

@ -6,7 +6,7 @@ from settings import config
from mimetypes import guess_type from mimetypes import guess_type
from paramecio.cromosoma.webmodel import WebModel from paramecio.cromosoma.webmodel import WebModel
from paramecio.citoplasma.datetime import set_timezone from paramecio.citoplasma.datetime import set_timezone
from itsdangerous import JSONWebSignatureSerializer #from itsdangerous import JSONWebSignatureSerializer
from paramecio.citoplasma.keyutils import create_key_encrypt, create_key_encrypt_256, create_key from paramecio.citoplasma.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
from paramecio.wsgiapp import app from paramecio.wsgiapp import app
#from paramecio.citoplasma.sessions import after_session #from paramecio.citoplasma.sessions import after_session

View file

@ -6,7 +6,7 @@ from paramecio.modules.admin.models.admin import UserAdmin
from paramecio.citoplasma.i18n import load_lang, I18n from paramecio.citoplasma.i18n import load_lang, I18n
from paramecio.citoplasma.urls import make_url, add_get_parameters, redirect from paramecio.citoplasma.urls import make_url, add_get_parameters, redirect
from paramecio.citoplasma.sessions import get_session, generate_session from paramecio.citoplasma.sessions import get_session, generate_session
from bottle import get,post,response,request from bottle import get,post,response,request, Bottle
from settings import config from settings import config
from settings import config_admin from settings import config_admin
from paramecio.citoplasma.adminutils import get_menu, get_language, make_admin_url, check_login, login_model from paramecio.citoplasma.adminutils import get_menu, get_language, make_admin_url, check_login, login_model

View file

@ -38,8 +38,9 @@ theme='default'
#base_modules="modules" #base_modules="modules"
#Type server used for connect to the internet... #Type server used for connect to the internet...
# With bottle 0.13 is better use gunicorn
server_used="cherrypy" server_used="gunicorn"
#Module showed in index #Module showed in index
@ -135,11 +136,3 @@ yes_static=True
#Database mysql config, if you want anything... #Database mysql config, if you want anything...
#WebModel.connections={'default': {'name': 'default', 'host': 'localhost', 'user': 'root', 'password': '', 'db': 'example', 'charset': 'utf8mb4', 'set_connection': False} } #WebModel.connections={'default': {'name': 'default', 'host': 'localhost', 'user': 'root', 'password': '', 'db': 'example', 'charset': 'utf8mb4', 'set_connection': False} }
#Please dont change this line if you don't know what you are doing
for module in modules:
module+='.settings.config'
module_path=module.replace('.','/')+'.py'
if os.path.isfile(module_path):
mod=import_module(module)

View file

@ -1,3 +1,5 @@
# Principal app
import bottle import bottle
app=bottle.app() app=bottle.app()

View file

@ -5,32 +5,32 @@ import os
from setuptools import setup, find_packages from setuptools import setup, find_packages
if sys.version_info < (3, 3): if sys.version_info < (3, 8):
raise NotImplementedError("Sorry, you need at least Python 3.3 for use paramecio.") raise NotImplementedError("Sorry, you need at least Python 3.8 for use paramecio.")
#import paramecio #import paramecio
# Pillow should be installed after if you need ImageField # Pillow should be installed after if you need ImageField
# If you install passlib and bcrypt, the password system will use bcrypt by default, if not, will use native crypt libc # If you install passlib and bcrypt, the password system will use bcrypt by default, if not, will use native crypt libc
setup(name='paramecio', setup(name='paramecio',
version='1.5.0', version='1.6.0',
description='Simple Web Framework based in bottlepy and Mako.', description='Simple Web Framework based in bottlepy and Mako.',
long_description='This framework is a simple framework used for create web apps. Paramecio is modular and fast. By default have a module called admin that can be used for create admin sites', long_description='This framework is a simple framework used for create web apps. Paramecio is modular and fast. By default have a module called admin that can be used for create admin sites',
author='Antonio de la Rosa Caballero', author='Antonio de la Rosa Caballero',
author_email='antonio.delarosa@coesinfo.com', author_email='antonio.delarosa@salirdelhoyo.com',
url='https://github.com/paramecio/parameciofm/', url='https://git.cuchulu.com/paramecio/parameciofm/',
packages=['paramecio', 'paramecio.i18n', 'paramecio.settings'], packages=['paramecio', 'paramecio.i18n', 'paramecio.settings'],
include_package_data=True, include_package_data=True,
install_requires=['bottle', 'mako', 'pymysql', 'sqlalchemy', install_requires=['bottle', 'mako', 'pymysql', 'sqlalchemy', 'oslo.concurrency', 'itsdangerous', 'colorama','cherrypy', 'arrow'],
'oslo.concurrency', 'itsdangerous', 'colorama','cherrypy', 'arrow'],
entry_points={'console_scripts': [ entry_points={'console_scripts': [
'paramecio = paramecio.console:start', 'paramecio = paramecio.console:start',
'parameciodb = paramecio.cromosoma.dbamin.start'
]}, ]},
license='GPLV3', license='AGPLV3',
platforms = 'any', platforms = 'any',
classifiers=['Development Status :: 1 - Beta', classifiers=['Development Status :: 1 - Beta',
'Intended Audience :: Developers', 'Intended Audience :: Developers',
'License :: OSI Approved :: GPLV2 License', 'License :: OSI Approved :: AGPLV3 License',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries',
'Topic :: Internet :: WWW/HTTP :: HTTP Servers', 'Topic :: Internet :: WWW/HTTP :: HTTP Servers',
'Topic :: Internet :: WWW/HTTP :: WSGI', 'Topic :: Internet :: WWW/HTTP :: WSGI',
@ -38,9 +38,6 @@ setup(name='paramecio',
'Topic :: Internet :: WWW/HTTP :: WSGI :: Server', 'Topic :: Internet :: WWW/HTTP :: WSGI :: Server',
'Topic :: Software Development :: Libraries :: Application Frameworks', 'Topic :: Software Development :: Libraries :: Application Frameworks',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.8'
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6'
], ],
) )