feat: make best-effort to maintain order of stacks and cards
This commit is contained in:
parent
f9762512f4
commit
4f69fc0a4b
8 changed files with 90 additions and 24 deletions
|
@ -1,6 +1,12 @@
|
|||
//
|
||||
use crate::nextcloud::model::{NextcloudCardDescription, NextcloudCardTitle};
|
||||
use crate::trello::model::card::{TrelloCardDescription, TrelloCardName};
|
||||
use crate::nextcloud::model::NextcloudStackTitle;
|
||||
use crate::{
|
||||
nextcloud::model::{NextcloudCardDescription, NextcloudCardTitle, NextcloudOrder},
|
||||
trello::model::{
|
||||
card::{TrelloCardDescription, TrelloCardName, TrelloCardPosition},
|
||||
list::{TrelloListName, TrelloListPosition},
|
||||
},
|
||||
};
|
||||
|
||||
impl From<&TrelloCardName> for NextcloudCardTitle {
|
||||
fn from(value: &TrelloCardName) -> Self {
|
||||
|
@ -13,3 +19,21 @@ impl From<&TrelloCardDescription> for NextcloudCardDescription {
|
|||
Self::new(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TrelloListName> for NextcloudStackTitle {
|
||||
fn from(value: &TrelloListName) -> Self {
|
||||
Self::new(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TrelloListPosition> for NextcloudOrder {
|
||||
fn from(value: &TrelloListPosition) -> Self {
|
||||
Self::new(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TrelloCardPosition> for NextcloudOrder {
|
||||
fn from(value: &TrelloCardPosition) -> Self {
|
||||
Self::new(value.0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,9 @@ pub(crate) async fn run(ctx: &FullCtx) -> color_eyre::Result<()> {
|
|||
.map(TrelloBoardId::new)
|
||||
.expect("find selected board");
|
||||
// get list of trello stacks for the selected board
|
||||
let trello_stacks = trello_client.board(&trello_board_id).await.result?.lists;
|
||||
let mut trello_stacks = trello_client.board(&trello_board_id).await.result?.lists;
|
||||
// sort stacks by position
|
||||
trello_stacks.sort_by_key(|s| s.pos);
|
||||
// prompt user to select some stacks
|
||||
let trello_stack_names = trello_stacks
|
||||
.iter()
|
||||
|
|
|
@ -56,10 +56,12 @@ impl Actor for ImportStackActor {
|
|||
|
||||
crate::p!(this.ctx.prt, "Importing stack: {}", this.trello_stack.name);
|
||||
// - get the list of trello cards in the stack
|
||||
let trello_cards = trello_client
|
||||
let mut trello_cards = trello_client
|
||||
.list_cards(&TrelloListId::new(this.trello_stack.id.as_ref()))
|
||||
.await
|
||||
.result?;
|
||||
// sort cards by their position
|
||||
trello_cards.sort_by_key(|card| card.pos);
|
||||
|
||||
// - for each card in the trello stack
|
||||
for trello_card in trello_cards.into_iter() {
|
||||
|
|
|
@ -21,26 +21,41 @@ use crate::{
|
|||
|
||||
async fn create_any_missing_stacks(
|
||||
ctx: &FullCtx,
|
||||
selected_trello_stack_names: &[String],
|
||||
selected_trello_stacks: &[TrelloList],
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
nextcloud_stacks: Vec<Stack>,
|
||||
) -> color_eyre::Result<()> {
|
||||
let deck_client: DeckClient = ctx.deck_client();
|
||||
|
||||
// identify any stacks by name from those selected in trello that are missing in nextcloud
|
||||
let missing_stack_names = selected_trello_stack_names
|
||||
let missing_stacks = selected_trello_stacks
|
||||
.iter()
|
||||
.filter(|s| !nextcloud_stacks.iter().any(|ns| ns.title.deref() == *s))
|
||||
.filter(|trello_stack| {
|
||||
!nextcloud_stacks
|
||||
.iter()
|
||||
.any(|nextcloud_stack| nextcloud_stack.title.deref() == trello_stack.name.deref())
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
if !missing_stack_names.is_empty() {
|
||||
crate::p!(ctx.prt, "Missing stacks: {:?}", missing_stack_names);
|
||||
if !missing_stacks.is_empty() {
|
||||
crate::p!(
|
||||
ctx.prt,
|
||||
"Missing stacks: {:?}",
|
||||
missing_stacks
|
||||
.iter()
|
||||
.map(|s| s.name.as_ref())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
// create any missing stacks in nextcloud
|
||||
// for each missing stack
|
||||
for missing_stack_name in missing_stack_names.into_iter() {
|
||||
for missing_stack in missing_stacks.into_iter() {
|
||||
// - create the stack
|
||||
let stack = deck_client
|
||||
.create_stack(nextcloud_board_id, &missing_stack_name.clone().into())
|
||||
.create_stack(
|
||||
nextcloud_board_id,
|
||||
(&missing_stack.name).into(),
|
||||
(&missing_stack.pos).into(),
|
||||
)
|
||||
.await
|
||||
.result?;
|
||||
p!(ctx.prt, "Created stack: {}", stack.title);
|
||||
|
@ -83,7 +98,12 @@ impl Actor for ImportStacksActor {
|
|||
|
||||
on_actor_start!(this, actor_ref, {
|
||||
// spawn a new ImportStack actor for each trello_stack named in selected_trello_stack_names
|
||||
|
||||
let mut trello_stacks = vec![];
|
||||
std::mem::swap(&mut this.trello_stacks, &mut trello_stacks);
|
||||
let selected_trello_stacks = trello_stacks
|
||||
.into_iter()
|
||||
.filter(|s| this.selected_trello_stack_names.contains(s.name.as_ref()))
|
||||
.collect::<Vec<_>>();
|
||||
// get list of nextcloud stacks in the selected board
|
||||
let nextcloud_stacks = this
|
||||
.ctx
|
||||
|
@ -93,7 +113,7 @@ impl Actor for ImportStacksActor {
|
|||
.result?;
|
||||
create_any_missing_stacks(
|
||||
&this.ctx,
|
||||
&this.selected_trello_stack_names,
|
||||
&selected_trello_stacks,
|
||||
this.nextcloud_board_id,
|
||||
nextcloud_stacks,
|
||||
)
|
||||
|
@ -107,11 +127,7 @@ impl Actor for ImportStacksActor {
|
|||
.result?;
|
||||
|
||||
// for each selected trello stack
|
||||
for selected_trello_stack in this
|
||||
.trello_stacks
|
||||
.iter()
|
||||
.filter(|s| this.selected_trello_stack_names.contains(s.name.as_ref()))
|
||||
{
|
||||
for selected_trello_stack in selected_trello_stacks {
|
||||
let nextcloud_stack_id = //this.
|
||||
nextcloud_stacks
|
||||
.iter()
|
||||
|
|
|
@ -7,6 +7,7 @@ use kxio::{
|
|||
use reqwest::multipart;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::nextcloud::model::NextcloudOrder;
|
||||
use crate::{
|
||||
api_result::APIResult,
|
||||
f,
|
||||
|
@ -118,13 +119,14 @@ impl<'ctx> DeckClient<'ctx> {
|
|||
pub(crate) async fn create_stack(
|
||||
&self,
|
||||
board_id: NextcloudBoardId,
|
||||
stack_title: &NextcloudStackTitle,
|
||||
stack_title: NextcloudStackTitle,
|
||||
stack_order: NextcloudOrder,
|
||||
) -> APIResult<Stack> {
|
||||
self.request_with_body(
|
||||
f!("boards/{board_id}/stacks"),
|
||||
json!({
|
||||
"title": stack_title,
|
||||
"order": 999,
|
||||
"order": stack_order,
|
||||
})
|
||||
.to_string(),
|
||||
|net, url| net.post(url),
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
use clap::Parser;
|
||||
|
||||
use crate::execute::Execute;
|
||||
use crate::nextcloud::model::NextcloudOrder;
|
||||
use crate::{p, FullCtx};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
|
@ -50,7 +51,11 @@ impl Execute for NextcloudStackCommand {
|
|||
} => {
|
||||
let api_result = ctx
|
||||
.deck_client()
|
||||
.create_stack((*board_id).into(), &stack_title.clone().into())
|
||||
.create_stack(
|
||||
(*board_id).into(),
|
||||
stack_title.clone().into(),
|
||||
NextcloudOrder::new(999),
|
||||
)
|
||||
.await;
|
||||
if *dump {
|
||||
p!(ctx.prt, "{}", api_result.text);
|
||||
|
|
|
@ -11,8 +11,8 @@ newtype!(TrelloCardName, String, Display, "Card Name");
|
|||
newtype!(TrelloCardDescription, String, Display, "Card Description");
|
||||
newtype!(TrelloCardDue, String, Display, "Card Due");
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Display)]
|
||||
pub struct TrelloCardPosition(i64);
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Display)]
|
||||
pub(crate) struct TrelloCardPosition(pub i64);
|
||||
|
||||
impl<'de> Deserialize<'de> for TrelloCardPosition {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
use derive_more::derive::Display;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use crate::newtype;
|
||||
|
||||
|
@ -14,8 +14,23 @@ newtype!(
|
|||
"List Name"
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Display)]
|
||||
pub(crate) struct TrelloListPosition(pub i64);
|
||||
|
||||
impl<'de> Deserialize<'de> for TrelloListPosition {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
// First deserialize as f64
|
||||
let value = f64::deserialize(deserializer)?;
|
||||
// Convert to i64 by rounding
|
||||
Ok(TrelloListPosition(value.round() as i64))
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
pub(crate) struct TrelloList {
|
||||
pub(crate) id: TrelloListId,
|
||||
pub(crate) name: TrelloListName,
|
||||
pub(crate) pos: TrelloListPosition,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue