コンテンツにスキップ

インポート

概要

APIを使用してユーザ情報をインポートします。

事前準備

APIの操作を行う前にAPIキーの作成を実施してください。

リクエスト

下記のリクエスト形式で、CSVデータとJSONパラメータを送信します。

設定項目 値
HTTPメソッド POST
Content-Type multipart/form-data
文字コード UTF-8
URL http://{サーバー名}/api/users/import (※1)
Body 下記「Bodyに指定する項目」を参照のこと

(※1){サーバー名}の部分は、適宜、環境に合わせて編集してください。

Bodyに指定する項目

項目名 値
parameters 下記「APIパラメータ」の内容をJSON形式の文字列で指定
file 登録するCSVファイルのバイナリデータ

APIパラメータ

項目名 設定例 備考
ApiVersion 1.1 APIバージョン
ApiKey 3da0fa3a7R61faf821... 取得したAPIキー
Encoding Shift-JIS CSVファイルのエンコーディング。"UTF-8"または"Shift-JIS"を指定

実行例のサンプル

PowerShell(version6.0 以降)のサンプル

PowerShell
$uri = 'http://servername/api/users/import'
$filePath = "./sample.csv"
$form = @{
    parameters = ConvertTo-Json @{
        ApiVersion = 1.1;
        ApiKey = "4d84b4773a58bbc3c4...";
        Encoding = "UTF-8";
    };
    file = Get-Item -Path $filePath;
}
Invoke-WebRequest -Uri $uri -Method Post -Form $form

Pythonのサンプル

Python
import requests
import json

url = "https://servername/api/users/import"
filePath = "./sample.csv"
data = {
    "parameters": json.dumps({
        "ApiVersion" : 1.1,
        "ApiKey" : "4d84b4773a58bbc3c4...",
        "Encoding" : "UTF-8"
    })            
}
files = {
    "file":("sample.csv", open(filePath,"rb"), "text/csv")
}
response = requests.post(url, data, files=files)
print(response.content.decode())

レスポンス

下記の形式のjsonデータが返却されます。

JSON
{
  "Id":0,
  "StatusCode":200,
  "Message":"50 件追加し、12 件更新しました。"
}

サンプルコード

Note

コード内の【 ... 】は適宜修正してください。

1. 人事CSVを正としてユーザーを同期し、退職者は削除せず無効化する

人事システムが出力したユーザーCSV(ログインID・氏名・社員番号・メールアドレス・組織コード)を読み込み、ユーザーのインポートAPIでまとめて作成・更新します。プリザンターにいてCSVから消えたユーザー(退職者)は、削除せず無効化します。 無効だったユーザーがCSVに戻ってきた場合(再入社など)は、有効に戻ります。

実務での使いどころ

入社・退職・異動・改姓は毎月のように発生します。人事システムとプリザンターのユーザーがずれると、退職者がログインできたまま残ったり、異動した人の所属組織が古いままになったりします。人事CSVを正として定期的に実行すれば、追加・更新・退職者の無効化を、人が突き合わせることなく反映できます。

退職者を削除せず無効化するのは、過去レコードの作成者・担当者の表示を保つためです。組織も同じ考え方で同期する場合は、「組織更新」のサンプルを先に実行してください。

事前準備

1:テナント管理者権限のAPIキーを用意してください。

2:人事システム側から、以下の形式のCSVファイル(UTF-8)を出力してください。毎回、全ユーザーをフルで出力する形式である必要があります(差分のみの形式では、CSVに無い=退職と判定できません)。

列名 内容
login_id ログインID。プリザンターのログインIDと突合するキー
user_name 氏名
user_code ユーザコード(社員番号など)
mail メールアドレス(任意)
dept_code 所属組織の組織コード(任意)

3:下記サンプルコードのKEEP_LOGIN_IDSに、CSVに含まれなくても無効化しないユーザー(管理者、このスクリプトのAPIキーのユーザー、システム連携用など)のログインIDを入れてください。既定値はAdministratorです。

4:最初はDRY_RUN = Trueで実行し、無効化の対象に意図しないユーザーが含まれていないことを確かめてからFalseにしてください。

Python
import csv
import json
from pathlib import Path

import requests

# ==============================
# Pleasanter 接続設定
# ==============================

BASE_URL = "【URL】"
API_KEY = "【APIキー】"

API_VERSION = 1.1

# ==============================
# ファイル設定(環境にあわせ適宜変更してください)
# ==============================
INPUT_DIR = Path("./input")
USERS_CSV = INPUT_DIR / "users.csv"
OUTPUT_DIR = Path("./output")
IMPORT_CSV = OUTPUT_DIR / "users_import.csv"

# CSVに無くても無効化しないユーザーのログインID
# (管理者、このスクリプトのAPIキーのユーザー、システム連携用など。環境にあわせて足してください)
KEEP_LOGIN_IDS = {"Administrator"}

# True の場合、実際の更新は行わず対象だけを出力する
DRY_RUN = False

# ユーザーインポートのCSVの見出し
IMPORT_HEADERS = ["ログインID", "名前", "ユーザコード", "メールアドレス", "組織コード", "無効"]


# ==============================
# CSV入出力
# ==============================
def read_csv(path):
    with open(path, "r", encoding="utf-8-sig", newline="") as f:
        return [
            {k.strip(): (v or "").strip() for k, v in row.items()}
            for row in csv.DictReader(f)
        ]


def write_import_csv(path, rows):
    path.parent.mkdir(parents=True, exist_ok=True)
    with open(path, "w", encoding="utf-8", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(IMPORT_HEADERS)
        for row in rows:
            writer.writerow(
                [
                    row["login_id"],
                    row["user_name"],
                    row["user_code"],
                    row["mail"],
                    row["dept_code"],
                    "0",  # CSVにいる人は有効にする(再入社した人も有効に戻る)
                ]
            )


# ==============================
# Pleasanter API操作
# ==============================
def post_json(url, payload):
    r = requests.post(
        url,
        json=payload,
        headers={"Content-Type": "application/json"},
        timeout=60,
    )
    r.raise_for_status()
    return r.json()


def get_all(path):
    # 1回で取れる件数(既定200件)を超えても全件を取得する
    rows, offset = [], 0
    while True:
        data = post_json(
            f"{BASE_URL}/api/{path}/get",
            {"ApiVersion": API_VERSION, "ApiKey": API_KEY, "Offset": offset},
        )
        response = data.get("Response", {})
        page = response.get("Data", [])
        rows.extend(page)
        offset += len(page)
        if not page or offset >= response.get("TotalCount", 0):
            return rows


def get_users():
    return {row["LoginId"]: row for row in get_all("users") if row.get("LoginId")}


def get_dept_codes():
    return {row["DeptCode"] for row in get_all("depts") if row.get("DeptCode")}


def import_users(path):
    # CSVにいる人をまとめて作成・更新する
    with open(path, "rb") as f:
        r = requests.post(
            f"{BASE_URL}/api/users/import",
            data={
                "parameters": json.dumps(
                    {"ApiVersion": API_VERSION, "ApiKey": API_KEY, "Encoding": "UTF-8"}
                )
            },
            files={"file": (path.name, f, "text/csv")},
            timeout=300,
        )
    r.raise_for_status()
    return r.json()


def disable_user(user_id):
    # Disabled だけを送る(メールアドレス・所属組織などはそのまま残る)
    return post_json(
        f"{BASE_URL}/api/users/{user_id}/update",
        {"ApiVersion": API_VERSION, "ApiKey": API_KEY, "Disabled": True},
    )


# ==============================
# メイン処理
# ==============================
def main():
    user_rows = read_csv(USERS_CSV)
    csv_login_ids = {row["login_id"] for row in user_rows}

    # プリザンターに無い組織コードがあれば、1件も変えずに止める
    # (そのまま取り込むと、エラーにならずに所属組織が空になる)
    dept_codes = get_dept_codes()
    unknown = sorted(
        {row["dept_code"] for row in user_rows if row["dept_code"]} - dept_codes
    )
    if unknown:
        raise SystemExit(
            f"プリザンターに無い組織コードがあるため中止しました: {', '.join(unknown)}"
        )

    existing_users = get_users()
    new_login_ids = sorted(csv_login_ids - existing_users.keys())

    # CSVから消えた人(既に無効な人・無効化しない人は除く)
    to_disable = [
        user
        for login_id, user in sorted(existing_users.items())
        if login_id not in csv_login_ids
        and login_id not in KEEP_LOGIN_IDS
        and not user.get("Disabled")
    ]

    write_import_csv(IMPORT_CSV, user_rows)
    print(f"インポート用CSV: {IMPORT_CSV}({len(user_rows)}件、うち新規 {len(new_login_ids)}件)")
    for user in to_disable:
        print(f"無効化の対象: {user['LoginId']} {user.get('Name', '')}")

    if DRY_RUN:
        print("DRY_RUN=True のため、インポートと無効化は実行しません")
        return

    result = import_users(IMPORT_CSV)
    print(f"インポート: {result.get('Message')}")

    for user in to_disable:
        disable_user(user["UserId"])
    print(f"無効化: {len(to_disable)}件")


if __name__ == "__main__":
    main()
  • CSVの組織コードにプリザンターに無いものがあると、何も変えずに止まります。そのまま取り込むと、エラーにならずに所属組織が空になるためです。
  • 無効化は、ユーザーの更新APIでDisabledのみを送ります。インポートで無効にすると、メールアドレスの列を空で送ることになり、既存のメールアドレスが消えるためです。
  • パスワードはこのサンプルでは設定しません。新しく作られたユーザーの初回ログインの方法(パスワードの初期設定、SSO・LDAPなど)は、運用に合わせて用意してください。

エラー時の確認事項

・API使用時の注意点やエラーが発生する場合の確認事項
・FAQ:変更後の設定ファイルやAPIリクエスト(JSON形式)が正しく認識されない場合の確認事項