-
Notifications
You must be signed in to change notification settings - Fork 5
Add support for OAI-PMH 2.0 #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # encoding: ISO-8859-1 with UTF-8 escapes | ||
| # This file allows to configure the OAI-PMH metadata parsing. See OaiPmh2.scala | ||
|
|
||
| # Marker string used to find beginning of an history section in the <description> tags | ||
| historyMarker=Entry procedures: | ||
| # GLAM institution | ||
| institution=City Archives of Springfield | ||
| # Filename prefix stripped to find GLAM reference from filename | ||
| prefix=FOO1234_ | ||
| # Name of the fonds being imported | ||
| fonds=John Smith Fonds | ||
| # Description language | ||
| lang=en | ||
| # You can define any property to replace artist names by a mediawiki template | ||
| SMITH,_John_(10/10/1875-10/10/1931)._Author={{Creator:John Smith}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,7 +22,7 @@ trait ImageUICallback { | |
| } | ||
|
|
||
| /** a data editor with a thumbnail preview for an image File */ | ||
| final class ImageUI(file:File, icon:Option[Icon], thumbnailMaxSize:Int, programHeading:String, programIcon:Image, callback:ImageUICallback) extends JPanel { | ||
| final class ImageUI(file:File, oaipmh:Vector[OaiPmh2], icon:Option[Icon], thumbnailMaxSize:Int, programHeading:String, programIcon:Image, callback:ImageUICallback) extends JPanel { | ||
| private val thumbDimension = new Dimension(thumbnailMaxSize, thumbnailMaxSize) | ||
|
|
||
| private var uploadSuccessful:Option[Boolean] = None | ||
|
|
@@ -155,20 +155,35 @@ final class ImageUI(file:File, icon:Option[Icon], thumbnailMaxSize:Int, programH | |
|
|
||
| // BETTER move unparsers and parsers together | ||
|
|
||
| private val exif = EXIF extract file | ||
| private val exifDate = exif.date cata ("", _ format "yyyy-MM-dd HH:mm:ss") | ||
| private val exifGPS = exif.gps cata ("", it => it.latitude.toString + "," + it.longitude.toString) | ||
| private val exifHeading = exif.heading cata ("", _.toString) | ||
| private val exifDesc = exif.description getOrElse "" | ||
| private val fixedName = Filename fix file.getName | ||
|
|
||
| uploadEditor setSelected false | ||
| nameEditor setText fixedName | ||
| descriptionEditor setText exifDesc | ||
| dateEditor setText exifDate | ||
| coordinatesEditor setText exifGPS | ||
| headingEditor setText exifHeading | ||
| categoriesEditor setText "" | ||
| private def getExif:ImageData = { | ||
| val exif = EXIF extract file | ||
| ImageData( | ||
| file, | ||
| false, | ||
| Filename fix file.getName, | ||
| exif.description getOrElse "", | ||
| exif.date cata ("", _ format "yyyy-MM-dd HH:mm:ss"), | ||
| exif.gps cata ("", it => it.latitude.toString + "," + it.longitude.toString), | ||
| exif.heading cata ("", _.toString), | ||
| "" | ||
| ) | ||
| } | ||
|
|
||
| def loadImageData(data:ImageData) { | ||
| uploadEditor setSelected data.upload | ||
| nameEditor setText data.name | ||
| descriptionEditor setText data.description | ||
| dateEditor setText data.date | ||
| coordinatesEditor setText data.coordinates | ||
| headingEditor setText data.heading | ||
| categoriesEditor setText data.categories | ||
| } | ||
|
|
||
| private val exifData:ImageData = getExif | ||
don-vip marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // initialize from EXIF data, if any | ||
| loadImageData(exifData) | ||
| // initialize from OAI-PMH data, if any | ||
| oaipmh.map(_.getImageData(exifData)).filter(_.isDefined).map(_.map(loadImageData)) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this looks wrong:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I replaced it by a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I pushed a commit that solves most of the issues. The last ones I fail to answer them, especially this one. |
||
|
|
||
| // BETTER could be a trait | ||
| override def getMaximumSize():Dimension = | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| package commonist.util | ||
|
|
||
| import commonist.data._ | ||
|
|
||
| import scala.xml._ | ||
|
|
||
| import scutil.log._ | ||
|
|
||
| /** | ||
| * Parse and extract image metadata from OAI-PMH 2.0 files. | ||
| * See www.openarchives.org/OAI/openarchivesprotocol.html | ||
| */ | ||
| class OaiPmh2(doc:Elem, props:Map[String,String]) extends Logging { | ||
|
|
||
| /** Extract text from node and fix badly formatted XML strings (escaped twice) **/ | ||
| private def text(e:NodeSeq) = { | ||
| e.map(_.text).mkString("\n").trim() | ||
| .replaceAll("&", "&").replaceAll(""", "\"").replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">") | ||
| } | ||
|
|
||
| /** | ||
| * Formats name as follows: "filename_without_ext - title.ext" | ||
| * Makes sure we don't have two consecutive dots if title ends by a dot | ||
| */ | ||
| private def formatName(name:String, title:String):String = { | ||
| name.replaceFirst("[.][^.]+$", "") + " - " + title + name.substring(name.lastIndexOf(".")).replaceAll("\\.\\.", ".") | ||
| } | ||
|
|
||
| /** Attempt to get a nicer artist value from properties, otherwise return raw creator */ | ||
| private def artist(creator:String):String = { | ||
| val prop = creator.replaceAll(" ", "_") | ||
| props get prop getOrElse creator | ||
| } | ||
|
|
||
| /** Detect public domain mention in various languages, otherwise return raw rights */ | ||
| private def permission(rights:String):String = { | ||
| val lowercase = rights.toLowerCase() | ||
| if (lowercase.contains("public domain") | ||
| || lowercase.contains("domaine public") | ||
| || lowercase.contains("gemeinfreiheit") | ||
| || lowercase.contains("dominio público") | ||
| ) | ||
| "{{PD-old|PD-70}}" | ||
| else rights | ||
| } | ||
|
|
||
| /** Detect photographs from format, otherwise return raw format */ | ||
| private def medium(format:String):String = { | ||
| val lowercase = format.toLowerCase() | ||
| if (lowercase.contains("photo") || lowercase.contains("foto")) | ||
| "{{Technique|photograph}}" | ||
| else format | ||
| } | ||
|
|
||
| /** Detect size in centimeters, otherwise return raw format */ | ||
| private def dimensions(format:String):String = { | ||
| ".*; (\\d+) x (\\d+) cm ;.*".r.findAllMatchIn(format).foreach { m => | ||
| return "{{Size|unit=cm|height=" + m.group(1) + "|width=" + m.group(2) + "}}" | ||
| } | ||
| "" | ||
| } | ||
|
|
||
| /** localize given string according to language defined in properties */ | ||
| private def localized(text:String):String = { | ||
| if (text.nonEmpty) | ||
| "{{" + props("lang") + "|" + text + "}}" | ||
| else text | ||
| } | ||
|
|
||
| /** Artwork description */ | ||
| private def artwork(dc:Node, filenameWithOutExt:String):String = { | ||
| val historyMarker = props("historyMarker") | ||
| val institution = props("institution") | ||
| val prefix = props("prefix") | ||
| val fonds = props("fonds") | ||
|
|
||
| val id = filenameWithOutExt.replaceAll(prefix, "") | ||
| val fullDescription = text(dc \ "description") | ||
| val historyIndex = fullDescription.indexOf(historyMarker) | ||
| val objectHistory = if (historyIndex >= 0) fullDescription.substring(historyIndex + historyMarker.length).trim() else "" | ||
| val description = if (historyIndex >= 0) fullDescription.substring(0, historyIndex).trim() else fullDescription | ||
| val format = text(dc \ "format").replaceAll("image/jpeg", "").trim() | ||
|
|
||
| "{{Artwork\n" + | ||
| "|ID={{" + institution + " - FET link|" + id + "}}\n" + | ||
| "|artist=" + artist(text(dc \ "creator")) + "\n" + | ||
| "|credit line=\n" + | ||
| "|date=" + text(dc \ "date") + "\n" + | ||
| "|location=\n" + | ||
| "|description=" + localized(description) + "\n" + | ||
| "|dimensions=" + dimensions(format) + "\n" + | ||
| "|gallery={{Institution:" + institution + "}}\n" + | ||
| "|medium=" + medium(format) + "\n" + | ||
| "|object history=" + localized(objectHistory) + "\n" + | ||
| "|permission=" + permission(text(dc \ "rights")) + "\n" + | ||
| "|references=\n" + | ||
| "|source={{" + fonds + " - " + institution + "}}\n" + | ||
| "|title=" + localized(text(dc \ "title")) + "\n" + | ||
| "}}", | ||
| } | ||
|
|
||
| /** Fills image metadata if found via its filename */ | ||
| def getImageData(data:ImageData):Option[ImageData] = { | ||
| val filenameWithOutExt = data.file.getName().replaceFirst("[.][^.]+$", ""); | ||
| val list:List[Node] = (doc \\ "dc").find(dc => text(dc \ "relation").contains(filenameWithOutExt + ".")).toList | ||
| if (list.size == 1) { | ||
| val dc:Node = list(0) | ||
|
|
||
| Some(ImageData( | ||
| data.file, | ||
| data.upload, | ||
| // "filename - title.ext" | ||
| formatName(data.name, text(dc \ "title")), | ||
| // {{Artwork}} description | ||
| artwork(dc, filenameWithOutExt), | ||
| text(dc \ "date"), | ||
| data.coordinates, | ||
| data.heading, | ||
| data.categories | ||
| )) | ||
| } else if (list.size > 1) { | ||
| WARN("Found several records for " + filenameWithOutExt) | ||
| Option.empty | ||
| } else { | ||
| Option.empty | ||
| } | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.