loreal-deliverables-airtable/create_deliverables_fields.py
DJP 9c7026f419 L'Oreal Deliverables Airtable integration
- create_deliverables_fields.py: one-time schema setup (56 fields)
- deliverables_sync.py: manual JSON-to-Airtable sync tool
- deliverables_service.py: production daemon with watchdog file watching,
  batch upsert, processed/failed file handling, and daily HTML email reports
- loreal-deliverables.service: systemd unit for server deployment
- Server: box-cli-01 at /home/dalim/LOREAL-AIRTABLE/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 13:24:13 -05:00

131 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""
Create fields in the L'Oreal Deliverables Airtable table.
Run once to set up the table schema before using deliverables_sync.py.
"""
from pyairtable import Api
import time
import sys
# Configuration
PAT_TOKEN = "pat32GcLDAyyT4V45.928c9f011206e1b8d26608436f8dcef6feb3eabcfe59a27e70459ac6dff54dde"
BASE_ID = "apptPeKvHf7wZqjc5"
TABLE_ID = "tbldCaDgXF9ewNjPq"
# Field definitions: (name, type, options_or_None)
FIELDS = [
# --- Upsert Key ---
("Number", "singleLineText", None),
# --- JobDetails ---
("Job_BusinessArea", "singleLineText", None),
("Job_Company", "singleLineText", None),
("Job_Campaign", "singleLineText", None),
("Job_Title", "singleLineText", None),
("Job_Notes", "multilineText", None),
("Job_ClientCode", "singleLineText", None),
("Job_StudioCode", "singleLineText", None),
("Job_CampaignCode", "singleLineText", None),
("Job_DueDate", "date", {"dateFormat": {"name": "iso"}}),
("Job_BriefDate", "date", {"dateFormat": {"name": "iso"}}),
("Job_LiveDate", "date", {"dateFormat": {"name": "iso"}}),
("Job_ExternalReference", "singleLineText", None),
("Job_Type", "singleLineText", None),
("Job_JobCategory", "singleLineText", None),
("Job_MediaType", "singleLineText", None),
("Job_MediaSubType", "singleLineText", None),
("Job_LanguageName", "singleLineText", None),
("Job_LanguageCode", "singleLineText", None),
("Job_CountryName", "singleLineText", None),
("Job_CountryCode", "singleLineText", None),
("Job_AutomationWorkflow", "singleLineText", None),
("Job_Outputs", "multilineText", None),
# --- Activation Data (from JobDetails.activation_data) ---
("Activation_Status", "singleLineText", None),
("Activation_Destination", "singleLineText", None),
("Activation_Format", "singleLineText", None),
("Activation_MasterJob", "singleLineText", None),
("Activation_CreativeExecution", "singleLineText", None),
("Activation_PimAssetId", "singleLineText", None),
("Activation_PimImageUrl", "singleLineText", None),
("Activation_MasterAsset", "singleLineText", None),
("Activation_MasterAutomation", "singleLineText", None),
("Activation_Submedia", "singleLineText", None),
("Activation_Jobcat", "singleLineText", None),
("Activation_Country", "singleLineText", None),
("Activation_Language", "singleLineText", None),
("Activation_MediaType", "singleLineText", None),
("Activation_Qty", "singleLineText", None),
("Activation_LiveDate", "date", {"dateFormat": {"name": "iso"}}),
("Activation_ReadyForProductionDate", "date", {"dateFormat": {"name": "iso"}}),
# --- ProjectDetails ---
("Project_BusinessArea", "singleLineText", None),
("Project_Title", "singleLineText", None),
("Project_ExternalReference", "singleLineText", None),
("Project_Type", "singleLineText", None),
("Project_Description", "multilineText", None),
("Project_StartDate", "date", {"dateFormat": {"name": "iso"}}),
("Project_EndDate", "date", {"dateFormat": {"name": "iso"}}),
("Project_AdditionalAttributes", "multilineText", None),
# --- AssetDetails ---
("Asset_Filename", "singleLineText", None),
("Asset_Title", "singleLineText", None),
("Asset_Description", "singleLineText", None),
("Asset_Type", "singleLineText", None),
("Asset_Version", "number", {"precision": 0}),
("Asset_Uploaded", "date", {"dateFormat": {"name": "iso"}}),
("Asset_Status", "singleLineText", None),
("Asset_Details", "multilineText", None),
]
def main():
dry_run = "--dry-run" in sys.argv
if dry_run:
print("DRY RUN - No fields will be created\n")
print(f"Target: Base {BASE_ID} / Table {TABLE_ID}")
print(f"Fields to create: {len(FIELDS)}\n")
api = Api(PAT_TOKEN)
table = api.table(BASE_ID, TABLE_ID)
created = 0
skipped = 0
failed = 0
for i, (name, field_type, options) in enumerate(FIELDS, 1):
label = f"[{i}/{len(FIELDS)}] {name} ({field_type})"
if dry_run:
print(f" Would create: {label}")
continue
try:
kwargs = {"name": name, "field_type": field_type}
if options:
kwargs["options"] = options
result = table.create_field(**kwargs)
print(f" Created: {label} -> {result.id}")
created += 1
except Exception as e:
err = str(e)
if "already exists" in err.lower() or "duplicate" in err.lower():
print(f" Skipped (exists): {label}")
skipped += 1
else:
print(f" FAILED: {label} - {err}")
failed += 1
time.sleep(0.3)
print(f"\nDone! Created: {created}, Skipped: {skipped}, Failed: {failed}")
if __name__ == "__main__":
main()