Skip to content

Instantly share code, notes, and snippets.

@toofusan
Last active November 30, 2018 15:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save toofusan/44579c8e09c057b948b032e39a72dc41 to your computer and use it in GitHub Desktop.
Save toofusan/44579c8e09c057b948b032e39a72dc41 to your computer and use it in GitHub Desktop.
freee APIを使ったVR勤怠打刻の実装例
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Freee;
public class FreeeEventHandler : MonoBehaviour {
Freee.Client client;
[SerializeField] private string appID;
[SerializeField] private string secret;
[SerializeField] private string redirectURL;
[SerializeField] private string authorization_code;
[SerializeField] private string company_name;
[SerializeField] private string employee_name;
private int company_id;
private int employee_id;
[SerializeField] private SystemMessageController _SystemMessageController;
[SerializeField] private TimeClocksController m_TimeClocksController;
private void Start () {
string refresh_token = PlayerPrefs.GetString("refresh_token", "");
client = new Freee.Client(appID, secret, redirectURL);
if (string.IsNullOrEmpty(refresh_token)) {
Debug.Log("authorization_code");
client.authorizationCode = authorization_code;
} else {
Debug.Log("refresh_token");
client.refreshToken = refresh_token;
}
StartCoroutine(client.GenerateAccessToken(OnGenerateAccessToken));
}
private void OnGenerateAccessToken(bool success, string response) {
if (!success) {
_SystemMessageController.ShowMessageWithSad(response);
return;
}
GetCompanies();
}
void GetCompanies() {
StartCoroutine(client.Get("https://api.freee.co.jp/api/1/companies", new Dictionary<string, string>(), OnGetCompanies));
}
void OnGetCompanies(bool success, string response) {
if (!success) {
string msg = JsonUtility.FromJson<Message>(response).message;
_SystemMessageController.ShowMessageWithSad(msg);
return;
}
Company[] companies = JsonUtility.FromJson<Companies>(response).companies;
var company = companies.First(c => c.display_name == company_name);
company_id = company.id;
GetLoginUser();
}
void GetLoginUser() {
string employee_endpoint = "https://api.freee.co.jp/hr/api/v1/users/me";
StartCoroutine(client.Get(employee_endpoint, new Dictionary<string, string>(), OnGetLoginUser));
}
private void OnGetLoginUser(bool success, string response) {
if (!success) {
string msg = JsonUtility.FromJson<Message>(response).message;
_SystemMessageController.ShowMessageWithSad(msg);
return;
}
LoginUserCompany[] companies = JsonUtility.FromJson<LoginUser>(response).companies;
var company = companies.First(e => e.id == company_id);
employee_id = company.employee_id;
GetTimeClocksAvailableTypes();
}
private void GetTimeClocksAvailableTypes() {
string endpoint = "https://api.freee.co.jp/hr/api/v1/employees/" + employee_id + "/time_clocks/available_types";
Dictionary<string, string> parameter = new Dictionary<string, string> {
{"company_id", company_id.ToString()}
};
StartCoroutine(client.Get(endpoint, parameter, OnGetTimeClocksAvailableTypes));
}
void OnGetTimeClocksAvailableTypes(bool success, string response) {
if (!success) {
string msg = JsonUtility.FromJson<Message>(response).message;
_SystemMessageController.ShowMessageWithSad(msg);
return;
}
string[] available_types = JsonUtility.FromJson<TimeClocksAvailableTypes>(response).available_types;
m_TimeClocksController.SetAvailableTypes(available_types);
GetTimeClocks();
}
private void GetTimeClocks() {
string endpoint = "https://api.freee.co.jp/hr/api/v1/employees/" + employee_id + "/time_clocks";
Dictionary<string, string> parameter = new Dictionary<string, string> {
{"company_id", company_id.ToString()},
{"from_date", DateTime.Now.ToString("yyyy-MM-dd")},
{"to_date", DateTime.Now.ToString("yyyy-MM-dd")}
};
StartCoroutine(client.Get(endpoint, parameter, OnGetTimeClocks));
}
private void OnGetTimeClocks(bool success, string response) {
if (!success) {
string msg = JsonUtility.FromJson<Message>(response).message;
_SystemMessageController.ShowMessageWithSad(msg);
return;
}
TimeClock[] timeClocks = JsonUtility.FromJson<TimeClocksResponse>(response).items;
m_TimeClocksController.SetTimeClocksInfo(timeClocks);
}
public void PostTimeClocks(string type) {
string endpoint = "https://api.freee.co.jp/hr/api/v1/employees/" + employee_id + "/time_clocks";
string p = TimeClockRequestJson(type, DateTime.Now);
StartCoroutine(client.Post(endpoint, p, OnPostTimeClocks));
}
void OnPostTimeClocks(bool success, string response) {
if (!success) {
string msg = JsonUtility.FromJson<Message>(response).message;
_SystemMessageController.ShowMessageWithSad(msg);
return;
}
TimeClock tc = JsonUtility.FromJson<PostTimeClocksResponse>(response).employee_time_clock;
switch(tc.type) {
case "clock_in":
_SystemMessageController.ShowMessageWithSmile("出勤しました\n今日も一日頑張ろう!");
break;
case "break_begin":
_SystemMessageController.ShowMessageWithSmile("休憩を開始したよ\nゆっくり休んでね");
break;
case "break_end":
_SystemMessageController.ShowMessageWithSmile("休憩は終わりだよ\nもう一息がんばろう!");
break;
case "clock_out":
_SystemMessageController.ShowMessageWithSmile("退勤しました\nおつかれさま!", true);
break;
}
GetTimeClocksAvailableTypes();
}
private string TimeClockRequestJson(string type, DateTime dt)
{
TimeClockRequest r = new TimeClockRequest();
r.company_id = company_id;
r.type = type;
r.base_date = dt.ToString("yyyy-MM-dd");
return JsonUtility.ToJson(r);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gazable : MonoBehaviour {
protected bool m_IsGazed {get; set;}
virtual public void GazeBegin()
{
m_IsGazed = true;
}
virtual public void GazeComplete()
{
}
virtual public void GazeEnd()
{
m_IsGazed = false;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GazableUnitychan : Gazable {
[SerializeField] private TimeClocksController m_TimeClockController;
[SerializeField] private SphereCollider m_Collider;
[SerializeField] private Slider m_Slider;
private CanvasGroup m_SliderCanvasGroup;
private RectTransform m_SliderRectTransform;
private bool gazeCompleted;
private void Awake() {
m_SliderCanvasGroup = m_Slider.gameObject.GetComponent<CanvasGroup>();
m_SliderRectTransform = m_Slider.GetComponent<RectTransform>();
}
void Update()
{
if (m_IsGazed)
{
m_Slider.value += 0.008f;
if (m_Slider.value >= 1f && !gazeCompleted)
{
gazeCompleted = true;
GazeComplete();
}
}
if (m_SliderRectTransform != null) {
m_SliderRectTransform.LookAt(Camera.main.transform);
m_SliderRectTransform.Rotate(new Vector3(0f, 180f, 0f));
}
}
public override void GazeBegin() {
base.GazeBegin();
if (m_SliderCanvasGroup != null) m_SliderCanvasGroup.alpha = 1f;
}
public override void GazeComplete() {
m_TimeClockController.PostSimpleTimeClocks();
}
public override void GazeEnd()
{
base.GazeEnd();
gazeCompleted = false;
m_Slider.value = 0f;
if (m_SliderCanvasGroup != null) m_SliderCanvasGroup.alpha = 0f;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GazePointer : MonoBehaviour {
[SerializeField] private Transform m_Camera;
[SerializeField] private LayerMask m_ExclusionLayers;
[SerializeField] private float m_RayLength = 500f;
private Gazable m_GazeTarget;
void Start () {
m_GazeTarget = null;
}
void Update () {
if (m_Camera == null) return;
Ray ray = new Ray(m_Camera.position, m_Camera.forward);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, m_RayLength, ~m_ExclusionLayers)) {
GameObject target = hit.collider.gameObject;
Gazable gazable = target.GetComponent<Gazable>();
if (gazable != null) {
m_GazeTarget = gazable;
m_GazeTarget.GazeBegin();
} else {
ReleaseGazeTarget();
}
} else {
ReleaseGazeTarget();
}
}
void ReleaseGazeTarget() {
if (m_GazeTarget != null) {
m_GazeTarget.GazeEnd();
}
m_GazeTarget = null;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
public class SystemMessageController : MonoBehaviour {
[SerializeField] private Transform m_MessagePanel;
[SerializeField] private float messagePanelSize = 0.0006f;
[SerializeField] private float messagePanelDuration = 0.6f;
[SerializeField] private Animator m_UnitychanAnimator;
private AnimatorStateInfo currentState;
[SerializeField] private UnityChan.FaceUpdate m_UnitychanFaceUpdate;
[SerializeField] private float animSpeed = 0.5f;
private void Start () {
currentState = m_UnitychanAnimator.GetCurrentAnimatorStateInfo(0);
}
private void Update() {
if (m_UnitychanAnimator.GetBool("Jump")) {
currentState = m_UnitychanAnimator.GetCurrentAnimatorStateInfo(0);
if (currentState.fullPathHash != Animator.StringToHash("Custom Layer.Jumping@loop")) return;
m_UnitychanAnimator.SetBool("Jump", false);
}
}
public void ShowMessageWithSmile(string message, bool Jumping = false, float remainTime = 5f) {
if (Jumping) m_UnitychanAnimator.SetBool("Jump", true);
ShowMessage(message, "smile@sd_hmd", 5f);
}
public void ShowMessageWithSad(string message, float remainTime = 5f) {
ShowMessage(message, "sad@sd_hmd", 5f);
}
private void ShowMessage(string msg, string face, float remainTime) {
m_UnitychanFaceUpdate.OnCallChangeFace(face);
SetMessagePanel(
msg,
remainTime,
(() => m_UnitychanFaceUpdate.OnCallChangeFace("default@sd_hmd"))
);
}
private void SetMessagePanel(string message, float remainTime, TweenCallback callback = null) {
m_MessagePanel.Find("Panel/Text").GetComponent<Text>().text = message;
m_MessagePanel.GetComponent<RectTransform>().localScale = new Vector3(0f, 0f, 0f);
Sequence seq = DOTween.Sequence();
seq.Append(
m_MessagePanel.GetComponent<RectTransform>().DOScale(messagePanelSize, messagePanelDuration).SetEase(Ease.InOutQuart)
);
seq.Append(
m_MessagePanel.GetComponent<RectTransform>().DOScale(0f, messagePanelDuration).SetEase(Ease.InOutQuart).SetDelay(remainTime).OnComplete(callback)
);
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using Freee;
public class TimeClocksController : MonoBehaviour
{
[SerializeField] private FreeeEventHandler _FreeeEventHandler;
[SerializeField] private TextMeshProUGUI _Datetime;
[SerializeField] private TextMeshProUGUI _Status;
[SerializeField] private GazableButton _ClockInButton;
[SerializeField] private GazableButton _BreakBeginButton;
[SerializeField] private GazableButton _BreakEndButton;
[SerializeField] private GazableButton _ClockOutButton;
[SerializeField] private GazableButton _TestClockInButton;
[SerializeField] private GazableButton _TestClockOutButton;
CultureInfo culture = System.Globalization.CultureInfo.GetCultureInfo("ja-JP");
private string[] m_AvailableTypes;
public void Awake() {
_ClockInButton.GazeCompleteAction += PostTimeClocks;
_ClockInButton.Init();
_TestClockInButton.GazeCompleteAction += PostTimeClocks;
_TestClockInButton.Init();
_BreakBeginButton.GazeCompleteAction += PostTimeClocks;
_BreakBeginButton.Init();
_BreakEndButton.GazeCompleteAction += PostTimeClocks;
_BreakEndButton.Init();
_ClockOutButton.GazeCompleteAction += PostTimeClocks;
_ClockOutButton.Init();
_TestClockOutButton.GazeCompleteAction += PostTimeClocks;
_TestClockOutButton.Init();
}
private void Update() {
if (_Datetime != null) {
_Datetime.text = DateTime.Now.ToString("yyyy年MM月dd日 HH:mm:ss");
}
}
public void SetAvailableTypes(string[] types) {
m_AvailableTypes = types;
SetButtons(types);
_Status.text = statusText(types);
}
public void SetTimeClocksInfo(TimeClock[] tcs) {
string s = DateTime.Now.ToString("yyyy年MM月dd日(ddd)", culture) + "\n";
if (tcs.Length >= 1) {
foreach (TimeClock tc in tcs) {
s += typeString(tc.type) + ":" + DateTime.Parse(tc.datetime).ToString("HH:mm") + "\n";
}
} else {
s += "打刻履歴なし";
}
transform.Find("TimeClocksInfo/Info").GetComponent<TextMeshProUGUI>().text = s;
}
public void PostSimpleTimeClocks() {
_FreeeEventHandler.PostTimeClocks(simpleAvailableType(m_AvailableTypes));
}
void SetButtons(string[] types) {
_ClockInButton.gameObject.SetActive(Array.IndexOf(types, "clock_in") >= 0);
_BreakBeginButton.gameObject.SetActive(Array.IndexOf(types, "break_begin") >= 0);
_BreakEndButton.gameObject.SetActive(Array.IndexOf(types, "break_end") >= 0);
_ClockOutButton.gameObject.SetActive(Array.IndexOf(types, "clock_out") >= 0);
}
private string statusText(string[] types) {
if (Array.IndexOf(types, "clock_in") >= 0) {
return "出勤しましょう";
} else if (Array.IndexOf(types, "break_begin") >= 0) {
return "出勤中です";
} else if (Array.IndexOf(types, "break_end") >= 0) {
return "休憩中です";
} else {
return "退勤しました";
}
}
private string typeString(string type) {
if (type == "clock_in") {
return "出勤  ";
} else if (type == "break_begin") {
return "休憩開始";
} else if (type == "break_end") {
return "休憩終了";
} else if (type == "clock_out") {
return "退勤  ";
} else {
return "";
}
}
private string simpleAvailableType(string[] types) {
if (Array.IndexOf(types, "clock_in") >= 0) {
return "clock_in";
} else if (Array.IndexOf(types, "clock_out") >= 0) {
return "clock_out";
} else {
return "";
}
}
void PostTimeClocks(string s)
{
string type = "";
switch (s) {
case "出勤":
type = "clock_in";
break;
case "休憩開始":
type = "break_begin";
break;
case "休憩終了":
type = "break_end";
break;
case "退勤":
type = "clock_out";
break;
}
_FreeeEventHandler.PostTimeClocks(type);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment