- recoded lib
- add sync.py (synchronisize boards)
This commit is contained in:
parent
7bd62d9ec9
commit
271f9b3469
73
README.md
73
README.md
|
@ -1,47 +1,74 @@
|
||||||
# Nextcloud Deck importieren/exportieren
|
# Nextcloud Deck importieren/exportieren
|
||||||
|
|
||||||
## Installation
|
## Voraussetzung
|
||||||
|
|
||||||
1. Download https://github.com/johappel/nextcloud-import-export/archive/refs/heads/main.zip
|
[Python](https://www.python.org/downloads/) muss installiert sein. Du kannst diese [Anleitung](https://kinsta.com/de/wissensdatenbank/python-installieren/) nutzen.
|
||||||
oder auf der Komandozeile git clone https://github.com/johappel/nextcloud-import-export.git
|
|
||||||
2. Gehe in das Verzeichnis nextcloud-import-export (cd nextcloud-import-export)
|
|
||||||
3. Führe auf der Komandozeile aus:`pip install requests`
|
|
||||||
4. Kopiere die Datei "sample.config.py" nach "config.py" und trage dort die Daten zu deinen Nextcloudinstanzen ein
|
|
||||||
|
|
||||||
|
## Optional: Git installieren
|
||||||
|
|
||||||
## Verwendung
|
Installiere [Git](https://git-scm.com/book/de/v2/Erste-Schritte-Git-installieren): Das ermöglicht dir, die Skripte aktuell zu halten und dich an der Entwicklung zu beteiligen.
|
||||||
Um ein bestimmtes Deck auf eine andfere Nextcloud Instanz zu kopieren gibst du auf der Komandozeile an:
|
|
||||||
|
|
||||||
```python
|
## Installation der Skripte
|
||||||
python clone.py --"Name des Decks"
|
|
||||||
```
|
1. [Download](https://github.com/johappel/nextcloud-import-export/archive/refs/heads/main.zip) der Skripte oder mit Git
|
||||||
oder
|
`git clone https://github.com/johappel/nextcloud-import-export.git`
|
||||||
```python
|
2. Gehe in das Verzeichnis nextcloud-import-export (cd nextcloud-import-export)
|
||||||
python3 clone.py --"Name des Decks"
|
3. Führe auf der Kommandozeile aus: `pip install requests`
|
||||||
|
4. Kopiere die Datei "sample.config.py" nach "config.py" und trage dort die Daten zu deinen Nextcloud-Instanzen ein.
|
||||||
|
5. Mit diesem Befehl auf der Kommandozeile holst du die neueste Version:
|
||||||
|
`git pull`
|
||||||
|
|
||||||
|
## Anwendungsfälle
|
||||||
|
|
||||||
|
Um ein bestimmtes Deck auf eine andere Nextcloud-Instanz zu kopieren, gibst du auf der Kommandozeile einen der folgenden Befehle ein:
|
||||||
|
|
||||||
|
1. Um ein Deck zu kopieren:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python clone.py --board "Name des Decks"
|
||||||
```
|
```
|
||||||
|
|
||||||
Um alle Decks zu kopieren, gibst du ein:
|
2. Um ein bestehendes Deck auf der Zielinstanz zu löschen und zu ersetzen:
|
||||||
|
|
||||||
```python
|
```sh
|
||||||
|
python sync.py --board "Name des Decks" --replace
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Um ein bestehendes Deck auf der Zielinstanz mit den Daten, Karten und Stacks der Originalinstanz synchron zu halten:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python sync.py --board "Name des Decks"
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Um ein Backup aller Decks mit Datum des Backups im Titel auf der Zielinstanz zu sichern:
|
||||||
|
|
||||||
|
```sh
|
||||||
python backup.py
|
python backup.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Dank der großartigen Arbeit von @svbergerem:
|
Dank der großartigen Arbeit von @svbergerem:
|
||||||
https://gist.github.com/svbergerem/5914d7f87764901aefddba125af99938
|
https://gist.github.com/svbergerem/5914d7f87764901aefddba125af99938
|
||||||
|
|
||||||
### Funktionen des Skripts
|
### Funktionen des Skripts
|
||||||
|
|
||||||
1. **Daten von der Quellinstanz abrufen:**
|
1. **Daten von der Quellinstanz abrufen:**
|
||||||
- `getBoards()`: Ruft die Liste aller Boards ab.
|
Gib bei den folgenden Funktionen als Parameter 'from' oder 'to' ein, je nachdem, ob du die Daten von der Quellinstanz oder der Zielinstanz abfragst:
|
||||||
- `getBoardDetails(boardId)`: Ruft Details eines spezifischen Boards ab.
|
- `getBoards(from_or_to)`: Ruft die Liste aller Boards ab.
|
||||||
- `getStacks(boardId)`: Ruft die Stacks eines Boards ab.
|
- `getBoardDetails(boardId, from_or_to)`: Ruft Details eines spezifischen Boards ab.
|
||||||
- `getStacksArchived(boardId)`: Ruft die archivierten Stacks eines Boards ab.
|
- `getStacks(boardId, from_or_to)`: Ruft die Stacks eines Boards ab.
|
||||||
|
- `getStacksArchived(boardId, from_or_to)`: Ruft die archivierten Stacks eines Boards ab.
|
||||||
2. **Daten zur Zielinstanz übertragen:**
|
2. **Daten zur Zielinstanz übertragen:**
|
||||||
- `createBoard(title, color)`: Erstellt ein Board.
|
- `createBoard(title, color)`: Erstellt ein Board.
|
||||||
- `createLabel(title, color, boardId)`: Erstellt ein Label in einem Board.
|
- `createLabel(title, color, boardId)`: Erstellt ein Label in einem Board.
|
||||||
- `createStack(title, order, boardId)`: Erstellt einen Stack in einem Board.
|
- `createStack(title, order, boardId)`: Erstellt einen Stack in einem Board.
|
||||||
- `createCard(title, ctype, order, description, duedate, boardId, stackId)`: Erstellt eine Karte in einem Stack.
|
- `createCard(title, ctype, order, description, duedate, boardId, stackId)`: Erstellt eine Karte in einem Stack.
|
||||||
- `assignLabel(labelId, cardId, boardId, stackId)`: Weist ein Label einer Karte zu.
|
- `assignLabel(labelId, cardId, boardId, stackId)`: Weist einer Karte ein Label zu.
|
||||||
- `archiveCard(card, boardId, stackId)`: Archiviert eine Karte.
|
- `archiveCard(card, boardId, stackId)`: Archiviert eine Karte.
|
||||||
- `copyCard(card, boardIdTo, stackIdTo, labelsMap)`: Kopiert eine Karte, einschließlich ihrer Labels und archiviertem Status.
|
- `copyCard(card, boardIdTo, stackIdTo, labelsMap)`: Kopiert eine Karte, einschließlich ihrer Labels und des archivierten Status.
|
||||||
|
- `deleteBoard(boardIdTo)`: Löscht ein Board.
|
||||||
|
- `deleteStacks(boardIdTo)`: Löscht alle Listen eines Boards.
|
||||||
|
- `deleteLabels(boardIdTo)`: Löscht alle Karten eines Boards.
|
||||||
|
|
||||||
|
# @todo:
|
||||||
|
- Synchronisation verbessern (nur dezidierte Karten ersetzen)
|
||||||
|
- Grafisches User Interface
|
||||||
|
|
Binary file not shown.
12
backup.py
12
backup.py
|
@ -1,4 +1,10 @@
|
||||||
from .lib import *
|
from datetime import datetime
|
||||||
|
from lib import *
|
||||||
|
|
||||||
|
# datetime object containing current date and time
|
||||||
|
now = datetime.now()
|
||||||
|
# dd/mm/YY H:M:S
|
||||||
|
dt_string = now.strftime("_%Y-%m-%d-%H-%M-%S")
|
||||||
|
|
||||||
boards = getBoards()
|
boards = getBoards()
|
||||||
|
|
||||||
|
@ -6,9 +12,9 @@ boards = getBoards()
|
||||||
for board in boards:
|
for board in boards:
|
||||||
boardIdFrom = board['id']
|
boardIdFrom = board['id']
|
||||||
# create board
|
# create board
|
||||||
createdBoard = createBoard(board['title'], board['color'])
|
createdBoard = createBoard(board['title']+dt_string, board['color'])
|
||||||
boardIdTo = createdBoard['id']
|
boardIdTo = createdBoard['id']
|
||||||
print('Created board', board['title'])
|
print('Created board', board['title'], dt_string)
|
||||||
|
|
||||||
# create labels
|
# create labels
|
||||||
boardDetails = getBoardDetails(board['id'])
|
boardDetails = getBoardDetails(board['id'])
|
||||||
|
|
80
clone.py
80
clone.py
|
@ -4,52 +4,68 @@ import lib
|
||||||
# Argumente von der Kommandozeile einlesen
|
# Argumente von der Kommandozeile einlesen
|
||||||
parser = argparse.ArgumentParser(description='Klonen eines bestimmten Boards von einer Nextcloud-Instanz zu einer anderen.')
|
parser = argparse.ArgumentParser(description='Klonen eines bestimmten Boards von einer Nextcloud-Instanz zu einer anderen.')
|
||||||
parser.add_argument('--board', type=str, required=True, help='Der Titel des zu klonenden Boards.')
|
parser.add_argument('--board', type=str, required=True, help='Der Titel des zu klonenden Boards.')
|
||||||
|
parser.add_argument('--replace', action='store_true', help='Ersetze das Board im Ziel, falls es bereits existiert.')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Board-Titel, den wir klonen möchten
|
# Board-Titel, den wir klonen möchten
|
||||||
board_to_clone = args.board
|
board_to_clone = args.board
|
||||||
|
|
||||||
# Hole alle Boards von der Quellinstanz
|
# Hole alle Boards von der Quellinstanz
|
||||||
boards = lib.getBoards()
|
boards_from = lib.getBoards()
|
||||||
|
|
||||||
# Finde das gewünschte Board
|
# Finde das gewünschte Board
|
||||||
board_to_clone_data = next((board for board in boards if board['title'] == board_to_clone), None)
|
board_to_clone_data = next((board for board in boards_from if board['title'] == board_to_clone), None)
|
||||||
|
|
||||||
if not board_to_clone_data:
|
if not board_to_clone_data:
|
||||||
print(f'Board "{board_to_clone}" nicht gefunden.')
|
print(f'Board "{board_to_clone}" nicht gefunden.')
|
||||||
else:
|
else:
|
||||||
boardIdFrom = board_to_clone_data['id']
|
boardIdFrom = board_to_clone_data['id']
|
||||||
# Erstelle das Board in der Zielinstanz
|
|
||||||
createdBoard = lib.createBoard(board_to_clone_data['title'], board_to_clone_data['color'])
|
|
||||||
boardIdTo = createdBoard['id']
|
|
||||||
print(f'Board "{board_to_clone}" erstellt')
|
|
||||||
|
|
||||||
# Kopiere die Labels des Boards
|
# Überprüfe, ob das Board im Ziel bereits existiert
|
||||||
boardDetails = lib.getBoardDetails(boardIdFrom)
|
boards_to = lib.getBoards('to')
|
||||||
labelsMap = {}
|
existing_board_to = next((board for board in boards_to if board['title'] == board_to_clone and 0 == board['deletedAt']), None)
|
||||||
for label in boardDetails['labels']:
|
|
||||||
createdLabel = lib.createLabel(label['title'], label['color'], boardIdTo)
|
|
||||||
labelsMap[label['id']] = createdLabel['id']
|
|
||||||
|
|
||||||
# Kopiere die Stacks und Karten des Boards
|
# Löschen wenn der parameter --replace gesetzt wurde und das Board existiert
|
||||||
stacks = lib.getStacks(boardIdFrom)
|
if args.replace and existing_board_to:
|
||||||
stacksMap = {}
|
# Lösche das bestehende Board im Ziel
|
||||||
for stack in stacks:
|
print(f'Lösche Board: {existing_board_to["id"]}')
|
||||||
createdStack = lib.createStack(stack['title'], stack['order'], boardIdTo)
|
lib.deleteBoard(existing_board_to['id'])
|
||||||
stackIdTo = createdStack['id']
|
print(f'Board "{board_to_clone}" im Ziel gelöscht')
|
||||||
stacksMap[stack['id']] = stackIdTo
|
|
||||||
print(f' Stapel "{stack['title']}" erstellt')
|
|
||||||
|
|
||||||
if 'cards' in stack:
|
# Erstelle das Board in der Zielinstanz, wenn es nicht existiert oder ersetzt werden soll
|
||||||
for card in stack['cards']:
|
if not existing_board_to or args.replace:
|
||||||
lib.copyCard(card, boardIdTo, stackIdTo, labelsMap)
|
createdBoard = lib.createBoard(board_to_clone_data['title'], board_to_clone_data['color'])
|
||||||
print(f' {len(stack["cards"])} Karten erstellt')
|
boardIdTo = createdBoard['id']
|
||||||
|
print(f'Board "{board_to_clone}" erstellt')
|
||||||
|
|
||||||
# Kopiere die archivierten Stacks und Karten des Boards
|
# Kopiere die Labels des Boards
|
||||||
stacks = lib.getStacksArchived(boardIdFrom)
|
boardDetails = lib.getBoardDetails(boardIdFrom)
|
||||||
for stack in stacks:
|
labelsMap = {}
|
||||||
if 'cards' in stack:
|
for label in boardDetails['labels']:
|
||||||
print(f' Stack "{stack['title']}"')
|
createdLabel = lib.createLabel(label['title'], label['color'], boardIdTo)
|
||||||
for card in stack['cards']:
|
labelsMap[label['id']] = createdLabel['id']
|
||||||
lib.copyCard(card, boardIdTo, stacksMap[stack['id']], labelsMap)
|
|
||||||
print(f' {len(stack["cards"])} archivierte Karten erstellt')
|
# Kopiere die Stacks und Karten des Boards
|
||||||
|
stacks = lib.getStacks(boardIdFrom)
|
||||||
|
stacksMap = {}
|
||||||
|
for stack in stacks:
|
||||||
|
createdStack = lib.createStack(stack['title'], stack['order'], boardIdTo)
|
||||||
|
stackIdTo = createdStack['id']
|
||||||
|
stacksMap[stack['id']] = stackIdTo
|
||||||
|
print(f' Stapel "{stack["title"]}" erstellt')
|
||||||
|
|
||||||
|
if 'cards' in stack:
|
||||||
|
for card in stack['cards']:
|
||||||
|
lib.copyCard(card, boardIdTo, stackIdTo, labelsMap)
|
||||||
|
print(f' {len(stack["cards"])} Karten erstellt')
|
||||||
|
|
||||||
|
# Kopiere die archivierten Stacks und Karten des Boards
|
||||||
|
stacks = lib.getStacksArchived(boardIdFrom)
|
||||||
|
for stack in stacks:
|
||||||
|
if 'cards' in stack:
|
||||||
|
print(f' Stack "{stack["title"]}"')
|
||||||
|
for card in stack['cards']:
|
||||||
|
lib.copyCard(card, boardIdTo, stacksMap[stack['id']], labelsMap)
|
||||||
|
print(f' {len(stack["cards"])} archivierte Karten erstellt')
|
||||||
|
else:
|
||||||
|
print(f'Board "{board_to_clone}" existiert bereits und wird nicht ersetzt (verwenden Sie --replace, um zu ersetzen).')
|
||||||
|
|
140
lib.py
140
lib.py
|
@ -1,9 +1,6 @@
|
||||||
# thanks to the awesome work of @svbergerem
|
|
||||||
# -> svbergerem/nextcloud-deck-export-import.py
|
|
||||||
# https://gist.github.com/svbergerem/5914d7f87764901aefddba125af99938
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import config
|
import config
|
||||||
|
import base64
|
||||||
|
|
||||||
urlFrom = config.urlFrom
|
urlFrom = config.urlFrom
|
||||||
authFrom = config.authFrom
|
authFrom = config.authFrom
|
||||||
|
@ -11,123 +8,66 @@ authFrom = config.authFrom
|
||||||
urlTo = config.urlTo
|
urlTo = config.urlTo
|
||||||
authTo = config.authTo
|
authTo = config.authTo
|
||||||
|
|
||||||
|
|
||||||
headers = {'OCS-APIRequest': 'true', 'Content-Type': 'application/json'}
|
headers = {'OCS-APIRequest': 'true', 'Content-Type': 'application/json'}
|
||||||
|
|
||||||
def getBoards():
|
def make_request(method, endpoint, from_to='from', json=None):
|
||||||
response = requests.get(
|
if from_to == 'from':
|
||||||
f'{urlFrom}/index.php/apps/deck/api/v1.0/boards',
|
url = urlFrom
|
||||||
auth=authFrom,
|
auth = authFrom
|
||||||
headers=headers)
|
else: # from_to == 'to'
|
||||||
|
url = urlTo
|
||||||
|
auth = authTo
|
||||||
|
|
||||||
|
response = requests.request(method, f'{url}{endpoint}', auth=auth, headers=headers, json=json)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return response.json()
|
return response.json()
|
||||||
|
|
||||||
def getBoardDetails(boardId):
|
def getBoards(from_to='from'):
|
||||||
response = requests.get(
|
boards = make_request('GET', '/index.php/apps/deck/api/v1.0/boards', from_to)
|
||||||
f'{urlFrom}/index.php/apps/deck/api/v1.0/boards/{boardId}',
|
return [board for board in boards if 0 == board['deletedAt']]
|
||||||
auth=authFrom,
|
|
||||||
headers=headers)
|
|
||||||
response.raise_for_status()
|
|
||||||
return response.json()
|
|
||||||
|
|
||||||
def getStacks(boardId):
|
def getBoardDetails(boardId, from_to='from'):
|
||||||
response = requests.get(
|
return make_request('GET', f'/index.php/apps/deck/api/v1.0/boards/{boardId}', from_to)
|
||||||
f'{urlFrom}/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks',
|
|
||||||
auth=authFrom,
|
|
||||||
headers=headers)
|
|
||||||
response.raise_for_status()
|
|
||||||
return response.json()
|
|
||||||
|
|
||||||
def getStacksArchived(boardId):
|
def getStacks(boardId, from_to='from'):
|
||||||
response = requests.get(
|
return make_request('GET', f'/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks', from_to)
|
||||||
f'{urlFrom}/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks/archived',
|
|
||||||
auth=authFrom,
|
def getStacksArchived(boardId, from_to='from'):
|
||||||
headers=headers)
|
return make_request('GET', f'/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks/archived', from_to)
|
||||||
response.raise_for_status()
|
|
||||||
return response.json()
|
|
||||||
|
|
||||||
def createBoard(title, color):
|
def createBoard(title, color):
|
||||||
response = requests.post(
|
board = make_request('POST', '/index.php/apps/deck/api/v1.0/boards', 'to', json={'title': title, 'color': color})
|
||||||
f'{urlTo}/index.php/apps/deck/api/v1.0/boards',
|
|
||||||
auth=authTo,
|
|
||||||
json={
|
|
||||||
'title': title,
|
|
||||||
'color': color
|
|
||||||
},
|
|
||||||
headers=headers)
|
|
||||||
response.raise_for_status()
|
|
||||||
board = response.json()
|
|
||||||
boardId = board['id']
|
boardId = board['id']
|
||||||
# remove all default labels
|
# remove all default labels
|
||||||
for label in board['labels']:
|
for label in board['labels']:
|
||||||
labelId = label['id']
|
labelId = label['id']
|
||||||
response = requests.delete(
|
make_request('DELETE', f'/index.php/apps/deck/api/v1.0/boards/{boardId}/labels/{labelId}', 'to')
|
||||||
f'{urlTo}/index.php/apps/deck/api/v1.0/boards/{boardId}/labels/{labelId}',
|
|
||||||
auth=authTo,
|
|
||||||
headers=headers)
|
|
||||||
response.raise_for_status()
|
|
||||||
return board
|
return board
|
||||||
|
|
||||||
def createLabel(title, color, boardId):
|
def createLabel(title, color, boardId):
|
||||||
response = requests.post(
|
return make_request('POST', f'/index.php/apps/deck/api/v1.0/boards/{boardId}/labels', 'to', json={'title': title, 'color': color})
|
||||||
f'{urlTo}/index.php/apps/deck/api/v1.0/boards/{boardId}/labels',
|
|
||||||
auth=authTo,
|
|
||||||
json={
|
|
||||||
'title': title,
|
|
||||||
'color': color
|
|
||||||
},
|
|
||||||
headers=headers)
|
|
||||||
response.raise_for_status()
|
|
||||||
return response.json()
|
|
||||||
|
|
||||||
def createStack(title, order, boardId):
|
def createStack(title, order, boardId):
|
||||||
response = requests.post(
|
return make_request('POST', f'/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks', 'to', json={'title': title, 'order': order})
|
||||||
f'{urlTo}/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks',
|
|
||||||
auth=authTo,
|
|
||||||
json={
|
|
||||||
'title': title,
|
|
||||||
'order': order
|
|
||||||
},
|
|
||||||
headers=headers)
|
|
||||||
response.raise_for_status()
|
|
||||||
return response.json()
|
|
||||||
|
|
||||||
def createCard(title, ctype, order, description, duedate, boardId, stackId):
|
def createCard(title, ctype, order, description, duedate, boardId, stackId):
|
||||||
response = requests.post(
|
try:
|
||||||
f'{urlTo}/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks/{stackId}/cards',
|
return make_request('POST', f'/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks/{stackId}/cards', 'to',
|
||||||
auth=authTo,
|
json={'title': title, 'type': ctype, 'order': order, 'description': description, 'duedate': duedate})
|
||||||
json={
|
except requests.exceptions.HTTPError as e:
|
||||||
'title': title,
|
print(f"Error creating card: {e}")
|
||||||
'type': ctype,
|
print(f"Response: {e.response.text}")
|
||||||
'order': order,
|
raise
|
||||||
'description': description,
|
|
||||||
'duedate': duedate
|
|
||||||
},
|
|
||||||
headers=headers)
|
|
||||||
response.raise_for_status()
|
|
||||||
return response.json()
|
|
||||||
|
|
||||||
def assignLabel(labelId, cardId, boardId, stackId):
|
def assignLabel(labelId, cardId, boardId, stackId):
|
||||||
response = requests.put(
|
make_request('PUT', f'/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignLabel', 'to', json={'labelId': labelId})
|
||||||
f'{urlTo}/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignLabel',
|
|
||||||
auth=authTo,
|
|
||||||
json={
|
|
||||||
'labelId': labelId
|
|
||||||
},
|
|
||||||
headers=headers)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
def archiveCard(card, boardId, stackId):
|
def archiveCard(card, boardId, stackId):
|
||||||
cardId = card['id']
|
|
||||||
card['archived'] = True
|
card['archived'] = True
|
||||||
response = requests.put(
|
make_request('PUT', f'/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{card["id"]}', 'to', json=card)
|
||||||
f'{urlTo}/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}',
|
|
||||||
auth=authTo,
|
|
||||||
json=card,
|
|
||||||
headers=headers)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
def copyCard(card, boardIdTo, stackIdTo, labelsMap):
|
def copyCard(card, boardIdTo, stackIdTo, labelsMap):
|
||||||
|
print(f"Copying card '{card['title']}' to board {boardIdTo}, stack {stackIdTo}")
|
||||||
createdCard = createCard(
|
createdCard = createCard(
|
||||||
card['title'],
|
card['title'],
|
||||||
card['type'],
|
card['type'],
|
||||||
|
@ -146,4 +86,16 @@ def copyCard(card, boardIdTo, stackIdTo, labelsMap):
|
||||||
if card['archived']:
|
if card['archived']:
|
||||||
archiveCard(createdCard, boardIdTo, stackIdTo)
|
archiveCard(createdCard, boardIdTo, stackIdTo)
|
||||||
|
|
||||||
|
# Löschfunktionen auf der Zielinstanz
|
||||||
|
def deleteBoard(boardId):
|
||||||
|
make_request('DELETE', f'/index.php/apps/deck/api/v1.0/boards/{boardId}', 'to')
|
||||||
|
|
||||||
|
def deleteStacks(boardId):
|
||||||
|
stacks = getStacks(boardId, 'to')
|
||||||
|
for stack in stacks:
|
||||||
|
make_request('DELETE', f'/index.php/apps/deck/api/v1.0/boards/{boardId}/stacks/{stack["id"]}', 'to')
|
||||||
|
|
||||||
|
def deleteLabels(boardId):
|
||||||
|
boardDetails = getBoardDetails(boardId, 'to')
|
||||||
|
for label in boardDetails['labels']:
|
||||||
|
make_request('DELETE', f'/index.php/apps/deck/api/v1.0/boards/{boardId}/labels/{label["id"]}', 'to')
|
||||||
|
|
107
sync.py
Normal file
107
sync.py
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import argparse
|
||||||
|
import requests
|
||||||
|
import lib
|
||||||
|
|
||||||
|
# Argumente von der Kommandozeile einlesen
|
||||||
|
parser = argparse.ArgumentParser(description='Clone or sync a specific board from one Nextcloud instance to another.')
|
||||||
|
parser.add_argument('--board', type=str, required=True, help='The title of the board to clone or sync.')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Board-Titel, den wir klonen oder synchronisieren möchten
|
||||||
|
board_to_clone = args.board
|
||||||
|
|
||||||
|
# Hole alle Boards von der Quellinstanz
|
||||||
|
source_boards = lib.getBoards()
|
||||||
|
|
||||||
|
# Finde das gewünschte Board in der Quellinstanz
|
||||||
|
board_to_clone_data = next((board for board in source_boards if board['title'] == board_to_clone), None)
|
||||||
|
|
||||||
|
if not board_to_clone_data:
|
||||||
|
print(f'Board "{board_to_clone}" nicht gefunden.')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
boardIdFrom = board_to_clone_data['id']
|
||||||
|
|
||||||
|
# Hole alle Boards von der Zielinstanz
|
||||||
|
target_boards = lib.getBoards('to')
|
||||||
|
|
||||||
|
# Überprüfen, ob das Board in der Zielinstanz existiert
|
||||||
|
target_board_data = next((board for board in target_boards if board['title'] == board_to_clone), None)
|
||||||
|
|
||||||
|
if target_board_data:
|
||||||
|
boardIdTo = target_board_data['id']
|
||||||
|
print(f'Board "{board_to_clone}" already exists. Syncing...')
|
||||||
|
else:
|
||||||
|
# Erstelle das Board in der Zielinstanz
|
||||||
|
createdBoard = lib.createBoard(board_to_clone_data['title'], board_to_clone_data['color'])
|
||||||
|
boardIdTo = createdBoard['id']
|
||||||
|
print(f'Created board "{board_to_clone}"')
|
||||||
|
|
||||||
|
# Kopiere oder synchronisiere die Labels des Boards
|
||||||
|
boardDetails = lib.getBoardDetails(boardIdFrom)
|
||||||
|
labelsMap = {}
|
||||||
|
target_board_details = lib.getBoardDetails(boardIdTo,'to')
|
||||||
|
|
||||||
|
# Existierende Labels in der Zielinstanz sammeln
|
||||||
|
existing_labels = {label['title']: label['id'] for label in target_board_details['labels']}
|
||||||
|
|
||||||
|
for label in boardDetails['labels']:
|
||||||
|
if label['title'] in existing_labels:
|
||||||
|
labelsMap[label['id']] = existing_labels[label['title']]
|
||||||
|
else:
|
||||||
|
createdLabel = lib.createLabel(label['title'], label['color'], boardIdTo)
|
||||||
|
labelsMap[label['id']] = createdLabel['id']
|
||||||
|
|
||||||
|
# Kopiere oder synchronisiere die Stacks und Karten des Boards
|
||||||
|
stacks = lib.getStacks(boardIdFrom)
|
||||||
|
target_stacks = lib.getStacks(boardIdTo,'to')
|
||||||
|
stacksMap = {}
|
||||||
|
|
||||||
|
# Existierende Stacks in der Zielinstanz sammeln
|
||||||
|
existing_stacks = {stack['title']: stack['id'] for stack in target_stacks}
|
||||||
|
|
||||||
|
for stack in stacks:
|
||||||
|
if stack['title'] in existing_stacks:
|
||||||
|
stackIdTo = existing_stacks[stack['title']]
|
||||||
|
stacksMap[stack['id']] = stackIdTo
|
||||||
|
print(f' Stack "{stack["title"]}" already exists. Syncing...')
|
||||||
|
else:
|
||||||
|
createdStack = lib.createStack(stack['title'], stack['order'], boardIdTo)
|
||||||
|
stackIdTo = createdStack['id']
|
||||||
|
stacksMap[stack['id']] = stackIdTo
|
||||||
|
print(f' Created stack "{stack["title"]}"')
|
||||||
|
|
||||||
|
if 'cards' in stack:
|
||||||
|
for card in stack['cards']:
|
||||||
|
try:
|
||||||
|
lib.copyCard(card, boardIdTo, stackIdTo, labelsMap)
|
||||||
|
except requests.exceptions.HTTPError as e:
|
||||||
|
print(f' Failed to create card "{card["title"]}". Error: {e}')
|
||||||
|
print(f' Response: {e.response.text}')
|
||||||
|
print(f' Created {len(stack["cards"])} cards')
|
||||||
|
|
||||||
|
# Kopiere oder synchronisiere die archivierten Stacks und Karten des Boards
|
||||||
|
archived_stacks = lib.getStacksArchived(boardIdFrom)
|
||||||
|
target_archived_stacks = lib.getStacksArchived(boardIdTo,'to')
|
||||||
|
|
||||||
|
# Existierende archivierte Stacks in der Zielinstanz sammeln
|
||||||
|
existing_archived_stacks = {stack['title']: stack['id'] for stack in target_archived_stacks}
|
||||||
|
|
||||||
|
for stack in archived_stacks:
|
||||||
|
if stack['title'] in existing_archived_stacks:
|
||||||
|
stackIdTo = existing_archived_stacks[stack['title']]
|
||||||
|
print(f' Archived stack "{stack['title']}" already exists. Syncing...')
|
||||||
|
else:
|
||||||
|
createdStack = lib.createStack(stack['title'], stack['order'], boardIdTo)
|
||||||
|
stackIdTo = createdStack['id']
|
||||||
|
stacksMap[stack['id']] = stackIdTo
|
||||||
|
print(f' Created archived stack "{stack['title']}"')
|
||||||
|
|
||||||
|
if 'cards' in stack:
|
||||||
|
for card in stack['cards']:
|
||||||
|
try:
|
||||||
|
lib.copyCard(card, boardIdTo, stackIdTo, labelsMap)
|
||||||
|
except requests.exceptions.HTTPError as e:
|
||||||
|
print(f' Failed to create archived card "{card["title"]}". Error: {e}')
|
||||||
|
print(f' Response: {e.response.text}')
|
||||||
|
print(f' Created {len(stack["cards"])} archived cards')
|
Loading…
Reference in a new issue