Small Bumps on fingertips water blisters and pain associated
dyshidrotic eczema pompholyx treatment

| Step | Treatment |
|---|---|
| First-line | High-potency topical corticosteroids (e.g., clobetasol) - especially with occlusion at night |
| Symptom relief | Cold compresses for burning; greasy emollients (petrolatum/Aquaphor) to moisturize and prevent fissures |
| Fissures | Cyanoacrylate ("superglue") to seal cracks and reduce pain |
| Acute flares | Short course of oral corticosteroids |
| Refractory cases | Topical calcineurin inhibitors (tacrolimus), phototherapy (PUVA), or systemic immunosuppressants |
| New option | Dupilumab - two recent systematic reviews (2025-2026) confirm efficacy and safety for chronic hand eczema including dyshidrosis [PMID: 39501849, PMID: 39976781] |
| Protection | Vinyl gloves during wet work; cotton gloves under vinyl to prevent sweat maceration |
| Feature | Dyshidrotic Eczema | Herpetic Whitlow | Contact Dermatitis |
|---|---|---|---|
| Location | Sides of fingers, palms, soles | Distal fingertip only | Exposed contact areas |
| Distribution | Bilateral, symmetric | Usually 1 finger | Asymmetric, pattern |
| Itch vs. Pain | Intense itch + burning | Burning + pain | Itch predominates |
| Fever/systemic signs | No | Sometimes | No |
| Recurrence | Common | Yes (herpes recurs) | With re-exposure |
| Treatment | Topical steroids | Antivirals (acyclovir) | Avoid trigger + steroids |
Are your blisters on one finger or multiple fingers across palms?
Generate a symptom tracker to monitor your blister progression
ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace not found"
mkdir -p /home/daytona/workspace/blister-tracker
~/blister-tracker/create_tracker.py
import openpyxl
from openpyxl.styles import (
PatternFill, Font, Alignment, Border, Side, GradientFill
)
from openpyxl.utils import get_column_letter
from openpyxl.chart import LineChart, Reference, BarChart
from openpyxl.chart.series import DataPoint
from openpyxl.worksheet.datavalidation import DataValidation
from datetime import date, timedelta
wb = openpyxl.Workbook()
# ─── Color palette ───────────────────────────────────────────────
TEAL_DARK = "1A6B72"
TEAL_MID = "2E9EA8"
TEAL_LIGHT = "D6F0F3"
ORANGE = "E87722"
RED_LIGHT = "FDDEDE"
RED_DARK = "C0392B"
GREEN_LIGHT = "DFF5E3"
GREEN_DARK = "1E7C3A"
YELLOW = "FFF3CD"
GREY_LIGHT = "F5F5F5"
GREY_MED = "D0D0D0"
WHITE = "FFFFFF"
DARK_TEXT = "1A1A2E"
ORANGE_LIGHT= "FEE8D6"
def solid(hex_color):
return PatternFill("solid", fgColor=hex_color)
def thin_border(left=True, right=True, top=True, bottom=True):
s = Side(style="thin", color="BBBBBB")
n = Side(style=None)
return Border(
left=s if left else n,
right=s if right else n,
top=s if top else n,
bottom=s if bottom else n
)
def header_font(size=11, bold=True, color=WHITE):
return Font(name="Arial", size=size, bold=bold, color=color)
def body_font(size=10, bold=False, color=DARK_TEXT):
return Font(name="Arial", size=size, bold=bold, color=color)
def center():
return Alignment(horizontal="center", vertical="center", wrap_text=True)
def left_align():
return Alignment(horizontal="left", vertical="center", wrap_text=True)
# ══════════════════════════════════════════════════════════════════
# SHEET 1 — DAILY LOG
# ══════════════════════════════════════════════════════════════════
ws1 = wb.active
ws1.title = "Daily Log"
ws1.sheet_view.showGridLines = False
ws1.freeze_panes = "A5"
# Column widths
col_widths = {
"A": 13, "B": 14, "C": 14, "D": 14, "E": 14,
"F": 14, "G": 16, "H": 16, "I": 14, "J": 16,
"K": 18, "L": 18, "M": 22
}
for col, width in col_widths.items():
ws1.column_dimensions[col].width = width
# Row heights
ws1.row_dimensions[1].height = 40
ws1.row_dimensions[2].height = 22
ws1.row_dimensions[3].height = 28
ws1.row_dimensions[4].height = 36
# Title banner (row 1, A1:M1)
ws1.merge_cells("A1:M1")
c = ws1["A1"]
c.value = "🩹 BLISTER PROGRESSION SYMPTOM TRACKER"
c.font = Font(name="Arial", size=18, bold=True, color=WHITE)
c.fill = solid(TEAL_DARK)
c.alignment = center()
# Subtitle (row 2)
ws1.merge_cells("A2:M2")
c = ws1["A2"]
c.value = "Track daily blister symptoms to monitor progression and share with your doctor"
c.font = Font(name="Arial", size=10, italic=True, color="555555")
c.fill = solid(TEAL_LIGHT)
c.alignment = center()
# Instructions row (row 3)
ws1.merge_cells("A3:M3")
c = ws1["A3"]
c.value = ("HOW TO USE: Fill in one row per day. "
"Pain & Itch = 0 (none) → 10 (worst). "
"Blister Stage: 1=New vesicle 2=Filling 3=Peaked 4=Drying 5=Healed. "
"Highlight rows in RED if you see infection signs (pus, fever, red streaking).")
c.font = Font(name="Arial", size=9, color=DARK_TEXT)
c.fill = solid(YELLOW)
c.alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
# Column headers (row 4)
headers = [
"Date",
"Fingers\nAffected",
"Hand(s)\nAffected",
"Blister\nCount",
"Blister\nSize (mm)",
"Blister\nStage (1-5)",
"Pain\nScore (0-10)",
"Itch\nScore (0-10)",
"Burning\nSensation?",
"Skin Color\nAround Blister",
"Treatment\nApplied Today",
"Trigger /\nExposure",
"Notes / Doctor\nQuestions"
]
for col_idx, hdr in enumerate(headers, start=1):
cell = ws1.cell(row=4, column=col_idx)
cell.value = hdr
cell.font = header_font(size=10)
cell.fill = solid(TEAL_MID)
cell.alignment = center()
cell.border = thin_border()
# Pre-fill 30 days of date rows starting from today
start_date = date(2026, 6, 6)
finger_options = ["Right index", "Right middle", "Right ring", "Left index",
"Left middle", "Multiple R", "Multiple L", "Both hands"]
hand_options = ["Right only", "Left only", "Both hands"]
color_options = ["Normal", "Pink/Red", "Dark red", "Brown", "White/Pale"]
treatment_options = ["None", "Cold compress", "Topical steroid cream",
"Moisturiser/emollient", "Antiviral cream", "Oral steroid",
"Acyclovir (oral)", "Valacyclovir", "Superglue (fissure)"]
# Data validations
dv_stage = DataValidation(type="whole", operator="between", formula1="1", formula2="5",
showErrorMessage=True, errorTitle="Invalid Stage",
error="Enter 1 (new) to 5 (healed)")
dv_score = DataValidation(type="whole", operator="between", formula1="0", formula2="10",
showErrorMessage=True, errorTitle="Invalid Score",
error="Enter 0 to 10")
dv_yn = DataValidation(type="list", formula1='"Yes,No,Mild"',
showDropDown=False)
dv_color = DataValidation(type="list",
formula1='"Normal,Pink/Red,Dark red,Brown,White/Pale"',
showDropDown=False)
ws1.add_data_validation(dv_stage)
ws1.add_data_validation(dv_score)
ws1.add_data_validation(dv_yn)
ws1.add_data_validation(dv_color)
for i in range(30):
row = 5 + i
ws1.row_dimensions[row].height = 20
entry_date = start_date + timedelta(days=i)
fill = solid(WHITE) if i % 2 == 0 else solid(GREY_LIGHT)
for col in range(1, 14):
cell = ws1.cell(row=row, column=col)
cell.fill = fill
cell.font = body_font()
cell.border = thin_border()
cell.alignment = center()
# Date column pre-filled
ws1.cell(row=row, column=1).value = entry_date
ws1.cell(row=row, column=1).number_format = "DD-MMM-YYYY"
ws1.cell(row=row, column=1).font = Font(name="Arial", size=10, bold=True, color=TEAL_DARK)
# Apply validations
dv_stage.sqref += f"F{row}"
dv_score.sqref += f"G{row} H{row}"
dv_yn.sqref += f"I{row}"
dv_color.sqref += f"J{row}"
# Conditional formatting hint row at bottom
hint_row = 36
ws1.merge_cells(f"A{hint_row}:M{hint_row}")
c = ws1.cell(row=hint_row, column=1)
c.value = ("⚠ SEEK MEDICAL ATTENTION if you notice: pus or cloudy fluid in blisters | "
"red streaking from the blister | fever | severe swelling | pain score ≥ 8 "
"| no improvement after 2-3 weeks")
c.font = Font(name="Arial", size=9, bold=True, color=RED_DARK)
c.fill = solid(RED_LIGHT)
c.alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
ws1.row_dimensions[hint_row].height = 28
# ══════════════════════════════════════════════════════════════════
# SHEET 2 — BODY MAP (Finger selector)
# ══════════════════════════════════════════════════════════════════
ws2 = wb.create_sheet("Finger Map & Severity")
ws2.sheet_view.showGridLines = False
# Title
ws2.merge_cells("A1:N1")
c = ws2["A1"]
c.value = "FINGER BLISTER MAP — Mark affected fingers each day"
c.font = Font(name="Arial", size=14, bold=True, color=WHITE)
c.fill = solid(TEAL_DARK)
c.alignment = center()
ws2.row_dimensions[1].height = 36
# Instructions
ws2.merge_cells("A2:N2")
c = ws2["A2"]
c.value = ("Use the color key below. In each cell enter 0 = Clear, "
"1 = New blister, 2 = Active/painful, 3 = Drying, 4 = Healed. "
"Date columns are pre-filled for 30 days.")
c.font = Font(name="Arial", size=9, italic=True, color=DARK_TEXT)
c.fill = solid(TEAL_LIGHT)
c.alignment = left_align()
ws2.row_dimensions[2].height = 20
# Color key
key_data = [
("0 = Clear / No blister", WHITE, DARK_TEXT),
("1 = New vesicle (forming)", "FFF9C4", "7B6000"),
("2 = Active / painful", "FFCCBC", "BF360C"),
("3 = Drying / crusting", "D7CCC8", "4E342E"),
("4 = Healed", "C8E6C9", "1B5E20"),
]
ws2.row_dimensions[4].height = 18
ws2.merge_cells("A4:B4")
ws2.cell(row=4, column=1).value = "COLOR KEY:"
ws2.cell(row=4, column=1).font = Font(name="Arial", size=10, bold=True, color=DARK_TEXT)
for ki, (label, bg, fg) in enumerate(key_data):
r = 5 + ki
ws2.row_dimensions[r].height = 18
ws2.merge_cells(f"A{r}:C{r}")
cell = ws2.cell(row=r, column=1)
cell.value = label
cell.font = Font(name="Arial", size=9, bold=True, color=fg)
cell.fill = solid(bg)
cell.alignment = left_align()
cell.border = thin_border()
# Finger map table
fingers = [
"RIGHT HAND - Thumb",
"RIGHT HAND - Index",
"RIGHT HAND - Middle",
"RIGHT HAND - Ring",
"RIGHT HAND - Little",
"── separator ──",
"LEFT HAND - Thumb",
"LEFT HAND - Index",
"LEFT HAND - Middle",
"LEFT HAND - Ring",
"LEFT HAND - Little",
"── separator ──",
"RIGHT PALM",
"LEFT PALM",
]
map_start_row = 12
ws2.row_dimensions[map_start_row - 1].height = 20
# Header: Date columns
ws2.column_dimensions["A"].width = 26
ws2.cell(row=map_start_row, column=1).value = "Finger / Area"
ws2.cell(row=map_start_row, column=1).font = header_font()
ws2.cell(row=map_start_row, column=1).fill = solid(TEAL_DARK)
ws2.cell(row=map_start_row, column=1).alignment = center()
ws2.cell(row=map_start_row, column=1).border = thin_border()
for d in range(30):
col = 2 + d
col_letter = get_column_letter(col)
ws2.column_dimensions[col_letter].width = 9
hdr_cell = ws2.cell(row=map_start_row, column=col)
hdr_cell.value = (start_date + timedelta(days=d)).strftime("%d-%b")
hdr_cell.font = header_font(size=8)
hdr_cell.fill = solid(TEAL_MID)
hdr_cell.alignment = center()
hdr_cell.border = thin_border()
dv_map = DataValidation(type="whole", operator="between", formula1="0", formula2="4",
showErrorMessage=True)
ws2.add_data_validation(dv_map)
for fi, finger in enumerate(fingers):
row = map_start_row + 1 + fi
ws2.row_dimensions[row].height = 18
name_cell = ws2.cell(row=row, column=1)
is_sep = "separator" in finger
if is_sep:
ws2.merge_cells(f"A{row}:{get_column_letter(31)}{row}")
name_cell.value = ""
name_cell.fill = solid(GREY_MED)
continue
name_cell.value = finger
name_cell.font = Font(name="Arial", size=9, bold=("HAND" in finger or "PALM" in finger),
color=DARK_TEXT)
name_cell.fill = solid(TEAL_LIGHT) if "RIGHT" in finger else solid(ORANGE_LIGHT)
name_cell.alignment = left_align()
name_cell.border = thin_border()
for d in range(30):
col = 2 + d
cell = ws2.cell(row=row, column=col)
cell.value = 0
cell.fill = solid(WHITE)
cell.font = body_font(size=9)
cell.alignment = center()
cell.border = thin_border()
dv_map.sqref += f"{get_column_letter(col)}{row}"
# ══════════════════════════════════════════════════════════════════
# SHEET 3 — CHARTS / TREND
# ══════════════════════════════════════════════════════════════════
ws3 = wb.create_sheet("Trend Charts")
ws3.sheet_view.showGridLines = False
ws3.merge_cells("A1:L1")
c = ws3["A1"]
c.value = "SYMPTOM TREND OVERVIEW — Auto-updates as you fill the Daily Log"
c.font = Font(name="Arial", size=13, bold=True, color=WHITE)
c.fill = solid(TEAL_DARK)
c.alignment = center()
ws3.row_dimensions[1].height = 32
# Instruction
ws3.merge_cells("A2:L2")
c = ws3["A2"]
c.value = ("Charts below pull from the Daily Log sheet. "
"Pain score (col G) and Itch score (col H) are plotted against dates. "
"Blister count (col D) is shown as a bar chart.")
c.font = Font(name="Arial", size=9, italic=True, color=DARK_TEXT)
c.fill = solid(TEAL_LIGHT)
c.alignment = left_align()
ws3.row_dimensions[2].height = 18
# Pain + Itch line chart
pain_itch_chart = LineChart()
pain_itch_chart.title = "Pain & Itch Scores Over Time (0-10)"
pain_itch_chart.style = 10
pain_itch_chart.y_axis.title = "Score (0-10)"
pain_itch_chart.x_axis.title = "Day"
pain_itch_chart.y_axis.scaling.min = 0
pain_itch_chart.y_axis.scaling.max = 10
pain_itch_chart.width = 20
pain_itch_chart.height = 12
pain_data = Reference(wb["Daily Log"], min_col=7, max_col=7, min_row=4, max_row=34)
itch_data = Reference(wb["Daily Log"], min_col=8, max_col=8, min_row=4, max_row=34)
pain_itch_chart.add_data(pain_data, titles_from_data=True)
pain_itch_chart.add_data(itch_data, titles_from_data=True)
# Style series
pain_itch_chart.series[0].graphicalProperties.line.solidFill = RED_DARK
pain_itch_chart.series[0].graphicalProperties.line.width = 20000
pain_itch_chart.series[1].graphicalProperties.line.solidFill = TEAL_MID
pain_itch_chart.series[1].graphicalProperties.line.width = 20000
dates_ref = Reference(wb["Daily Log"], min_col=1, min_row=5, max_row=34)
pain_itch_chart.set_categories(dates_ref)
ws3.add_chart(pain_itch_chart, "A4")
# Blister count bar chart
blister_chart = BarChart()
blister_chart.title = "Blister Count Over Time"
blister_chart.style = 10
blister_chart.y_axis.title = "Number of Blisters"
blister_chart.x_axis.title = "Day"
blister_chart.width = 20
blister_chart.height = 12
blister_data = Reference(wb["Daily Log"], min_col=4, max_col=4, min_row=4, max_row=34)
blister_chart.add_data(blister_data, titles_from_data=True)
blister_chart.series[0].graphicalProperties.solidFill = TEAL_MID
blister_chart.set_categories(dates_ref)
ws3.add_chart(blister_chart, "A24")
# ══════════════════════════════════════════════════════════════════
# SHEET 4 — DOCTOR SUMMARY
# ══════════════════════════════════════════════════════════════════
ws4 = wb.create_sheet("Doctor Summary")
ws4.sheet_view.showGridLines = False
ws4.column_dimensions["A"].width = 30
ws4.column_dimensions["B"].width = 52
ws4.merge_cells("A1:B1")
c = ws4["A1"]
c.value = "DOCTOR VISIT SUMMARY — Print and bring to your appointment"
c.font = Font(name="Arial", size=13, bold=True, color=WHITE)
c.fill = solid(TEAL_DARK)
c.alignment = center()
ws4.row_dimensions[1].height = 34
sections = [
("PATIENT INFORMATION", None, TEAL_MID, WHITE),
("Patient Name:", "", None, None),
("Date of Birth:", "", None, None),
("Date of First Blister:", "", None, None),
("Date of Visit:", "", None, None),
("", "", None, None),
("BLISTER CHARACTERISTICS", None, TEAL_MID, WHITE),
("Location (fingers/palms):", "", None, None),
("One hand or both?:", "", None, None),
("Approximate blister count:", "", None, None),
("Blister size:", "", None, None),
("Blister appearance:", "Clear / Cloudy / Bloody", None, None),
("Do blisters merge?:", "Yes / No", None, None),
("", "", None, None),
("SYMPTOMS", None, TEAL_MID, WHITE),
("Peak pain score (0-10):", "", None, None),
("Peak itch score (0-10):", "", None, None),
("Burning sensation?:", "Yes / No / Sometimes", None, None),
("Fever associated?:", "Yes / No", None, None),
("Skin redness / streaking?:", "Yes / No", None, None),
("", "", None, None),
("HISTORY & TRIGGERS", None, TEAL_MID, WHITE),
("Previous episodes?:", "Yes / No — How often?", None, None),
("Known allergies:", "", None, None),
("Occupation / wet work exposure:", "", None, None),
("Recent stress?:", "Yes / No", None, None),
("Contact with new products/metals?:", "", None, None),
("Similar rash elsewhere on body?:", "Yes / No — Where?", None, None),
("", "", None, None),
("TREATMENTS TRIED", None, TEAL_MID, WHITE),
("Topical treatments used:", "", None, None),
("Oral medications taken:", "", None, None),
("Did any treatment help?:", "Yes / No / Partial", None, None),
("Side effects noticed?:", "", None, None),
("", "", None, None),
("QUESTIONS FOR DOCTOR", None, TEAL_MID, WHITE),
("Q1:", "Is this dyshidrotic eczema, contact dermatitis, or herpetic whitlow?", None, None),
("Q2:", "Do I need patch testing for allergens?", None, None),
("Q3:", "Should I use a prescription-strength steroid cream?", None, None),
("Q4:", "When should I worry about infection?", None, None),
("Q5:", "Add your own question here:", None, None),
]
for i, item in enumerate(sections):
row = i + 2
ws4.row_dimensions[row].height = 22
label, value, hdr_fill, hdr_font_color = item
if hdr_fill: # Section header
ws4.merge_cells(f"A{row}:B{row}")
cell = ws4.cell(row=row, column=1)
cell.value = label
cell.font = Font(name="Arial", size=10, bold=True, color=hdr_font_color)
cell.fill = solid(hdr_fill)
cell.alignment = center()
cell.border = thin_border()
elif label == "": # Spacer
for col in [1, 2]:
ws4.cell(row=row, column=col).fill = solid(GREY_LIGHT)
ws4.row_dimensions[row].height = 8
else:
a = ws4.cell(row=row, column=1)
a.value = label
a.font = Font(name="Arial", size=10, bold=True, color=TEAL_DARK)
a.fill = solid(GREY_LIGHT)
a.alignment = left_align()
a.border = thin_border()
b = ws4.cell(row=row, column=2)
b.value = value
b.font = body_font(size=10, color="888888" if value else DARK_TEXT)
b.fill = solid(WHITE)
b.alignment = left_align()
b.border = thin_border()
# Footer note
footer_row = len(sections) + 3
ws4.merge_cells(f"A{footer_row}:B{footer_row}")
c = ws4.cell(row=footer_row, column=1)
c.value = ("Generated by Orris AI Health Assistant | "
"This tracker is for informational purposes only and does not replace medical advice.")
c.font = Font(name="Arial", size=8, italic=True, color="AAAAAA")
c.alignment = center()
ws4.row_dimensions[footer_row].height = 18
# ══════════════════════════════════════════════════════════════════
# Tab colors
# ══════════════════════════════════════════════════════════════════
ws1.sheet_properties.tabColor = "1A6B72"
ws2.sheet_properties.tabColor = "E87722"
ws3.sheet_properties.tabColor = "2E9EA8"
ws4.sheet_properties.tabColor = "1E7C3A"
# Save
out_path = "/home/daytona/workspace/blister-tracker/Blister_Symptom_Tracker.xlsx"
wb.save(out_path)
print(f"Saved: {out_path}")
python3 /home/daytona/workspace/blister-tracker/create_tracker.py
~/blister-tracker/create_tracker.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/blister-tracker/create_tracker.py'
python3 /home/daytona/workspace/blister-tracker/create_tracker.py
python3 -c " import openpyxl wb = openpyxl.Workbook() ws = wb.active wb.save('/home/daytona/workspace/blister-tracker/test.xlsx') print('save works') "
Transmission ?