136 lines
6.0 KiB
Python
136 lines
6.0 KiB
Python
from PIL import Image, ImageDraw, ImageFont
|
|
from pathlib import Path
|
|
|
|
OUT = Path('/home/hyein/jh-mh/장헌산업/exports')
|
|
PNG = OUT / 'work_data_erd_dark.png'
|
|
W, H = 1400, 1900
|
|
img = Image.new('RGB', (W, H), '#17191a')
|
|
d = ImageDraw.Draw(img)
|
|
font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', 17)
|
|
font_b = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', 18)
|
|
font_t = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', 24)
|
|
font_s = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', 13)
|
|
|
|
BG = '#17191a'
|
|
CARD = '#1e2224'
|
|
CARD2 = '#202426'
|
|
LINE = '#4b5256'
|
|
LINE2 = '#697176'
|
|
TEXT = '#e6ecef'
|
|
MUTED = '#9aa4aa'
|
|
PK = '#f4d06f'
|
|
FK = '#8cc7ff'
|
|
ACCENT = '#d0d6d9'
|
|
|
|
entities = {
|
|
'member': {
|
|
'xy': (150, 360), 'w': 250,
|
|
'fields': ['PK MemberNo', 'korName', 'teamName', 'rankName', 'groupCode', 'rankCode', 'isRetired']
|
|
},
|
|
'dallyproject': {
|
|
'xy': (185, 720), 'w': 290,
|
|
'fields': ['PK id', 'FK MemberNo', 'WorkDate', 'EntryPCode', 'EntryTime', 'LeaveTime', 'OverTime', 'TotalHours', 'RegularHours', 'OvertimeHours']
|
|
},
|
|
'work_calendar_day': {
|
|
'xy': (70, 1190), 'w': 315,
|
|
'fields': ['PK memberNo + workDate', 'korName', 'teamName', 'rankName', 'sqlHours', 'sqlProjectCodes', 'siteCount', 'siteProjectCodes', 'siteWorkTexts', 'hasSql', 'hasSite']
|
|
},
|
|
'work_calendar_detail': {
|
|
'xy': (515, 920), 'w': 310,
|
|
'fields': ['PK id', 'source: sql / site', 'FK memberNo', 'workDate', 'projectCode', 'projectName', 'workText', 'jobType', 'hours', 'regularHours', 'overtimeHours', 'personCount']
|
|
},
|
|
'project_alias': {
|
|
'xy': (655, 70), 'w': 315,
|
|
'fields': ['PK projectCode', 'shortName', 'bridge/project name']
|
|
},
|
|
'site_worksheet_record': {
|
|
'xy': (680, 430), 'w': 340,
|
|
'fields': ['PK projectCode + workDate', 'PK memberNo + korName', 'FK memberNo', 'jobType', 'workText', 'note', 'personCount']
|
|
},
|
|
'site_worksheet_worker_cache': {
|
|
'xy': (960, 170), 'w': 360,
|
|
'fields': ['PK projectCode + workDate', 'PK korName + jobType', 'workText', 'note', 'personCount', 'syncedAt']
|
|
},
|
|
'site_worksheet_day_sync': {
|
|
'xy': (930, 760), 'w': 310,
|
|
'fields': ['PK projectCode + workDate', 'syncedAt']
|
|
},
|
|
'site_worksheet_menu_sync': {
|
|
'xy': (860, 1090), 'w': 335,
|
|
'fields': ['PK projectCode + workDate', 'PK selMenu', '2 = normal worksheet', '3 = tension/temp works', 'syncedAt']
|
|
},
|
|
}
|
|
|
|
# compute heights
|
|
for e in entities.values():
|
|
e['h'] = 52 + len(e['fields']) * 26 + 18
|
|
|
|
|
|
def rounded_box(x, y, w, h, name, fields):
|
|
d.rounded_rectangle((x, y, x+w, y+h), radius=6, fill=CARD, outline='#3c4246', width=2)
|
|
d.rectangle((x, y, x+w, y+42), fill=CARD2)
|
|
d.line((x, y+42, x+w, y+42), fill='#3c4246', width=1)
|
|
d.text((x+16, y+12), name, font=font_b, fill=TEXT)
|
|
yy = y + 58
|
|
for f in fields:
|
|
color = TEXT
|
|
if f.startswith('PK'):
|
|
color = PK
|
|
elif f.startswith('FK'):
|
|
color = FK
|
|
d.text((x+18, yy), f, font=font_s, fill=color)
|
|
yy += 26
|
|
|
|
|
|
def pt(name, side):
|
|
e = entities[name]
|
|
x, y = e['xy']; w = e['w']; h = e['h']
|
|
if side == 'l': return (x, y+h//2)
|
|
if side == 'r': return (x+w, y+h//2)
|
|
if side == 't': return (x+w//2, y)
|
|
if side == 'b': return (x+w//2, y+h)
|
|
|
|
|
|
def poly(points, label=None, label_pos=None):
|
|
d.line(points, fill=LINE, width=2, joint='curve')
|
|
for x, y in (points[0], points[-1]):
|
|
d.ellipse((x-4, y-4, x+4, y+4), fill=ACCENT)
|
|
if label:
|
|
x, y = label_pos or points[len(points)//2]
|
|
tw = d.textlength(label, font=font_s)
|
|
d.rounded_rectangle((x-tw/2-7, y-12, x+tw/2+7, y+12), radius=4, fill=BG, outline='#363b3f')
|
|
d.text((x-tw/2, y-9), label, font=font_s, fill=MUTED)
|
|
|
|
# title
|
|
d.text((70, 35), 'Work Data ERD', font=font_t, fill=TEXT)
|
|
d.text((70, 68), 'SQL work records + ERP site worksheet integration', font=font_s, fill=MUTED)
|
|
|
|
# relation lines behind cards
|
|
poly([pt('member','b'), (275, 665), pt('dallyproject','t')], 'MemberNo', (300, 650))
|
|
poly([pt('dallyproject','b'), (330, 1085), (520, 1085), pt('work_calendar_detail','l')], 'source=sql', (450, 1072))
|
|
poly([pt('work_calendar_detail','l'), (425, 1100), (425, 1350), pt('work_calendar_day','r')], 'daily summary', (426, 1280))
|
|
poly([pt('member','l'), (55, 460), (55, 1370), pt('work_calendar_day','l')], 'MemberNo', (56, 820))
|
|
poly([pt('member','r'), (520, 500), (520, 560), pt('site_worksheet_record','l')], 'MemberNo', (520, 538))
|
|
poly([pt('project_alias','b'), (815, 330), pt('site_worksheet_record','t')], 'projectCode', (810, 340))
|
|
poly([pt('project_alias','r'), (1070, 170), pt('site_worksheet_worker_cache','l')], 'projectCode', (1050, 145))
|
|
poly([pt('site_worksheet_worker_cache','b'), (1050, 405), (1000, 405), pt('site_worksheet_record','r')], 'match name/date/project', (1010, 410))
|
|
poly([pt('site_worksheet_record','b'), (740, 880), pt('work_calendar_detail','t')], 'source=site', (715, 850))
|
|
poly([pt('site_worksheet_day_sync','t'), (1005, 650), (1080, 400), pt('site_worksheet_worker_cache','b')], 'project+date fetched', (1040, 645))
|
|
poly([pt('site_worksheet_menu_sync','t'), (935, 1030), (1085, 400), pt('site_worksheet_worker_cache','b')], 'menu 2/3 fetched', (950, 1035))
|
|
poly([pt('project_alias','l'), (560, 185), (560, 825), pt('dallyproject','r')], 'EntryPCode', (560, 420))
|
|
poly([pt('project_alias','b'), (750, 780), pt('work_calendar_detail','r')], 'projectName', (755, 760))
|
|
|
|
# draw cards
|
|
for name, e in entities.items():
|
|
x, y = e['xy']
|
|
rounded_box(x, y, e['w'], e['h'], name, e['fields'])
|
|
|
|
# subtle legend
|
|
d.rounded_rectangle((70, 1740, 1270, 1845), radius=8, fill='#1c2022', outline='#343a3e')
|
|
d.text((90, 1765), 'Flow', font=font_b, fill=TEXT)
|
|
d.text((90, 1795), 'dallyproject -> work_calendar_detail -> work_calendar_day', font=font_s, fill=MUTED)
|
|
d.text((90, 1820), 'site_worksheet_worker_cache -> site_worksheet_record -> work_calendar_detail/day', font=font_s, fill=MUTED)
|
|
|
|
img.save(PNG)
|
|
print(PNG)
|