diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..5309500
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "paramecio/modules/admin/media/js/jsutils"]
+ path = paramecio/modules/admin/media/js/jsutils
+ url = git@git.cuchulu.com:paramecio/jsutils.git
diff --git a/LICENSE.txt b/LICENSE.txt
index 10926e8..be3f7b2 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,23 +1,21 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
- Copyright (C) 2007 Free Software Foundation, Inc.
+ Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
+our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
+software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
@@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
The precise terms and conditions for copying, distribution and
modification follow.
@@ -72,7 +60,7 @@ modification follow.
0. Definitions.
- "This License" refers to version 3 of the GNU General Public License.
+ "This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
@@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
- 13. Use with the GNU Affero General Public License.
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
+under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
+Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
+GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
+versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
@@ -635,41 +633,29 @@ the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
+ 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 General Public License for more details.
+ GNU Affero General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
-
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
diff --git a/MANIFEST.in b/MANIFEST.in
index 22fb9d9..8497f71 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -2,8 +2,7 @@ include paramecio/*.py
include README.md
include LICENSE
include REQUIREMENTS
-recursive-include paramecio/citoplasma *
+recursive-include paramecio/libraries *
recursive-include paramecio/frontend *
-recursive-include paramecio/cromosoma *
recursive-include paramecio/settings *
-recursive-include paramecio/modules *
\ No newline at end of file
+recursive-include paramecio/modules *
diff --git a/README.md b/README.md
index 3af5b9d..7d67b25 100644
--- a/README.md
+++ b/README.md
@@ -12,22 +12,19 @@ Also, you need the next software installed in your os:
### Python 3.4 or later.
-Paramecio should work fine in 3.3 but is tested in 3.4 and 3.5 python 3 versions.
+Paramecio should work fine since python 3.6 but is tested in 3.12 and 3.13 python 3 versions.
In Debian and Ubuntu you can install Python 3 using the next command: `apt-get install python3`.
-In Fedora and other Red Hat derived distros you can use `yum install python3`. In RedHat/Centos 6 or 7 you need install [Ius Repos](https://ius.io/GettingStarted/) for get sane versions of python3. In new Fedora versions you can use dnf, a drop-in replacement for yum: `dnf install python3`.
+In Fedora and other Red Hat derived distros you can use `dnf install python3`.
### MySQL or MariaDB database servers.
-MariaDB 10.0 and later are recommended.
+MariaDB 10.6 and later are recommended.
In Debian and Ubuntu you can install MariaDB using the next command: `apt-get install mariadb-server`.
-In Fedora and other Red Hat derived distros you can use `yum install mariadb-server`.
-In RedHat/Centos 6 probably you need install adittional repositories for get latest versions of mariadb, but with MySQL 5.5, Paramecio should work fine.
-
-When you will install the mysql server, you should create a new user and database for Paramecio.
+In Fedora and other Red Hat derived distros you can use `dnf install mariadb-server`.
### Pip
@@ -35,7 +32,7 @@ Pip is the package manager of python. You can use the package manager of your os
In Debian and Ubuntu you can install pip using the next command: `apt-get install python3-pip`.
-In Fedora and other Red Hat derived distros you can use `yum install python3-pip`. Of course, the command can change if you use Centos 6/7 with **Ius repos**.
+In Fedora and other Red Hat derived distros you can use `dnf install python3-pip`.
### Git
@@ -43,7 +40,7 @@ In Fedora and other Red Hat derived distros you can use `yum install python3-pip
In Debian and Ubuntu you can install git using the next command: `apt-get install git`.
-In Fedora and other Red Hat derived distros you can use `yum install git` or `dnf install git` in last fedora versions.
+In Fedora and other Red Hat derived distros you can use `dnf install git`.
## Install Paramecio Framework
@@ -59,8 +56,6 @@ This command will install in your server paramecio framework with its dependenci
When Paramecio finish the installing, you can create your first paramecio site with `paramecio` command.
-> If you install passlib and bcrypt python modules, your paramecio install will use bcrypt algorithm for crypt system passwords. If not, default system implementation crypt algorithm (normally the more strong algorithm available in the system) will be used.
-
### Tipical errors
If you get an error in your installation of dependencies how MarkupSafe or SqlAlchemy, please install gcc or install manually mako and sqlalchemy with your system package manager. For example, for debian and ubuntu:
diff --git a/paramecio/index.py b/paramecio/app.py
similarity index 83%
rename from paramecio/index.py
rename to paramecio/app.py
index f57610a..9792fdf 100644
--- a/paramecio/index.py
+++ b/paramecio/app.py
@@ -4,18 +4,18 @@ from bottle import route, get, post, run, default_app, abort, request, response,
from settings import config
#from beaker.middleware import SessionMiddleware
from mimetypes import guess_type
-from paramecio.cromosoma.webmodel import WebModel
-from paramecio.citoplasma.datetime import set_timezone
-from itsdangerous import JSONWebSignatureSerializer
-from paramecio.citoplasma.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
+from paramecio.libraries.db.webmodel import WebModel
+from paramecio.libraries.datetime import set_timezone
+#from itsdangerous import JSONWebSignatureSerializer
+from paramecio.libraries.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
from paramecio.wsgiapp import app
-#from paramecio.citoplasma.sessions import after_session
+#from paramecio.libraries.sessions import after_session
modules_pass=False
#app.reset()
-#from paramecio.citoplasma.sessions import generate_session
+#from paramecio.libraries.sessions import generate_session
#Prepare links for static.
#WARNING: only use this feature in development, not in production.
@@ -43,6 +43,10 @@ if hasattr(config, 'error_reporting'):
error_reporting=config.error_reporting
def prepare_app():
+
+ # In mod_wsgi, make strange thing with reloading.
+
+ app.reset()
def print_memory():
print(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)
@@ -52,7 +56,7 @@ def prepare_app():
module_loaded=None
#Getting paths for loaded modules for use in media load files
-
+ """
for module in config.modules:
#controller_path=sys.modules[module]
@@ -62,6 +66,7 @@ def prepare_app():
base_module=module.split('.')[-1]
arr_module_path[base_module]=controller_base
+ """
#app.add_hook('before_request', print_memory)
"""
@@ -80,11 +85,11 @@ def prepare_app():
app_mounts={}
for key_app, added_app in config.apps.items():
-
- controller_path=import_module(added_app[0])
-
- controller_base=os.path.dirname(controller_path.__file__)
+ controller_path=import_module(added_app[0])
+
+ controller_base=os.path.dirname(controller_path.__file__)
+ print(controller_base)
dir_controllers=os.listdir(controller_base)
for controller in dir_controllers:
@@ -97,23 +102,25 @@ def prepare_app():
a=import_module(module_app)
- app_name=getattr(a, added_app[1])
+ if added_app[1]!='':
+
+ app_name=getattr(a, added_app[1])
- #app.register_blueprint(app_name, url_prefix=added_app[2])
-
- #app.mount(added_app[2], app_name)
- app_mounts[added_app[2]]=app_name
+ app_mounts[added_app[2]]=app_name
arr_module_path[key_app]=os.path.dirname(sys.modules[module_app].__file__)
for k_app,v_app in app_mounts.items():
-
- app.mount(k_app, v_app)
+ #print(k_app)
+ if k_app!='/':
+ app.mount(k_app, v_app)
+ elif k_app!='':
+ app.merge(v_app)
set_timezone()
if error_reporting:
- from paramecio.citoplasma.error_reporting import ErrorReportingPlugin
+ from paramecio.libraries.error_reporting import ErrorReportingPlugin
app.install(ErrorReportingPlugin())
@@ -144,20 +151,21 @@ application=app
try:
- from settings import modules
-
+ #from settings import modules
prepare_app()
except:
-
+
+ app.reset()
+
@app.route('/')
def catch_errors(all='/'):
try:
from pathlib import Path
- from settings import modules
+ #from settings import modules
import time
prepare_app()
- p=Path('index.py')
+ p=Path('app.py')
p.touch()
time.sleep(1)
except:
diff --git a/paramecio/citoplasma/i18n.py b/paramecio/citoplasma/i18n.py
deleted file mode 100644
index a205934..0000000
--- a/paramecio/citoplasma/i18n.py
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/usr/bin/env python3
-
-from importlib import import_module
-from paramecio.citoplasma.sessions import get_session
-import json
-from bottle import request
-
-yes_session=False
-
-i18n_module={}
-
-def load_lang(*args):
-
- for module in args:
-
- lang_path=module[0]+'.i18n.'+module[1]
-
- try:
-
- i18n_module[lang_path]=import_module(lang_path)
-
- pass
-
- except:
- pass
-
- # here load the language
-
-
-class I18n:
-
- default_lang='en-US'
-
- dict_i18n=['en-US', 'es-ES']
-
- l={}
-
- #@staticmethod
- #def set_lang(code_lang):
- # if default_lang
-
-
- @staticmethod
- def get_default_lang():
-
- lang=I18n.default_lang
-
- s=get_session()
-
- lang=s.get('lang', lang)
-
- return lang
-
- @staticmethod
- def lang(module, symbol, text_default, lang=None):
-
- if not lang:
- lang=I18n.get_default_lang()
-
- I18n.l[lang]=I18n.l.get(lang, {})
-
- I18n.l[lang][module]=I18n.l[lang].get(module, {})
-
- I18n.l[lang][module][symbol]=I18n.l[lang][module].get(symbol, text_default)
-
- return I18n.l[lang][module][symbol]
-
- @staticmethod
- def extract_value(value):
-
- value=json.loads(value)
-
- lang=I18n.get_default_lang()
-
- if value[lang]!='':
-
- return value[lang]
-
- return value[I18n.default_lang]
-
- @staticmethod
- def get_browser_lang():
-
- return request.headers.get('Accept-Language', 'en-US')
-
- @staticmethod
- def lang_json(module, symbol, text_default):
-
- arr_final={}
-
- for l in I18n.dict_i18n:
- arr_final[l]=I18n.lang(module, symbol, text_default, l)
-
- return json.dumps(arr_final)
-
diff --git a/paramecio/citoplasma/keyutils.py b/paramecio/citoplasma/keyutils.py
deleted file mode 100644
index 4cb4b8a..0000000
--- a/paramecio/citoplasma/keyutils.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from hashlib import sha512, sha256
-from base64 import b64encode
-from os import urandom
-
-# Functions for create random strings usando urandom
-
-def create_key_encrypt(n=10):
-
- return sha512(urandom(n)).hexdigest()
-
-def create_key_encrypt_256(n=10):
-
- return sha256(urandom(n)).hexdigest()
-
-def create_key(n=10):
-
- rand_bytes=urandom(n)
-
- return b64encode(rand_bytes).decode('utf-8')[0:-2]
diff --git a/paramecio/console.py b/paramecio/console.py
index 2e949a7..1f45d49 100644
--- a/paramecio/console.py
+++ b/paramecio/console.py
@@ -7,8 +7,8 @@ import getpass
import re
from pathlib import Path
from base64 import b64encode
-from paramecio.cromosoma.webmodel import WebModel
-from paramecio.modules.admin.models.admin import UserAdmin
+from paramecio.libraries.db.webmodel import WebModel
+from paramecio.modules.admin2.models.admin import UserAdmin2, LoginTries2
from subprocess import call
from urllib.parse import urlparse
@@ -52,7 +52,7 @@ def start():
print('Error: cannot create the directory. Check if exists and if you have permissions')
exit()
- # Create folder settings and copy index.py, admin.py
+ # Create folder settings and copy app.py, admin.py
path_settings=args.path+'/settings'
@@ -74,11 +74,11 @@ def start():
try:
- shutil.copy(workdir+'/frontend/index.py', args.path+'/index.py')
+ shutil.copy(workdir+'/frontend/app.py', args.path+'/app.py')
except:
- print('Error: cannot copy the file index.py. Check if exists and if you have permissions for this task')
+ print('Error: cannot copy the file app.py. Check if exists and if you have permissions for this task')
try:
@@ -112,7 +112,7 @@ def start():
print('Error: cannot copy the file create_module.py. Check if exists and if you have permissions for this task')
-
+ """
try:
shutil.copy(workdir+'/settings/modules.py', path_settings+'/modules.py')
@@ -120,6 +120,7 @@ def start():
except:
print('Error: cannot copy the file modules.py. Check if exists and if you have permissions for this task')
+ """
if args.symlink==True:
try:
@@ -221,7 +222,9 @@ def start():
conn=WebModel.connection()
- useradmin=UserAdmin(conn)
+ useradmin=UserAdmin2(conn)
+
+ logintries=LoginTries2(conn)
# Check if db exists
@@ -248,13 +251,15 @@ def start():
try:
- shutil.copy(workdir+'/settings/modules.py.admin', path_settings+'/modules.py')
+ #shutil.copy(workdir+'/settings/modules.py.admin', path_settings+'/modules.py')
- shutil.copy(workdir+'/settings/config_admin.py.sample', path_settings+'/config_admin.py')
+ #shutil.copy(workdir+'/settings/config_admin.py.sample', path_settings+'/config_admin.py')
sql=useradmin.create_table()
- if not useradmin.query(sql):
+ tries_sql=logintries.create_table()
+
+ if not useradmin.query(sql) or not useradmin.query(tries_sql) :
print('Error: cannot create table admin, you can create this table with padmin.py')
else:
@@ -265,7 +270,8 @@ def start():
f.close()
- config_text=config_text.replace("modules=['paramecio.modules.welcome']", "modules=['paramecio.modules.welcome', 'paramecio.modules.admin', 'paramecio.modules.lang']")
+ #config_text=config_text.replace("modules=['paramecio.modules.welcome']", "modules=['paramecio.modules.welcome', 'paramecio.modules.lang']")
+ config_text=config_text.replace("apps={'welcome': ['paramecio.modules.welcome', 'welcome_app', '/'], 'lang': ['paramecio.modules.lang', '', '']}", "apps={'welcome': ['paramecio.modules.welcome', 'welcome_app', '/'], 'lang': ['paramecio.modules.lang', '', ''], 'admin2': ['paramecio.modules.admin2', 'admin_app', '/admin/']}")
with open(path_settings+'/config.py', 'w') as f:
@@ -360,7 +366,7 @@ def start():
os.chdir(args.path)
#Regenerating modules.py
-
+ """
regenerate='regenerate.py'
os.chmod(regenerate, 0o755)
@@ -370,7 +376,7 @@ def start():
exit(1)
else:
print('Regeneration of modules.py finished')
-
+ """
# Installing models
padmin='padmin.py'
@@ -385,7 +391,7 @@ def start():
models_files=os.listdir(models_path)
- m=re.compile(".*\.py$")
+ m=re.compile(r".*\.py$")
underscore=re.compile("^__.*")
diff --git a/paramecio/create_module.py b/paramecio/create_module.py
index 96e7c87..5f8e68f 100644
--- a/paramecio/create_module.py
+++ b/paramecio/create_module.py
@@ -8,8 +8,11 @@ import getpass
from pathlib import Path
from settings import config
from importlib import import_module
+import re
def start():
+ """Module for create new modules for paramecio
+ """
parser=argparse.ArgumentParser(description='A tool for create new modules for paramecio')
@@ -37,17 +40,68 @@ def start():
#f=open('modules/'+args.path+'/index.py', 'w')
+ name_module=os.path.basename(args.path)
+
try:
- shutil.copy(workdir+'/examples/index.py', 'modules/'+args.path)
+ shutil.copy(workdir+'/examples/app.py', 'modules/'+args.path)
+
+ with open('modules/'+args.path+'/app.py') as f:
+
+ app_file=f.read()
+
+ app_file=app_file.replace('/example', '/'+name_module)
+
+ with open('modules/'+args.path+'/app.py', 'w') as f:
+
+ f.write(app_file)
+
+ pass
except:
print('Error: cannot copy controller example. Check if you have permissions')
exit(1)
+ # Edit config.py
+
+ #module_final='modules.'+name_module
+ module_final=f"'{name_module}': ['modules.{name_module}', '', '']"
+
+ try:
+
+ with open('./settings/config.py') as f:
+
+ #modules_final='\''+'\', \''.join(final_modules)+'\''
+
+ p=re.compile(r"^apps=\{(.*)\}$")
+
+ #config_file=p.sub(r"modules=[\1, "+modules_final+"]", "modules=['paramecio.modules.welcome', 'paramecio.modules.admin', 'paramecio.modules.lang', 'modules.pastafari', 'modules.monit', 'modules.example']")
+
+ final_config=''
+
+ for line in f:
+ if p.match(line):
+ line=p.sub(r"apps={\1, "+module_final+"}", line)
+ final_config+=line
+
+ with open('./settings/config.py', 'w') as f:
+
+ f.write(final_config)
+
+ print('Updated configuration for add new modules...')
+
+
+ except:
+
+ print('Cannot update configuration, you need add the new module by hand')
+
+ # Reload config
+
+ #config.modules.append(module_final)
+
# Regenerate modules
- regenerate_modules_config()
+ #regenerate_modules_config()
def regenerate_modules_config():
@@ -76,7 +130,7 @@ def regenerate_modules_config():
for controller in dir_controllers:
- if controller.find('.py')!=-1 and controller.find('__init__')==-1:
+ if controller.find('.py')!=-1 and controller.find('_')==-1:
controller_py=controller.replace('.py', '')
@@ -99,11 +153,8 @@ def regenerate_modules_config():
print("-"*60)
exit(1)
- f=open('./settings/modules.py', 'w')
-
- f.write("".join(modules))
-
- f.close()
+ with open('./settings/modules.py', 'w') as f:
+ f.write("".join(modules))
if __name__=="__main__":
start()
diff --git a/paramecio/cromosoma/corefields.py b/paramecio/cromosoma/corefields.py
deleted file mode 100644
index 4c24ba3..0000000
--- a/paramecio/cromosoma/corefields.py
+++ /dev/null
@@ -1,262 +0,0 @@
-from paramecio.cromosoma.webmodel import PhangoField
-from paramecio.cromosoma import coreforms
-from paramecio.citoplasma.i18n import I18n
-
-class IntegerField(PhangoField):
-
- """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):
- super(IntegerField, self).__init__(name, size, required)
- self.default_value=0
-
- def check(self, value):
-
- """Method for check if value is integer
-
- Args:
- value (int): The value to check
-
- """
-
- self.error=False
- self.txt_error=''
-
- try:
-
- value=str(int(value))
-
- if value=="0" and self.required==True:
- self.txt_error="The value is zero"
- self.error=True
- except:
-
- value="0"
- self.txt_error="The value is zero"
- self.error=True
-
- return value
-
- def get_type_sql(self):
-
- """Method for return the sql code for this type
-
- """
-
- return 'INT('+str(self.size)+') NOT NULL DEFAULT "0"'
-
-class BigIntegerField(IntegerField):
-
- """Class that figure an big integer sql type field.
-
- Only change the sql type with respect to IntegerField
-
- """
-
- def get_type_sql(self):
-
- """Method for return the sql code for this type
-
- """
-
- return 'BIGINT('+str(self.size)+') NOT NULL DEFAULT "0"'
-
-
-class FloatField(PhangoField):
-
- """Class that figure an float 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):
- super(FloatField, self).__init__(name, size, required)
-
- self.error_default="The value is zero"
- self.default_value=0
-
- def check(self, value):
-
- """Method for check if value is integer
-
- Args:
- value (float): The value to check
-
- """
-
- self.error=False
- self.txt_error=''
-
- try:
-
- value=str(value)
-
- if value.find(',')!=-1:
- value=value.replace(',', '.')
-
- value=str(float(value))
-
- if value==0 and self.required==True:
- self.txt_error=self.error_default
- self.error=True
- except:
-
- value="0"
- self.txt_error=self.error_default
- self.error=True
-
- return value
-
- def get_type_sql(self):
-
- return 'FLOAT NOT NULL DEFAULT "0"'
-
-class DecimalField(FloatField):
-
- def get_type_sql(self):
-
- return 'DECIMAL(20, 2) NOT NULL DEFAULT "0"'
-
-class DoubleField(FloatField):
-
- def get_type_sql(self):
-
- return 'DOUBLE NOT NULL DEFAULT "0"'
-
-class CharField(PhangoField):
-
- pass
-
-class TextField(PhangoField):
-
- def __init__(self, name, required=False):
- super().__init__(name, 11, required)
-
- self.set_default='NOT NULL'
-
- def get_type_sql(self):
-
- """Method for return the sql code for this type
-
- """
-
- return 'TEXT '+self.set_default
-
-class HTMLField(TextField):
-
- def __init__(self, name, required=False):
- super().__init__(name, required)
-
- def check(self, value):
-
- return re.sub('<.*?script?>', '', value)
-
-
-class ForeignKeyField(IntegerField):
-
- def __init__(self, name, related_table, size=11, required=False, identifier_field='id', named_field="id", select_fields=[]):
-
- super(ForeignKeyField, self).__init__(name, size, required)
-
- self.table_id=related_table.name_field_id
-
- self.table_name=related_table.name
-
- self.related_model=related_table
-
- self.identifier_field=identifier_field
-
- self.named_field=named_field
-
- self.select_fields=select_fields
-
- self.foreignkey=True
-
- self.change_form(coreforms.SelectModelForm, [related_table, self.named_field, self.identifier_field])
-
- self.default_value=None
-
- def check(self, value):
-
- value=super().check(value)
-
- if value=='0' or value==0:
- value='NULL'
-
- return value
-
- def get_type_sql(self):
-
- """Method for return the sql code for this type
-
- """
-
- return 'INT NULL'
-
-
-class BooleanField(IntegerField):
-
- def __init__(self, name, size=1):
-
- required=False
-
- self.yes_text=I18n.lang('common', 'yes', 'Yes')
- self.no_text=I18n.lang('common', 'no', 'No')
-
- super(IntegerField, self).__init__(name, size, required)
-
- self.default_error="Need 0 or 1 value"
- self.default_value=0
-
- def check(self, value):
-
- self.error=False
- self.txt_error=''
-
- try:
-
- value=int(value)
-
- if value<0 or value>1:
- self.txt_error=self.default_error
- self.error=True
-
- except:
-
- self.error=True
- self.txt_error=self.default_error
- value=0
-
- value=str(value)
-
- return value
-
- def get_type_sql(self):
-
- """Method for return the sql code for this type
-
- """
-
- return 'BOOLEAN NOT NULL DEFAULT "0"'
-
- def show_formatted(self, value):
-
- value=int(value)
-
- if value==0:
- value=self.no_text
- else:
- value=self.yes_text
-
- return str(value)
diff --git a/paramecio/cromosoma/extrafields/arrayfield.py b/paramecio/cromosoma/extrafields/arrayfield.py
deleted file mode 100644
index 93d26b6..0000000
--- a/paramecio/cromosoma/extrafields/arrayfield.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from paramecio.cromosoma.webmodel import PhangoField,WebModel
-import json
-
-class ArrayField(PhangoField):
-
- def __init__(self, name, field_type, required=False):
-
- super().__init__(name, required)
-
- self.field_type=field_type
-
- self.error_default='Sorry, the json array is invalid'
-
- self.set_default='NOT NULL'
-
- def check(self, value):
-
- if type(value).__name__=='str':
- try:
- value=json.loads(value)
- except json.JSONDecodeError:
-
- value=[]
- self.error=True
- self.txt_error=self.error_default
-
- elif type(value).__name__!='list':
-
- value=[]
- self.error=True
- self.txt_error='Sorry, the json array is invalid'
-
- if type(self.field_type).__name__!='ArrayField':
- for k,v in enumerate(value):
-
- value[k]=self.field_type.check(v)
-
- final_value=json.dumps(value)
-
- final_value=WebModel.escape_sql(final_value)
-
- return final_value
-
- def get_type_sql(self):
-
- return 'TEXT '+self.set_default
-
- def show_formatted(self, value):
-
- return ", ".join(value)
-
- def loads(self, value):
-
- try:
-
- return json.loads(value)
- except:
-
- return False
diff --git a/paramecio/cromosoma/extrafields/colorfield.py b/paramecio/cromosoma/extrafields/colorfield.py
deleted file mode 100644
index 673698e..0000000
--- a/paramecio/cromosoma/extrafields/colorfield.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from paramecio.cromosoma.corefields import IntegerField
-from paramecio.cromosoma.extraforms.colorform import ColorForm
-
-class ColorField(IntegerField):
-
- def __init__(self, name, size=11, required=False):
- super().__init__(name, size, required)
-
- self.name_form=ColorForm
-
- def check(self, value):
-
- value=str(value).replace('#', '0x')
-
- value=int(value, 16)
-
- if value<0 or value>0xffffff:
- value=0
-
- return value
- def get_hex_color(self, value):
-
- value=str(hex(int(value))).replace('0x', '')
-
- c=len(value)
-
- if(c<6):
- repeat=6-c
- value=('0'*repeat)+value
-
- value='#'+value
-
- return value
-
- def show_formatted(self, value):
-
- value=str(hex(int(value))).replace('0x', '')
-
- c=len(value)
-
- if(c<6):
- repeat=6-c
- value=('0'*repeat)+value
-
- value='#'+value
-
- return '' % value;
diff --git a/paramecio/cromosoma/extrafields/datefield.py b/paramecio/cromosoma/extrafields/datefield.py
deleted file mode 100644
index 0ca8995..0000000
--- a/paramecio/cromosoma/extrafields/datefield.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from paramecio.cromosoma.corefields import PhangoField
-from paramecio.citoplasma import datetime
-from paramecio.cromosoma.extraforms.dateform import DateForm
-
-class DateField(PhangoField):
-
- def __init__(self, name, size=255, required=False):
-
- super().__init__(name, size, required)
-
- self.name_form=DateForm
-
- self.utc=True
-
- self.error_default='Error: Date format invalid'
-
- def check(self, value):
-
- if self.utc:
-
- value=datetime.local_to_gmt(value)
-
- elif not datetime.obtain_timestamp(value):
-
- self.error=True
- self.txt_error=self.error_default
- return ''
-
- if value==False:
-
- self.error=True
- self.txt_error=self.error_default
- return ''
-
- return value
-
- def show_formatted(self, value):
-
- return datetime.format_date(value)
diff --git a/paramecio/cromosoma/extrafields/datetimefield.py b/paramecio/cromosoma/extrafields/datetimefield.py
deleted file mode 100644
index 4a5c57c..0000000
--- a/paramecio/cromosoma/extrafields/datetimefield.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from paramecio.cromosoma.corefields import PhangoField
-from paramecio.citoplasma import datetime
-from paramecio.cromosoma.extraforms.dateform import DateForm
-
-class DateTimeField(PhangoField):
-
- def __init__(self, name, size=255, required=False):
-
- super().__init__(name, size, required)
-
- self.name_form=DateForm
-
- self.utc=False
-
- self.error_default='Error: Date format invalid'
-
- def check(self, value):
-
- if self.utc:
-
- value=datetime.local_to_gmt(value)
-
- elif not datetime.obtain_timestamp(value):
-
- self.error=True
- self.txt_error=self.error_default
- return None
-
- if value==False:
-
- self.error=True
- self.txt_error=self.error_default
- return None
- else:
-
- """
- format_date_txt="YYYY/MM/DD"
-
- format_time_txt="HH:mm:ss"
- """
-
- value=datetime.format_local_strtime('YYYY-MM-DD HH:mm:ss', value)
-
- return value
-
- def show_formatted(self, value):
-
- # Convert to paramecio value
- if value!=None:
- value=str(value)
- value=value.replace('-', '').replace(':', '').replace(' ', '')
-
- return datetime.format_date(value)
-
- else:
- return ''
-
- def get_type_sql(self):
-
- """Method for return the sql code for this type
-
- """
-
- return 'DATETIME NULL'
diff --git a/paramecio/cromosoma/extrafields/dictfield.py b/paramecio/cromosoma/extrafields/dictfield.py
deleted file mode 100644
index 1ef816e..0000000
--- a/paramecio/cromosoma/extrafields/dictfield.py
+++ /dev/null
@@ -1,54 +0,0 @@
-from paramecio.cromosoma.webmodel import WebModel, PhangoField
-
-try:
- import ujson as json
-except:
- import json
-
-class DictField(PhangoField):
-
- def __init__(self, name, field_type, required=False):
-
- super().__init__(name, required)
-
- self.field_type=field_type
-
- self.error_default='Sorry, the json dict is invalid'
-
- self.set_default='NOT NULL'
-
- def check(self, value):
-
- if type(value).__name__=='str':
- try:
- value=json.loads(value)
- except json.JSONDecodeError:
-
- value={}
- self.error=True
- self.txt_error=self.error_default
-
- elif type(value).__name__!='dict':
-
- value={}
- self.error=True
- self.txt_error=self.error_default
-
- for k,v in value.items():
-
- value[k]=self.field_type.check(v)
-
- final_value=json.dumps(value)
-
- #final_value=WebModel.escape_sql(final_value)
-
- return final_value
-
- def get_type_sql(self):
-
- return 'TEXT '+self.set_default
-
- def show_formatted(self, value):
-
- return ", ".join(value)
-
diff --git a/paramecio/cromosoma/extrafields/emailfield.py b/paramecio/cromosoma/extrafields/emailfield.py
deleted file mode 100644
index bd35b5c..0000000
--- a/paramecio/cromosoma/extrafields/emailfield.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from paramecio.cromosoma.corefields import CharField
-import re
-
-mail_pattern=re.compile("\w[\w\.-]*@\w[\w\.-]+\.\w+")
-
-class EmailField(CharField):
-
- def __init__(self, name, size=1024, required=False):
-
- super().__init__(name, size, required)
-
- self.error_default='Error: No valid format'
-
- def check(self, value):
-
- value=super().check(value)
-
- self.error=False
- self.txt_error=''
-
- if not mail_pattern.match(value):
-
- self.error=True
- value=""
- self.txt_error=self.error_default
-
- return value
diff --git a/paramecio/cromosoma/extraforms/colorform.py b/paramecio/cromosoma/extraforms/colorform.py
deleted file mode 100644
index 8b7872f..0000000
--- a/paramecio/cromosoma/extraforms/colorform.py
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env python3
-
-from paramecio.cromosoma.coreforms import BaseForm
-from paramecio.citoplasma.mtemplates import standard_t
-
-class ColorForm(BaseForm):
-
- def __init__(self, name, value):
-
- super().__init__(name, value)
-
- self.t=standard_t
-
- def form(self):
-
-
- return self.t.load_template('forms/colorform.phtml', name_form=self.name_field_id, default_value=self.default_value, form=self)
diff --git a/paramecio/examples/app.py b/paramecio/examples/app.py
new file mode 100644
index 0000000..a6de3f5
--- /dev/null
+++ b/paramecio/examples/app.py
@@ -0,0 +1,14 @@
+from paramecio.libraries.mtemplates import env_theme, PTemplate
+from paramecio.libraries.urls import make_url
+from bottle import request
+from settings import config
+from paramecio.wsgiapp import app
+
+env=env_theme(__file__)
+t=PTemplate(env)
+
+@app.route('/example')
+def home():
+
+ return "Hello World!!"
+
diff --git a/paramecio/examples/index.py b/paramecio/examples/index.py
deleted file mode 100644
index 0237aa8..0000000
--- a/paramecio/examples/index.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from paramecio.citoplasma.mtemplates import env_theme, PTemplate
-from paramecio.citoplasma.urls import make_url
-from bottle import route, request
-from settings import config
-
-env=env_theme(__file__)
-
-@route('/example')
-def home():
-
- t=PTemplate(env)
-
- return "Hello World!!"
-
diff --git a/paramecio/frontend/index.py b/paramecio/frontend/app.py
similarity index 76%
rename from paramecio/frontend/index.py
rename to paramecio/frontend/app.py
index 96233db..0acc1a7 100644
--- a/paramecio/frontend/index.py
+++ b/paramecio/frontend/app.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
from paramecio.wsgiapp import app
-from paramecio.index import run_app
+from paramecio.app import run_app
application=app
diff --git a/paramecio/frontend/i18nadmin.py b/paramecio/frontend/i18nadmin.py
index 7b215f9..6eed6a7 100644
--- a/paramecio/frontend/i18nadmin.py
+++ b/paramecio/frontend/i18nadmin.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-from paramecio.citoplasma.check_i18n import start
+from paramecio.libraries.check_i18n import start
start()
diff --git a/paramecio/frontend/padmin.py b/paramecio/frontend/padmin.py
index 0ecf3de..307fea2 100644
--- a/paramecio/frontend/padmin.py
+++ b/paramecio/frontend/padmin.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-from paramecio.cromosoma.dbadmin import start
+from paramecio.libraries.db.dbadmin import start
start()
diff --git a/paramecio/i18n/admin.py b/paramecio/i18n/admin.py
index 8c545d9..a899a71 100644
--- a/paramecio/i18n/admin.py
+++ b/paramecio/i18n/admin.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-from paramecio.citoplasma.i18n import I18n
+from paramecio.libraries.i18n import I18n
I18n.l['en-US']=I18n.l.get('en-US', {})
diff --git a/paramecio/i18n/common.py b/paramecio/i18n/common.py
index d260ca6..3cb8913 100644
--- a/paramecio/i18n/common.py
+++ b/paramecio/i18n/common.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-from paramecio.citoplasma.i18n import I18n
+from paramecio.libraries.i18n import I18n
I18n.l['en-US']=I18n.l.get('en-US', {})
diff --git a/paramecio/citoplasma/__init__.py b/paramecio/libraries/__init__.py
similarity index 100%
rename from paramecio/citoplasma/__init__.py
rename to paramecio/libraries/__init__.py
diff --git a/paramecio/citoplasma/adminutils.py b/paramecio/libraries/adminutils.py
similarity index 89%
rename from paramecio/citoplasma/adminutils.py
rename to paramecio/libraries/adminutils.py
index c890ab4..7698755 100644
--- a/paramecio/citoplasma/adminutils.py
+++ b/paramecio/libraries/adminutils.py
@@ -1,14 +1,14 @@
#!/usr/bin/env python3
from collections import OrderedDict
-from paramecio.citoplasma.sessions import get_session
-from paramecio.citoplasma.urls import make_url
-from paramecio.citoplasma.i18n import I18n
-from paramecio.citoplasma.httputils import GetPostFiles
-from paramecio.citoplasma.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
-from paramecio.cromosoma.formsutils import generate_csrf
+from paramecio.libraries.sessions import get_session
+from paramecio.libraries.urls import make_url
+from paramecio.libraries.i18n import I18n
+from paramecio.libraries.httputils import GetPostFiles
+from paramecio.libraries.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
+from paramecio.libraries.db.formsutils import generate_csrf
from bottle import response,request
-from paramecio.cromosoma.webmodel import WebModel
+from paramecio.libraries.db.webmodel import WebModel
from time import time
try:
@@ -98,7 +98,8 @@ def get_menu(modules_admin):
if len(menu[mod[2]])<4:
menu[mod[2]].append('')
-
+ else:
+ menu[mod[2]][3]=''.format(menu[mod[2]][3])
else:
menu[mod[2]]=mod[0]
@@ -114,6 +115,8 @@ def get_menu(modules_admin):
if len(menu[submod[2]])<4:
menu[submod[2]].append('')
+ else:
+ menu[submod[2]][3]=''.format(menu[submod[2]][3])
return menu
@@ -158,7 +161,7 @@ def login_model(ModelLogin, session='', enable_tries=False):
user_admin.conditions=['WHERE username=%s', [username]]
- arr_user=user_admin.select_a_row_where(['id', 'username', 'password', 'privileges', 'lang', 'num_tries', 'email'])
+ arr_user=user_admin.select_a_row_where(['id', 'username', 'password', 'privileges', 'lang', 'num_tries', 'email', 'theme'])
if arr_user==False:
@@ -180,6 +183,7 @@ def login_model(ModelLogin, session='', enable_tries=False):
s[session+'lang']=arr_user['lang']
s[session+'email']=arr_user['email']
s[session+'username']=arr_user['username']
+ s[session+'theme']=str(arr_user['theme'])
if s['lang']=='':
s['lang']=I18n.default_lang
diff --git a/paramecio/citoplasma/base_admin.py b/paramecio/libraries/base_admin.py
similarity index 79%
rename from paramecio/citoplasma/base_admin.py
rename to paramecio/libraries/base_admin.py
index 06e9673..4e40cfe 100644
--- a/paramecio/citoplasma/base_admin.py
+++ b/paramecio/libraries/base_admin.py
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
-from paramecio.citoplasma.mtemplates import PTemplate
-from paramecio.citoplasma.adminutils import check_login, get_language, get_menu
-from paramecio.cromosoma.webmodel import WebModel
-from paramecio.citoplasma.sessions import get_session
-from paramecio.citoplasma.i18n import I18n
+from paramecio.libraries.mtemplates import PTemplate
+from paramecio.libraries.adminutils import check_login, get_language, get_menu
+from paramecio.libraries.db.webmodel import WebModel
+from paramecio.libraries.sessions import get_session
+from paramecio.libraries.i18n import I18n
try:
diff --git a/paramecio/citoplasma/check_i18n.py b/paramecio/libraries/check_i18n.py
similarity index 97%
rename from paramecio/citoplasma/check_i18n.py
rename to paramecio/libraries/check_i18n.py
index 96ccba4..7d5832b 100644
--- a/paramecio/citoplasma/check_i18n.py
+++ b/paramecio/libraries/check_i18n.py
@@ -5,7 +5,7 @@ import os
import re
from pathlib import Path
from importlib import import_module
-from paramecio.citoplasma.i18n import I18n
+from paramecio.libraries.i18n import I18n
from settings import config
pattern=re.compile('^\w+\.(py|html|phtml|js)$')
@@ -48,6 +48,8 @@ def start():
#lang_t=re.compile("\${lang\('("+module_base+"?)',\s+'(.*?)',\s+'(.*?)'\)\}")
lang_t=re.compile("lang\('("+module_base+"?)',\s+'(.*?)',\s+'(.*?)'\)")
+ lang_s=re.compile("slang\('(.*?)',\s+'(.*?)'\)"
+
if not os.path.isdir(path):
print("Error: directory to scan doesn't exists")
@@ -91,7 +93,7 @@ def start():
file_lang="#!/usr/bin/env python3\n\n"
- file_lang+="from paramecio.citoplasma.i18n import I18n\n\n"
+ file_lang+="from paramecio.libraries.i18n import I18n\n\n"
for lang in I18n.dict_i18n:
diff --git a/paramecio/citoplasma/datetime.py b/paramecio/libraries/datetime.py
similarity index 99%
rename from paramecio/citoplasma/datetime.py
rename to paramecio/libraries/datetime.py
index 9ad9703..4287712 100644
--- a/paramecio/citoplasma/datetime.py
+++ b/paramecio/libraries/datetime.py
@@ -6,7 +6,7 @@ try:
from settings import config
except:
config={}
-#from paramecio.citoplasma.sessions import get_session
+#from paramecio.libraries.sessions import get_session
from os import environ
"""Simple hook for timedate functions from Arrow datetime module. Maybe in the future use native python datetime functions or other libraries. Is simply an abstraction for not depend of particular library.
diff --git a/paramecio/cromosoma/.gitignore b/paramecio/libraries/db/.gitignore
similarity index 100%
rename from paramecio/cromosoma/.gitignore
rename to paramecio/libraries/db/.gitignore
diff --git a/paramecio/cromosoma/LICENSE b/paramecio/libraries/db/LICENSE
similarity index 100%
rename from paramecio/cromosoma/LICENSE
rename to paramecio/libraries/db/LICENSE
diff --git a/paramecio/cromosoma/README.md b/paramecio/libraries/db/README.md
similarity index 100%
rename from paramecio/cromosoma/README.md
rename to paramecio/libraries/db/README.md
diff --git a/paramecio/cromosoma/__init__.py b/paramecio/libraries/db/__init__.py
similarity index 100%
rename from paramecio/cromosoma/__init__.py
rename to paramecio/libraries/db/__init__.py
diff --git a/paramecio/libraries/db/corefields.py b/paramecio/libraries/db/corefields.py
new file mode 100644
index 0000000..fbe71a9
--- /dev/null
+++ b/paramecio/libraries/db/corefields.py
@@ -0,0 +1,426 @@
+"""
+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 .
+"""
+
+from paramecio.libraries.db.webmodel import PhangoField
+from paramecio.libraries.db import coreforms
+from paramecio.libraries.i18n import I18n
+#from bs4 import BeautifulSoup
+import bleach
+
+class IntegerField(PhangoField):
+
+ """Class that figure an integer sql type field.
+ """
+
+ 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)
+ self.default_value=0
+ self.jtype='integer'
+ self.jformat='int64'
+ self.jexample='12345'
+
+ self.type_sql='int({})'.format(self.size)
+
+ def check(self, value):
+
+ """Method for check if value is integer
+
+ Args:
+ value (int): The value to check
+
+ """
+
+ self.error=False
+ self.txt_error=''
+
+ try:
+
+ value=str(int(value))
+
+ if value=="0" and self.required==True:
+ self.txt_error="The value is zero"
+ self.error=True
+ except:
+
+ value="0"
+ self.txt_error="The value is zero"
+ self.error=True
+
+ return value
+
+ def get_type_sql(self):
+
+ """Method for return the sql code for this type
+
+ """
+
+ return 'INT('+str(self.size)+') NOT NULL DEFAULT "0"'
+
+class BigIntegerField(IntegerField):
+
+ """Class that figure an big integer sql type field.
+
+ Only change the sql type with respect to 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):
+
+ """Method for return the sql code for this type
+
+ """
+
+ return 'BIGINT('+str(self.size)+') NOT NULL DEFAULT "0"'
+
+
+class FloatField(PhangoField):
+
+ """Class that figure an float 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):
+ super(FloatField, self).__init__(name, size, required)
+
+ self.error_default="The value is zero"
+ self.default_value=0
+ self.type_sql='float'.format(self.size)
+
+ self.jtype='float'
+
+ def check(self, value):
+
+ """Method for check if value is integer
+
+ Args:
+ value (float): The value to check
+
+ """
+
+ self.error=False
+ self.txt_error=''
+
+ try:
+
+ value=str(value)
+
+ if value.find(',')!=-1:
+ value=value.replace(',', '.')
+
+ value=str(float(value))
+
+ if value==0 and self.required==True:
+ self.txt_error=self.error_default
+ self.error=True
+ except:
+
+ value="0"
+ self.txt_error=self.error_default
+ self.error=True
+
+ return value
+
+ def get_type_sql(self):
+
+ return 'FLOAT NOT NULL DEFAULT "0"'
+
+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)'
+ self.jtype='number'
+
+ def get_type_sql(self):
+
+ return 'DECIMAL(20, 2) NOT NULL DEFAULT "0"'
+
+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'
+ self.jtype='float'
+
+ def get_type_sql(self):
+
+ return 'DOUBLE NOT NULL DEFAULT "0"'
+
+class CharField(PhangoField):
+ """Simple alias for PhangoField"""
+
+ pass
+
+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):
+ """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)
+
+ self.type_sql='text'
+
+ self.set_default='NOT NULL'
+
+ def get_type_sql(self):
+
+ """Method for return the sql code for this type
+
+ """
+
+ return 'TEXT '+self.set_default
+
+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):
+ """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)
+ 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 "
+ """
+
+ 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):
+ """Check method for html values
+
+ This check method use beautifulsoap for clean and format html code
+ """
+
+ # leach.clean('
"trial"
', 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('"', '"')
+ else:
+
+ return value
+
+ """
+
+ value=bleach.clean('
"trial"
', tags=self.trusted_tags)
+
+ if self.escape:
+
+ return value.replace('"', '"')
+ else:
+
+ return value
+
+
+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=[]):
+ """
+ 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)
+
+ self.table_id=related_table.name_field_id
+
+ self.table_name=related_table.name
+
+ self.related_model=related_table
+
+ self.identifier_field=identifier_field
+
+ self.named_field=named_field
+
+ self.select_fields=select_fields
+
+ self.foreignkey=True
+
+ self.change_form(coreforms.SelectModelForm, [related_table, self.named_field, self.identifier_field])
+
+ self.default_value=None
+
+ def check(self, value):
+
+ value=super().check(value)
+
+ if value=='0' or value==0:
+ value=None
+
+ return value
+
+ def get_type_sql(self):
+
+ """Method for return the sql code for this type
+
+ """
+
+ return 'INT NULL'
+
+
+class BooleanField(IntegerField):
+ """Field for boolean values
+ """
+
+ 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
+
+ self.yes_text=I18n.lang('common', 'yes', 'Yes')
+ self.no_text=I18n.lang('common', 'no', 'No')
+
+ super(IntegerField, self).__init__(name, size, required)
+
+ self.default_error="Need 0 or 1 value"
+ self.default_value=0
+
+ self.type_sql='tinyint(1)'
+
+ def check(self, value):
+
+ self.error=False
+ self.txt_error=''
+
+ try:
+
+ value=int(value)
+
+ if value<0 or value>1:
+ self.txt_error=self.default_error
+ self.error=True
+ value=0
+
+ except:
+
+ self.error=True
+ self.txt_error=self.default_error
+ value=0
+
+ value=str(value)
+
+ return value
+
+ def get_type_sql(self):
+
+ """Method for return the sql code for this type
+
+ """
+
+ return 'BOOLEAN NOT NULL DEFAULT "0"'
+
+ def show_formatted(self, value):
+
+ value=int(value)
+
+ if value==0:
+ value=self.no_text
+ else:
+ value=self.yes_text
+
+ return str(value)
diff --git a/paramecio/cromosoma/coreforms.py b/paramecio/libraries/db/coreforms.py
similarity index 52%
rename from paramecio/cromosoma/coreforms.py
rename to paramecio/libraries/db/coreforms.py
index 3247ddc..76d8a7a 100644
--- a/paramecio/cromosoma/coreforms.py
+++ b/paramecio/libraries/db/coreforms.py
@@ -1,13 +1,55 @@
#!/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 .
+"""
+
+
from collections import OrderedDict
from html import escape
#Forms para python3
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):
+ """
+ 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
+ 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.name=name
@@ -17,26 +59,45 @@ class BaseForm:
self.field=None
self.required=False
self.txt_error=''
+ self.error=False
self.name_field_id=self.name+'_form'
self.help=''
+ self.placeholder=''
def form(self):
+ """Method for returm the html code of the form
+ """
- return ''
+ return ''
def show_formatted(self, value):
+ """Method for show the value of form formatted
+
+ Args:
+ value (mixed): The value of field form
+ """
return value
#Method for escape value for html input. DON'T CHANGE IF YOU DON'T KNOWN WHAT ARE YOU DOING
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)
return value.replace('"', '"').replace("'", ''')
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
@@ -45,6 +106,8 @@ class BaseForm:
return ""
class SimpleTextForm(BaseForm):
+ """Form for simple text
+ """
def __init__(self, name, value):
super().__init__(name, value)
@@ -56,6 +119,8 @@ class SimpleTextForm(BaseForm):
return super().form()+' '+self.after_text
class TextForm(BaseForm):
+ """Form for simple text form
+ """
def __init__(self, name, value):
super(TextForm, self).__init__(name, value)
@@ -65,6 +130,8 @@ class TextForm(BaseForm):
return ''
class PasswordForm(BaseForm):
+ """Form for password forms
+ """
def __init__(self, name, value, show_password=False):
super(PasswordForm, self).__init__(name, value)
@@ -79,6 +146,8 @@ class PasswordForm(BaseForm):
return value
class HiddenForm(BaseForm):
+ """Form for hidden forms
+ """
def __init__(self, name, value):
super(HiddenForm, self).__init__(name, value)
@@ -86,8 +155,16 @@ class HiddenForm(BaseForm):
class SelectForm(BaseForm):
+ """Form for select html form
+ """
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: