Skip to content

Instantly share code, notes, and snippets.

@code-boxx
Last active October 7, 2023 05:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save code-boxx/8ab25db70152d940f642cc6fa80f486f to your computer and use it in GitHub Desktop.
Save code-boxx/8ab25db70152d940f642cc6fa80f486f to your computer and use it in GitHub Desktop.
Category & Subcategory In Pyhton

PYTHON CATEGORY & SUBCATEGORY

https://code-boxx.com/categories-subcategories-python/

NOTES

  1. Run unpack.bat (Windows) unpack.sh (Linux/Mac). This will automatically:
    • Create a virtual environment - virtualenv venv.
    • Activate the virtual environment - venv\scripts\activate (Windows) venv/bin/activate (Mac/Linux)
    • Run python S1B_create.py to create the category database.
    • Run python S3_draw.py and draw the nested categories.
  2. Also see S4_actions.py for the "admin functions".

LICENSE

Copyright by Code Boxx

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

CREATE TABLE category (
category_id INTEGER,
parent_id INTEGER DEFAULT 0,
category_name TEXT NOT NULL,
PRIMARY KEY("category_id" AUTOINCREMENT)
);
CREATE INDEX parent_id ON category (parent_id);
INSERT INTO `category` (`category_id`, `parent_id`, `category_name`) VALUES
(1, 0, 'Electronics'),
(2, 1, 'Computers'),
(3, 1, 'Cameras'),
(4, 2, 'Desktop'),
(5, 2, 'Laptop');
# (A) LOAD PACKAGES
import sqlite3, os
from sqlite3 import Error
# (B) DATABASE + SQL FILE
DBFILE = "category.db"
SQLFILE = "S1A_category.sql"
# (C) DELETE OLD DATABASE IF EXIST
if os.path.exists(DBFILE):
os.remove(DBFILE)
# (D) IMPORT SQL
conn = sqlite3.connect(DBFILE)
with open(SQLFILE) as f:
conn.executescript(f.read())
conn.commit()
conn.close()
print("Database created!")
# (A) LOAD SQLITE MODULE
import sqlite3
DBFILE = "category.db"
# (B) GET CATEGORIES
def get (parent=0):
# (B1) CONNECT
conn = sqlite3.connect(DBFILE)
cursor = conn.cursor()
# (B2) GET CATEGORY WITH PARENT ID
cursor.execute(
"SELECT * FROM `category` WHERE `parent_id`=?",
(parent,)
)
rows = cursor.fetchall()
# (B3) NO CHILDREN IN THIS CATEGORY
if len(rows)==0:
return None
# (B4) ARRANGE DATA & RECURSIVE
data = {}
for r in rows:
data[r[0]] = {
"n" : r[2],
"c" : get(r[0])
}
return data
# (C) SAVE CATEGORY
def save (name, parent, id=None):
# (C1) CONNECT TO DATABASE
conn = sqlite3.connect(DBFILE)
cursor = conn.cursor()
# (C2) ADD OR UPDATE SQL
if id is None:
sql = "INSERT INTO `category` (`category_name`, `parent_id`) VALUES (?,?)"
data = (name, parent,)
else:
sql = "UPDATE `category` SET `category_name`=?, `parent_id`=? WHERE `category_id`=?"
data = (name, parent, id,)
# (C3) GO!
cursor.execute(sql, data)
conn.commit()
conn.close()
return True
# (D) GET CHILD CATEGORIES
def getchildren (id):
conn = sqlite3.connect(DBFILE)
cursor = conn.cursor()
cursor.execute(
"SELECT `category_id` FROM `category` WHERE `parent_id`=?",
(id,)
)
cat = ()
for r in cursor.fetchall():
cat = cat + r
for id in cat:
cat = cat + getchildren(id)
return cat
# (E) GET "LEGAL" PARENT IDS
def getswitchable (id):
# (E1) PARENT ID CANNOT BE SELF AND CHILDREN
illegal = getchildren(id)
illegal = illegal + (id,)
illegal = ",".join([str(v) for v in illegal])
# (E2) GET ALL "LEGAL" PARENT ID
conn = sqlite3.connect(DBFILE)
cursor = conn.cursor()
cursor.execute(f"SELECT `category_id` FROM `category` WHERE `category_id` NOT IN ({illegal})")
cat = (0,)
for r in cursor.fetchall():
cat = cat + r
return cat
# (F) DELETE CATEGORY
def delete (id):
# (F1) CONNECT TO DATABASE
conn = sqlite3.connect(DBFILE)
cursor = conn.cursor()
# (F2) REVERT ALL CHILDREN TO ROOT
children = getchildren(id)
if (len(children)>0):
children = ",".join([str(v) for v in children])
cursor.execute(f"UPDATE `category` SET `parent_id`=0 WHERE `category_id` IN ({children})")
# (F3) DELETE CATEGORY
cursor.execute(
"DELETE FROM `category` WHERE `category_id`=?",
(id,)
)
conn.commit()
conn.close()
return True
# (A) LOAD MODULES
import S2_lib as cat
# (B) DRAW CATEGORIES
def draw (data, level=0):
for id, cat in data.items():
# (B1) INDENTATION
if level>0:
for i in range(level):
print(" ", end="")
# (B2) CATEGORY ID & NAME
print(f"({id}) {cat['n']}")
if cat["c"] is not None:
draw(cat["c"], level+1)
# (C) GO!
draw(cat.get())
# (A) LOAD MODULES
import S2_lib as cat
# (B) ADD A NEW CATEGORY
#cat.save("Smartphones", 2)
#cat.save("Tablets", 2)
# (C) UPDATE CATEGORY
#all = cat.getswitchable(2)
#print(all)
#cat.save("Computerzzz", 3, 2)
# (D) DELETE CATEGORY
cat.delete(2)
virtualenv venv
call venv\Scripts\activate
python S1B_create.py
python S3_draw.py
virtualenv venv
source "venv/bin/activate"
python S1B_create.py
python S3_draw.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment