using JSON, BibTeX # Downloading using URIParser, HTTP #= Mapping between zotero and BibTeX. =# # https://github.com/retorquere/zotero-better-bibtex/blob/master/translators/Better%20BibLaTeX.ts "Map zotero reference types to bibtex" const zotero_type_map = Dict( "artwork" => "artwork", "audioRecording" => "audio", "bill" => "legislation", "blogPost" => "online", "book" => "book", "bookSection" => "incollection", "case" => "jurisdiction", "computerProgram" => "software", "conferencePaper" => "inproceedings", "dictionaryEntry" => "inreference", "document" => "misc", "email" => "letter", "encyclopediaArticle" => "misc", # "inreference", "film" => "movie", "forumPost" => "online", "hearing" => "jurisdiction", "instantMessage" => "misc", "interview" => "misc", "journalArticle" => "article", "letter" => "letter", "magazineArticle" => "article", "manuscript" => "unpublished", "map" => "misc", "newspaperArticle" => "article", "patent" => "patent", "podcast" => "audio", "presentation" => "unpublished", "radioBroadcast" => "audio", "report" => "report", "statute" => "legislation", "thesis" => "thesis", "tvBroadcast" => "video", "videoRecording" => "video", "webpage" => "online" ) # , subtype: "magazine"}, # , subtype: "newspaper"}, "Map zotero fields to bibtex fields" const zotero_field_map = Dict( "title" => "title", "pages" => "pages", "DOI" => "DOI", "ISSN" => "ISSN", "publisher" => "publisher", "publicationTitle" => "journal", "date" => "year", "volume" => "volume", "issue" => "number" # FIXME: ADD TAGS? ) "Parses the `creatorType` field and creates a string with authors" function creatortoauthor(c::Dict) ctype = c["creatorType"] if ctype != "author" error("Add this creator type($(ctype)) to the supported ones!") end """{$(c["lastName"]), $(c["firstName"])}""" end "Takes all zotero creators, converts and join them" authors(c) = join(creatortoauthor.(c), " and ") #= Functions used to interface with the zotero connector =# """Creates the response by using the response body `res`, the default `headers` and the headers to add (`extraheaders`). Defines the hook `set_headers` (before setting them) and `post_headers_merge` (before sending the response) """ function setzoteroheaders(res, extraheaders::Dict) @hook :set_headers newheaders = merge(headers, extraheaders) @hook :post_headers_merge (newheaders,) Dict(:headers => newheaders, :body => res) end """ The zotero connector sometimes pings the program to see if we are listening (e.g., before sending the data). This must support both GET and POST. Defines the hook `ping` """ function pong(app) @hook :ping if app[:method] == "POST" headers = Dict("Content-Type" => "application/json") # FIXME: get(libraries[currentlibrary],"autosnapshot", false) response = json(Dict("prefs" => Dict("automaticSnapshots" => false ))) else headers = Dict("Content-Type" => "text/html") response = """ Zotero Connector Server is Available Zotero Connector Server is Available""" end response, headers end """ Defines the hook `get_collection` """ function collection(any, libraries, currentlibrary) @hook :get_collection libraryName = "developing" json(Dict( "libraryID" => 1, "libraryName" => "default", "libraryEditable" => !libraries[currentlibrary]["readonly"], "editable" => true, # collection-level parameters "id" => 1, # collection-level "name" => currentlibrary )), Dict("Content-Type" => "application/json") end """ Save page snapshot. Defines the hooks `pre_save_snapshot` and `post_save_snapshot`. """ function savesnapshot(req, libraries, currentlibrary) @hook :pre_save_snapshot if libraries[currentlibrary]["readonly"] warn("Library is readonly") return "no", Dict() end parsed = JSON.parse(String(req[:data])) if !parsed["skipSnapshot"] info("Saving page snapshot") open("./devel/snapshot.html", "w") do f write(f, parsed["html"]) end else info("NOT saving page snapshot") end @hook :post_save_snapshot "savesnapshot", Dict() end """ Download (asynchronously) all the attachments. Defines `pre_get_attachment` and `post_get_attachment` """ function getattachment(id, attachment) @hook :pre_get_attachment title = attachment["title"] url = attachment["url"] mime = nothing if "mimeType" in keys(attachment) mime = MIME(attachment["mimeType"]) end name = basename(URI(url).path) name == "" && (name = title * ".html") path = expanduser(libraries[currentlibrary]["path"]) mkpath("$path/$id") file = "$path/$id/$name" try # FIXME: we should tell zotero-connector which is a success and which isn't @spawn HTTP.open("GET", url) do resource open(file, "w") do file write(file, resource) end @hook :post_get_attachment end end file end function saveitems(req::Dict, libraries, currentlibrary) @hook :pre_receive if libraries[currentlibrary]["readonly"] warn("Library is readonly") return "no", Dict{String,String}("status"=> string(403)) end parsed = JSON.parse(String(req[:data])) foreach(i -> addtobibliography!(bibliography, i...), parseitem.(parsed["items"])) @hook(:after_receive,parsed) "ok", Dict{String,String}("status"=> string(201)) end function parseitem(item::Dict) tp = zotero_type_map[item["itemType"]] data = BibTeX.Citation{Symbol(tp)}() data["author"] = authors(item["creators"]) for k in keys(item) if k in keys(zotero_field_map) data[zotero_field_map[k]] = item[k] end end id = createcitekey(data) filenames = getattachment.(id, item["attachments"]) data["file"] = join(filenames, "; ") try (id,data) catch warn("Type "* item["itemType"] * " does not exists!") (nothing, nothing) end end