First commit
This commit is contained in:
commit
0e5052676b
1
LICENSE.md
Normal file
1
LICENSE.md
Normal file
@ -0,0 +1 @@
|
||||
This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. To view a copy of this license, visit https://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
|
22
README.md
Normal file
22
README.md
Normal file
@ -0,0 +1,22 @@
|
||||
# CostaPy
|
||||
|
||||
Python Web Framework. Build with CherryPy and Mako.
|
||||
|
||||
## License
|
||||
|
||||
CostaPy
|
||||
|
||||
Copyright (C) 2022 Dita Aji Pratama
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see https://www.gnu.org/licenses/.
|
30
_sidebar.md
Normal file
30
_sidebar.md
Normal file
@ -0,0 +1,30 @@
|
||||
* [Home](/)
|
||||
* [Getting Starter](pages/getting-starter.md)
|
||||
* [Known the structure](pages/structure.md)
|
||||
* core (extension script)
|
||||
* html
|
||||
* authentication
|
||||
* [loggorilla](pages/core/loggorilla.md)
|
||||
* uploading
|
||||
* mailme
|
||||
* Configuration
|
||||
* [Server](pages/configuration/server.md)
|
||||
* [Global Variable](pages/configuration/globalvar.md)
|
||||
* [Directory](pages/configuration/directory.md)
|
||||
* [Templating](pages/configuration/template.md)
|
||||
* [Database](pages/configuration/database.md)
|
||||
* content
|
||||
* [Handler](pages/content/handler.md)
|
||||
* [Import the modules](pages/content/handler?id=import-the-modules)
|
||||
* [Routing the handler](pages/content/handler?id=routing-the-handler)
|
||||
* [Add modules into handler](pages/content/handler?id=add-modules-into-handler)
|
||||
* [Request with JSON](pages/content/handler?id=request-with-json)
|
||||
* [Request with POST](pages/content/handler?id=request-with-post)
|
||||
* [Explanation](pages/content/handler?id=explanation)
|
||||
* [Session](pages/content/handler?id=session)
|
||||
* [Sample](pages/content/handler?id=sample)
|
||||
* [Modules](pages/content/modules.md)
|
||||
* static
|
||||
* error pages
|
||||
* [Main Process](pages/main-process.md)
|
||||
* Sessioning
|
BIN
images/structure.png
Normal file
BIN
images/structure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
24
index.html
Normal file
24
index.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>CostaPy</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="Description">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/dark.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: 'CostaPy',
|
||||
repo: '',
|
||||
loadSidebar: true
|
||||
}
|
||||
</script>
|
||||
<!-- Docsify v4 -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/emoji.min.js"></script>
|
||||
</body>
|
||||
</html>
|
29
pages/configuration/database.md
Normal file
29
pages/configuration/database.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Database (config/database.py)
|
||||
|
||||
This is the sample template for configure it
|
||||
|
||||
db_default = {
|
||||
'host' : 'localhost',
|
||||
'user' : 'root',
|
||||
'password' : '',
|
||||
'database' : 'your_db',
|
||||
'autocommit' : True,
|
||||
}
|
||||
|
||||
You also can make more than 1 database configuration like this
|
||||
|
||||
db_default = {
|
||||
'host' : 'localhost',
|
||||
'user' : 'root',
|
||||
'password' : '',
|
||||
'database' : 'your_db',
|
||||
'autocommit' : True,
|
||||
}
|
||||
|
||||
db_other = {
|
||||
'host' : 'localhost',
|
||||
'user' : 'root',
|
||||
'password' : '',
|
||||
'database' : 'other_db',
|
||||
'autocommit' : True,
|
||||
}
|
34
pages/configuration/directory.md
Normal file
34
pages/configuration/directory.md
Normal file
@ -0,0 +1,34 @@
|
||||
# Directory (config/directory.py)
|
||||
|
||||
`directory.py` is the place for storing your path. It is useful to calling the path more efficiently. there is 2 method that you can store your path. store it in variable for templating configuration, and store it as object for routing the url.
|
||||
|
||||
This is for a static error pages. This variable will be use in `server` configuration.
|
||||
|
||||
def erpadir(err):
|
||||
return f'static/error/{err}.html'
|
||||
|
||||
This is example that use for templating. This variable will be use in `template` configuration.
|
||||
|
||||
user_page = "static/user/page"
|
||||
user_template = "static/user/template"
|
||||
|
||||
admin_page = "static/admin/page"
|
||||
admin_template = "static/admin/template"
|
||||
|
||||
email_page = "static/email/page"
|
||||
email_template = "static/email/template"
|
||||
|
||||
And this is example that use for routing the url
|
||||
|
||||
dirconfig = {
|
||||
'/' :
|
||||
{
|
||||
'tools.sessions.on' : True ,
|
||||
'tools.staticdir.root' : os.path.abspath(os.getcwd()) ,
|
||||
},
|
||||
'/your_dir' :
|
||||
{
|
||||
'tools.staticdir.on' : True ,
|
||||
'tools.staticdir.dir' : './static/your-dir' ,
|
||||
},
|
||||
}
|
11
pages/configuration/globalvar.md
Normal file
11
pages/configuration/globalvar.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Global Variable (config/globalvar.py)
|
||||
|
||||
`globalvar.py` is the place for storing your Global Variable.
|
||||
|
||||
GV_base_url </br>
|
||||
Is the variable for your base URL (without `/` in the end).
|
||||
|
||||
GV_title </br>
|
||||
Is the variable for your web title.
|
||||
|
||||
You can put anything in here. like a variable or def.
|
27
pages/configuration/server.md
Normal file
27
pages/configuration/server.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Server (config/server.py)
|
||||
|
||||
`server` is the place to configure the server things.
|
||||
|
||||
`tools.sessions.on` <br>
|
||||
**Default:** `True` <br>
|
||||
**Description:** Enable a sessions
|
||||
|
||||
`engine.autoreload.on` <br>
|
||||
**Default:** `False` <br>
|
||||
**Description:** Auto Reload when source code change. Don't use it in production.
|
||||
|
||||
`request.show_tracebacks` <br>
|
||||
**Default:** `False` <br>
|
||||
**Description:** Show traceback for debugging in development purposes.
|
||||
|
||||
It have the configuration for the static error page for error code 403, 404, and 500 too.
|
||||
|
||||
from config import directory
|
||||
|
||||
update = {
|
||||
...
|
||||
'error_page.403' : directory.erpadir(403),
|
||||
'error_page.404' : directory.erpadir(404),
|
||||
'error_page.500' : directory.erpadir(500),
|
||||
...
|
||||
}
|
34
pages/configuration/template.md
Normal file
34
pages/configuration/template.md
Normal file
@ -0,0 +1,34 @@
|
||||
# Templating (config/template.py)
|
||||
|
||||
Templating is useful when you had more than 1 website template for difference use case. For an example, when you had user and admin in the use case actor, let say we can do the website for user have a navbar and footer, and the website for admin have a navbar and sidebar.
|
||||
|
||||
Before you create a template, make sure your `directory` configuration is ready for storing templates and pages. For an example:
|
||||
|
||||
user_page = "static/user/pages"
|
||||
user_template = "static/user/template"
|
||||
|
||||
To create the template, you need to insert this code in `def __init__(self)`
|
||||
|
||||
self.html_user_pages = html.main.get_html(directory.user_page)
|
||||
self.html_user_template = html.main.get_html(directory.user_template)
|
||||
|
||||
if you had admin template or email template, you just need to add the code. for the example like this
|
||||
|
||||
self.html_user_pages = html.main.get_html(directory.user_page)
|
||||
self.html_user_template = html.main.get_html(directory.user_template)
|
||||
|
||||
self.html_admin_pages = html.main.get_html(directory.admin_page)
|
||||
self.html_admin_template = html.main.get_html(directory.admin_template)
|
||||
|
||||
self.html_email_pages = html.main.get_html(directory.email_page)
|
||||
self.html_email_template = html.main.get_html(directory.email_template)
|
||||
|
||||
and then you need create function for each of your template in main class like this
|
||||
|
||||
def user(self, page):
|
||||
params_list = {
|
||||
"template" : self.html_user_template ["user.html" ],
|
||||
"topnav" : self.html_user_template ["user-topnav.html" ],
|
||||
"container" : self.html_user_pages [page+".html" ]
|
||||
}
|
||||
return params_list
|
243
pages/content/handler.md
Normal file
243
pages/content/handler.md
Normal file
@ -0,0 +1,243 @@
|
||||
# Handling the modules
|
||||
|
||||
## Import the modules
|
||||
|
||||
import modules.api.jwt as api_jwt
|
||||
|
||||
We can see the `modules.api.jwt` in the import, It mean `modules/api/jwt.py`.
|
||||
|
||||
## Routing the handler
|
||||
|
||||
The routing is starting in this class:
|
||||
|
||||
@cherrypy.tools.accept(media="application/json")
|
||||
class handler(pages.main):
|
||||
|
||||
def __init__(self):
|
||||
pages.main.__init__(self)
|
||||
|
||||
def index(self, **kwargs):
|
||||
...
|
||||
index.exposed = True
|
||||
|
||||
def <page_name>(self, **kwargs):
|
||||
...
|
||||
<page_name>.exposed = True
|
||||
|
||||
The `index` on there is `yourdomain.com/`.
|
||||
|
||||
let say the `<page_name>` is `about` page. so the route for the `about` is `yourdomain.com/about`.
|
||||
|
||||
How about if you want to create a route like this?: `yourdomain.com/about/profile` & `yourdomain.com/about/contact`.
|
||||
|
||||
The syntax will like this:
|
||||
|
||||
@cherrypy.tools.accept(media="application/json")
|
||||
class handler(pages.main):
|
||||
|
||||
def __init__(self):
|
||||
pages.main.__init__(self)
|
||||
|
||||
def index(self, **kwargs):
|
||||
kwargs["params_page"] = pages.main().user("home")
|
||||
return user_home.main().html(kwargs)
|
||||
index.exposed = True
|
||||
|
||||
class about(pages.main):
|
||||
|
||||
def profile(self, **kwargs):
|
||||
...
|
||||
profile.exposed = True
|
||||
|
||||
def contact(self, **kwargs):
|
||||
...
|
||||
contact.exposed = True
|
||||
|
||||
about=about()
|
||||
|
||||
## Add modules into handler
|
||||
|
||||
The handler request have a 2 kind method:
|
||||
- Request with JSON
|
||||
- Request with POST
|
||||
|
||||
### Request with JSON
|
||||
|
||||
when you create the handler with JSON request, the pattern will look like this:
|
||||
|
||||
def <page_name>(self, **kwargs):
|
||||
<authentication_token_check>
|
||||
<session_token>
|
||||
<default_response>
|
||||
if cherrypy.request.method == 'OPTIONS':
|
||||
cherrypy_cors.preflight(allowed_methods=['GET', 'POST'])
|
||||
if cherrypy.request.method == 'POST':
|
||||
try:
|
||||
cherrypy.serving.response.headers['Content-Type'] = 'application/json'
|
||||
kwargs["body"] = cherrypy.request.body.read()
|
||||
...
|
||||
<template_parameter>
|
||||
<summon_module>
|
||||
<response>
|
||||
...
|
||||
except Exception as e:
|
||||
<error_response>
|
||||
return <response>
|
||||
<page_name>.exposed = True
|
||||
|
||||
### Request with POST
|
||||
|
||||
The handler with HTML request pattern look more simple than JSON request one. It look like this:
|
||||
|
||||
def <page_name>(self, **kwargs):
|
||||
<authentication_token_check>
|
||||
<session_token>
|
||||
<template_parameter>
|
||||
<summon_module>
|
||||
<response>
|
||||
return <response>
|
||||
<page_name>.exposed = True
|
||||
|
||||
You can make it simple the pattern to look like this:
|
||||
|
||||
def <page_name>(self, **kwargs):
|
||||
<authentication_token_check>
|
||||
<session_token>
|
||||
<template_parameter>
|
||||
return <summon_module>
|
||||
<page_name>.exposed = True
|
||||
|
||||
### Explanation
|
||||
|
||||
The `<page_name>` is where you name the page.
|
||||
|
||||
`<authentication_token_check>` is the optional. It looks like this:
|
||||
|
||||
authentication.token_check(f"{globalvar.GV_base_url}/?message=forbidden")
|
||||
|
||||
`<session_token>` is optional. It will use to get the session token and bring it into modules. Here is the example:
|
||||
|
||||
kwargs["session_token"] = cherrypy.session.get("token")
|
||||
|
||||
`<default_response>` is the optional one. It is for safely giving return.
|
||||
|
||||
When you want to use a template, we can use `<template_parameter>` on there.
|
||||
|
||||
For an example we want to use `user` and `email` template, so this is a sample:
|
||||
|
||||
kwargs["template_user" ] = pages.main().user("register")
|
||||
kwargs["template_email" ] = pages.main().email("contact")
|
||||
|
||||
The `template_user` and `template_email` now can be use in the module.
|
||||
|
||||
`<summon_module>`, `<response>`, and `<error_response>` is the custom things.
|
||||
|
||||
You can create a JSON response like this:
|
||||
|
||||
module = api_jwt.main().change(kwargs)
|
||||
response = json.dumps(module, indent=2)
|
||||
return response.encode()
|
||||
|
||||
or HTML response like this:
|
||||
|
||||
return page_register.main().html(kwargs)
|
||||
|
||||
For the `return`, you can change it into `raise` for redirect. Here is the example:
|
||||
|
||||
...
|
||||
response = api_auth.main().register(kwargs)
|
||||
if response["status"] == "success":
|
||||
raise cherrypy.HTTPRedirect(f"{globalvar.GV_base_url}/?message=success")
|
||||
else:
|
||||
raise cherrypy.HTTPRedirect(f"{globalvar.GV_base_url}/?message=failed")
|
||||
...
|
||||
|
||||
### Session
|
||||
|
||||
To keep session, we can use `cherrypy.session` like this:
|
||||
|
||||
...
|
||||
response = api_auth.main().login(kwargs)
|
||||
if response["status"] == "success":
|
||||
token = response["data"]["token" ]
|
||||
username = response["data"]["username" ]
|
||||
try:
|
||||
cherrypy.session["token" ] = str( token )
|
||||
cherrypy.session["username" ] = str( username )
|
||||
raise cherrypy.HTTPRedirect('/?message=success')
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
raise cherrypy.HTTPRedirect('/login?message=failed')
|
||||
else:
|
||||
raise cherrypy.HTTPRedirect('/login?message=failed')
|
||||
...
|
||||
|
||||
And for the logout session, you can use this:
|
||||
|
||||
...
|
||||
cherrypy.lib.sessions.expire()
|
||||
raise cherrypy.HTTPRedirect(f"{globalvar.GV_base_url}/?message=logout")
|
||||
...
|
||||
|
||||
## Sample
|
||||
|
||||
### JSON method
|
||||
|
||||
def change(self, **kwargs):
|
||||
response = '{}'
|
||||
if cherrypy.request.method == 'OPTIONS':
|
||||
cherrypy_cors.preflight(allowed_methods=['GET', 'POST'])
|
||||
if cherrypy.request.method == 'POST':
|
||||
try:
|
||||
cherrypy.serving.response.headers['Content-Type'] = 'application/json'
|
||||
kwargs["body"] = cherrypy.request.body.read()
|
||||
module = api_jwt.main().change(kwargs)
|
||||
response = json.dumps(module, indent = 2)
|
||||
except Exception as e:
|
||||
response = '{}'
|
||||
return response.encode()
|
||||
change.exposed = True
|
||||
|
||||
### POST method
|
||||
|
||||
For giving JSON response:
|
||||
|
||||
def change(self, **kwargs):
|
||||
module = api_jwt.main().change(kwargs)
|
||||
response = json.dumps(module, indent=2)
|
||||
return response.encode()
|
||||
change.exposed = True
|
||||
|
||||
For giving HTML response with Template page:
|
||||
|
||||
def register(self, **kwargs):
|
||||
kwargs["params_page"] = pages.main().user("register")
|
||||
return page_register.main().html(kwargs)
|
||||
register.exposed = True
|
||||
|
||||
as you can see in here `pages.main().user("register")`, you need to add template pages in the parameter if you want to use a template.
|
||||
|
||||
### Combine method
|
||||
|
||||
Here is another JSON response that use a Template page:
|
||||
|
||||
def contact(self, **kwargs):
|
||||
balikan = '{}'
|
||||
if cherrypy.request.method == 'OPTIONS':
|
||||
cherrypy_cors.preflight(allowed_methods=['GET', 'POST'])
|
||||
if cherrypy.request.method == 'POST':
|
||||
try:
|
||||
cherrypy.serving.response.headers['Content-Type'] = 'application/json'
|
||||
kwargs["body"] = cherrypy.request.body.read()
|
||||
kwargs["email"] = pages.main().contact()
|
||||
response = api_contact.main().contact(kwargs)
|
||||
balikan = json.dumps(response, indent = 2)
|
||||
except Exception as e:
|
||||
print(f"DEBUG ERROR: {e}")
|
||||
balikan = '{}'
|
||||
return balikan.encode()
|
||||
contact.exposed = True
|
||||
|
||||
you can add the Template on the parameter like this:
|
||||
|
||||
kwargs["email"] = pages.main().contact()
|
60
pages/content/modules.md
Normal file
60
pages/content/modules.md
Normal file
@ -0,0 +1,60 @@
|
||||
# CostaPy Modules
|
||||
|
||||
## 1. Create APIADDR variable for LogGorilla
|
||||
|
||||
APIADDR = "/your/api/page/directory"
|
||||
|
||||
## 2. Declare default response
|
||||
|
||||
response = {}
|
||||
|
||||
## 3. Connect database
|
||||
|
||||
main_db = mariadb.connect(**database.main_db)
|
||||
cursor = main_db.cursor(dictionary=True)
|
||||
|
||||
## 4. Declare parameters
|
||||
|
||||
For POST method
|
||||
|
||||
jwt_token = params["jwt" ]
|
||||
|
||||
id = params["id" ]
|
||||
name = params["name" ]
|
||||
email = params["email" ]
|
||||
|
||||
For JSON method
|
||||
|
||||
body = params["body"].decode()
|
||||
form_param = json.loads(body)
|
||||
|
||||
jwt_token = form_param["jwt" ]
|
||||
|
||||
id = form_param["id" ]
|
||||
name = form_param["name" ]
|
||||
email = form_param["email" ]
|
||||
|
||||
## 5. Get payload from JWT
|
||||
|
||||
public_key = open('.ssh/id_rsa.pub', 'r').read()
|
||||
key = serialization.load_ssh_public_key(public_key.encode())
|
||||
header_data = jwt.get_unverified_header(jwt_token)
|
||||
payload = jwt.decode(
|
||||
jwt = jwt_token,
|
||||
key = key,
|
||||
algorithms = [header_data['alg'], ]
|
||||
)
|
||||
|
||||
session_id = payload["session_id"]
|
||||
|
||||
## 6. Main progress (try/except)
|
||||
|
||||
See `main-process.md` for the detail.
|
||||
|
||||
## 7. Close database
|
||||
|
||||
main_db.close()
|
||||
|
||||
## 8. Return response
|
||||
|
||||
return response
|
18
pages/core/loggorilla.md
Normal file
18
pages/core/loggorilla.md
Normal file
@ -0,0 +1,18 @@
|
||||
# LogGorilla
|
||||
|
||||
`loggorilla` is the extension script for logging.
|
||||
|
||||
There is 4 type for this logging availability:
|
||||
- `prcss` for logging a process
|
||||
- `fyinf` for logging a value
|
||||
- `accss` for logging an access
|
||||
- `error` for logging the error
|
||||
|
||||
Here is the example
|
||||
|
||||
APIADDR = "/your/api/page/directory"
|
||||
|
||||
loggorilla.prcss(APIADDR, f"Checking authority" )
|
||||
loggorilla.fyinf(APIADDR, f"Username: {username}" )
|
||||
loggorilla.prcss(APIADDR, f"{username} try to logged in and failed" )
|
||||
loggorilla.error(APIADDR, f"Username and Password is Incorrect" )
|
48
pages/getting-starter.md
Normal file
48
pages/getting-starter.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Getting Starter
|
||||
|
||||
## Requirement
|
||||
|
||||
You need a Python and this libraries to use CostaPy:
|
||||
- cherrypy
|
||||
- cherrypy-cors
|
||||
- mako
|
||||
- mysql-connector
|
||||
- bcrypt
|
||||
- pyjwt[crypto]
|
||||
|
||||
## Download
|
||||
|
||||
Download from repository
|
||||
|
||||
git clone https://github.com/ditaAjiPratama/costapy
|
||||
|
||||
## Installation
|
||||
|
||||
You can install it with run this command
|
||||
|
||||
sh install.sh
|
||||
|
||||
Here is the completed command
|
||||
|
||||
sudo apt-get install -y python3-pip
|
||||
pip install --upgrade pip
|
||||
pip install cherrypy
|
||||
pip install cherrypy-cors
|
||||
pip install mako
|
||||
pip install mysql-connector
|
||||
pip install bcrypt
|
||||
pip install pyjwt[crypto]
|
||||
|
||||
## Usage
|
||||
|
||||
Use this command to start the web service
|
||||
|
||||
python<ver> costa.py <ip_address> <port> <service_name>
|
||||
|
||||
For an example like this
|
||||
|
||||
python3 costa.py localhost 80 My_Service
|
||||
|
||||
You can use nohup too and running it in the background like this
|
||||
|
||||
nohup python3 costa.py localhost 80 My_Service &
|
167
pages/main-process.md
Normal file
167
pages/main-process.md
Normal file
@ -0,0 +1,167 @@
|
||||
# CostaPy Modules - Main Process
|
||||
|
||||
## Giving the response
|
||||
|
||||
Main response have 3 options:
|
||||
- status
|
||||
- desc
|
||||
- data
|
||||
|
||||
The response status only have 2 options:
|
||||
- success
|
||||
- failed
|
||||
|
||||
Example for the success response:
|
||||
|
||||
response["status" ] = "success"
|
||||
response["desc" ] = "Product list collected"
|
||||
response["data" ] = {
|
||||
"product" : product_list,
|
||||
"other" : "Some random text"
|
||||
}
|
||||
|
||||
Example for the failed response:
|
||||
|
||||
response["status" ] = "failed"
|
||||
response["desc" ] = "Something went wrong"
|
||||
response["data" ] = {
|
||||
"exception" : str(e)
|
||||
}
|
||||
|
||||
## Fetching data
|
||||
|
||||
### fetchone
|
||||
|
||||
cursor.execute(f"SELECT * FROM tablename WHERE fieldname = {id} ")
|
||||
row = cursor.fetchone()
|
||||
|
||||
token = row['token' ].decode()
|
||||
id = row['id' ]
|
||||
name = row['name' ]
|
||||
email = row['email' ]
|
||||
|
||||
### fetchall
|
||||
|
||||
cursor.execute(f"SELECT * FROM product ")
|
||||
product_list = cursor.fetchall()
|
||||
|
||||
### Nested fetchall
|
||||
|
||||
Variables:
|
||||
- `l1` is mean `List` level `1`
|
||||
- `c2` is mean `Count` level `2`
|
||||
- `d3` is mean `Data` level `3`
|
||||
- etc
|
||||
|
||||
Sample:
|
||||
|
||||
texture_list = []
|
||||
|
||||
cursor.execute(f"SELECT * FROM tableone WHERE fieldname = '{key}' ")
|
||||
l1 = cursor.fetchall()
|
||||
c1 = 0
|
||||
for d1 in l1:
|
||||
|
||||
texture_list.append({
|
||||
"id" :d1["id" ],
|
||||
"name" :d1["name" ],
|
||||
"desc" :d1["desc" ]
|
||||
})
|
||||
|
||||
cursor.execute(f"SELECT * FROM tabletwo WHERE keyfield = '{d1['id']}' ")
|
||||
l2 = cursor.fetchone()
|
||||
texture_list[c1]["owner"] = l2
|
||||
|
||||
cursor.execute(f"SELECT * FROM tablethree WHERE keyfield = '{d1['id']}' ")
|
||||
l2 = cursor.fetchall()
|
||||
texture_list[c1]["file"] = []
|
||||
c2 = 0
|
||||
for d2 in l2:
|
||||
texture_list[c1]["file"].append({
|
||||
"id" :d2["id" ],
|
||||
"filedir" :d2["filedir" ],
|
||||
"filetype" :d2["filetype" ]
|
||||
})
|
||||
|
||||
c2 += 1
|
||||
|
||||
c1 += 1
|
||||
|
||||
## Get the last row ID from insert query
|
||||
|
||||
cursor.execute(f"INSERT INTO `product_files` VALUES (DEFAULT, '{webdir}', '{filename}' ) ")
|
||||
product_files_lastrowid = cursor.lastrowid
|
||||
|
||||
## Begin, Rollback, and Commit
|
||||
|
||||
Begin, rollback, and commit can be useful if you use more than 1 process that cannot be separate. For example: more than 1 table insertion query, inserting query while upload success, etc.
|
||||
|
||||
The pattern:
|
||||
|
||||
cursor.execute("BEGIN;")
|
||||
try:
|
||||
# Process and response
|
||||
except Exception as e:
|
||||
cursor.execute("ROLLBACK;")
|
||||
# Process and response when failed
|
||||
cursor.execute("COMMIT;")
|
||||
|
||||
Sample:
|
||||
|
||||
cursor.execute("BEGIN;")
|
||||
|
||||
try:
|
||||
|
||||
cursor.execute(f"INSERT INTO `files` VALUES (DEFAULT, '{webdir}', '{filename}' ) ")
|
||||
files_lastrowid = cursor.lastrowid
|
||||
|
||||
cursor.execute(f"INSERT INTO `thumbnail` VALUES (DEFAULT, '{image}', '{files_lastrowid}' ) ")
|
||||
|
||||
response["status" ] = "success"
|
||||
response["desc" ] = "insert success"
|
||||
|
||||
loggorilla.prcss(APIADDR, f"insert success")
|
||||
|
||||
except Exception as e:
|
||||
|
||||
cursor.execute("ROLLBACK;")
|
||||
|
||||
response["status" ] = "failed"
|
||||
response["desc" ] = "There is error when processing try. See the exception for the clue."
|
||||
response["data" ] = {
|
||||
"exception" : str(e)
|
||||
}
|
||||
|
||||
loggorilla.error(APIADDR, f"{str(e)}")
|
||||
|
||||
cursor.execute("COMMIT;")
|
||||
|
||||
## File management
|
||||
|
||||
### Uploading
|
||||
|
||||
mediafile = params["mediafile"]
|
||||
name = "helloworld"
|
||||
ext = pathlib.Path(mediafile.filename).suffix
|
||||
dir = f"/srv/media/material/texture"
|
||||
|
||||
uploading.main(mediafile, name+ext, dir)
|
||||
|
||||
It will be overwrite if the file already on there.
|
||||
|
||||
### Removing
|
||||
|
||||
import pathlib
|
||||
import glob
|
||||
import os
|
||||
|
||||
dir = pathlib.Path(f"/srv/media/material/texture")
|
||||
name = "helloworld.png"
|
||||
|
||||
for row in glob.iglob(os.path.join(dir, name)):
|
||||
os.remove(row)
|
||||
|
||||
It can combining with `*`, for example:
|
||||
|
||||
dir = pathlib.Path(f"/srv/media/product/*/file")
|
||||
name = "*.zip"
|
17
pages/structure.md
Normal file
17
pages/structure.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Known the structure
|
||||
|
||||
![CostaPy structure](../images/structure.png "CostaPy have a 4 type of file")
|
||||
|
||||
It have a 4 type of file:
|
||||
- Main
|
||||
- Core
|
||||
- Configuration
|
||||
- Content
|
||||
|
||||
`Main` is the file that you will run with the Python.
|
||||
|
||||
`Core` is the place to put the extension script.
|
||||
|
||||
`Configuration` is for configuration.
|
||||
|
||||
and `Content` is the place for you to create a content.
|
Loading…
Reference in New Issue
Block a user