├── .gitignore
├── source
├── icon.png
├── icons
│ ├── Warning.png
│ ├── icon_label.png
│ ├── icon_type.png
│ ├── icon_folder.png
│ └── paperpAlfred_ico.png
├── 33BF8A0E-B7AA-495E-B89F-1F8239DAECBE.png
├── 6DF20719-C29E-41E0-AEA3-E92AD68EA188.png
├── E2E84778-9472-48A7-851A-87F07879DA5C.png
├── prefs.plist
├── config.py
├── demo_db.py
├── labels.py
├── myTypes.py
├── folders.py
├── papers.py
├── paperpAlfred_functions.py
├── build_db.py
└── info.plist
├── images
├── pp_json.png
├── demo_v1.0.gif
├── alfred_prefs.png
└── paperpAlfred.png
├── releases
├── paperpAlfred_2.0.alfredworkflow
├── paperpAlfred_2.1.alfredworkflow
├── paperpAlfred_2.2.alfredworkflow
└── paperpAlfred_2.3.alfredworkflow
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.pyc
3 | __pycache__
4 | sandbox
5 | prefs.plist
6 |
--------------------------------------------------------------------------------
/source/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/source/icon.png
--------------------------------------------------------------------------------
/images/pp_json.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/images/pp_json.png
--------------------------------------------------------------------------------
/images/demo_v1.0.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/images/demo_v1.0.gif
--------------------------------------------------------------------------------
/images/alfred_prefs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/images/alfred_prefs.png
--------------------------------------------------------------------------------
/images/paperpAlfred.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/images/paperpAlfred.png
--------------------------------------------------------------------------------
/source/icons/Warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/source/icons/Warning.png
--------------------------------------------------------------------------------
/source/icons/icon_label.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/source/icons/icon_label.png
--------------------------------------------------------------------------------
/source/icons/icon_type.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/source/icons/icon_type.png
--------------------------------------------------------------------------------
/source/icons/icon_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/source/icons/icon_folder.png
--------------------------------------------------------------------------------
/source/icons/paperpAlfred_ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/source/icons/paperpAlfred_ico.png
--------------------------------------------------------------------------------
/releases/paperpAlfred_2.0.alfredworkflow:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/releases/paperpAlfred_2.0.alfredworkflow
--------------------------------------------------------------------------------
/releases/paperpAlfred_2.1.alfredworkflow:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/releases/paperpAlfred_2.1.alfredworkflow
--------------------------------------------------------------------------------
/releases/paperpAlfred_2.2.alfredworkflow:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/releases/paperpAlfred_2.2.alfredworkflow
--------------------------------------------------------------------------------
/releases/paperpAlfred_2.3.alfredworkflow:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/releases/paperpAlfred_2.3.alfredworkflow
--------------------------------------------------------------------------------
/source/33BF8A0E-B7AA-495E-B89F-1F8239DAECBE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/source/33BF8A0E-B7AA-495E-B89F-1F8239DAECBE.png
--------------------------------------------------------------------------------
/source/6DF20719-C29E-41E0-AEA3-E92AD68EA188.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/source/6DF20719-C29E-41E0-AEA3-E92AD68EA188.png
--------------------------------------------------------------------------------
/source/E2E84778-9472-48A7-851A-87F07879DA5C.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannicoppola/paperpAlfred/HEAD/source/E2E84778-9472-48A7-851A-87F07879DA5C.png
--------------------------------------------------------------------------------
/source/prefs.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PAPLIBRARY
6 | ~/Downloads/Paperpile - Library - Jan 22.json
7 | PAPPATH
8 | ~/Library/CloudStorage/GoogleDrive-giovannicoppola@gmail.com/My Drive/Paperpile
9 |
10 |
11 |
--------------------------------------------------------------------------------
/source/config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 |
5 |
6 | MAXRES = os.path.expanduser(os.getenv('MAXRESULTS', ''))
7 | LIBRARY_FILE = os.path.expanduser(os.getenv('PAPLIBRARY'))
8 |
9 | # BIBTEX_FILE = os.path.expanduser(os.getenv('PAPLIBRARY_BIB'))
10 |
11 | WF_BUNDLE = os.getenv('alfred_workflow_bundleid')
12 | WF_FOLDER = os.path.expanduser('~')+"/Library/Caches/com.runningwithcrayons.Alfred/Workflow Data/"+WF_BUNDLE+"/"
13 | INDEX_DB = WF_FOLDER+"index.db"
14 | TIMESTAMP = WF_FOLDER+'timestamp.txt'
15 | TIMESTAMP_BIBTEX = WF_FOLDER+'timestamp_bibTeX.txt'
16 |
17 | if not os.path.exists(WF_FOLDER):
18 | os.makedirs(WF_FOLDER)
19 |
20 |
--------------------------------------------------------------------------------
/source/demo_db.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Rebuilding the database
5 | # rewritten script
6 | #
7 | # previously created on Sunday, February 28, 2021
8 | # March 2022 updated to Python3, eliminated dependencies
9 |
10 | import json
11 |
12 |
13 |
14 |
15 | def importingCompleteLibrary (myFile): # to import complete library before filtering, not used here
16 | ## reading JSON data in
17 | with open(myFile, "r") as read_file:
18 | json_data = json.load(read_file)
19 |
20 | mySubset = [x for x in json_data if "labelsNamed" in x and 'interesting' in x['labelsNamed']]
21 |
22 | with open("demo_library.json", "w") as f:
23 | json.dump(mySubset, f,indent=4)
24 |
25 | myFile = 'path/to/library'
26 | importingCompleteLibrary(myFile)
27 |
28 |
29 |
--------------------------------------------------------------------------------
/source/labels.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # v1.0 giovanni, March 2021, from @deanishe tutorial
3 | # v2.0 Feb 2022 - Updated for Python3
4 |
5 |
6 | import sys
7 | import sqlite3
8 | from config import INDEX_DB
9 | import json
10 |
11 |
12 |
13 | myQuery=sys.argv[1]
14 |
15 |
16 |
17 | db = sqlite3.connect(INDEX_DB)
18 | cursor = db.cursor()
19 | result = {"items": []}
20 |
21 | if (not myQuery):
22 | cursor.execute("""SELECT * FROM Labels ORDER BY CAST (totalLabel as integer) DESC""")
23 | resultsQ = cursor.fetchall()
24 |
25 | else:
26 | try:
27 | cursor.execute("""SELECT * FROM Labels WHERE label MATCH ? ORDER BY CAST (totalLabel as integer) DESC""",(myQuery+ '*',))
28 | resultsQ = cursor.fetchall()
29 |
30 | except sqlite3.OperationalError as err:
31 | # If the query is invalid, show an appropriate warning and exit
32 | result= {"items": [{
33 | "title": "Error: " + str(err),
34 | "subtitle": "Invalid Query",
35 | "arg": ";;",
36 | "icon": {
37 | "path": "icons/Warning.png"
38 | }
39 | }]}
40 | print (json.dumps(result))
41 | raise err
42 |
43 |
44 | if (resultsQ):
45 | for xx in resultsQ:
46 |
47 | result["items"].append({
48 |
49 | "title": xx[0] + " (" + xx[1]+")" ,
50 | "subtitle": xx[3],
51 |
52 | "valid": True,
53 | "icon": {
54 | "path": "icons/icon_label.png"
55 | },
56 | "variables": {
57 | "mySource": 'label',
58 | "myLabelID": xx[2],
59 | }
60 | })
61 |
62 | print (json.dumps(result))
63 |
64 |
65 |
66 | if (myQuery and not resultsQ):
67 | result["items"].append({
68 | "title": "No matches",
69 | "subtitle": "Try a different query",
70 |
71 | "arg": "",
72 | "icon": {
73 |
74 | "path": "icons/Warning.png"
75 | }
76 | })
77 |
78 | print (json.dumps(result))
79 |
80 |
81 |
--------------------------------------------------------------------------------
/source/myTypes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -*- coding: utf-8 -*-
3 | # giovanni, March 2021, from @deanishe's template
4 | # Modified from books, to show paper types for filtering
5 | # Tuesday, March 15, 2022, 2:52 PM update to Python3
6 |
7 |
8 | import sys
9 | import sqlite3
10 | from config import INDEX_DB
11 | import json
12 |
13 |
14 | myQuery=sys.argv[1]
15 |
16 |
17 |
18 | db = sqlite3.connect(INDEX_DB)
19 | cursor = db.cursor()
20 | result = {"items": []}
21 |
22 | if (not myQuery):
23 | cursor.execute("""SELECT * FROM Types ORDER BY CAST (totalType as integer) DESC""")
24 | resultsQ = cursor.fetchall()
25 |
26 | else:
27 | try:
28 | cursor.execute("""SELECT * FROM Types WHERE type MATCH ? ORDER BY CAST (totalType as integer) DESC""",(myQuery+ '*',))
29 | resultsQ = cursor.fetchall()
30 |
31 | except sqlite3.OperationalError as err:
32 | # If the query is invalid, show an appropriate warning and exit
33 | result= {"items": [{
34 | "title": "Error: " + str(err),
35 | "subtitle": "Invalid Query",
36 | "arg": ";;",
37 | "icon": {
38 | "path": "icons/Warning.png"
39 | }
40 | }]}
41 | print (json.dumps(result))
42 | raise err
43 |
44 |
45 | if (resultsQ):
46 | for xx in resultsQ:
47 |
48 | result["items"].append({
49 |
50 | "title": xx[0] + " (" + xx[1]+")" ,
51 |
52 |
53 | "valid": True,
54 | "icon": {
55 | "path": "icons/icon_type.png"
56 | },
57 | "variables": {
58 | "mySource": 'type',
59 | "myTypeID": xx[0],
60 | }
61 | })
62 |
63 | print (json.dumps(result))
64 |
65 |
66 |
67 | if (myQuery and not resultsQ):
68 | result["items"].append({
69 | "title": "No matches",
70 | "subtitle": "Try a different query",
71 |
72 | "arg": "",
73 | "icon": {
74 |
75 | "path": "icons/Warning.png"
76 | }
77 | })
78 |
79 | print (json.dumps(result))
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/source/folders.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # v1.0 giovanni, March 2021, from @deanishe tutorial
3 | # v2.0 Feb 2022 - Updated for Python3
4 |
5 |
6 | import sys
7 | import sqlite3
8 | from config import INDEX_DB
9 | import json
10 |
11 |
12 | # getting the user query
13 | myQuery=sys.argv[1]
14 |
15 | result = {"items": []}
16 |
17 |
18 | db = sqlite3.connect(INDEX_DB)
19 | cursor = db.cursor()
20 |
21 | if (not myQuery):
22 | cursor.execute("""SELECT * FROM Folders ORDER BY CAST (totalFolder as integer) DESC""")
23 | resultsQ = cursor.fetchall()
24 |
25 | else:
26 | try:
27 |
28 | cursor.execute("""SELECT * FROM Folders WHERE folder MATCH ? ORDER BY CAST (totalFolder as integer) DESC""",(myQuery+ '*',))
29 | resultsQ = cursor.fetchall()
30 |
31 |
32 | except sqlite3.OperationalError as err:
33 | # If the query is invalid, show an appropriate warning and exit
34 | result= {"items": [{
35 | "title": "Error: " + str(err),
36 | "subtitle": "Invalid Query",
37 | "arg": ";;",
38 | "icon": {
39 |
40 | "path": "icons/Warning.png"
41 | }
42 | }]}
43 | print (json.dumps(result))
44 | raise err
45 |
46 |
47 | if (resultsQ):
48 | for xx in resultsQ:
49 |
50 | result["items"].append({
51 |
52 | "title": xx[0] + " (" + xx[1]+")" ,
53 | "subtitle": xx[3],
54 |
55 | "valid": True,
56 | "icon": {
57 | "path": "icons/icon_folder.png"
58 | },
59 | "variables": {
60 | "mySource": 'folder',
61 | "myFolderID": xx[2],
62 | }
63 | })
64 |
65 | print (json.dumps(result))
66 |
67 |
68 |
69 | if (myQuery and not resultsQ):
70 | result["items"].append({
71 | "title": "No matches",
72 | "subtitle": "Try a different query",
73 |
74 |
75 | "icon": {
76 |
77 | "path": "icons/Warning.png"
78 | }
79 | })
80 |
81 | print (json.dumps(result))
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/source/papers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # Search Paperpile library using Alfred
4 | # Search engine structure and code from deanishe@deanishe.net -- THANK YOU!
5 | # MIT Licence. See http://opensource.org/licenses/MIT
6 | #
7 | # November 2020 - March 2021
8 | #https://github.com/giovannicoppola/paperpAlfred/blob/main/README.md
9 |
10 | # February 2022, updated version for Python3
11 |
12 |
13 |
14 | import sys
15 | import os
16 | import json
17 | import sqlite3
18 |
19 |
20 |
21 | from config import INDEX_DB, MAXRES, LIBRARY_FILE, TIMESTAMP
22 | from build_db import *
23 |
24 |
25 |
26 |
27 | try:
28 |
29 | new_time = int(os.path.getmtime(LIBRARY_FILE))
30 |
31 | except: #error if the library file is missing
32 | result= {"items": [{
33 | "title": "Library file missing!",
34 | "subtitle": f"cannot locate the Paperpile library file: {LIBRARY_FILE}, {MAXRES}",
35 | "arg": "",
36 | "icon": {
37 |
38 | "path": "icons/Warning.png"
39 | }
40 | }]}
41 | print (json.dumps(result))
42 | sys.exit("Script aborted – file missing")
43 |
44 | if not os.path.exists(TIMESTAMP):
45 | with open(TIMESTAMP, "w") as f:
46 | f.write(str(new_time))
47 | f.close
48 | createLibrary (LIBRARY_FILE)
49 |
50 |
51 |
52 | ## checking the timestamp
53 | with open(TIMESTAMP) as f:
54 | old_time = int(f.readline()) #getting the old UNIX timestamp
55 | f.close
56 |
57 |
58 | if new_time != old_time:
59 |
60 | with open(TIMESTAMP, "w") as f:
61 | f.write(str(new_time))
62 | f.close
63 |
64 | createLibrary (LIBRARY_FILE)
65 |
66 |
67 | # getting the user query
68 | myQuery=sys.argv[1]
69 |
70 | result = {"items": []}
71 |
72 | orderSel = "DESC"
73 |
74 | if "--a" in myQuery:
75 | orderSel = "ASC"
76 | myQuery = myQuery.replace (' --a','')
77 |
78 | #getting the source of the script
79 | mySource=os.path.expanduser(os.getenv('mySource', ''))
80 | myLabelID=os.path.expanduser(os.getenv('myLabelID', ''))
81 | myFolderID=os.path.expanduser(os.getenv('myFolderID', ''))
82 | myTypeID=os.path.expanduser(os.getenv('myTypeID', ''))
83 |
84 | if mySource == "label":
85 | myQuery = "labelID:"+myLabelID+' '+myQuery
86 |
87 | if mySource == "folder":
88 | myQuery = "folderID:"+myFolderID+' '+myQuery
89 |
90 | if mySource == "type":
91 | myQuery = "type:"+myTypeID+' '+myQuery
92 |
93 |
94 |
95 | # Search!
96 | db = sqlite3.connect(INDEX_DB)
97 | cursor = db.cursor()
98 |
99 |
100 | try:
101 | # cursor.execute(""" SELECT _id, abstract, citekey, fileName,first, folder,folderID, fullReference, journal, label,labelID, last, pdfFlag, pmid, subtitle, title, gdrive_id, type, year
102 | # FROM papers WHERE abstract || citekey LIKE ?
103 | # ORDER BY year """ +orderSel + """ LIMIT """ + MAXRES + """ """, (myQuery,))
104 |
105 |
106 | cursor.execute("""SELECT _id,abstract, citekey, fileName, first, folder,folderID, fullReference, journal, label,labelID, last, pdfFlag, pmid, subtitle, title, gdrive_id, type, year FROM
107 | (SELECT papers
108 | AS r, _id, abstract, citekey, fileName,first, folder,folderID, fullReference, journal, label,labelID, last, pdfFlag, pmid, subtitle, title, gdrive_id, type, year
109 | FROM papers WHERE papers MATCH ?)
110 | ORDER BY year """ +orderSel + """ LIMIT """ + MAXRES + """ """, (myQuery + '*',))
111 |
112 |
113 |
114 |
115 | results = cursor.fetchall()
116 |
117 | except sqlite3.OperationalError as err:
118 | # If the query is invalid, show an appropriate warning and exit
119 | result= {"items": [{
120 | "title": "Error: " + str(err),
121 | "subtitle": "Invalid Query",
122 | "arg": ";;",
123 | "icon": {
124 |
125 | "path": "icons/Warning.png"
126 | }
127 | }]}
128 | print (json.dumps(result))
129 | raise err
130 |
131 |
132 |
133 |
134 | if (not myQuery):
135 | introDial= {"items": [{
136 | "title": "Welcome to paperpAlfred 👋",
137 | "subtitle": "Enter a query or ↩️ for help",
138 | "valid": True,
139 | "arg": "ShowHelpWindow",
140 | "icon": {
141 | "path": "icons/paperpAlfred_ico.png"
142 | }
143 | }]}
144 | print (json.dumps(introDial))
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 | # Output results to Alfred
153 | if (myQuery and results):
154 | myResLen = str(len (results))
155 | countR=1
156 | for (_id, abstract, citekey, fileName, first, folder,folderID, fullReference, journal, label,labelID, last, pdfFlag, pmid, subtitle, title, gdrive_id, type, year) in results:
157 | aut_journ = str(countR) + '/' + myResLen + pdfFlag + subtitle + " 🏷" + label
158 |
159 |
160 | result["items"].append({
161 | "title": title,
162 | "subtitle": aut_journ,
163 | "variables": {
164 | "myFileName": fileName,
165 | "FullReference": fullReference,
166 | "shortPMID": subtitle+" "+pmid,
167 | "myAbstract": abstract,
168 | "myCitekey": citekey,
169 | "gdrive_id": gdrive_id,
170 | "paperpileID": _id
171 | },
172 | "valid": True,
173 |
174 | "icon": {
175 |
176 | "path": "icons/paperpAlfred_ico.png"
177 | }
178 | })
179 | countR += 1
180 |
181 |
182 | print (json.dumps(result))
183 |
184 |
185 |
186 |
187 | if (myQuery and not results):
188 | result= {"items": [{
189 | "title": "No matches",
190 | "subtitle": "Try a different query",
191 |
192 | "arg": "",
193 | "icon": {
194 |
195 | "path": "icons/Warning.png"
196 | }
197 | }]}
198 |
199 | print (json.dumps(result))
200 |
201 |
202 |
203 |
204 |
--------------------------------------------------------------------------------
/source/paperpAlfred_functions.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 |
4 | # Functions for paperpAlfred:
5 | # log (for logging and debugging)
6 |
7 | # JSONtoDB (to rebuild the database)
8 | #
9 |
10 |
11 | import sys
12 | import os
13 | import sqlite3
14 | import time
15 | import bibtexparser
16 | import re
17 | from config import INDEX_DB
18 |
19 | #Partly cloudy ⛅️ 🌡️+73°F (feels +77°F, 55%) 🌬️↗4mph 🌘 Wed Sep 21 16:59:51 2022
20 | #W38Q3 – 264 ➡️ 100 – 133 ❇️ 232
21 | startTime = time.time()
22 |
23 |
24 |
25 |
26 |
27 | def build_BibTeX_db (BIBTEX_FILE):
28 | with open(BIBTEX_FILE) as bibtex_file:
29 | #bib_database = bibtexparser.load(bibtex_file)
30 | bib_database = bibtexparser.bparser.BibTexParser(common_strings=True).parse_file(bibtex_file) # this is for the string in the month
31 |
32 | #print(bib_database.entries)
33 |
34 | myJSONlist = (bib_database.entries)
35 |
36 | #formatting the author block
37 | for myEntry in myJSONlist:
38 | if "author" in myEntry:
39 | myAuthors = myEntry['author'].split("and ")
40 |
41 | #print (f"number of authors: {len(myAuthors)}")
42 | authorBlock = ''
43 |
44 | authorCount = 0
45 | for eachAuthor in myAuthors:
46 | authorCount += 1
47 | #print (eachAuthor)
48 | if ',' in eachAuthor:
49 | lastName,firstName = eachAuthor.split(",",1)
50 | firstInitials = "".join(item[0].upper() for item in firstName.split())
51 | #print (f"last name: {lastName}, initials: {firstInitials}")
52 | #print (f"{lastName} {firstInitials}")
53 | eachAuthor_name = f"{lastName} {firstInitials}"
54 | else:
55 | eachAuthor_name = eachAuthor.strip()
56 |
57 | #assigning first and last author
58 | if authorCount == 1:
59 | firstAuthor = eachAuthor_name.split()[0]
60 | myEntry['firstAuthor'] = firstAuthor
61 | linker = ''
62 | else:
63 | linker = ', '
64 | if authorCount == len (myAuthors):
65 | lastAuthor = eachAuthor_name.split()[0]
66 | myEntry['lastAuthor'] = lastAuthor
67 |
68 | authorBlock = f"{authorBlock}{linker}{eachAuthor_name}"
69 | myEntry['authorBlock'] = authorBlock
70 |
71 |
72 | #stripping Journal of periods
73 | if "journal" in myEntry:
74 | myEntry['journal'] = re.sub(r'\.', '', myEntry['journal'])
75 | else:
76 | myEntry['journal'] = ''
77 |
78 | # strip {}
79 | if myEntry['title'][0] == "{":
80 | myEntry['title'] = myEntry['title'][1:]
81 | if myEntry['title'][-1] == "}":
82 | myEntry['title'] = myEntry['title'][:-1]
83 |
84 | if "pages" in myEntry:
85 | pagesBlock = f":{myEntry['pages']}."
86 | else:
87 | pagesBlock = ""
88 |
89 | if "pmid" in myEntry:
90 | pmidBlock = f" PMID: {myEntry['pmid']}."
91 | else:
92 | pmidBlock = ""
93 | myEntry['pmid'] = ''
94 |
95 | if "volume" not in myEntry:
96 | myEntry['volume'] = "-"
97 |
98 | if "annot" not in myEntry:
99 | myEntry['annot'] = ""
100 |
101 | shortRef = f"{firstAuthor}-{lastAuthor}, {myEntry['journal']} {myEntry['year']} {myEntry['pmid']}"
102 | myEntry['shortRef'] = shortRef
103 | #print (shortRef)
104 |
105 | fullRef = f"{authorBlock}. {myEntry['title']}. {myEntry['journal']} {myEntry['year']};{myEntry['volume']}{pagesBlock}{pmidBlock}"
106 | myEntry['fullRef'] = fullRef
107 | #print (fullRef)
108 | # renaming problematic names
109 | if "ID" in myEntry:
110 | myEntry['myID'] = myEntry['ID']
111 | del myEntry['ID']
112 |
113 | if "file" in myEntry:
114 | myEntry['myFile'] = myEntry['file']
115 | del myEntry['file']
116 |
117 |
118 |
119 |
120 | #print (authorBlock)
121 | #output = "".join(item[0].upper() for item in input.split())
122 |
123 |
124 | #Huang AY, Taylor AMW, Ghogha A, Pribadi M, Wang Q, Kim TSJ, Cahill CM, Coppola G, Evans CJ. Genetic and functional analysis of a Pacific hagfish opioid system. J Neurosci Res 2022;1:19-34. PMID: 32830380
125 |
126 | #myJSON = json.dumps(bib_database.entries)
127 |
128 |
129 | JSONtoDB (myJSONlist, "BibTeX_db", INDEX_DB)
130 |
131 |
132 |
133 | def log(s, *args):
134 | if args:
135 | s = s % args
136 | print(s, file=sys.stderr)
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | def JSONtoDB (myJSON,myTable, mydatabase):
148 | column_list = []
149 | column = []
150 | for data in myJSON:
151 |
152 | column = list(data.keys())
153 | for col in column:
154 | if col not in column_list:
155 | column_list.append(col)
156 |
157 | value = []
158 | values = []
159 | for data in myJSON:
160 | for i in column_list:
161 | value.append(str(dict(data).get(i)))
162 | values.append(list(value))
163 | value.clear()
164 |
165 |
166 |
167 | create_statement = "create VIRTUAL table " + myTable + " USING FTS3 ({0})".format(" text,".join(column_list))
168 |
169 | insert_statement = "insert into " + myTable + " ({0}) values (?{1})".format(",".join(column_list), ",?" * (len(column_list)-1))
170 | drop_statement = "DROP TABLE IF EXISTS "+ myTable
171 |
172 | # execution
173 | db=sqlite3.connect(mydatabase)
174 | c = db.cursor()
175 | c.execute(drop_statement)
176 | c.execute(create_statement)
177 | c.executemany(insert_statement , values)
178 | values.clear()
179 | db.commit()
180 |
181 |
182 |
183 |
184 |
185 |
186 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # paperpAlfred
2 |
3 |
4 | Search your [Paperpile](https://paperpile.com/) library with [Alfred](https://www.alfredapp.com/)
5 |
6 | 
7 |
8 |
9 | 
11 |
12 |
13 |
14 |
15 | - [Setting up](#setting-up)
16 | - [Basic Usage](#basic-usage)
17 | - [Output](#output)
18 | - [Known Issues](#known-issues)
19 | - [Acknowledgments](#acknowledgments)
20 | - [Changelog](#changelog)
21 | - [Feedback](#feedback)
22 |
23 |
24 |
25 |
26 |
Setting up paperpAlfred
27 |
28 | 1. Download the workflow from Github and double-click to install paperpAlfred
29 |
30 | 2. Download your Paperpile library
31 | - in Paperpile, cogwheel > Settings > Export > Export to JSON
32 |
33 |
34 | 3. Set the `PAPLIBRARY` path
35 | - Copy the library file path to the clipboard
36 |
37 | - In Finder, right-click the file, press option (⌥), the select `Copy [FileName] as Pathname`
38 | - Open the 'Configure Workflow' window in paperpAlfred preferences
39 | - set path to library as the `Paperpile Library` value
40 |
41 | 4. _Optional:_ Set `Paperpile Path` (Path to Paperpile in Google Drive)
42 | - this will allow to open PDFs in the system viewer
43 |
44 | 5. _Optional:_ change the max number of results returned (default: 99)
45 | - Set the `MAXRESULTS` value in the 'Configure Workflow' window in paperpAlfred
46 | - Set the `Key Prefix` (citekey prefix) in the 'Configure Workflow' window in paperpAlfred
47 |
48 | 6. _Optional:_ Setup hotkeys to launch
49 | - main search
50 | - filter by label
51 | - filter by folder
52 | - filter by type
53 |
54 |
55 | Basic Usage
56 |
57 | ## Simple search
58 | - launch paperpAlfred by typing `ppp` or using an optional hotkey
59 | - words or names will be searched across all fields
60 |
61 | ### Filter by label first
62 | - entering `ppl` (or optional hotkey) will show a list of labels, the number of items in each label, and the count of item types
63 | - select the label by pressing `return`. PaperpAlfred will now search within that label
64 | - `option-return (⌥⏎)` will open the label in Paperpile
65 | ### Filter by folder first
66 | - entering `ppf` (or optional hotkey) will show a list of folders, the number of items in each folder, and the count of item types
67 | - select the folder by pressing `return`. PaperpAlfred will now search within that folder.
68 | - `option-return (⌥⏎)` will open the folder in Paperpile
69 | - Note: Added feature ✅ the Paperpile web interface does not allow to search by folder
70 | ### Filter by type first
71 | - entering `ppty` will show a list of publication types and the number of items in each
72 | - select the folder by pressing `return`. paperpAlfred will now search within that item type
73 | - `shift-return (⇧⏎)` will open the folder in Paperpile
74 |
75 | ## Advanced search
76 | - enter `field:`, where `field` is any of the fields below. Example: `year:2022`
77 | - `title`
78 | - `abstract`
79 | - `citekey`
80 | - `first` first author
81 | - `last` last author
82 | - `journal`
83 | - `folder`
84 | - `label`
85 | - `pmid`
86 | - `year`
87 | - `type` publication type
88 |
89 | Output
90 |
91 | - Alfred returns the top 99 results, numbered. The max number of results returned can be set in Preferences (see [Setting up](#setting-up))
92 | - results are sorted by year (most recent first). Adding `--a` to the query will invert this order.
93 | - The main text will return the title.
94 | - the subtext will return the following:
95 | - record count/total result count
96 | - 📜if a PDF is available
97 | - short ID string (first-last author, journal, year)
98 | - associated labels
99 |
100 | ## Acting on results
101 | Once the right item is found, the user has seven options to act on it:
102 | 1. `return (⏎)` will open the PDF in the system viewer (Preview for most users), provided that the `PAPPATH` has been set (see [Setting up](#setting-up)). Rarely this might not work (see [Known Issues](#known-issues))
103 | 2. `shift-return (⇧⏎)` will show the **abstract** and copy it to clipboard
104 | 3. `control-return (⌃⏎)` will show the **complete reference** and copy it to the clipboard
105 | 4. `option-return (⌥⏎)` will copy a **short reference** (First-last author, journal, year, PMID) to the clipboard
106 | 5. `command-return (⌘⏎)` will copy the **citation key** to the clipboard
107 | 6. `command-shift-return (⌘⇧⏎)` will open the PDF in Google drive
108 | 7. `command-option-return (⌘⌥⏎)` will open the record in Paperpile
109 |
110 | Known Issues
111 |
112 | - incomplete records will not be imported
113 | - special characters (e.g. ü) will need to be entered in order to match the record
114 | - File opening is currently using a name search. The Paperpile file naming logic is not entirely clear to me, the folder structure is deprecated and there might be a small number of cases where the PDF might not be retrievable via paperpAlfred. Google drive view should still work in these cases.
115 | - Currently tested mainly with research papers, reviews etc. There might be untested use cases for other types of publications.
116 | - label and folder search in the main window (i.e. using `label:` and `folder:`) will not be exact matches (e.g. AD will also return GWAS_AD). Match will be exact when starting from folder and label window.
117 |
118 | Acknowledgments
119 |
120 | - [Dean Jackson](https://github.com/deanishe) for their incredible help on the Alfred mailing list and for creating [alfred-index-demo](https://github.com/deanishe/alfred-index-demo), and other scripts used as templates for this workflow.
121 | - Jirka from Paperpile for support on the path-to-file issue
122 | - Alain T, StackExchange user:5237560 (https://nebularena.wordpress.com) for help with a Python script
123 |
124 | Changelog
125 |
126 | - 12-04-2022: version 2.1 (Alfred 5)
127 | - 03-15-2022: version 2.0 (Python3, removed dependencies)
128 | - 03-17-2021: version 1.0
129 |
130 |
131 | Feedback
132 | Feedback welcome! If you notice a bug, or have ideas for new features, please feel free to get in touch either here, or on the [Paperpile](https://forum.paperpile.com)/[Alfred](https://www.alfredforum.com) forums.
133 |
134 |
--------------------------------------------------------------------------------
/source/build_db.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Rebuilding the database
5 | # rewritten script
6 | #
7 | # previously created on Sunday, February 28, 2021
8 | # March 2022 updated to Python3, eliminated dependencies
9 |
10 | #Note it currently fails if none of the references are in folders. will need to fix
11 |
12 | import json
13 | import sqlite3
14 | import re
15 | import sys
16 | import collections
17 |
18 | def log(s, *args):
19 | if args:
20 | s = s % args
21 | print(s, file=sys.stderr)
22 |
23 |
24 |
25 |
26 | def importingCompleteLibrary (myFile,mydatabase): # to import complete library before filtering, not used here
27 | ## reading JSON data in
28 | with open(myFile, "r") as read_file:
29 | json_data = json.load(read_file)
30 |
31 |
32 | ## getting the list of columns (not all the records have the same columns, nor we can assume that for future dicts)
33 | # thanks to https://www.codeproject.com/Tips/4067936/Load-JSON-File-with-Array-of-Objects-to-SQLite3-On
34 | column_list = []
35 | column = []
36 | for data in json_data:
37 |
38 | column = list(data.keys())
39 | for col in column:
40 | if col not in column_list:
41 | column_list.append(col)
42 |
43 |
44 |
45 | value = []
46 | values = []
47 | for data in json_data:
48 | for i in column_list:
49 | value.append(str(dict(data).get(i)))
50 | values.append(list(value))
51 | value.clear()
52 |
53 | create_statement = "create table if not exists myLibrary ({0})".format(" text,".join(column_list))
54 | insert_statement = "insert into myLibrary ({0}) values (?{1})".format(",".join(column_list), ",?" * (len(column_list)-1))
55 | drop_statement = "DROP TABLE IF EXISTS myLibrary"
56 | # execution
57 | db=sqlite3.connect(mydatabase)
58 | c = db.cursor()
59 | c.execute(drop_statement)
60 | c.execute(create_statement)
61 | c.executemany(insert_statement , values)
62 | values.clear()
63 | db.commit()
64 |
65 |
66 | def JSONtoDB (myJSON,myTable, mydatabase):
67 | column_list = []
68 | column = []
69 | for data in myJSON:
70 |
71 | column = list(data.keys())
72 | for col in column:
73 | if col not in column_list:
74 | column_list.append(col)
75 |
76 | value = []
77 | values = []
78 | for data in myJSON:
79 | for i in column_list:
80 | value.append(str(dict(data).get(i)))
81 | values.append(list(value))
82 | value.clear()
83 |
84 |
85 |
86 | create_statement = "create VIRTUAL table " + myTable + " USING FTS3 ({0})".format(" text,".join(column_list))
87 |
88 | insert_statement = "insert into " + myTable + " ({0}) values (?{1})".format(",".join(column_list), ",?" * (len(column_list)-1))
89 | drop_statement = "DROP TABLE IF EXISTS "+ myTable
90 |
91 | # execution
92 | db=sqlite3.connect(mydatabase)
93 | c = db.cursor()
94 | c.execute(drop_statement)
95 | c.execute(create_statement)
96 | c.executemany(insert_statement , values)
97 | values.clear()
98 | db.commit()
99 |
100 |
101 |
102 | def createLibrary (myLibrary):
103 |
104 | from config import INDEX_DB
105 | ## reading JSON data in
106 | with open(myLibrary, "r") as read_file:
107 | mydata = json.load(read_file)
108 |
109 |
110 | ### creating a second JSON with a subset of the fields, plus some formatted fields
111 |
112 | #list of elements to get from original JSON
113 | mySubsetFields = {'title','published','abstract','issue','author','pages','citekey','journal','volume','pmid','labelsNamed','foldersNamed','labels','folders','attachments','subfolders','gdrive_id','_id','pubtype','kind'}
114 |
115 | # creating the subset (mySubset object)
116 | mySubset = []
117 | for item in mydata:
118 | my_dict={}
119 | if item['incomplete']==1: ## skipping incomplete items
120 | continue
121 |
122 | for myfield in mySubsetFields:
123 | if myfield in item:
124 | my_dict[myfield]=item.get(myfield)
125 | mySubset.append(my_dict)
126 |
127 | #combining the authors in authorBlock
128 | for item in mySubset:
129 | #log (item['_id'])
130 | #declaration block, and defaults
131 | authorBlock =''
132 | labelBlock =''
133 | labelIDBlock =''
134 | folderIDBlock =''
135 | first =''
136 | last =''
137 | folderBlock =''
138 | myFileName =''
139 | item.setdefault('pmid', '-')
140 | item.setdefault('citekey', '-')
141 | item.setdefault('journal', '-')
142 | item.setdefault('published', '-')
143 | item.setdefault('abstract', '')
144 | item.setdefault('label', '')
145 | item.setdefault('author', '')
146 | item.setdefault('issue', '')
147 | item.setdefault('pages', '')
148 | item.setdefault('fileName', '')
149 | item.setdefault('gdrive_id', '')
150 | item.setdefault('_id', '')
151 | item.setdefault('pdfFlag', ' ')
152 | item.setdefault('type', '')
153 |
154 |
155 |
156 | # stripping dots from journal names
157 | item['journal'] = re.sub(r'\.', '', item['journal'])
158 |
159 | for myAuthor in item['author']:
160 | if myAuthor['formatted'] == item['author'][-1]['formatted']:
161 | authorBlock += myAuthor['formatted']
162 | else:
163 | authorBlock += myAuthor['formatted']+', '
164 | firstAuthorLN=''
165 | lastAuthorLN=''
166 |
167 | if (len(item['author'])) == 0:
168 | if ('last' in item['author']):
169 | firstAuthorLN= item['author']['last']
170 | if (len(item['author'])) > 0:
171 | if ('last' in item['author'][0]):
172 | firstAuthorLN= item['author'][0]['last']
173 |
174 | if ('last' in item['author'][-1]):
175 | lastAuthorLN = item['author'][-1]['last']
176 | if ('year' in item['published'] and item['published']['year'] is not None):
177 | pubYear = item['published']['year']
178 | else:
179 | pubYear = "-"
180 |
181 | item.update({'first':firstAuthorLN})
182 | item.update({'last':lastAuthorLN})
183 |
184 | item.update({'year':pubYear})
185 | myJournal = item['journal']
186 |
187 | # assigning publication type
188 | myType=item['pubtype']
189 | myKinds = ['Commentary','Review','News']
190 | if 'kind' in item:
191 | if item['kind'] in myKinds:
192 | myType=item['kind']
193 | item.update({'type':myType})
194 |
195 |
196 |
197 | # flattening labels
198 | if 'labelsNamed' in item:
199 | for myLabel in item['labelsNamed']:
200 | if myLabel == item['labelsNamed'][-1]:
201 | labelBlock += myLabel
202 | else:
203 | labelBlock += myLabel+','
204 | item.update({'label':labelBlock})
205 |
206 | # flattening labelIDs
207 | for myLabel in item['labels']:
208 | if myLabel == item['labels'][-1]:
209 | labelIDBlock += myLabel
210 | else:
211 | labelIDBlock += myLabel+','
212 | item.update({'labelID':labelIDBlock})
213 |
214 |
215 | # flattening folders
216 | if 'foldersNamed' in item:
217 | for myFolder in item['foldersNamed']:
218 | if myFolder == item['foldersNamed'][-1]:
219 | folderBlock += myFolder
220 | else:
221 | folderBlock += myFolder +','
222 | item.update({'folder':folderBlock})
223 |
224 | # flattening folderIDs
225 | for myFolder in item['folders']:
226 | if myFolder == item['folders'][-1]:
227 | folderIDBlock += myFolder
228 | else:
229 | folderIDBlock += myFolder +','
230 | item.update({'folderID':folderIDBlock})
231 |
232 | # PDF name or search string
233 | if (len(item['attachments'])) >0:
234 |
235 | # checking source_filename
236 | if item['attachments'][0]['source_filename'] == "[article_pdf].pdf":
237 | myFilename = item['attachments'][0]['filename']
238 | else:
239 | myTitle=item['title']
240 | myFilename = firstAuthorLN + '*' + myTitle[0:30]+ '*'
241 | # search string including the first author and the first 30 characters of the title. should work in most cases.
242 |
243 | # eliminating extra space after ellipsis. another option is to always take the first 30 chars of title
244 | myFilename = myFilename.replace("... ", "... ")
245 |
246 | # escaping exclamation point in title??
247 | #myFilename = myFilename.replace("!", "\!")
248 |
249 | item.update({'fileName':myFilename})
250 | item.update({'pdfFlag':'📜'})
251 |
252 | # storing google drive ID
253 | if ('gdrive_id' in item['attachments'][0]):
254 | myGDrive_ID = item['attachments'][0]['gdrive_id']
255 | item.update({'gdrive_id':myGDrive_ID})
256 |
257 |
258 |
259 |
260 |
261 | # compiling the subtitle
262 | #log (f"{firstAuthorLN}-{lastAuthorLN}, {myJournal} {pubYear}")
263 | subtitle = f"{firstAuthorLN}-{lastAuthorLN}, {myJournal} {pubYear}"
264 |
265 | item.update({'subtitle':subtitle})
266 |
267 |
268 |
269 |
270 | authorBlock = authorBlock+'.'
271 | ## generating full reference style used is with PMID, can be changed, or maybe I can have a couple of styles, not sure it is worth creating a universal solution
272 | fullRef = authorBlock + ' ' + item['title']+'. '+item['journal'] + ' ' + pubYear+';'+item['issue']+':'+item['pages']+'. PMID: '+item['pmid']
273 | item.update({'fullReference':fullRef})
274 |
275 |
276 |
277 |
278 | # generating label lists and file type counts (counting pub types for each label)
279 | # thanks to Alain T, StackExchange user:5237560 (https://nebularena.wordpress.com)
280 |
281 | counters = {dd['type']:0 for dd in mySubset if 'type' in dd} # if types not fixed
282 | counters['Total'] = 0
283 | counters['labelID'] = ''
284 | myOutput = dict()
285 |
286 | for item in mySubset: # go through dictionary list
287 | myLabels = item['label'].split(",")
288 | myLabelIDs = item['labelID'].split(",")
289 |
290 | itemType = item.get('type',None) # get the type
291 |
292 | lcc=-1 #label count
293 | for labell in myLabels: # go through labels list
294 | lcc += 1
295 | if labell:
296 | if labell in myOutput:
297 | labelCounts = myOutput[labell] #if a label exists, get the current type counts
298 | else:
299 | myOutput[labell] = labelCounts = dict (counters)
300 | labelCounts['labelID'] = myLabelIDs[lcc] #fetches the label ID
301 | if itemType : labelCounts[itemType] += 1 # count items for type if any
302 | labelCounts['Total'] += 1
303 |
304 | myFinalCount = []
305 | for key, val in myOutput.items():
306 | mySubText = ''
307 | val = {k.lower(): v for k, v in val.items()} #converting to lowercase
308 | val = {k.replace('pp_', ''): v for k, v in val.items()} # eliminating pp
309 |
310 |
311 | # this section below is to convert previous code and could be made better
312 | valSub = {k: val[k] for k in set(list(val.keys())) - set(['labelid','total'])} #subsetting the type totals, so that they can be sorted
313 | valSub = sorted(valSub.items(), key=lambda x: x[1], reverse=True) # sorting toitals for each type
314 | valSub = collections.OrderedDict(valSub) #converting back into a dictionary
315 |
316 |
317 | for key2, val2 in valSub.items():
318 | if val2 != 0 and key2 != 'total' and key2 != 'labelid': #formatting all fields except total and labelID
319 | myText = '{} ({}) '.format(key2, val2)
320 | mySubText = mySubText + myText
321 | myFinalCount.append ({
322 | 'label': key,
323 | 'totalLabel': val['total'],
324 | 'LabelID': val['labelid'],
325 | 'summaryLabel': mySubText
326 | })
327 |
328 | # creating the table in the sqlite database
329 |
330 | JSONtoDB (myJSON=myFinalCount,myTable='Labels', mydatabase=INDEX_DB)
331 |
332 |
333 | # generating folder list and counts
334 |
335 | counters = {dd['type']:0 for dd in mySubset if 'type' in dd} # if types not fixed
336 | counters['Total'] = 0
337 | counters['folderID'] = ''
338 | myOutput = dict()
339 |
340 | for item in mySubset: # go through dictionary list
341 | myLabels = item['folder'].split(",")
342 | myLabelIDs = item['folderID'].split(",")
343 | itemType = item.get('type',None) # get the type
344 |
345 | lcc=-1 #label count
346 | for labell in myLabels: # go through labels list
347 | lcc += 1
348 | if labell:
349 | if labell in myOutput:
350 | labelCounts = myOutput[labell] #if a folder exists, get the current type counts
351 | else:
352 | myOutput[labell] = labelCounts = dict (counters)
353 | labelCounts['folderID'] = myLabelIDs[lcc] #fetches the folder ID
354 | if itemType : labelCounts[itemType] += 1 # count items for type if any
355 | labelCounts['Total'] += 1
356 |
357 | #print (myOutput)
358 | myFinalCount = []
359 | for key, val in myOutput.items():
360 | mySubText = ''
361 | val = {k.lower(): v for k, v in val.items()}
362 | val = {k.replace('pp_', ''): v for k, v in val.items()}
363 |
364 | # this section below is to convert previous code and could be made better
365 | valSub = {k: val[k] for k in set(list(val.keys())) - set(['folderid','total'])} #subsetting the type totals, so that they can be sorted
366 | valSub = sorted(valSub.items(), key=lambda x: x[1], reverse=True) # sorting toitals for each type
367 | valSub = collections.OrderedDict(valSub) #converting back into a dictionary
368 |
369 | for key2, val2 in valSub.items():
370 |
371 | if val2 != 0 and key2 != 'total' and key2 != 'folderid':
372 | myText = '{} ({}) '.format(key2, val2)
373 | mySubText = mySubText + myText
374 | myFinalCount.append ({
375 | 'folder': key,
376 | 'totalFolder': val['total'],
377 | 'FolderID': val['folderid'],
378 | 'summaryFolder': mySubText
379 | })
380 |
381 | # creating the table in the sqlite database
382 | JSONtoDB (myJSON=myFinalCount,myTable='Folders', mydatabase=INDEX_DB)
383 |
384 |
385 |
386 | ### creating a list of unique types
387 | myTypeList=[]
388 | for item in mySubset:
389 | myTypes = item['type'].split(",")
390 | for myCurrType in myTypes:
391 | if myCurrType:
392 | myTypeList.append(myCurrType)
393 |
394 |
395 | myTypeList = [x.replace('PP_', '') for x in myTypeList]
396 | myTypeList = [x.replace('_', ' ') for x in myTypeList]
397 | myTypeList = [each_string.capitalize() for each_string in myTypeList]
398 |
399 | myTypeSummary = collections.Counter(myTypeList)
400 |
401 | myFinalCount = []
402 | for key, val in myTypeSummary.items():
403 | myFinalCount.append ({
404 | 'type': key,
405 | 'totalType': val
406 |
407 | })
408 |
409 |
410 | JSONtoDB (myJSON=myFinalCount,myTable='Types', mydatabase=INDEX_DB)
411 |
412 |
413 |
414 |
415 |
416 | # preparing the final output: excluding items no longer needed
417 | myFinalSubset = []
418 | toExclude = ['author','published','issue','pages','volume','labelsNamed','foldersNamed','labels','folders','attachments','subfolders','pubtype','kind']
419 |
420 | for item in mySubset:
421 |
422 | myItem={}
423 | myItem = {k: item[k] for k in item.keys() - toExclude}
424 | myFinalSubset.append(myItem)
425 |
426 | JSONtoDB (myJSON=myFinalSubset,myTable='papers', mydatabase=INDEX_DB)
427 |
428 |
429 |
430 |
431 |
432 | # if os.path.exists(INDEX_DB):
433 | # os.remove(INDEX_DB)
434 | # # deleting the existing index db, so the papers.py script is forced to rebuild
435 |
436 |
437 |
438 |
439 | #if __name__ == "__main__":
440 | #importingLibrary(LIBRARY_FILE, MY_DATABASE)
441 | #createLibrary (LIBRARY_FILE)
442 |
--------------------------------------------------------------------------------
/source/info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | bundleid
6 | giovanni.paperpAlfred
7 | category
8 | Productivity
9 | connections
10 |
11 | 1AAAEDC3-C0EE-47D4-83C7-2A5AB352295E
12 |
13 |
14 | destinationuid
15 | D94E41FF-2E5C-420B-919A-5A2990C3F4A6
16 | modifiers
17 | 0
18 | modifiersubtext
19 |
20 | vitoclose
21 |
22 |
23 |
24 | destinationuid
25 | 1D9857A2-7E4B-4E14-9556-B38C14893DD9
26 | modifiers
27 | 0
28 | modifiersubtext
29 |
30 | vitoclose
31 |
32 |
33 |
34 | 21B88AAB-784B-4782-B4D6-607BDB0F7C25
35 |
36 |
37 | destinationuid
38 | 3B12BCC5-45EE-4E9D-95FB-0B156B0704B3
39 | modifiers
40 | 0
41 | modifiersubtext
42 |
43 | vitoclose
44 |
45 |
46 |
47 | destinationuid
48 | 8D033AF8-08E7-4097-B0BF-5BB509FAF1AB
49 | modifiers
50 | 0
51 | modifiersubtext
52 |
53 | vitoclose
54 |
55 |
56 |
57 | 28606112-A338-475C-9D0A-17F14E5D6E40
58 |
59 |
60 | destinationuid
61 | E2E84778-9472-48A7-851A-87F07879DA5C
62 | modifiers
63 | 0
64 | modifiersubtext
65 |
66 | vitoclose
67 |
68 |
69 |
70 | 33BF8A0E-B7AA-495E-B89F-1F8239DAECBE
71 |
72 |
73 | destinationuid
74 | 3FDF7937-4C98-4A75-B7C6-AF4AF5FBB5D7
75 | modifiers
76 | 262144
77 | modifiersubtext
78 | Open Folder in Paperpile
79 | vitoclose
80 |
81 |
82 |
83 | destinationuid
84 | E2E84778-9472-48A7-851A-87F07879DA5C
85 | modifiers
86 | 0
87 | modifiersubtext
88 |
89 | vitoclose
90 |
91 |
92 |
93 | 4797204D-1BEC-49C9-8577-07F086902572
94 |
95 |
96 | destinationuid
97 | 6DF20719-C29E-41E0-AEA3-E92AD68EA188
98 | modifiers
99 | 0
100 | modifiersubtext
101 |
102 | vitoclose
103 |
104 |
105 |
106 | 6DF20719-C29E-41E0-AEA3-E92AD68EA188
107 |
108 |
109 | destinationuid
110 | 439299E4-26DE-4954-9EE4-6AB9A055C4DC
111 | modifiers
112 | 262144
113 | modifiersubtext
114 | Open Label in Paperpile
115 | vitoclose
116 |
117 |
118 |
119 | destinationuid
120 | E2E84778-9472-48A7-851A-87F07879DA5C
121 | modifiers
122 | 0
123 | modifiersubtext
124 |
125 | vitoclose
126 |
127 |
128 |
129 | 837B6FCB-ACFA-46BE-AEC2-10135A81A784
130 |
131 |
132 | destinationuid
133 | E2E84778-9472-48A7-851A-87F07879DA5C
134 | modifiers
135 | 0
136 | modifiersubtext
137 |
138 | vitoclose
139 |
140 |
141 |
142 | DD1DC957-AD49-4CC1-A425-2D6FF0F6F2D5
143 |
144 |
145 | destinationuid
146 | C15FD31B-73B9-4D65-9FC9-B88846234B1C
147 | modifiers
148 | 0
149 | modifiersubtext
150 |
151 | sourceoutputuid
152 | B8F4A19F-1244-4D35-BFAF-7B30C48F9337
153 | vitoclose
154 |
155 |
156 |
157 | destinationuid
158 | C81A90A7-A0A8-4A02-8BC5-9315BC9BB108
159 | modifiers
160 | 0
161 | modifiersubtext
162 |
163 | vitoclose
164 |
165 |
166 |
167 | E2E84778-9472-48A7-851A-87F07879DA5C
168 |
169 |
170 | destinationuid
171 | 21B88AAB-784B-4782-B4D6-607BDB0F7C25
172 | modifiers
173 | 262144
174 | modifiersubtext
175 | Show and copy to clipboard complete reference
176 | vitoclose
177 |
178 |
179 |
180 | destinationuid
181 | 0F60EE65-A185-4BBF-B450-70AF97DF20D0
182 | modifiers
183 | 524288
184 | modifiersubtext
185 | Copy First-Last, Journal, Year, PMID to clipboard
186 | vitoclose
187 |
188 |
189 |
190 | destinationuid
191 | 09F993A7-F21C-4541-8C43-6641A0B6E1D8
192 | modifiers
193 | 1048576
194 | modifiersubtext
195 | Copy citation key to clipboard
196 | vitoclose
197 |
198 |
199 |
200 | destinationuid
201 | 1AAAEDC3-C0EE-47D4-83C7-2A5AB352295E
202 | modifiers
203 | 131072
204 | modifiersubtext
205 | Show (and copy to clipboard) abstract
206 | vitoclose
207 |
208 |
209 |
210 | destinationuid
211 | DD1DC957-AD49-4CC1-A425-2D6FF0F6F2D5
212 | modifiers
213 | 0
214 | modifiersubtext
215 |
216 | vitoclose
217 |
218 |
219 |
220 | destinationuid
221 | 30EEB713-8B4D-41AF-A310-C8793A498639
222 | modifiers
223 | 1179648
224 | modifiersubtext
225 | Open PDF in Google Drive
226 | vitoclose
227 |
228 |
229 |
230 | destinationuid
231 | D942F3A6-D97B-4FA5-80ED-18B88282CF61
232 | modifiers
233 | 1572864
234 | modifiersubtext
235 | Open record in Paperpile
236 | vitoclose
237 |
238 |
239 |
240 | E31F5435-FCA6-40FB-AC97-768A01A29B7B
241 |
242 |
243 | destinationuid
244 | 33BF8A0E-B7AA-495E-B89F-1F8239DAECBE
245 | modifiers
246 | 0
247 | modifiersubtext
248 |
249 | vitoclose
250 |
251 |
252 |
253 |
254 | createdby
255 | @giovannicoppoIa
256 | description
257 | Search your Paperpile library with Alfred
258 | disabled
259 |
260 | name
261 | paperpAlfred
262 | objects
263 |
264 |
265 | config
266 |
267 | autopaste
268 |
269 | clipboardtext
270 | {var:FullReference}
271 | ignoredynamicplaceholders
272 |
273 | transient
274 |
275 |
276 | type
277 | alfred.workflow.output.clipboard
278 | uid
279 | 3B12BCC5-45EE-4E9D-95FB-0B156B0704B3
280 | version
281 | 3
282 |
283 |
284 | config
285 |
286 | action
287 | 0
288 | argument
289 | 0
290 | focusedappvariable
291 |
292 | focusedappvariablename
293 |
294 | hotkey
295 | 37
296 | hotmod
297 | 1310720
298 | hotstring
299 | L
300 | leftcursor
301 |
302 | modsmode
303 | 0
304 | relatedAppsMode
305 | 0
306 |
307 | type
308 | alfred.workflow.trigger.hotkey
309 | uid
310 | 4797204D-1BEC-49C9-8577-07F086902572
311 | version
312 | 2
313 |
314 |
315 | config
316 |
317 | alfredfiltersresults
318 |
319 | alfredfiltersresultsmatchmode
320 | 2
321 | argumenttreatemptyqueryasnil
322 |
323 | argumenttrimmode
324 | 0
325 | argumenttype
326 | 1
327 | escaping
328 | 102
329 | keyword
330 | ppl
331 | queuedelaycustom
332 | 3
333 | queuedelayimmediatelyinitially
334 |
335 | queuedelaymode
336 | 0
337 | queuemode
338 | 1
339 | runningsubtext
340 |
341 | script
342 | export PATH=/opt/homebrew/bin:/usr/local/bin:$PATH
343 | /usr/bin/python3 labels.py "$1"
344 |
345 |
346 |
347 | scriptargtype
348 | 1
349 | scriptfile
350 |
351 | subtext
352 |
353 | title
354 |
355 | type
356 | 5
357 | withspace
358 |
359 |
360 | type
361 | alfred.workflow.input.scriptfilter
362 | uid
363 | 6DF20719-C29E-41E0-AEA3-E92AD68EA188
364 | version
365 | 3
366 |
367 |
368 | config
369 |
370 | concurrently
371 |
372 | escaping
373 | 102
374 | script
375 | open "https://paperpile.com/app/label/$myLabelID"
376 | scriptargtype
377 | 0
378 | scriptfile
379 |
380 | type
381 | 0
382 |
383 | type
384 | alfred.workflow.action.script
385 | uid
386 | 439299E4-26DE-4954-9EE4-6AB9A055C4DC
387 | version
388 | 2
389 |
390 |
391 | type
392 | alfred.workflow.utility.junction
393 | uid
394 | 21B88AAB-784B-4782-B4D6-607BDB0F7C25
395 | version
396 | 1
397 |
398 |
399 | config
400 |
401 | alignment
402 | 0
403 | backgroundcolor
404 |
405 | fadespeed
406 | 0
407 | fillmode
408 | 0
409 | font
410 |
411 | ignoredynamicplaceholders
412 |
413 | largetypetext
414 | {var:FullReference}
415 | textcolor
416 |
417 | wrapat
418 | 50
419 |
420 | type
421 | alfred.workflow.output.largetype
422 | uid
423 | 8D033AF8-08E7-4097-B0BF-5BB509FAF1AB
424 | version
425 | 3
426 |
427 |
428 | config
429 |
430 | action
431 | 0
432 | argument
433 | 0
434 | focusedappvariable
435 |
436 | focusedappvariablename
437 |
438 | hotkey
439 | 3
440 | hotmod
441 | 1310720
442 | hotstring
443 | F
444 | leftcursor
445 |
446 | modsmode
447 | 0
448 | relatedAppsMode
449 | 0
450 |
451 | type
452 | alfred.workflow.trigger.hotkey
453 | uid
454 | E31F5435-FCA6-40FB-AC97-768A01A29B7B
455 | version
456 | 2
457 |
458 |
459 | config
460 |
461 | alfredfiltersresults
462 |
463 | alfredfiltersresultsmatchmode
464 | 2
465 | argumenttreatemptyqueryasnil
466 |
467 | argumenttrimmode
468 | 0
469 | argumenttype
470 | 1
471 | escaping
472 | 102
473 | keyword
474 | ppf
475 | queuedelaycustom
476 | 3
477 | queuedelayimmediatelyinitially
478 |
479 | queuedelaymode
480 | 0
481 | queuemode
482 | 1
483 | runningsubtext
484 |
485 | script
486 | export PATH=/opt/homebrew/bin:/usr/local/bin:$PATH
487 | /usr/bin/python3 folders.py "$1"
488 |
489 |
490 | scriptargtype
491 | 1
492 | scriptfile
493 |
494 | subtext
495 |
496 | title
497 |
498 | type
499 | 5
500 | withspace
501 |
502 |
503 | type
504 | alfred.workflow.input.scriptfilter
505 | uid
506 | 33BF8A0E-B7AA-495E-B89F-1F8239DAECBE
507 | version
508 | 3
509 |
510 |
511 | config
512 |
513 | concurrently
514 |
515 | escaping
516 | 102
517 | script
518 | open "https://paperpile.com/app/folder/$myFolderID"
519 | scriptargtype
520 | 0
521 | scriptfile
522 |
523 | type
524 | 0
525 |
526 | type
527 | alfred.workflow.action.script
528 | uid
529 | 3FDF7937-4C98-4A75-B7C6-AF4AF5FBB5D7
530 | version
531 | 2
532 |
533 |
534 | config
535 |
536 | autopaste
537 |
538 | clipboardtext
539 | {var:myAbstract}
540 | ignoredynamicplaceholders
541 |
542 | transient
543 |
544 |
545 | type
546 | alfred.workflow.output.clipboard
547 | uid
548 | D94E41FF-2E5C-420B-919A-5A2990C3F4A6
549 | version
550 | 3
551 |
552 |
553 | config
554 |
555 | autopaste
556 |
557 | clipboardtext
558 | {var:shortPMID}
559 | ignoredynamicplaceholders
560 |
561 | transient
562 |
563 |
564 | type
565 | alfred.workflow.output.clipboard
566 | uid
567 | 0F60EE65-A185-4BBF-B450-70AF97DF20D0
568 | version
569 | 3
570 |
571 |
572 | config
573 |
574 | autopaste
575 |
576 | clipboardtext
577 | {var:KEY_PREFIX}{var:myCitekey}
578 | ignoredynamicplaceholders
579 |
580 | transient
581 |
582 |
583 | type
584 | alfred.workflow.output.clipboard
585 | uid
586 | 09F993A7-F21C-4541-8C43-6641A0B6E1D8
587 | version
588 | 3
589 |
590 |
591 | config
592 |
593 | alignment
594 | 0
595 | backgroundcolor
596 |
597 | fadespeed
598 | 0
599 | fillmode
600 | 0
601 | font
602 |
603 | ignoredynamicplaceholders
604 |
605 | largetypetext
606 | {var:myAbstract}
607 | textcolor
608 |
609 | wrapat
610 | 50
611 |
612 | type
613 | alfred.workflow.output.largetype
614 | uid
615 | 1D9857A2-7E4B-4E14-9556-B38C14893DD9
616 | version
617 | 3
618 |
619 |
620 | type
621 | alfred.workflow.utility.junction
622 | uid
623 | 1AAAEDC3-C0EE-47D4-83C7-2A5AB352295E
624 | version
625 | 1
626 |
627 |
628 | config
629 |
630 | alfredfiltersresults
631 |
632 | alfredfiltersresultsmatchmode
633 | 2
634 | argumenttreatemptyqueryasnil
635 |
636 | argumenttrimmode
637 | 0
638 | argumenttype
639 | 1
640 | escaping
641 | 102
642 | keyword
643 | ppty
644 | queuedelaycustom
645 | 3
646 | queuedelayimmediatelyinitially
647 |
648 | queuedelaymode
649 | 0
650 | queuemode
651 | 1
652 | runningsubtext
653 |
654 | script
655 | export PATH=/opt/homebrew/bin:/usr/local/bin:$PATH
656 | /usr/bin/python3 myTypes.py "$1"
657 | scriptargtype
658 | 1
659 | scriptfile
660 |
661 | subtext
662 |
663 | title
664 |
665 | type
666 | 5
667 | withspace
668 |
669 |
670 | type
671 | alfred.workflow.input.scriptfilter
672 | uid
673 | 837B6FCB-ACFA-46BE-AEC2-10135A81A784
674 | version
675 | 3
676 |
677 |
678 | config
679 |
680 | alignment
681 | 0
682 | backgroundcolor
683 | #000000FF
684 | fadespeed
685 | 0
686 | fillmode
687 | 0
688 | font
689 | Menlo Regular
690 | ignoredynamicplaceholders
691 |
692 | largetypetext
693 | - Simple search: `ppp` -- query across all fields
694 |
695 | - Filter by label first: `ppl` (or optional hotkey)
696 | - ⌃⏎ open label in Paperpile
697 |
698 | - Filter by folder first: `ppf` (or optional hotkey)
699 | - ⌃⏎ open folder in Paperpile
700 |
701 | - Filter by item type first: `ppty`
702 |
703 | - Advanced search
704 | - title:
705 | - abstract:
706 | - citekey:
707 | - journal:
708 | - folder:
709 | - label:
710 | - pmid:
711 | - year:
712 | - first: [first author]
713 | - last: [last author]
714 | - type: [publication type]
715 |
716 | - Output
717 | 1. ⏎ open PDF with system viewer
718 | 2. ⇧⏎ abstract (show and copy to clipboard)
719 | 3. ⌃⏎ complete reference (show and copy to clipboard)
720 | 4. ⌥⏎ [First-last author, journal, year, PMID] to clipboard
721 | 5. ⌘⏎ citation key to clipboard
722 | 6. ⌘⇧⏎ open PDF in Google drive
723 | 7. ⌘⌥⏎ open record in Paperpile
724 | textcolor
725 | #00F900FF
726 | wrapat
727 | 50
728 |
729 | type
730 | alfred.workflow.output.largetype
731 | uid
732 | C15FD31B-73B9-4D65-9FC9-B88846234B1C
733 | version
734 | 3
735 |
736 |
737 | config
738 |
739 | action
740 | 0
741 | argument
742 | 0
743 | focusedappvariable
744 |
745 | focusedappvariablename
746 |
747 | hotkey
748 | 35
749 | hotmod
750 | 1310720
751 | hotstring
752 | P
753 | leftcursor
754 |
755 | modsmode
756 | 0
757 | relatedAppsMode
758 | 0
759 |
760 | type
761 | alfred.workflow.trigger.hotkey
762 | uid
763 | 28606112-A338-475C-9D0A-17F14E5D6E40
764 | version
765 | 2
766 |
767 |
768 | config
769 |
770 | alfredfiltersresults
771 |
772 | alfredfiltersresultsmatchmode
773 | 0
774 | argumenttreatemptyqueryasnil
775 |
776 | argumenttrimmode
777 | 0
778 | argumenttype
779 | 1
780 | escaping
781 | 102
782 | keyword
783 | ppp
784 | queuedelaycustom
785 | 3
786 | queuedelayimmediatelyinitially
787 |
788 | queuedelaymode
789 | 0
790 | queuemode
791 | 1
792 | runningsubtext
793 | ❗Please wait while I rebuild the database❗
794 | script
795 | export PATH=/opt/homebrew/bin:/usr/local/bin:$PATH
796 | /usr/bin/python3 papers.py "$1"
797 |
798 |
799 |
800 |
801 |
802 | scriptargtype
803 | 1
804 | scriptfile
805 |
806 | subtext
807 |
808 | title
809 | Welcome to paperpAlfred 👋
810 | type
811 | 0
812 | withspace
813 |
814 |
815 | type
816 | alfred.workflow.input.scriptfilter
817 | uid
818 | E2E84778-9472-48A7-851A-87F07879DA5C
819 | version
820 | 3
821 |
822 |
823 | config
824 |
825 | conditions
826 |
827 |
828 | inputstring
829 |
830 | matchcasesensitive
831 |
832 | matchmode
833 | 0
834 | matchstring
835 | ShowHelpWindow
836 | outputlabel
837 | ShowHelp
838 | uid
839 | B8F4A19F-1244-4D35-BFAF-7B30C48F9337
840 |
841 |
842 | elselabel
843 | else
844 | hideelse
845 |
846 |
847 | type
848 | alfred.workflow.utility.conditional
849 | uid
850 | DD1DC957-AD49-4CC1-A425-2D6FF0F6F2D5
851 | version
852 | 1
853 |
854 |
855 | config
856 |
857 | concurrently
858 |
859 | escaping
860 | 102
861 | script
862 | find "$PAPPATH" -name "$myFileName" | head -n 1 | tr \\n \\0 | xargs -0 open
863 |
864 | # older version opening multiple copies if >1 found
865 | # find /Users/giovanni/Google\ Drive/Paperpile/ -name "{query}" -exec open {} +
866 | scriptargtype
867 | 0
868 | scriptfile
869 |
870 | type
871 | 5
872 |
873 | type
874 | alfred.workflow.action.script
875 | uid
876 | C81A90A7-A0A8-4A02-8BC5-9315BC9BB108
877 | version
878 | 2
879 |
880 |
881 | config
882 |
883 | concurrently
884 |
885 | escaping
886 | 102
887 | script
888 | open "https://docs.google.com/file/d/$gdrive_id"
889 | scriptargtype
890 | 0
891 | scriptfile
892 |
893 | type
894 | 0
895 |
896 | type
897 | alfred.workflow.action.script
898 | uid
899 | 30EEB713-8B4D-41AF-A310-C8793A498639
900 | version
901 | 2
902 |
903 |
904 | config
905 |
906 | concurrently
907 |
908 | escaping
909 | 102
910 | script
911 | open "https://paperpile.com/app/p/$paperpileID"
912 | scriptargtype
913 | 0
914 | scriptfile
915 |
916 | type
917 | 0
918 |
919 | type
920 | alfred.workflow.action.script
921 | uid
922 | D942F3A6-D97B-4FA5-80ED-18B88282CF61
923 | version
924 | 2
925 |
926 |
927 | readme
928 | Search your Paperpile library with Alfred
929 | https://github.com/giovannicoppola/paperpAlfred
930 |
931 | Environment Variables
932 | - MAXRESULTS: max number of results returned (default: 99)
933 | - PAPLIBRARY: path to Paperpile library file (this is needed)
934 | - PAPPATH: path to Paperpile folder in Google Drive (optional, to open downloaded PDFs in system viewer)
935 | - KEY_PREFIX: prefix for citekeys (default: '@')
936 |
937 |
938 | Basic usage (`ppp` ↩️)
939 |
940 | - Simple search: `ppp` -- query across all fields
941 |
942 | - Filter by label first: `ppl` (or optional hotkey)
943 | - ⌃⏎ open label in Paperpile
944 |
945 | - Filter by folder first: `ppf` (or optional hotkey)
946 | - ⌃⏎ open folder in Paperpile
947 |
948 | - Filter by item type first: `ppty`
949 |
950 | - Advanced search
951 | - title:
952 | - abstract:
953 | - citekey:
954 | - journal:
955 | - folder:
956 | - label:
957 | - pmid:
958 | - year:
959 | - first: [first author]
960 | - last: [last author]
961 | - type: [publication type]
962 |
963 | - Output
964 | 1. ⏎ open PDF with system viewer
965 | 2. ⇧⏎ abstract (show and copy to clipboard)
966 | 3. ⌃⏎ complete reference (show and copy to clipboard)
967 | 4. ⌥⏎ [First-last author, journal, year, PMID] to clipboard
968 | 5. ⌘⏎ citation key to clipboard
969 | 6. ⌘⇧⏎ open PDF in Google drive
970 | 7. ⌘⌥⏎ open record in Paperpile
971 | uidata
972 |
973 | 09F993A7-F21C-4541-8C43-6641A0B6E1D8
974 |
975 | xpos
976 | 1000
977 | ypos
978 | 365
979 |
980 | 0F60EE65-A185-4BBF-B450-70AF97DF20D0
981 |
982 | note
983 | Copy Fist, Last, Journal, Year, and PMID to clipboard
984 | xpos
985 | 1125
986 | ypos
987 | 210
988 |
989 | 1AAAEDC3-C0EE-47D4-83C7-2A5AB352295E
990 |
991 | xpos
992 | 1290
993 | ypos
994 | 390
995 |
996 | 1D9857A2-7E4B-4E14-9556-B38C14893DD9
997 |
998 | xpos
999 | 1480
1000 | ypos
1001 | 380
1002 |
1003 | 21B88AAB-784B-4782-B4D6-607BDB0F7C25
1004 |
1005 | xpos
1006 | 790
1007 | ypos
1008 | 155
1009 |
1010 | 28606112-A338-475C-9D0A-17F14E5D6E40
1011 |
1012 | colorindex
1013 | 3
1014 | note
1015 | Set this Hotkey to launch paperAlfred
1016 | xpos
1017 | 30
1018 | ypos
1019 | 525
1020 |
1021 | 30EEB713-8B4D-41AF-A310-C8793A498639
1022 |
1023 | colorindex
1024 | 9
1025 | note
1026 | Open PDF in Google Drive
1027 | xpos
1028 | 950
1029 | ypos
1030 | 625
1031 |
1032 | 33BF8A0E-B7AA-495E-B89F-1F8239DAECBE
1033 |
1034 | colorindex
1035 | 7
1036 | note
1037 | Search by folder
1038 | xpos
1039 | 235
1040 | ypos
1041 | 200
1042 |
1043 | 3B12BCC5-45EE-4E9D-95FB-0B156B0704B3
1044 |
1045 | note
1046 | Show and copy to clipboard complete reference
1047 | xpos
1048 | 905
1049 | ypos
1050 | 15
1051 |
1052 | 3FDF7937-4C98-4A75-B7C6-AF4AF5FBB5D7
1053 |
1054 | colorindex
1055 | 8
1056 | note
1057 | Open Folder in Paperpile
1058 | xpos
1059 | 555
1060 | ypos
1061 | 200
1062 |
1063 | 439299E4-26DE-4954-9EE4-6AB9A055C4DC
1064 |
1065 | colorindex
1066 | 8
1067 | note
1068 | Open Label in Paperpile
1069 | xpos
1070 | 555
1071 | ypos
1072 | 40
1073 |
1074 | 4797204D-1BEC-49C9-8577-07F086902572
1075 |
1076 | colorindex
1077 | 3
1078 | note
1079 | Set this Hotkey to filter by label first
1080 | xpos
1081 | 30
1082 | ypos
1083 | 40
1084 |
1085 | 6DF20719-C29E-41E0-AEA3-E92AD68EA188
1086 |
1087 | colorindex
1088 | 1
1089 | note
1090 | Search by label
1091 | xpos
1092 | 235
1093 | ypos
1094 | 40
1095 |
1096 | 837B6FCB-ACFA-46BE-AEC2-10135A81A784
1097 |
1098 | colorindex
1099 | 2
1100 | note
1101 | Search by type
1102 | xpos
1103 | 235
1104 | ypos
1105 | 390
1106 |
1107 | 8D033AF8-08E7-4097-B0BF-5BB509FAF1AB
1108 |
1109 | xpos
1110 | 905
1111 | ypos
1112 | 165
1113 |
1114 | C15FD31B-73B9-4D65-9FC9-B88846234B1C
1115 |
1116 | xpos
1117 | 1225
1118 | ypos
1119 | 480
1120 |
1121 | C81A90A7-A0A8-4A02-8BC5-9315BC9BB108
1122 |
1123 | colorindex
1124 | 4
1125 | note
1126 | Open PDF with system viewer
1127 | xpos
1128 | 1225
1129 | ypos
1130 | 600
1131 |
1132 | D942F3A6-D97B-4FA5-80ED-18B88282CF61
1133 |
1134 | colorindex
1135 | 10
1136 | note
1137 | Open Paperpile Record
1138 | xpos
1139 | 1020
1140 | ypos
1141 | 805
1142 |
1143 | D94E41FF-2E5C-420B-919A-5A2990C3F4A6
1144 |
1145 | note
1146 | Show (and copy to clipboard) abstract
1147 | xpos
1148 | 1485
1149 | ypos
1150 | 200
1151 |
1152 | DD1DC957-AD49-4CC1-A425-2D6FF0F6F2D5
1153 |
1154 | xpos
1155 | 1095
1156 | ypos
1157 | 545
1158 |
1159 | E2E84778-9472-48A7-851A-87F07879DA5C
1160 |
1161 | colorindex
1162 | 10
1163 | note
1164 | Search your Paperpile library
1165 | xpos
1166 | 525
1167 | ypos
1168 | 530
1169 |
1170 | E31F5435-FCA6-40FB-AC97-768A01A29B7B
1171 |
1172 | colorindex
1173 | 3
1174 | note
1175 | Set this Hotkey to filter by folder first
1176 | xpos
1177 | 30
1178 | ypos
1179 | 195
1180 |
1181 |
1182 | userconfigurationconfig
1183 |
1184 |
1185 | config
1186 |
1187 | default
1188 | 99
1189 | placeholder
1190 |
1191 | required
1192 |
1193 | trim
1194 |
1195 |
1196 | description
1197 |
1198 | label
1199 | Maximum number of records returned
1200 | type
1201 | textfield
1202 | variable
1203 | MAXRESULTS
1204 |
1205 |
1206 | config
1207 |
1208 | default
1209 | @
1210 | placeholder
1211 |
1212 | required
1213 |
1214 | trim
1215 |
1216 |
1217 | description
1218 | Prefix for citekey
1219 | label
1220 | Key Prefix
1221 | type
1222 | textfield
1223 | variable
1224 | KEY_PREFIX
1225 |
1226 |
1227 | config
1228 |
1229 | default
1230 |
1231 | filtermode
1232 | 2
1233 | placeholder
1234 |
1235 | required
1236 |
1237 |
1238 | description
1239 | Location of the JSON Paperpile Library file
1240 | label
1241 | Paperpile Library
1242 | type
1243 | filepicker
1244 | variable
1245 | PAPLIBRARY
1246 |
1247 |
1248 | config
1249 |
1250 | default
1251 |
1252 | filtermode
1253 | 1
1254 | placeholder
1255 |
1256 | required
1257 |
1258 |
1259 | description
1260 | Path to Google Drive Paperpile folder
1261 | label
1262 | Paperpile path
1263 | type
1264 | filepicker
1265 | variable
1266 | PAPPATH
1267 |
1268 |
1269 | variablesdontexport
1270 |
1271 | version
1272 | 2.3
1273 | webaddress
1274 | https://github.com/giovannicoppola/paperpAlfred
1275 |
1276 |
1277 |
--------------------------------------------------------------------------------