מיזוג טקסט למסמך

במדריך הזה מוסבר איך להשתמש ב-Google Docs API כדי למזג מידע ממקור נתונים חיצוני אחד או יותר במסמך תבנית קיים.

תבנית היא סוג של מסמך שמכיל טקסט קבוע ופלייסהולדרים לתוכן דינמי. לדוגמה, תבנית חוזה יכולה להכיל טקסט קבוע עם מצייני מיקום לשם ולכתובת של הנמען. לאחר מכן האפליקציה ממזגת נתונים ספציפיים למשתמש בתבנית כדי ליצור את המסמך הסופי.

יש כמה סיבות לכך שהגישה הזו שימושית:

  • מעצבים יכולים לכוונן את העיצוב של מסמך באמצעות Google Docs. השיטה הזו פשוטה יותר מהתאמת פרמטרים באפליקציה כדי להגדיר את הפריסה המעובדת.

  • הפרדה בין תוכן לבין הצגה היא עיקרון עיצובי מוכר עם יתרונות רבים.

דיאגרמה שמראה איך נתונים ממקור מתמזגים בתבנית כדי ליצור מסמך.
איור 1. מיזוג נתונים בתבנית כדי ליצור מסמך.

איך מתבצע מיזוג מסמכים

דוגמה לשימוש ב-Docs API כדי למזג נתונים במסמך:

  1. יוצרים את המסמך באמצעות תוכן placeholder כדי לעצב אותו ולבחור את הפורמט. כל עיצוב טקסט שרוצים להחליף נשמר.

  2. לכל רכיב שמוסיפים, מחליפים את תוכן ה-placeholder בתג. חשוב להשתמש במחרוזות שסביר להניח שלא יופיעו בדרך כלל. לדוגמה, {{account-holder-name}} יכול להיות תג טוב.

  3. בקוד, משתמשים ב-Google Drive API כדי ליצור עותק של המסמך.

  4. בקוד, משתמשים בשיטה batchUpdate של Docs API עם שם המסמך וכוללים ReplaceAllTextRequest.

מזהי מסמכים מפנים למסמך מסוים, ואפשר להפיק אותם מכתובת ה-URL:

https://docs.google.com/document/d/DOCUMENT_ID/edit

ניהול תבניות

כדי ליצור תבניות למסמכים שהאפליקציה מגדירה ושהם בבעלותה, צריך להשתמש בחשבון ייעודי שמייצג את האפליקציה. חשבונות שירות הם בחירה טובה, והם מאפשרים להימנע מסיבוכים שקשורים למדיניות של Google Workspace שמגבילה שיתוף.

כשיוצרים מופעים של מסמכים מתבניות, תמיד צריך להשתמש בהרשאות של משתמש קצה. כך המשתמשים מקבלים שליטה מלאה במסמך שנוצר, ונמנעות בעיות שקשורות למגבלות לכל משתמש ב-Google Drive.

כדי ליצור תבנית באמצעות חשבון שירות, מבצעים את השלבים הבאים עם פרטי הכניסה של האפליקציה:

  1. ליצור מסמך באמצעות documents.create ב-Docs API.
  2. מעדכנים את ההרשאות כדי לאפשר לנמעני המסמך לקרוא אותו באמצעות permissions.create ב-Drive API.
  3. מעדכנים את ההרשאות כדי לאפשר ליוצרי תבניות לכתוב אליו באמצעות permissions.create ב-Drive API.
  4. עורכים את התבנית לפי הצורך.

כדי ליצור מופע של המסמך, פועלים לפי השלבים הבאים עם פרטי הכניסה של המשתמש:

  1. יוצרים עותק של התבנית באמצעות files.copy ב-Drive API.
  2. החלפת ערכים באמצעות documents.batchUpdate ב-Docs API.

דוגמה: מיזוג נתונים לתבנית

בדוגמת הקוד הבאה אפשר לראות איך מחליפים שני שדות בכל הכרטיסיות של תבנית בערכים אמיתיים כדי ליצור מסמך מוכן:

תמונה שבה רואים תבנית מסמך עם מצייני מיקום של תגים ואת המסמך הממוזג שנוצר.
איור 2. החלפת ערכים זמניים לשמירת מקום בתג בערכים.

כדי לבצע את המיזוג הזה, משתמשים בקוד הבא:

Java

String customerName = "Alice";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String date = formatter.format(LocalDate.now());

// Make a copy of the template document using the Drive API.
String copyTitle = "Merged Document";
File copyMetadata = new File().setName(copyTitle);
File documentCopyFile =
        driveService.files().copy(DOCUMENT_ID, copyMetadata).execute();
String documentCopyId = documentCopyFile.getId();

List requests = new ArrayList<>();
// One option for replacing all text is to specify all tab IDs.
requests.add(new Request()
        .setReplaceAllText(new ReplaceAllTextRequest()
                .setContainsText(new SubstringMatchCriteria()
                        .setText("{{customer-name}}")
                        .setMatchCase(true))
                .setReplaceText(customerName)
                .setTabsCriteria(new TabsCriteria()
                        .addTabIds(TAB_ID_1)
                        .addTabIds(TAB_ID_2)
                        .addTabIds(TAB_ID_3))));
// Another option is to omit TabsCriteria if you are replacing across all tabs.
requests.add(new Request()
        .setReplaceAllText(new ReplaceAllTextRequest()
                .setContainsText(new SubstringMatchCriteria()
                        .setText("{{date}}")
                        .setMatchCase(true))
                .setReplaceText(date)));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest();
service.documents().batchUpdate(documentCopyId, body.setRequests(requests)).execute();

Node.js

  let customerName = 'Alice';
  let date = yyyymmdd()
  let requests = [
    // One option for replacing all text is to specify all tab IDs.
    {
      replaceAllText: {
        containsText: {
          text: '{{customer-name}}',
          matchCase: true,
        },
        replaceText: customerName,
        tabsCriteria: {
          tabIds: [TAB_ID_1, TAB_ID_2, TAB_ID_3],
        },
      },
    },
    // Another option is to omit TabsCriteria if you are replacing across all tabs.
    {
      replaceAllText: {
        containsText: {
          text: '{{date}}',
          matchCase: true,
        },
        replaceText: date,
      },
    },
  ];

  // Make a copy of the template document using the Drive API.
  let copyTitle = 'Merged Document';
  driveService.files.copy({
    fileId: '1yBx6HSnu_gbV2sk1nChJOFo_g3AizBhr-PpkyKAwcTg',
    resource: {
      name: copyTitle,
    },
  }, (err, driveResponse) => {
    if (err) return console.log('The Drive API returned an error: ' + err);
    let documentCopyId = driveResponse.data.id;

    google.options({auth: auth});
    google
        .discoverAPI(
            'https://docs.googleapis.com/$discovery/rest?version=v1&key={YOUR_API_KEY}')
        .then(function(docs) {
          docs.documents.batchUpdate(
              {
                documentId: documentCopyId,
                resource: {
                  requests,
                },
              },
              (err, {data}) => {
                if (err) return console.log('The API returned an error: ' + err);
                console.log(data);
              });
        });
  });

Python

customer_name = 'Alice'
date = datetime.datetime.now().strftime("%y/%m/%d")

# Make a copy of the template document using the Drive API.
copy_title = 'Merged Document'
body = {
    'name': copy_title
}
drive_response = drive_service.files().copy(
    fileId=DOCUMENT_ID, body=body).execute()
document_copy_id = drive_response.get('id')

requests = [
        # One option for replacing all text is to specify all tab IDs.
        {
        'replaceAllText': {
            'containsText': {
                'text': '{{customer-name}}',
                'matchCase':  'true'
            },
            'replaceText': customer_name,
            'tabsCriteria': {
                'tabIds': [TAB_ID_1, TAB_ID_2, TAB_ID_3],
            },
        }},
        # Another option is to omit TabsCriteria if you are replacing across all tabs.
        {
        'replaceAllText': {
            'containsText': {
                'text': '{{date}}',
                'matchCase':  'true'
            },
            'replaceText': str(date),
        }
    }
]

result = service.documents().batchUpdate(
    documentId=document_copy_id, body={'requests': requests}).execute()

איך עובדים עם רשימות וטבלאות דינמיות

מיזוג מסמכים רגיל משתמש ב-ReplaceAllTextRequest כדי להחליף מצייני מיקום חד-פעמיים (כמו {{customer-name}} או {{date}}). עם זאת, אם הנתונים כוללים רשימה דינמית של פריטים (כמו שורות בחשבונית, רשימה של מוצרים שהוזמנו או טבלה דינמית), אי אפשר להשתמש בהחלפת טקסט רגילה כי מספר הפריטים לא ידוע במהלך עיצוב התבנית.

כדי לטפל בתוכן של רשימה דינמית, אפשר להשתמש באחת מהאסטרטגיות הבאות.

אפשרות 1: הוספת שורות לטבלת תבנית

אם מסמך התבנית כבר מכיל טבלה מעוצבת (לדוגמה, עם שורת כותרת ושורת placeholder אחת), אפשר לשכפל באופן דינמי את השורות ולמלא אותן לכל פריט ברשימה:

  1. קוראים את מבנה התבנית: משתמשים בשיטה documents.get כדי לאתר את הטבלה ולזהות את האינדקס של שורת התבנית.
  2. הוספת שורות חדשות: לכל פריט ברשימת הנתונים שלך (לא כולל הפריט הראשון, שאפשר להשתמש בשורה הקיימת בתבנית), קוראים לפונקציה InsertTableRowRequest כדי להוסיף שורה חדשה מתחת לשורה של התבנית.
  3. מאכלסים את נתוני התאים: מחליפים את ה-placeholder בשורה של התבנית בנתונים. בשביל השורות החדשות שנוצרו, משתמשים ב-‎ InsertTextRequest כדי להוסיף את הטקסט המתאים למיקום הקואורדינטות של כל תא.

דוגמאות להוספת שורות לטבלה מופיעות במאמר עבודה עם טבלאות.

אפשרות 2: החלפת תג בטבלה שנוצרה

אם רוצים לבנות את הטבלה מאפס באופן פרוגרמטי:

  1. מציבים תג placeholder: משתמשים בתג יחיד (כמו {{invoice-table}}) במסמך התבנית כדי לסמן את המקום שבו הרשימה צריכה להופיע.
  2. מאתרים את ה-placeholder: משתמשים בפעולת חיפוש כדי למצוא את אינדקס ההתחלה של התג.
  3. מחיקת הטקסט לדוגמה: משתמשים ב-DeleteContentRangeRequest כדי להסיר את הטקסט {{invoice-table}}.
  4. הוספת הטבלה: שולחים InsertTableRequest באינדקס ההתחלתי, ומציינים את מספר השורות והעמודות על סמך מקור הנתונים.
  5. כתיבת ערכים: מאכלסים כל תא בטבלה באופן רציף.

דוגמאות להוספת טבלאות באופן פרוגרמטי מופיעות במאמר בנושא עבודה עם טבלאות.