wip: finish converting some more projects
This commit is contained in:
60
Habittodo.Service/Extensions/Extensions.cs
Normal file
60
Habittodo.Service/Extensions/Extensions.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Habittodo.Model.Habitica;
|
||||
using Habittodo.Model.Habitica.Enums;
|
||||
using Habittodo.Model.Todoist;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Habittodo.Service.Extensions
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static ChecklistItem ToHabiticaChecklistItem(this Item item, string habiticaId = null)
|
||||
{
|
||||
if (!item.IsChild)
|
||||
return null;
|
||||
|
||||
var checklistItem = new ChecklistItem
|
||||
{
|
||||
Id = habiticaId,
|
||||
Text = item.Content
|
||||
};
|
||||
|
||||
return checklistItem;
|
||||
}
|
||||
|
||||
public static Task ToHabiticaTask(this Item item, string habiticaId = null)
|
||||
{
|
||||
if (item.IsChild)
|
||||
return null;
|
||||
|
||||
var taskTypeStr = Enum.GetName(typeof(TaskType), TaskType.Todo).ToLower();
|
||||
var task = new Task
|
||||
{
|
||||
Id = habiticaId,
|
||||
Text = item.Content,
|
||||
Type = taskTypeStr,
|
||||
Date = item.Due?.ToJavaScriptDateStr(),
|
||||
Priority = GetHabiticaDifficulty(item.Priority)
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
private static string GetHabiticaDifficulty(int todoistPriority)
|
||||
{
|
||||
switch (todoistPriority)
|
||||
{
|
||||
case 1:
|
||||
return "0.1";
|
||||
case 2:
|
||||
return "1";
|
||||
case 3:
|
||||
return "1.5";
|
||||
case 4:
|
||||
return "2";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
152
Habittodo.Service/HabiticaIntegrationService.cs
Normal file
152
Habittodo.Service/HabiticaIntegrationService.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using Habittodo.Data;
|
||||
using ChecklistItem = Habittodo.Model.Habitica.ChecklistItem;
|
||||
using Habittodo.Model.Habitica.Enums;
|
||||
using Habittodo.Model.Storage;
|
||||
using Habittodo.Model.Todoist;
|
||||
using Habittodo.Service.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Habittodo.Service
|
||||
{
|
||||
public class HabiticaIntegrationService
|
||||
{
|
||||
private HabiticaServiceClient habiticaClient { get; set; }
|
||||
private TableStorageClient storageClient { get; set; }
|
||||
|
||||
private string userId { get; set; }
|
||||
|
||||
public HabiticaIntegrationService(string habiticaUserId,
|
||||
string habiticaApiKey,
|
||||
string storageConnectionString,
|
||||
string userId)
|
||||
{
|
||||
this.habiticaClient = new HabiticaServiceClient(habiticaUserId, habiticaApiKey);
|
||||
this.storageClient = new TableStorageClient(storageConnectionString);
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public async Task Add(IEnumerable<Item> items)
|
||||
{
|
||||
foreach (var item in items.OrderBy(x => x.Parent_Id))
|
||||
{
|
||||
if (!item.IsChild)
|
||||
await AddTask(item);
|
||||
else
|
||||
await AddChecklistItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddTask(Item item)
|
||||
{
|
||||
if (item.IsChild)
|
||||
throw new ArgumentException("Item passed as arguement has a valid Parent_Id");
|
||||
|
||||
var task = (await habiticaClient.CreateTask(item.ToHabiticaTask())).Data;
|
||||
var link = new TodoHabitLink(userId, item.Id, task.Id);
|
||||
|
||||
await storageClient.InsertOrUpdate(link);
|
||||
await storageClient.InsertOrUpdate(link.Reverse());
|
||||
}
|
||||
|
||||
public async Task AddChecklistItem(Item item)
|
||||
{
|
||||
if (!item.IsChild)
|
||||
throw new ArgumentException("Item passed as arguement does not have a valid Parent_Id");
|
||||
|
||||
var habiticaTaskId = (await storageClient.RetrieveRecord<TodoHabitLink>(userId, item.Parent_Id)).HabiticaId;
|
||||
var checklistItem = (await habiticaClient.CreateChecklistItem(item.ToHabiticaChecklistItem(), habiticaTaskId)).Data.Checklist
|
||||
.First(x => x.Text == item.Content);
|
||||
|
||||
var link = new TodoHabitLink(userId, item.Id, checklistItem.Id, item.Parent_Id);
|
||||
await storageClient.InsertOrUpdate(link);
|
||||
await storageClient.InsertOrUpdate(link.Reverse(habiticaTaskId));
|
||||
}
|
||||
|
||||
public async Task Update(IEnumerable<Item> items)
|
||||
{
|
||||
foreach (var item in items.OrderBy(x => x.Parent_Id))
|
||||
{
|
||||
if (!item.IsChild)
|
||||
await UpdateTask(item);
|
||||
else
|
||||
await UpdateChecklistItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateTask(Item item)
|
||||
{
|
||||
if (item.IsChild)
|
||||
throw new ArgumentException("Item passed as arguement has a valid Parent_Id");
|
||||
|
||||
var habiticaId = (await storageClient.RetrieveRecord<TodoHabitLink>(userId, item.Id)).HabiticaId;
|
||||
await habiticaClient.UpdateTask(item.ToHabiticaTask(habiticaId));
|
||||
}
|
||||
|
||||
public async Task UpdateChecklistItem(Item item)
|
||||
{
|
||||
if (!item.IsChild)
|
||||
throw new ArgumentException("Item passed as arguement does not have a valid Parent_Id");
|
||||
|
||||
var habiticaTaskId = (await storageClient.RetrieveRecord<TodoHabitLink>(userId, item.Parent_Id)).HabiticaId;
|
||||
var habiticaChecklistId = (await storageClient.RetrieveRecord<TodoHabitLink>(userId, item.Id)).HabiticaId;
|
||||
await habiticaClient.UpdateChecklistItem(item.ToHabiticaChecklistItem(habiticaChecklistId), habiticaTaskId);
|
||||
}
|
||||
|
||||
public async Task Complete(IEnumerable<Item> items)
|
||||
{
|
||||
foreach (var item in items.OrderByDescending(x => x.Parent_Id))
|
||||
{
|
||||
if (!item.IsChild)
|
||||
await CompleteTask(item.Id);
|
||||
else
|
||||
await CompleteChecklistItem(item.Id, item.Parent_Id);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CompleteTask(string todoistId)
|
||||
{
|
||||
var habiticaId = (await storageClient.RetrieveRecord<TodoHabitLink>(userId, todoistId)).HabiticaId;
|
||||
await habiticaClient.ScoreTask(habiticaId, ScoreAction.Up);
|
||||
}
|
||||
|
||||
public async Task CompleteChecklistItem(string todoistId, string todoistParentId)
|
||||
{
|
||||
var habiticaTaskId = (await storageClient.RetrieveRecord<TodoHabitLink>(userId, todoistParentId)).HabiticaId;
|
||||
var habiticaChecklistId = (await storageClient.RetrieveRecord<TodoHabitLink>(userId, todoistId)).HabiticaId;
|
||||
await habiticaClient.ScoreChecklistItem(habiticaTaskId, habiticaChecklistId);
|
||||
}
|
||||
|
||||
public async Task Delete(IEnumerable<Item> items)
|
||||
{
|
||||
List<(Item Item, TodoHabitLink Link)> itemsAndLinks = new List<(Item, TodoHabitLink)>();
|
||||
foreach (var item in items)
|
||||
itemsAndLinks.Add((item, await storageClient.RetrieveRecord<TodoHabitLink>(userId, item.Id)));
|
||||
|
||||
foreach (var itemAndLink in itemsAndLinks.OrderByDescending(x => x.Link.TodoistParentId))
|
||||
{
|
||||
var item = itemAndLink.Item;
|
||||
var link = itemAndLink.Link;
|
||||
if (string.IsNullOrEmpty(link.TodoistParentId))
|
||||
await DeleteTask(item.Id);
|
||||
else
|
||||
await DeleteChecklistItem(item.Id, link.TodoistParentId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteTask(string todoistId)
|
||||
{
|
||||
var habiticaId = (await storageClient.RetrieveRecord<TodoHabitLink>(userId, todoistId)).HabiticaId;
|
||||
await habiticaClient.DeleteTask(habiticaId);
|
||||
}
|
||||
|
||||
public async Task DeleteChecklistItem(string todoistId, string todoistParentId)
|
||||
{
|
||||
var habiticaTaskId = (await storageClient.RetrieveRecord<TodoHabitLink>(userId, todoistParentId)).HabiticaId;
|
||||
var habiticaChecklistId = (await storageClient.RetrieveRecord<TodoHabitLink>(userId, todoistId)).HabiticaId;
|
||||
await habiticaClient.DeleteChecklistItem(habiticaTaskId, habiticaChecklistId);
|
||||
}
|
||||
}
|
||||
}
|
||||
119
Habittodo.Service/HabiticaServiceClient.cs
Normal file
119
Habittodo.Service/HabiticaServiceClient.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using HabiticaTask = Habittodo.Model.Habitica.Task;
|
||||
using ChecklistItem = Habittodo.Model.Habitica.ChecklistItem;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Habittodo.Model.Habitica.Responses;
|
||||
using Habittodo.Model.Habitica.Enums;
|
||||
using System;
|
||||
|
||||
namespace Habittodo.Service
|
||||
{
|
||||
public class HabiticaServiceClient
|
||||
{
|
||||
private string userId { get; set; }
|
||||
private string apiKey { get; set; }
|
||||
|
||||
private string baseUrl => "https://habitica.com/api/v3/";
|
||||
|
||||
public HabiticaServiceClient(string userId, string apiKey)
|
||||
{
|
||||
this.userId = userId;
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public async Task<HabiticaReponse<HabiticaTask>> CreateTask(HabiticaTask task)
|
||||
{
|
||||
using (var client = CreateWebClient())
|
||||
{
|
||||
var request = JsonConvert.SerializeObject(task);
|
||||
var json = await client.UploadStringTaskAsync($"{baseUrl}/tasks/user", "POST", request);
|
||||
|
||||
return JsonConvert.DeserializeObject<HabiticaReponse<HabiticaTask>>(json);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<HabiticaTask> UpdateTask(HabiticaTask task)
|
||||
{
|
||||
using (var client = CreateWebClient())
|
||||
{
|
||||
var request = JsonConvert.SerializeObject(task);
|
||||
var json = await client.UploadStringTaskAsync($"{baseUrl}/tasks/{task.Id}", "PUT", request);
|
||||
|
||||
return JsonConvert.DeserializeObject<HabiticaTask>(json);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteTask(string taskId)
|
||||
{
|
||||
using (var client = CreateWebClient())
|
||||
await client.UploadStringTaskAsync($"{baseUrl}/tasks/{taskId}", "DELETE", "");
|
||||
}
|
||||
|
||||
public async Task ScoreTask(string taskId, ScoreAction action)
|
||||
{
|
||||
var actionStr = Enum.GetName(action.GetType(), action).ToLower();
|
||||
using (var client = CreateWebClient())
|
||||
await client.UploadStringTaskAsync($"{baseUrl}/tasks/{taskId}/score/{actionStr}", "POST", "");
|
||||
}
|
||||
|
||||
//public async Task<HabiticaReponse<List<HabiticaTask>>> ReadTasks(TaskType taskType = TaskType.Todo)
|
||||
//{
|
||||
// var taskTypeStr = Enum.GetName(taskType.GetType(), taskType).ToLower();
|
||||
// using (var client = CreateWebClient())
|
||||
// {
|
||||
// var json = await client.DownloadStringTaskAsync($"{baseUrl}/tasks/user");
|
||||
// var response = JsonConvert.DeserializeObject<HabiticaReponse<List<HabiticaTask>>>(json);
|
||||
// response.Data.RemoveAll(x => x.Type != taskTypeStr);
|
||||
|
||||
// return response;
|
||||
// }
|
||||
//} //public async Task<HabiticaReponse<List<HabiticaTask>>> ReadTasks(TaskType taskType = TaskType.Todo)
|
||||
|
||||
public async Task<HabiticaReponse<HabiticaTask>> CreateChecklistItem(ChecklistItem checklistItem, string taskId)
|
||||
{
|
||||
using (var client = CreateWebClient())
|
||||
{
|
||||
var request = JsonConvert.SerializeObject(checklistItem);
|
||||
var json = await client.UploadStringTaskAsync($"{baseUrl}/tasks/{taskId}/checklist", "POST", request);
|
||||
|
||||
return JsonConvert.DeserializeObject<HabiticaReponse<HabiticaTask>>(json);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<HabiticaReponse<HabiticaTask>> UpdateChecklistItem(ChecklistItem checklistItem, string taskId)
|
||||
{
|
||||
using (var client = CreateWebClient())
|
||||
{
|
||||
var request = JsonConvert.SerializeObject(checklistItem);
|
||||
var json = await client.UploadStringTaskAsync($"{baseUrl}/tasks/{taskId}/checklist/{checklistItem.Id}", "PUT", request);
|
||||
|
||||
return JsonConvert.DeserializeObject<HabiticaReponse<HabiticaTask>>(json);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteChecklistItem(string taskId, string checklistItemId)
|
||||
{
|
||||
using (var client = CreateWebClient())
|
||||
await client.UploadStringTaskAsync($"{baseUrl}/tasks/{taskId}/checklist/{checklistItemId}", "DELETE", "");
|
||||
}
|
||||
|
||||
public async Task ScoreChecklistItem(string taskId, string checklistItemId)
|
||||
{
|
||||
using (var client = CreateWebClient())
|
||||
await client.UploadStringTaskAsync($"{baseUrl}/tasks/{taskId}/checklist/{checklistItemId}/score", "POST", "");
|
||||
}
|
||||
|
||||
private WebClient CreateWebClient()
|
||||
{
|
||||
var client = new WebClient();
|
||||
|
||||
client.Headers[HttpRequestHeader.ContentType] = "application/json";
|
||||
client.Headers["x-api-user"] = userId;
|
||||
client.Headers["x-api-key"] = apiKey;
|
||||
client.Headers["x-client"] = "dotnet-habitica-client";
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Habittodo.Service/Habittodo.Service.csproj
Normal file
16
Habittodo.Service/Habittodo.Service.csproj
Normal file
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Class1.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Habittodo.Data\Habittodo.Data.csproj" />
|
||||
<ProjectReference Include="..\Habittodo.Model\Habittodo.Model.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
111
Habittodo.Service/TodoistIntegrationService.cs
Normal file
111
Habittodo.Service/TodoistIntegrationService.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using Habittodo.Data;
|
||||
using Habittodo.Model.Storage;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Habittodo.Model.Todoist;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections;
|
||||
|
||||
namespace Habittodo.Service
|
||||
{
|
||||
public class TodoistIntegrationService
|
||||
{
|
||||
private TodoistServiceClient todoistClient { get; set; }
|
||||
private TableStorageClient storageClient { get; set; }
|
||||
|
||||
private string userId { get; set; }
|
||||
private string latestSyncToken { get; set; } = string.Empty;
|
||||
|
||||
public TodoistIntegrationService(string todoistApiKey,
|
||||
string storageConnectionString,
|
||||
string userId)
|
||||
{
|
||||
this.todoistClient = new TodoistServiceClient(todoistApiKey);
|
||||
this.storageClient = new TableStorageClient(storageConnectionString);
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public async Task<Items> ReadItemChanges()
|
||||
{
|
||||
var itemChangesResponse = await todoistClient.GetItemChanges(ReadLatestSyncToken());
|
||||
latestSyncToken = itemChangesResponse.Sync_token;
|
||||
|
||||
return new Items(itemChangesResponse.Items, storageClient, userId);
|
||||
}
|
||||
|
||||
public async Task CommitRead()
|
||||
{
|
||||
await storageClient.InsertOrUpdate(new TodoistSync(userId, latestSyncToken));
|
||||
}
|
||||
|
||||
private string ReadLatestSyncToken()
|
||||
{
|
||||
try
|
||||
{
|
||||
return storageClient.Query<TodoistSync>()
|
||||
.Where(x => x.PartitionKey == userId)
|
||||
.ToList()
|
||||
.OrderByDescending(x => x.Timestamp)
|
||||
.First().RowKey;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public class Items : IEnumerable<Item>
|
||||
{
|
||||
private List<Item> changedItems;
|
||||
private readonly TableStorageClient storageClient;
|
||||
private readonly string userId;
|
||||
|
||||
internal Items(List<Item> changedItems, TableStorageClient storageClient, string userId)
|
||||
{
|
||||
this.changedItems = changedItems;
|
||||
this.storageClient = storageClient;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public IEnumerable<Item> WhereAdded()
|
||||
{
|
||||
return changedItems
|
||||
.Where(x => !storageClient
|
||||
.Exists<TodoHabitLink>(userId, x.Id) && x.Is_deleted == 0)
|
||||
.OrderBy(x => x.Parent_Id);
|
||||
}
|
||||
|
||||
public IEnumerable<Item> WhereUpdated()
|
||||
{
|
||||
return changedItems
|
||||
.Where(x => storageClient
|
||||
.Exists<TodoHabitLink>(userId, x.Id) && x.Is_deleted == 0)
|
||||
.OrderBy(x => x.Parent_Id);
|
||||
}
|
||||
|
||||
public IEnumerable<Item> WhereCompleted()
|
||||
{
|
||||
return changedItems
|
||||
.Where(x => storageClient
|
||||
.Exists<TodoHabitLink>(userId, x.Id) && x.Is_deleted == 0 && x.Date_completed != null)
|
||||
.OrderByDescending(x => x.Parent_Id);
|
||||
}
|
||||
|
||||
public IEnumerable<Item> WhereDeleted()
|
||||
{
|
||||
return changedItems
|
||||
.Where(x => storageClient
|
||||
.Exists<TodoHabitLink>(userId, x.Id) && x.Is_deleted == 1);
|
||||
}
|
||||
|
||||
public IEnumerator<Item> GetEnumerator()
|
||||
{
|
||||
return changedItems.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Habittodo.Service/TodoistServiceClient.cs
Normal file
64
Habittodo.Service/TodoistServiceClient.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Habittodo.Model.Todoist.Responses;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Habittodo.Service
|
||||
{
|
||||
public class TodoistServiceClient
|
||||
{
|
||||
private string apiKey { get; set; }
|
||||
private string baseUrl => "https://api.todoist.com/sync/v8/";
|
||||
|
||||
public TodoistServiceClient(string apiKey)
|
||||
{
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public async Task<SyncResponse> GetItems()
|
||||
{
|
||||
var response = await GetItemChanges("*");
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<SyncResponse> GetItemChanges(string syncToken)
|
||||
{
|
||||
using (var client = CreateWebClient())
|
||||
{
|
||||
var body = InitializeRequestBody();
|
||||
body["sync_token"] = syncToken;
|
||||
body["resource_types"] = "[\"items\"]";
|
||||
|
||||
var json = await client.UploadStringTaskAsync($"{baseUrl}sync", RequestBodyToString(body));
|
||||
var response = JsonConvert.DeserializeObject<SyncResponse>(json);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
private string RequestBodyToString(Dictionary<string, string> body)
|
||||
{
|
||||
var bodyStr = "";
|
||||
foreach (var pair in body)
|
||||
bodyStr += $"{pair.Key}={pair.Value}&";
|
||||
|
||||
return bodyStr;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> InitializeRequestBody()
|
||||
{
|
||||
var body = new Dictionary<string, string>();
|
||||
body["token"] = apiKey;
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
private WebClient CreateWebClient()
|
||||
{
|
||||
var client = new WebClient();
|
||||
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user