Upload files to ''
This commit is contained in:
parent
3f2c06e06f
commit
35abc07e2d
|
@ -0,0 +1,58 @@
|
|||
import json
|
||||
import urllib.request
|
||||
import sys
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
def request(action, **params):
|
||||
return {'action': action, 'params': params, 'version': 6}
|
||||
|
||||
def invoke(action, **params):
|
||||
requestJson = json.dumps(request(action, **params)).encode('utf-8')
|
||||
response = json.load(urllib.request.urlopen(urllib.request.Request('http://localhost:8765', requestJson)))
|
||||
if len(response) != 2:
|
||||
raise Exception('response has an unexpected number of fields')
|
||||
if 'error' not in response:
|
||||
raise Exception('response is missing required error field')
|
||||
if 'result' not in response:
|
||||
raise Exception('response is missing required result field')
|
||||
if response['error'] is not None:
|
||||
raise Exception(response['error'])
|
||||
return response['result']
|
||||
|
||||
def getNoteTypes():
|
||||
return invoke('modelNames')
|
||||
|
||||
def getFields(noteType):
|
||||
return invoke('modelFieldNames', modelName=noteType)
|
||||
|
||||
def getDecks():
|
||||
return invoke('deckNames')
|
||||
|
||||
def getCards(noteType):
|
||||
cards = invoke('modelTemplates', modelName=noteType)
|
||||
return [card for card in cards]
|
||||
|
||||
def getTemplate(noteType, card):
|
||||
cards = invoke('modelTemplates', modelName=noteType)
|
||||
style = invoke('modelStyling', modelName=noteType)
|
||||
return cards[card], style['css']
|
||||
|
||||
def getTags():
|
||||
return invoke('getTags')
|
||||
|
||||
def addNote(note):
|
||||
options = {
|
||||
'allowDuplicate': False,
|
||||
'duplicateScope': note['deckName']
|
||||
}
|
||||
note['options'] = options
|
||||
return invoke('addNote', note=note)
|
||||
|
||||
if __name__ == '__main__':
|
||||
#print(getNoteTypes())
|
||||
#print(getFields('Ultimate Geography'))
|
||||
#print(getDecks())
|
||||
print(getTemplate('Basic'))
|
||||
#print(getTags())
|
||||
#print(addNote('Basic', 'Test', {'Front': 'Hello World!', 'Back': 'From Python...'}))
|
|
@ -0,0 +1,16 @@
|
|||
QUIT = 'q' # (q)uit
|
||||
|
||||
SELECT_NOTE_TYPE = 'n' # select (n)ote type
|
||||
SELECT_DECK = 'd' # select (d)eck
|
||||
SELECT_TAGS = 'g' # select ta(g)s
|
||||
PIN_FIELD = 'p' # (p)in field
|
||||
|
||||
ENTER = '\r' # (e)nter
|
||||
PREVIEW = 'f' # preview (f)ront
|
||||
PREVIEW_BACK = 'b' # preview (b)ack
|
||||
ADD = 'a' # (a)dd card
|
||||
|
||||
PREV = 'i' # j
|
||||
NEXT = 't' # k
|
||||
|
||||
ESCAPE = '\x1b'
|
|
@ -0,0 +1,201 @@
|
|||
import os
|
||||
import sys
|
||||
import tty
|
||||
import termios
|
||||
import signal
|
||||
import traceback
|
||||
import shutil
|
||||
|
||||
from subprocess import call
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from keys import *
|
||||
from menu import Menu
|
||||
from ankiconnect import *
|
||||
from preview import previewCard
|
||||
|
||||
def preview(*args):
|
||||
|
||||
# Get size in pixels
|
||||
sys.stdout.write('\x1b[16t')
|
||||
sys.stdout.flush()
|
||||
report = ''
|
||||
c = sys.stdin.read(1)
|
||||
while c != 't':
|
||||
report += c
|
||||
c = sys.stdin.read(1)
|
||||
report = report[2:].split(';')
|
||||
size = shutil.get_terminal_size((80, 20))
|
||||
h = int(report[1])*(size[1] - 1)
|
||||
w = int(report[2])*(size[0]//2 - 1)
|
||||
|
||||
previewCard(*args, width=w, height=h, htmlWidth=w)
|
||||
|
||||
#sys.stdout.write('\x1b[?25l\x1b[2J')
|
||||
#sys.stdout.flush()
|
||||
|
||||
def redraw(data):
|
||||
sys.stdout.write('\x1b[2J')
|
||||
data['menu'].draw()
|
||||
sys.stdout.flush()
|
||||
|
||||
def process(char, data):
|
||||
|
||||
def previewSide(side='Back'):
|
||||
card = getCards(current['modelName'])[0]
|
||||
template, style = getTemplate(current['modelName'], card)
|
||||
preview(template[side], style, current['fields'])
|
||||
|
||||
def changeView(view):
|
||||
data['view'] = view
|
||||
if view == 'selectTags':
|
||||
menu.setItems(getTags())
|
||||
menu.setMarked(current['tags'])
|
||||
elif view == 'selectDeck':
|
||||
menu.setItems(getDecks())
|
||||
elif view == 'selectNoteType':
|
||||
menu.setItems(getNoteTypes())
|
||||
elif view == 'addNote':
|
||||
menu.setItems(getFields(current['modelName']))
|
||||
menu.setMarked(data['pinned'])
|
||||
|
||||
menu = data['menu']
|
||||
current = data['current']
|
||||
|
||||
if char == SELECT_NOTE_TYPE:
|
||||
changeView('selectNoteType')
|
||||
elif char == SELECT_DECK:
|
||||
changeView('selectDeck')
|
||||
elif char == SELECT_TAGS:
|
||||
changeView('selectTags')
|
||||
elif char == PIN_FIELD:
|
||||
if data['view'] == 'addNote':
|
||||
menu.toggleMark()
|
||||
if menu.isCurrentMarked():
|
||||
data['pinned'].append(menu.selected())
|
||||
else:
|
||||
data['pinned'].remove(menu.selected())
|
||||
elif char == ENTER:
|
||||
if data['view'] == 'selectTags':
|
||||
menu.toggleMark()
|
||||
if menu.isCurrentMarked():
|
||||
current['tags'].append(menu.selected())
|
||||
else:
|
||||
current['tags'].remove(menu.selected())
|
||||
elif data['view'] == 'selectDeck':
|
||||
current['deckName'] = menu.selected()
|
||||
changeView('addNote')
|
||||
elif data['view'] == 'selectNoteType':
|
||||
current['modelName'] = menu.selected()
|
||||
data['pinned'] = []
|
||||
changeView('addNote')
|
||||
previewSide()
|
||||
elif data['view'] == 'addNote':
|
||||
|
||||
# Open editor
|
||||
if menu.selected() in current['fields']:
|
||||
content = current['fields'][menu.selected()]
|
||||
else:
|
||||
content = ''
|
||||
with NamedTemporaryFile(suffix=".html") as tf:
|
||||
tf.write(bytes(content, encoding='utf-8'))
|
||||
tf.flush()
|
||||
call([data['EDITOR'], tf.name])
|
||||
tf.seek(0)
|
||||
current['fields'][menu.selected()] = tf.read().decode('utf-8')
|
||||
sys.stdout.write('\x1b[?25l')
|
||||
sys.stdout.flush()
|
||||
previewSide()
|
||||
elif char == PREVIEW:
|
||||
previewSide('Front')
|
||||
elif char == PREVIEW_BACK:
|
||||
previewSide()
|
||||
elif char == ADD:
|
||||
addNote(current)
|
||||
for field in current['fields']:
|
||||
if field not in data['pinned']:
|
||||
current['fields'][field] = ''
|
||||
previewSide()
|
||||
elif char == PREV:
|
||||
menu.prev()
|
||||
elif char == NEXT:
|
||||
menu.next()
|
||||
elif char == ESCAPE:
|
||||
changeView('addNote')
|
||||
|
||||
if data['view'] == 'selectTags':
|
||||
head = {'Selecting': 'Tags'}
|
||||
elif data['view'] == 'selectDeck':
|
||||
head = {'Selecting': 'Deck'}
|
||||
elif data['view'] == 'selectNoteType':
|
||||
head = {'Selecting': 'Note type'}
|
||||
else:
|
||||
head = {
|
||||
'Note Type': current['modelName'],
|
||||
'Deck': current['deckName'],
|
||||
'Tags': ' '.join(current['tags'])
|
||||
}
|
||||
menu.setHead(head)
|
||||
|
||||
#sys.stdout.write(f'\x1b[1;1H{repr(char)}')
|
||||
sys.stdout.flush()
|
||||
|
||||
def reset(data):
|
||||
data['EDITOR'] = os.environ.get('EDITOR', 'vim')
|
||||
head = {
|
||||
'Note Type': 'Basic',
|
||||
'Deck': 'Default',
|
||||
'Tags': ''
|
||||
}
|
||||
data['noteType'] = 'Basic'
|
||||
data['deck'] = 'Default'
|
||||
data['view'] = 'addNote' # selectNoteType selectDeck selectTags
|
||||
data['pinned'] = []
|
||||
fields = getFields('Basic')
|
||||
data['current'] = {
|
||||
'deckName': 'Default',
|
||||
'modelName': 'Basic',
|
||||
'fields': {field: '' for field in fields},
|
||||
'tags': [],
|
||||
}
|
||||
data['menu'] = Menu(fields, head=head)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# Setup raw mode
|
||||
fd = sys.stdin.fileno()
|
||||
oldSettings = termios.tcgetattr(fd)
|
||||
tty.setraw(sys.stdin)
|
||||
|
||||
try:
|
||||
|
||||
# Hide the cursor and clear the screen
|
||||
sys.stdout.write('\x1b[?25l\x1b[2J')
|
||||
sys.stdout.flush()
|
||||
|
||||
# Data
|
||||
data = {}
|
||||
reset(data)
|
||||
|
||||
# Redraw on resize
|
||||
signal.signal(signal.SIGWINCH, lambda signum, frame: redraw(data))
|
||||
|
||||
# Read input
|
||||
process('', data)
|
||||
while True:
|
||||
char = sys.stdin.read(1)
|
||||
|
||||
if char == QUIT:
|
||||
break
|
||||
process(char, data)
|
||||
|
||||
# Avoid breaking the terminal after a crash
|
||||
except Exception:
|
||||
tb = traceback.format_exc()
|
||||
else:
|
||||
tb = ''
|
||||
|
||||
# Restore terminal settings
|
||||
sys.stdout.write('\x1b[?25h\n\r')
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, oldSettings)
|
||||
print(tb)
|
|
@ -0,0 +1,80 @@
|
|||
import sys
|
||||
import shutil
|
||||
|
||||
class Menu:
|
||||
def __init__(self, items, cursor=0, head={}):
|
||||
self.head = head
|
||||
self.items = items
|
||||
self.cursor = cursor
|
||||
self.marked = []
|
||||
self.draw()
|
||||
|
||||
def setHead(self, head):
|
||||
self.head = head
|
||||
self.draw()
|
||||
|
||||
def setItems(self, items, cursor=0):
|
||||
self.__init__(items, cursor)
|
||||
|
||||
def toggleMark(self):
|
||||
if self.cursor in self.marked:
|
||||
self.marked.remove(self.cursor)
|
||||
else:
|
||||
self.marked.append(self.cursor)
|
||||
self.draw()
|
||||
|
||||
def setMarked(self, marked):
|
||||
self.marked = [i for i in range(len(self.items)) if self.items[i] in marked]
|
||||
self.draw()
|
||||
|
||||
def isCurrentMarked(self):
|
||||
return self.cursor in self.marked
|
||||
|
||||
def isMarked(self, item):
|
||||
return item in [self.items[i] for i in self.marked]
|
||||
|
||||
def next(self):
|
||||
self.cursor += 1
|
||||
if self.cursor >= len(self.items):
|
||||
self.cursor = len(self.items) - 1
|
||||
self.draw()
|
||||
|
||||
def prev(self):
|
||||
self.cursor -= 1
|
||||
if self.cursor < 0:
|
||||
self.cursor = 0
|
||||
self.draw()
|
||||
|
||||
def selected(self):
|
||||
return self.items[self.cursor]
|
||||
|
||||
def draw(self):
|
||||
size = shutil.get_terminal_size((80, 20))
|
||||
w = size[0]
|
||||
|
||||
# Heading
|
||||
for i in range(size[1]):
|
||||
sys.stdout.write(f'\x1b[{i + 1};{w//2 + 1}H'+' '*(w//2))
|
||||
for row, key in enumerate(self.head):
|
||||
sys.stdout.write(f'\x1b[{row + 1};{w//2 + 1}H {key}: {self.head[key][:w//2 - len(key) - 3]}\x1b[0m')
|
||||
|
||||
offset = len(self.head) + 2
|
||||
if offset == 2:
|
||||
offset = 1
|
||||
h = size[1] - offset
|
||||
first = 0
|
||||
if len(self.items) > h:
|
||||
if self.cursor > (h - offset)/2:
|
||||
first = self.cursor - h//2 - 1
|
||||
if first < 0:
|
||||
first = 0
|
||||
elif len(self.items) - first < h + 1:
|
||||
first = len(self.items) - h - 1
|
||||
|
||||
for row, item in enumerate(self.items[first:h + first + 1]):
|
||||
mark = '\x1b[44m+' if row + first in self.marked else ' '
|
||||
text = mark+item[:w//2 - 1]+' '*(w//2 - 1 - len(item))
|
||||
if self.cursor == row + first:
|
||||
sys.stdout.write(f'\x1b[{row + offset};{w//2 + 1}H\x1b[7m{text}\x1b[0m')
|
||||
else:
|
||||
sys.stdout.write(f'\x1b[{row + offset};{w//2 + 1}H{text}\x1b[0m')
|
|
@ -0,0 +1,66 @@
|
|||
import imgkit
|
||||
import sys
|
||||
|
||||
from libsixel.encoder import Encoder, SIXEL_OPTFLAG_WIDTH, SIXEL_OPTFLAG_COLORS
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
HTML_WIDTH = 720
|
||||
|
||||
def preview(html, width, height, htmlWidth=HTML_WIDTH):
|
||||
encoder = Encoder()
|
||||
encoder.setopt(SIXEL_OPTFLAG_WIDTH, str(width))
|
||||
encoder.setopt(SIXEL_OPTFLAG_COLORS, '256')
|
||||
options = {
|
||||
'encoding': 'utf-8',
|
||||
'width': htmlWidth,
|
||||
'height': int(htmlWidth/width*height),
|
||||
'quiet': None,
|
||||
}
|
||||
with NamedTemporaryFile(suffix='.png') as img:
|
||||
with NamedTemporaryFile(suffix='.html') as page:
|
||||
page.write(bytes(html, encoding='utf-8'))
|
||||
page.flush()
|
||||
imgkit.from_file(page.name, img.name, options=options)
|
||||
sys.stdout.write('\x1b[1;1H')
|
||||
sys.stdout.flush()
|
||||
encoder.encode(img.name)
|
||||
|
||||
def previewCard(template, style, content, width, height, htmlWidth=HTML_WIDTH):
|
||||
for field in content:
|
||||
|
||||
# conditional sections
|
||||
formated = ''
|
||||
i = 0
|
||||
remove = False
|
||||
while i < len(template):
|
||||
section = template[i:i + len(field) + 5]
|
||||
if section in ('{{#'+field+'}}', '{{^'+field+'}}'):
|
||||
i += len(field) + 5
|
||||
if (section[2] == '#' and not content[field]) or (section[2] == '^' and content[field]):
|
||||
remove = True
|
||||
if section == '{{/'+field+'}}':
|
||||
i += len(field) + 5
|
||||
if remove:
|
||||
remove = False
|
||||
if not remove and i < len(template):
|
||||
formated += template[i]
|
||||
i += 1;
|
||||
|
||||
template = formated.replace('{{'+field+'}}', content[field])
|
||||
|
||||
template = '<style>'+style+'</style>'+template
|
||||
preview(TEMPLATE.replace('{{CONTENT}}', template), width, height, htmlWidth)
|
||||
|
||||
TEMPLATE_FILE = 'template.html'
|
||||
with open(TEMPLATE_FILE) as templateFile:
|
||||
TEMPLATE = templateFile.read()
|
||||
|
||||
if __name__ == '__main__':
|
||||
preview('<h1>Hello World!</h1>', 500, 500)
|
||||
previewCard('<h1>{{Front}}</h1>',
|
||||
'h1{color:cyan;}',
|
||||
{
|
||||
'Front': 'Hello World!',
|
||||
'Back': 'From Python...'
|
||||
},
|
||||
500, 500)
|
Loading…
Reference in New Issue