Current section
Files
Jump to
Current section
Files
lib/checker/checker.ex
defmodule BibtexParser.Checker do
@moduledoc false
def check(entries) when is_list(entries) do
errors = []
# Duplicate labels and titles.
errors = duplicate(entries, & &1.label, :duplicate_labels) ++ errors
errors = duplicate(entries, &Keyword.get(&1.tags, :title, nil), :duplicate_titles) ++ errors
errors = duplicate(entries, &Keyword.get(&1.tags, :url, nil), :duplicate_urls) ++ errors
errors = duplicate(entries, &Keyword.get(&1.tags, :doi, nil), :duplicate_dois) ++ errors
# Check individual entries.
errors =
entries
|> Enum.reduce(errors, fn entry, errors ->
case check(entry) do
[] ->
errors
errs ->
errors ++ [{entry.label, errs}]
end
end)
errors
end
def check(entry) do
errors = []
errors = errors ++ empty_tags(entry)
errors = errors ++ abbreviated_titles(entry)
errors = errors ++ missing_tags(entry)
errors
end
#############################################################################
# Global file checks.
defp duplicate(entries, lens, error) do
values =
entries
|> Enum.flat_map(fn entry ->
case lens.(entry) do
nil ->
[]
v ->
[v]
end
end)
uniques = values |> Enum.uniq()
dupes = (values -- uniques) |> Enum.uniq()
case dupes do
[] ->
[]
ds ->
[{error, ds}]
end
end
#############################################################################
# Individual entry checks.
defp empty_tags(entry) do
empty_tags =
entry.tags
|> Enum.filter(fn {_tag, content} ->
content == ""
end)
|> Enum.map(fn {tag, _} -> tag end)
case empty_tags do
[] ->
[]
ts ->
[empty_tags: ts]
end
end
defp abbreviated_titles(entry) do
# Ensure that if there is a title, journal, .. it has no flawed titles (i.e., a dot in the name or something.)
if Keyword.has_key?(entry.tags, :journal) and
Regex.match?(~r/\./, Keyword.get(entry.tags, :journal)) do
[abbreviated_journal_title: Keyword.get(entry.tags, :journal)]
else
[]
end
end
def missing_tags(entry) do
required = %{
"inproceedings" => ["author", "booktitle", "pages", "publisher", "title", "year"],
"article" => ["author", "journal", "number", "pages", "title", "volume", "year"],
"techreport" => ["author", "institution", "title", "year"],
"incollection" => ["author", "booktitle", "pages", "publisher", "title", "year"],
"book" => ["author", "publisher", "title", "year"],
"inbook" => ["author", "booktitle", "pages", "publisher", "title", "year"],
"proceedings" => ["editor", "publisher", "title", "year"],
"phdthesis" => ["author", "school", "title", "year"],
"mastersthesis" => ["author", "school", "title", "year"],
"electronic" => ["author", "title", "url", "year"],
"misc" => ["author", "howpublished", "title", "year"]
}
required_for_entry = Map.get(required, entry.type, [])
tags = entry.tags |> Enum.map(fn {k, _v} -> Atom.to_string(k) end)
case required_for_entry -- tags do
[] ->
[]
missing ->
[missing_tags: missing]
end
end
end